ADded fusion and new SDK

This commit is contained in:
Hazim Bin Ijaz 2025-09-19 19:43:49 +05:00
parent 1b12c8ab46
commit 344c0cdde3
6203 changed files with 1651765 additions and 2579502 deletions

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

6
.idea/.idea.BulletHell/.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: ed70b82c28b9c144cb980dfa5702e03b
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/RewardDailyEntry.prefab
uploadId: 777582

View File

@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 6fda033c0738a634eb415877fd65058d
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/RewardNewEntry.prefab
uploadId: 777582

View File

@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DailyReward 1
m_EditorClassIdentifier:
rewardId: reward1
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 7a278160a6c7f8544a3768e0bcc4553d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Rewards/DailyReward 1.asset
uploadId: 777582

View File

@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DailyReward 10
m_EditorClassIdentifier:
rewardId: reward10
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 500 diamonds
descriptionTranslated: []
rewardType: 0
amount: 500
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 06c0221dd1e929c47b05ee18757fe798
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Rewards/DailyReward 10.asset
uploadId: 777582

View File

@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DailyReward 2
m_EditorClassIdentifier:
rewardId: reward2
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 7bb80a829634a834f9581e598abec835
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Rewards/DailyReward 2.asset
uploadId: 777582

View File

@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DailyReward 3
m_EditorClassIdentifier:
rewardId: reward3
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 8772999151e122244a4b4e1e0740ae46
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Rewards/DailyReward 3.asset
uploadId: 777582

View File

@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DailyReward 4
m_EditorClassIdentifier:
rewardId: reward4
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 425913de335469442b2439a25a78a172
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Rewards/DailyReward 4.asset
uploadId: 777582

View File

@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DailyReward 5
m_EditorClassIdentifier:
rewardId: reward5
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 69da3ac32b731d2488c0f41a860c1498
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Rewards/DailyReward 5.asset
uploadId: 777582

View File

@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DailyReward 6
m_EditorClassIdentifier:
rewardId: reward6
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 115596442aeedba44a43288cfc2796d5
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Rewards/DailyReward 6.asset
uploadId: 777582

View File

@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DailyReward 7
m_EditorClassIdentifier:
rewardId: reward7
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: f4641227f3470f34c9a62b08d0dc99a4
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Rewards/DailyReward 7.asset
uploadId: 777582

View File

@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DailyReward 8
m_EditorClassIdentifier:
rewardId: reward8
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 6698d9bc522453a42a9edccae0d42f1f
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Rewards/DailyReward 8.asset
uploadId: 777582

View File

@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DailyReward 9
m_EditorClassIdentifier:
rewardId: reward9
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 2a1b7ad7800274e42b157224b6e73e6a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Rewards/DailyReward 9.asset
uploadId: 777582

View File

@ -0,0 +1,367 @@
using System;
using UnityEngine;
using static BulletHellTemplate.PlayerSave;
namespace BulletHellTemplate
{
/// <summary>
/// Displays and claims **Daily Rewards** using a *local-first, server-confirmed* flow.
///
/// Workflow:
/// 1. UI is built from locally cached <see cref="DailyRewardsData"/> (PlayerSave).
/// 2. When the player presses "Claim", minimal local validation runs (already claimed? already today? correct sequence?).
/// 3. If local checks pass, a server claim is sent via <see cref="BackendManager.Service"/>.
/// 4. On server success, reward is applied (inside the backend claim extension) and local cache is updated.
/// 5. UI refreshes.
///
/// This approach reduces server calls while keeping the server authoritative over the final grant.
/// </summary>
public sealed class DailyRewardManager : MonoBehaviour
{
/*────────────────────────── Inspector ──────────────────────────*/
[Header("UI Prefabs & Containers")]
[Tooltip("Prefab used to display each daily reward entry.")]
public RewardEntry rewardEntryPrefab;
[Tooltip("Parent transform that will hold all spawned daily reward entries.")]
public Transform rewardsContainer;
[Tooltip("Popup shown when a reward is successfully claimed.")]
public RewardPopup rewardPopup;
/*────────────────────────── Private State ──────────────────────*/
private DailyRewardsData localDailyData;
private RewardItem[] rewardItems;
private string currentLang;
// Cached flags used when building UI
private bool _alreadyClaimedToday;
private int _nextIndex; // index expected to be claimed next (sequential)
private int _daysSinceFirstClaim; // used only for visual gating
/*────────────────────────── Unity Events ───────────────────────*/
private void OnEnable()
{
currentLang = LanguageManager.LanguageManager.Instance.GetCurrentLanguage();
rewardItems = GameInstance.Singleton.dailyRewardItems;
// Pull local cache
localDailyData = PlayerSave.GetDailyRewardsLocal();
// Optionally perform a lazy local reset if the cycle completed AND at least 1 new day has passed.
TryLocalCycleResetIfNeeded();
// Precompute visual gating values
_alreadyClaimedToday = localDailyData.lastClaimDate != DateTime.MinValue &&
localDailyData.lastClaimDate.Date == DateTime.Now.Date;
_nextIndex = localDailyData.claimedRewards.Count;
if (localDailyData.firstClaimDate != DateTime.MinValue)
{
_daysSinceFirstClaim = Mathf.Max(
0,
(int)(DateTime.Now.Date - localDailyData.firstClaimDate.Date).TotalDays
);
}
else
{
_daysSinceFirstClaim = 0;
}
BuildUI();
}
/*────────────────────────── UI Build ───────────────────────────*/
/// <summary>
/// Spawns one entry per configured daily reward and wires up claim buttons
/// according to the current local state.
/// </summary>
private void BuildUI()
{
// Clear previous children
if (rewardsContainer != null)
{
for (int i = rewardsContainer.childCount - 1; i >= 0; --i)
Destroy(rewardsContainer.GetChild(i).gameObject);
}
if (rewardItems == null || rewardItems.Length == 0)
{
Debug.LogWarning("[DailyRewardManager] No daily rewards configured.");
return;
}
for (int i = 0; i < rewardItems.Length; i++)
{
var def = rewardItems[i];
RewardEntry entry = Instantiate(rewardEntryPrefab, rewardsContainer);
// Localized display strings
string title = GetTranslatedString(def.titleTranslated, def.title, currentLang);
string description = GetTranslatedString(def.descriptionTranslated, def.description, currentLang);
// Display day label (1-based for player clarity)
entry.Setup(
def.icon,
title,
description,
$"Day {i + 1}",
i + 1
);
// State assignment
if (localDailyData.claimedRewards.Contains(i))
{
entry.SetClaimed();
continue;
}
// If already claimed today, everything else is locked
if (_alreadyClaimedToday)
{
entry.SetLocked();
continue;
}
// Sequential gating: allow claim only for the next expected index
if (i == _nextIndex && i <= _daysSinceFirstClaim)
{
int capture = i; // closure
entry.EnableClaimButton(() => OnClaimButtonPressed(capture));
}
else
{
entry.SetLocked();
}
}
}
/*────────────────────────── Claim Flow ─────────────────────────*/
/// <summary>
/// Called by UI button; runs local validation and (if passed) performs an async server claim.
/// </summary>
private async void OnClaimButtonPressed(int dayIndex)
{
// Fast local validation; reduces unnecessary server requests.
if (!LocalCanClaim(dayIndex, out var failCode))
{
HandleClaimFail(failCode, dayIndex, isPreServer: true);
return;
}
// Build a temporary BattlePassItem to describe reward for local application & popup visuals.
RewardItem rewardDef = rewardItems[dayIndex];
BattlePassItem bpItem = BuildBattlePassMirror(rewardDef);
// Perform server-authorized claim.
RequestResult result = await BackendManager.Service.ClaimDailyRewardAsync(dayIndex, bpItem);
if (!result.Success)
{
HandleClaimFail(result.Reason, dayIndex, isPreServer: false);
return;
}
// Update local cache on success
if (!localDailyData.claimedRewards.Contains(dayIndex))
localDailyData.claimedRewards.Add(dayIndex);
if (localDailyData.claimedRewards.Count == 1)
localDailyData.firstClaimDate = DateTime.Now.Date;
localDailyData.lastClaimDate = DateTime.Now.Date;
PlayerSave.SetDailyRewardsLocal(localDailyData);
// Show popup (optional)
if (rewardPopup != null)
{
string title = GetTranslatedString(rewardDef.titleTranslated, rewardDef.title, currentLang);
string description = GetTranslatedString(rewardDef.descriptionTranslated, rewardDef.description, currentLang);
rewardPopup.Setup(rewardDef.icon, title, description);
}
// Rebuild UI with updated data
OnEnable(); // quick refresh
}
/// <summary>
/// Minimal local validation: already claimed? already claimed today? correct sequence?
/// failCode: "0" (already) / "1" (not available) / null (ok).
/// </summary>
private bool LocalCanClaim(int dayIndex, out string failCode)
{
failCode = null;
int total = rewardItems?.Length ?? 0;
if (dayIndex < 0 || dayIndex >= total)
{
failCode = "1";
return false;
}
if (localDailyData.claimedRewards.Contains(dayIndex))
{
failCode = "0";
return false;
}
if (localDailyData.lastClaimDate != DateTime.MinValue &&
localDailyData.lastClaimDate.Date == DateTime.Now.Date)
{
failCode = "0"; // already claimed today
return false;
}
int expected = localDailyData.claimedRewards.Count;
if (dayIndex > expected)
{
failCode = "1"; // trying to skip days
return false;
}
return true;
}
/// <summary>
/// Handles failed claim attempts (local or server) with consistent logging.
/// </summary>
private void HandleClaimFail(string reasonCode, int dayIndex, bool isPreServer)
{
string prefix = isPreServer ? "[DailyRewardManager][Local]" : "[DailyRewardManager][Server]";
switch (reasonCode)
{
case "0":
Debug.LogWarning($"{prefix} Day {dayIndex + 1} already claimed.");
break;
case "1":
Debug.LogWarning($"{prefix} Day {dayIndex + 1} not available yet.");
break;
default:
Debug.LogWarning($"{prefix} Claim failed (code:{reasonCode}).");
break;
}
}
/*────────────────────────── Reward Construction ─────────────────*/
/// <summary>
/// Builds a minimal <see cref="BattlePassItem"/> mirror from a <see cref="RewardItem"/> definition.
/// Used only to route through shared rewardapply code paths.
/// </summary>
private BattlePassItem BuildBattlePassMirror(RewardItem reward)
{
BattlePassItem temp = ScriptableObject.CreateInstance<BattlePassItem>();
temp.passId = "DailyReward_" + reward.rewardId;
temp.itemTitle = reward.title;
temp.itemDescription = reward.description ?? "Daily reward.";
temp.itemIcon = reward.icon;
temp.rewardTier = BattlePassItem.RewardTier.Free;
switch (reward.rewardType)
{
case RewardType.Currency:
temp.rewardType = BattlePassItem.RewardType.CurrencyReward;
temp.currencyReward = new CurrencyReward
{
currency = reward.currencyRewards[0],
amount = reward.amount
};
break;
case RewardType.Icon:
temp.rewardType = BattlePassItem.RewardType.IconReward;
temp.iconReward = reward.iconRewards[0];
break;
case RewardType.Frame:
temp.rewardType = BattlePassItem.RewardType.FrameReward;
temp.frameReward = reward.frameRewards[0];
break;
case RewardType.Character:
temp.rewardType = BattlePassItem.RewardType.CharacterReward;
temp.characterData = new CharacterData[] { reward.characterRewards[0] };
break;
case RewardType.InventoryItem:
temp.rewardType = BattlePassItem.RewardType.InventoryItemReward;
temp.inventoryItems = new InventoryItem[] { reward.inventoryItems[0] };
break;
}
return temp;
}
/*────────────────────────── Helpers ─────────────────────────────*/
/// <summary>
/// For UX: after a full cycle is completed, clear local progress if at least one new calendar day has passed.
/// This does NOT contact the server; server still validates claims.
/// </summary>
private void TryLocalCycleResetIfNeeded()
{
int total = GameInstance.Singleton.dailyRewardItems?.Length ?? 0;
if (total <= 0) return;
if (localDailyData == null)
localDailyData = PlayerSave.GetDailyRewardsLocal();
// Completed cycle?
if (localDailyData.claimedRewards.Count < total)
return;
// Wait at least one day after the last claim before resetting locally.
if (localDailyData.lastClaimDate == DateTime.MinValue)
return;
if ((DateTime.Now.Date - localDailyData.lastClaimDate.Date).TotalDays < 1d)
return;
localDailyData.claimedRewards.Clear();
localDailyData.firstClaimDate = DateTime.Now.Date;
localDailyData.lastClaimDate = DateTime.MinValue;
PlayerSave.SetDailyRewardsLocal(localDailyData);
}
/// <summary>
/// Returns a translated string (title) or the fallback if not found.
/// </summary>
public string GetTranslatedString(NameTranslatedByLanguage[] translations, string fallback, string lang)
{
if (translations != null)
{
foreach (var t in translations)
{
if (!string.IsNullOrEmpty(t.LanguageId) &&
t.LanguageId.Equals(lang, StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrEmpty(t.Translate))
return t.Translate;
}
}
return fallback;
}
/// <summary>
/// Returns a translated string (description) or the fallback if not found.
/// </summary>
public string GetTranslatedString(DescriptionTranslatedByLanguage[] translations, string fallback, string lang)
{
if (translations != null)
{
foreach (var t in translations)
{
if (!string.IsNullOrEmpty(t.LanguageId) &&
t.LanguageId.Equals(lang, StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrEmpty(t.Translate))
return t.Translate;
}
}
return fallback;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 380ed0821da70594d9810d7ef704eda5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Scripts/DailyRewardManager.cs
uploadId: 777582

View File

@ -0,0 +1,351 @@
using System;
using UnityEngine;
using static BulletHellTemplate.PlayerSave;
namespace BulletHellTemplate
{
/// <summary>
/// Displays and claims **New Player Rewards** with a *one-claim-per-day* rule.
///
/// Design:
/// • Rewards unlock over time based on days since account creation.
/// • Player may fall behind (missed logins) but can only claim **one** unclaimed day per calendar day.
/// (Catch-up over multiple days.)
/// • UI is built from locally cached <see cref="NewPlayerRewardsData"/>.
/// • On claim click → local validation → server validation → local cache update → UI refresh.
/// </summary>
public sealed class NewPlayerRewardManager : MonoBehaviour
{
/*────────────────────────── Inspector ──────────────────────────*/
[Header("UI Prefabs & Containers")]
[Tooltip("Prefab used to display each new-player reward entry.")]
public RewardEntry rewardEntryPrefab;
[Tooltip("Parent transform that will hold all spawned new-player reward entries.")]
public Transform rewardsContainer;
[Tooltip("Popup shown when a new-player reward is successfully claimed.")]
public RewardPopup rewardPopup;
[Header("Settings")]
[Tooltip("Optional hard cap for new-player reward days. If <=0, GameInstance data length is used.")]
public int totalRewardDaysOverride = 0;
/*────────────────────────── Private State ──────────────────────*/
private NewPlayerRewardsData localNewPlayerData;
private RewardItem[] rewardItems;
private string currentLang;
// Cached per-enable state
private bool _alreadyClaimedToday;
private int _daysSinceCreation;
private int _nextUnclaimedIndex; // sequential catch-up target
/*────────────────────────── Unity Events ───────────────────────*/
private void OnEnable()
{
currentLang = LanguageManager.LanguageManager.Instance.GetCurrentLanguage();
rewardItems = GameInstance.Singleton.newPlayerRewardItems;
// Pull local cache
localNewPlayerData = PlayerSave.GetNewPlayerRewardsLocal();
// Days since account creation
if (localNewPlayerData.accountCreationDate != DateTime.MinValue)
{
_daysSinceCreation = Mathf.Max(
0,
(int)(DateTime.Now.Date - localNewPlayerData.accountCreationDate.Date).TotalDays
);
}
else
{
_daysSinceCreation = 0;
}
// 1/day gating: already claimed today?
_alreadyClaimedToday = localNewPlayerData.lastClaimDate != DateTime.MinValue &&
localNewPlayerData.lastClaimDate.Date == DateTime.Now.Date;
// Next unclaimed reward index (sequential catch-up)
_nextUnclaimedIndex = localNewPlayerData.claimedRewards.Count;
BuildUI();
}
/*────────────────────────── UI Build ───────────────────────────*/
/// <summary>
/// Spawns UI entries for each configured new-player reward and enables the claim
/// button only on the **next unclaimed** day (if unlocked by time and not claimed today).
/// </summary>
private void BuildUI()
{
// Clear previous
if (rewardsContainer != null)
{
for (int i = rewardsContainer.childCount - 1; i >= 0; --i)
Destroy(rewardsContainer.GetChild(i).gameObject);
}
if (rewardItems == null || rewardItems.Length == 0)
{
Debug.LogWarning("[NewPlayerRewardManager] No new-player rewards configured.");
return;
}
int totalCap = totalRewardDaysOverride > 0
? Mathf.Min(totalRewardDaysOverride, rewardItems.Length)
: rewardItems.Length;
for (int i = 0; i < totalCap; i++)
{
var def = rewardItems[i];
RewardEntry entry = Instantiate(rewardEntryPrefab, rewardsContainer);
// Localized UI text
string title = GetTranslatedString(def.titleTranslated, def.title, currentLang);
string description = GetTranslatedString(def.descriptionTranslated, def.description, currentLang);
entry.Setup(
def.icon,
title,
description,
$"Day {i + 1}",
i + 1
);
// Already claimed?
if (localNewPlayerData.claimedRewards.Contains(i))
{
entry.SetClaimed();
continue;
}
// Locked because already claimed today's new-player reward?
if (_alreadyClaimedToday)
{
entry.SetLocked();
continue;
}
// Only the NEXT unclaimed reward can be claimed today (catch-up)
if (i == _nextUnclaimedIndex && i <= _daysSinceCreation)
{
int capture = i;
entry.EnableClaimButton(() => OnClaimButtonPressed(capture));
}
else
{
entry.SetLocked();
}
}
}
/*────────────────────────── Claim Flow ─────────────────────────*/
/// <summary>
/// Triggered by UI button; runs local validation then performs server claim.
/// </summary>
private async void OnClaimButtonPressed(int dayIndex)
{
if (!LocalCanClaim(dayIndex, out var failCode))
{
HandleClaimFail(failCode, dayIndex, isPreServer: true);
return;
}
// Build a BattlePass mirror so we can reuse reward-apply logic
RewardItem rewardDef = rewardItems[dayIndex];
BattlePassItem bpItem = BuildBattlePassMirror(rewardDef);
// Server validation + grant
RequestResult result = await BackendManager.Service.ClaimNewPlayerRewardAsync(dayIndex, bpItem);
if (!result.Success)
{
HandleClaimFail(result.Reason, dayIndex, isPreServer: false);
return;
}
// Update local cache
if (!localNewPlayerData.claimedRewards.Contains(dayIndex))
localNewPlayerData.claimedRewards.Add(dayIndex);
localNewPlayerData.lastClaimDate = DateTime.Now.Date;
PlayerSave.SetNewPlayerRewardsLocal(localNewPlayerData);
// Popup
if (rewardPopup != null)
{
string title = GetTranslatedString(rewardDef.titleTranslated, rewardDef.title, currentLang);
string description = GetTranslatedString(rewardDef.descriptionTranslated, rewardDef.description, currentLang);
rewardPopup.Setup(rewardDef.icon, title, description);
}
// Refresh UI
OnEnable();
}
/// <summary>
/// Minimal local gating for *one-per-day* catch-up.
/// failCode: "0" (already) / "1" (not available) / null (ok).
/// </summary>
private bool LocalCanClaim(int dayIndex, out string failCode)
{
failCode = null;
int totalCap = totalRewardDaysOverride > 0
? Mathf.Min(totalRewardDaysOverride, rewardItems.Length)
: rewardItems.Length;
// Range
if (dayIndex < 0 || dayIndex >= totalCap)
{
failCode = "1";
return false;
}
// Already claimed?
if (localNewPlayerData.claimedRewards.Contains(dayIndex))
{
failCode = "0";
return false;
}
// Already claimed a new-player reward today?
if (localNewPlayerData.lastClaimDate != DateTime.MinValue &&
localNewPlayerData.lastClaimDate.Date == DateTime.Now.Date)
{
failCode = "0"; // already today
return false;
}
// Sequential catch-up: must be the next unclaimed index
int expected = localNewPlayerData.claimedRewards.Count;
if (dayIndex != expected)
{
failCode = "1";
return false;
}
// Must be unlocked by time
if (dayIndex > _daysSinceCreation)
{
failCode = "1";
return false;
}
return true;
}
/// <summary>
/// Handles failed claim attempts (local or server) with consistent logging.
/// </summary>
private void HandleClaimFail(string reasonCode, int dayIndex, bool isPreServer)
{
string prefix = isPreServer ? "[NewPlayerRewardManager][Local]" : "[NewPlayerRewardManager][Server]";
switch (reasonCode)
{
case "0":
Debug.LogWarning($"{prefix} Day {dayIndex + 1} already claimed.");
break;
case "1":
Debug.LogWarning($"{prefix} Day {dayIndex + 1} not available yet.");
break;
default:
Debug.LogWarning($"{prefix} Claim failed (code:{reasonCode}).");
break;
}
}
/*────────────────────────── Reward Construction ─────────────────*/
/// <summary>
/// Builds a minimal <see cref="BattlePassItem"/> mirror from a <see cref="RewardItem"/> definition.
/// Used only to route through shared rewardapply code paths.
/// </summary>
private BattlePassItem BuildBattlePassMirror(RewardItem reward)
{
BattlePassItem temp = ScriptableObject.CreateInstance<BattlePassItem>();
temp.passId = "NewPlayerReward_" + reward.rewardId;
temp.itemTitle = reward.title;
temp.itemDescription = reward.description ?? "New-player reward.";
temp.itemIcon = reward.icon;
temp.rewardTier = BattlePassItem.RewardTier.Free;
switch (reward.rewardType)
{
case RewardType.Currency:
temp.rewardType = BattlePassItem.RewardType.CurrencyReward;
temp.currencyReward = new CurrencyReward
{
currency = reward.currencyRewards[0],
amount = reward.amount
};
break;
case RewardType.Icon:
temp.rewardType = BattlePassItem.RewardType.IconReward;
temp.iconReward = reward.iconRewards[0];
break;
case RewardType.Frame:
temp.rewardType = BattlePassItem.RewardType.FrameReward;
temp.frameReward = reward.frameRewards[0];
break;
case RewardType.Character:
temp.rewardType = BattlePassItem.RewardType.CharacterReward;
temp.characterData = new CharacterData[] { reward.characterRewards[0] };
break;
case RewardType.InventoryItem:
temp.rewardType = BattlePassItem.RewardType.InventoryItemReward;
temp.inventoryItems = new InventoryItem[] { reward.inventoryItems[0] };
break;
}
return temp;
}
/*────────────────────────── Helpers ─────────────────────────────*/
/// <summary>
/// Returns a translated string (title) or the fallback if not found.
/// </summary>
public string GetTranslatedString(NameTranslatedByLanguage[] translations, string fallback, string lang)
{
if (translations != null)
{
foreach (var t in translations)
{
if (!string.IsNullOrEmpty(t.LanguageId) &&
t.LanguageId.Equals(lang, StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrEmpty(t.Translate))
return t.Translate;
}
}
return fallback;
}
/// <summary>
/// Returns a translated string (description) or the fallback if not found.
/// </summary>
public string GetTranslatedString(DescriptionTranslatedByLanguage[] translations, string fallback, string lang)
{
if (translations != null)
{
foreach (var t in translations)
{
if (!string.IsNullOrEmpty(t.LanguageId) &&
t.LanguageId.Equals(lang, StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrEmpty(t.Translate))
return t.Translate;
}
}
return fallback;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: da920075f2f1e8a4f9ad88a4f9e512c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Scripts/NewAccountRewardManager.cs
uploadId: 777582

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: fd7692836f72bef4f96f118bce19c72a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Scripts/RewardEntry.cs
uploadId: 777582

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c700776657e0bb34b81e9f4b04bef575
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Scripts/RewardItem.cs
uploadId: 777582

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 054a15360702db9439f4a29003cc3fa2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Scripts/RewardPopup.cs
uploadId: 777582

View File

@ -0,0 +1,47 @@
using System.Collections.Generic;
using UnityEngine;
namespace BulletHellTemplate
{
/// <summary>
/// Handles saving and loading reward data from Firebase and PlayerPrefs.
/// Prevents multiple calls to Firebase during initialization.
/// </summary>
public static class RewardSaveManager
{
/// <summary>
/// Loads rewards from Firestore for a specified collection and invokes a callback with the data.
/// </summary>
public static void LoadRewards(string collectionName, System.Action<Dictionary<string, object>> onLoaded)
{
}
/// <summary>
/// Saves rewards to Firestore for a specified collection, and also stores them locally in PlayerPrefs.
/// </summary>
public static void SaveRewards(string collectionName, Dictionary<string, object> data)
{
SaveRewardsLocally(collectionName, data);
}
/// <summary>
/// Saves rewards data locally in PlayerPrefs as JSON.
/// </summary>
private static void SaveRewardsLocally(string collectionName, Dictionary<string, object> data)
{
string json = JsonUtility.ToJson(data);
PlayerPrefs.SetString(collectionName, json);
PlayerPrefs.Save();
}
/// <summary>
/// Loads rewards data from PlayerPrefs if available.
/// </summary>
public static Dictionary<string, object> LoadRewardsLocally(string collectionName)
{
string json = PlayerPrefs.GetString(collectionName, null);
return string.IsNullOrEmpty(json) ? null : JsonUtility.FromJson<Dictionary<string, object>>(json);
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 14a1f9538dc44a444b86779f45b4962e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/Scripts/RewardSaveManager.cs
uploadId: 777582

View File

@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 20e295e3dffe0ec4488423fbac55e740
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/DailyRewards/UINewAccountReward.prefab
uploadId: 777582

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: ed70b82c28b9c144cb980dfa5702e03b
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 6fda033c0738a634eb415877fd65058d
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,28 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DayleReward 1
m_EditorClassIdentifier:
rewardId: reward1
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 7a278160a6c7f8544a3768e0bcc4553d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,28 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DayleReward 10
m_EditorClassIdentifier:
rewardId: reward10
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 500 diamonds
descriptionTranslated: []
rewardType: 0
amount: 500
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 06c0221dd1e929c47b05ee18757fe798
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,28 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DayleReward 2
m_EditorClassIdentifier:
rewardId: reward2
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 7bb80a829634a834f9581e598abec835
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,28 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DayleReward 3
m_EditorClassIdentifier:
rewardId: reward3
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 8772999151e122244a4b4e1e0740ae46
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,28 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DayleReward 4
m_EditorClassIdentifier:
rewardId: reward4
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 425913de335469442b2439a25a78a172
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,28 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DayleReward 5
m_EditorClassIdentifier:
rewardId: reward5
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 69da3ac32b731d2488c0f41a860c1498
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,28 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DayleReward 6
m_EditorClassIdentifier:
rewardId: reward6
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 115596442aeedba44a43288cfc2796d5
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,28 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DayleReward 7
m_EditorClassIdentifier:
rewardId: reward7
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: f4641227f3470f34c9a62b08d0dc99a4
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,28 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DayleReward 8
m_EditorClassIdentifier:
rewardId: reward8
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 6698d9bc522453a42a9edccae0d42f1f
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,28 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c700776657e0bb34b81e9f4b04bef575, type: 3}
m_Name: DayleReward 9
m_EditorClassIdentifier:
rewardId: reward9
icon: {fileID: 21300000, guid: 8b4ed5f113462ce44a28290ff3d5f8e7, type: 3}
title: Diamond
titleTranslated: []
description: Receive 15 diamonds
descriptionTranslated: []
rewardType: 0
amount: 15
currencyRewards:
- {fileID: 11400000, guid: b0d79ad1b68bf714f987f3ac5cebb447, type: 2}
iconRewards: []
frameRewards: []
characterRewards: []
inventoryItems: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 2a1b7ad7800274e42b157224b6e73e6a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,250 +0,0 @@
using System;
using UnityEngine;
using static BulletHellTemplate.PlayerSave;
namespace BulletHellTemplate
{
/// <summary>
/// Manages daily rewards UI and claiming logic.
/// Firebase calls are delegated to BackendManager for server saving,
/// while local data is stored in PlayerSave.
/// </summary>
public class DayleRewardManager : MonoBehaviour
{
[Header("UI Prefabs and Containers")]
[Tooltip("All daily reward items for each day.")]
public RewardItem[] rewardItems;
[Tooltip("Prefab used to display each reward entry.")]
public RewardEntry rewardEntryPrefab;
[Tooltip("Parent transform to hold all spawned reward entries.")]
public Transform rewardsContainer;
[Tooltip("Popup shown when a reward is claimed.")]
public RewardPopup rewardPopup;
private DailyRewardsData localDailyData;
private int currentDayIndex;
private string currentLang;
/// <summary>
/// Called when the object becomes enabled.
/// Ensures data is loaded from Firebase if needed,
/// then displays the local daily rewards data.
/// </summary>
private async void OnEnable()
{
currentLang = LanguageManager.LanguageManager.Instance.GetCurrentLanguage();
// Load daily rewards data from local storage
localDailyData = PlayerSave.GetDailyRewardsLocal();
Debug.Log($"[DayleRewardManager] Now (local) = {DateTime.Now} / " +
$"firstClaimDate (localDailyData) = {localDailyData.firstClaimDate} / " +
$"(Now.Date - firstClaimDate.Date) = {(DateTime.Now.Date - localDailyData.firstClaimDate.Date).TotalDays}");
// Calculate how many days have passed since firstClaimDate
TimeSpan timeSinceFirstClaim = DateTime.Now.Date - localDailyData.firstClaimDate.Date;
currentDayIndex = (int)timeSinceFirstClaim.TotalDays;
Debug.Log($"[DayleRewardManager] currentDayIndex = {currentDayIndex}");
// Check if user surpassed total daily rewards
if (currentDayIndex >= rewardItems.Length)
{
currentDayIndex = rewardItems.Length - 1;
}
// Build UI
DisplayRewards();
}
/// <summary>
/// Builds the UI for daily rewards, enabling claim when appropriate.
/// </summary>
private void DisplayRewards()
{
// Clear old entries
if (rewardsContainer != null)
{
foreach (Transform child in rewardsContainer)
{
Destroy(child.gameObject);
}
}
if (rewardItems == null || rewardItems.Length == 0)
{
Debug.LogWarning("No daily rewards configured.");
return;
}
for (int i = 0; i < rewardItems.Length; i++)
{
RewardEntry entry = Instantiate(rewardEntryPrefab, rewardsContainer);
entry.Setup(
rewardItems[i].icon,
rewardItems[i].title,
rewardItems[i].description,
$"Day {i + 1}",
i + 1
);
// If already claimed
if (localDailyData.claimedRewards.Contains(i))
{
entry.SetClaimed();
}
// If day index is within or equal to currentDayIndex, enable claim (if not claimed)
else if (i <= currentDayIndex)
{
int dayIndex = i;
entry.EnableClaimButton(() => ClaimReward(dayIndex));
}
else
{
// Future days
entry.SetLocked();
}
}
}
/// <summary>
/// Claims the specified daily reward day,
/// then saves locally and on Firebase.
/// </summary>
private async void ClaimReward(int dayIndex)
{
// Already claimed?
if (localDailyData.claimedRewards.Contains(dayIndex))
{
Debug.LogWarning($"Reward for day {dayIndex + 1} is already claimed.");
return;
}
// If it's a future day, do not allow
if (dayIndex > currentDayIndex)
{
Debug.LogWarning($"Cannot claim day {dayIndex + 1} yet.");
return;
}
// Add to claimed
localDailyData.claimedRewards.Add(dayIndex);
// Show popup
RewardItem reward = rewardItems[dayIndex];
if (rewardPopup != null)
{
string titleTranslated = GetTranslatedString(
reward.titleTranslated,
reward.title,
currentLang
);
string descriptionTranslated = GetTranslatedString(
reward.descriptionTranslated,
reward.description,
currentLang
);
rewardPopup.Setup(reward.icon, titleTranslated, descriptionTranslated);
}
// Award in-game
AwardReward(reward);
// Save locally
PlayerSave.SetDailyRewardsLocal(localDailyData);
// Refresh UI
DisplayRewards();
// Also save to Firebase
await BackendManager.Singleton.SaveDailyRewardsDataAsync(localDailyData);
}
/// <summary>
/// Gives the actual reward to the player, e.g. currency or items.
/// </summary>
private void AwardReward(RewardItem reward)
{
switch (reward.rewardType)
{
case RewardType.Currency:
int currentAmount = MonetizationManager.GetCurrency(reward.currencyRewards[0].coinID);
int newAmount = currentAmount + reward.amount;
MonetizationManager.SetCurrency(reward.currencyRewards[0].coinID, newAmount);
break;
default:
BattlePassItem tempPassItem = ScriptableObject.CreateInstance<BattlePassItem>();
tempPassItem.passId = "Reward_" + reward.rewardId;
tempPassItem.itemTitle = reward.title;
tempPassItem.itemDescription = reward.description ?? "Reward from Daily Rewards.";
tempPassItem.itemIcon = reward.icon;
tempPassItem.rewardTier = BattlePassItem.RewardTier.Free;
switch (reward.rewardType)
{
case RewardType.Icon:
tempPassItem.rewardType = BattlePassItem.RewardType.IconReward;
tempPassItem.iconReward = reward.iconRewards[0];
break;
case RewardType.Frame:
tempPassItem.rewardType = BattlePassItem.RewardType.FrameReward;
tempPassItem.frameReward = reward.frameRewards[0];
break;
case RewardType.Character:
tempPassItem.rewardType = BattlePassItem.RewardType.CharacterReward;
tempPassItem.characterData = new CharacterData[] { reward.characterRewards[0] };
break;
case RewardType.InventoryItem:
tempPassItem.rewardType = BattlePassItem.RewardType.InventoryItemReward;
tempPassItem.inventoryItems = new InventoryItem[] { reward.inventoryItems[0] };
break;
}
MonetizationManager.Singleton.BattlePassUnlockItem(tempPassItem);
break;
}
}
/// <summary>
/// Retrieves a translated string or returns the fallback if not found.
/// </summary>
public string GetTranslatedString(NameTranslatedByLanguage[] translations, string fallback, string currentLang)
{
if (translations != null)
{
foreach (var trans in translations)
{
if (!string.IsNullOrEmpty(trans.LanguageId)
&& trans.LanguageId.Equals(currentLang)
&& !string.IsNullOrEmpty(trans.Translate))
{
return trans.Translate;
}
}
}
return fallback;
}
/// <summary>
/// Retrieves a translated description string or returns the fallback if not found.
/// </summary>
public string GetTranslatedString(DescriptionTranslatedByLanguage[] translations, string fallback, string currentLang)
{
if (translations != null)
{
foreach (var trans in translations)
{
if (!string.IsNullOrEmpty(trans.LanguageId)
&& trans.LanguageId.Equals(currentLang)
&& !string.IsNullOrEmpty(trans.Translate))
{
return trans.Translate;
}
}
}
return fallback;
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 74a2deb9e3aaf2549a4286b575d3f7d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,258 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using static BulletHellTemplate.PlayerSave;
namespace BulletHellTemplate
{
/// <summary>
/// Manages the new-account rewards UI and claiming logic.
/// All Firebase calls have been moved to BackendManager for server saving,
/// and PlayerSave for local data storage.
/// </summary>
public class NewAccountRewardManager : MonoBehaviour
{
[Header("Rewards Configuration")]
[Tooltip("Rewards for new players, typically one per day.")]
public RewardItem[] rewardItems;
[Header("UI Elements")]
[Tooltip("Prefab for displaying each reward in the list.")]
public RewardEntry rewardEntryPrefab;
[Tooltip("Container to hold the reward entries.")]
public Transform rewardsContainer;
[Tooltip("Popup shown when a reward is claimed.")]
public RewardPopup rewardPopup;
[Header("Settings")]
[Tooltip("Total days the new player rewards are available.")]
public int totalRewardDays = 7;
private NewPlayerRewardsData localNewPlayerData;
private int currentDayIndex;
private string currentLang;
/// <summary>
/// Called when the GameObject becomes enabled and active.
/// Loads local new-player reward data and displays them.
/// </summary>
private void OnEnable()
{
// Retrieve the new-player reward data from local storage
localNewPlayerData = PlayerSave.GetNewPlayerRewardsLocal();
currentLang = LanguageManager.LanguageManager.Instance.GetCurrentLanguage();
// Calculate how many days since account creation
TimeSpan timeSinceCreation = DateTime.Now.Date - localNewPlayerData.accountCreationDate.Date;
currentDayIndex = (int)timeSinceCreation.TotalDays;
DisplayNewPlayerRewards();
}
/// <summary>
/// Displays the new player rewards in the UI, enabling claim where applicable.
/// </summary>
private void DisplayNewPlayerRewards()
{
// Clear existing entries
if (rewardsContainer != null)
{
foreach (Transform child in rewardsContainer)
{
Destroy(child.gameObject);
}
}
if (rewardItems == null || rewardItems.Length == 0)
{
Debug.LogWarning("No new player rewards are configured.");
return;
}
// Create entries for each reward day
for (int i = 0; i < rewardItems.Length; i++)
{
RewardEntry entry = Instantiate(rewardEntryPrefab, rewardsContainer);
// Translate reward title/description
string titleTranslated = GetTranslatedString(
rewardItems[i].titleTranslated,
rewardItems[i].title,
currentLang
);
string descriptionTranslated = GetTranslatedString(
rewardItems[i].descriptionTranslated,
rewardItems[i].description,
currentLang
);
// Setup the UI element
entry.Setup(
rewardItems[i].icon,
titleTranslated,
descriptionTranslated,
rewardItems[i].rewardId,
i + 1
);
// If already claimed
if (localNewPlayerData.claimedRewards.Contains(i))
{
entry.SetClaimed();
}
// If day index <= currentDayIndex and within totalRewardDays
else if (i <= currentDayIndex && i < totalRewardDays)
{
int dayIndex = i;
entry.EnableClaimButton(() => ClaimNewPlayerReward(dayIndex));
}
else
{
// Lock
entry.SetLocked();
}
}
}
/// <summary>
/// Claims a new player reward for the specified day and updates local + server data.
/// </summary>
private async void ClaimNewPlayerReward(int dayIndex)
{
// If already claimed
if (localNewPlayerData.claimedRewards.Contains(dayIndex))
{
Debug.LogWarning($"Reward for day {dayIndex + 1} has already been claimed.");
return;
}
// Ensure it's not a future day
if (dayIndex > currentDayIndex)
{
Debug.LogWarning($"Cannot claim reward for day {dayIndex + 1} yet.");
return;
}
// Mark as claimed
localNewPlayerData.claimedRewards.Add(dayIndex);
// Award the in-game reward
RewardItem reward = rewardItems[dayIndex];
AwardReward(reward);
// Optionally show a popup
if (rewardPopup != null)
{
string titleTranslated = GetTranslatedString(
reward.titleTranslated,
reward.title,
currentLang
);
string descriptionTranslated = GetTranslatedString(
reward.descriptionTranslated,
reward.description,
currentLang
);
rewardPopup.Setup(reward.icon, titleTranslated, descriptionTranslated);
}
// Save locally
PlayerSave.SetNewPlayerRewardsLocal(localNewPlayerData);
// Refresh UI
DisplayNewPlayerRewards();
// Also save to server
await BackendManager.Singleton.SaveNewPlayerRewardsDataAsync(localNewPlayerData);
}
/// <summary>
/// Grants the reward to the player via MonetizationManager or other systems.
/// </summary>
private void AwardReward(RewardItem reward)
{
switch (reward.rewardType)
{
case RewardType.Currency:
int currentAmount = MonetizationManager.GetCurrency(reward.currencyRewards[0].coinID);
int newAmount = currentAmount + reward.amount;
MonetizationManager.SetCurrency(reward.currencyRewards[0].coinID, newAmount);
break;
default:
BattlePassItem tempPassItem = ScriptableObject.CreateInstance<BattlePassItem>();
tempPassItem.passId = "Reward_" + reward.rewardId;
tempPassItem.itemTitle = reward.title;
tempPassItem.itemDescription = reward.description ?? "Reward from New Player Rewards.";
tempPassItem.itemIcon = reward.icon;
tempPassItem.rewardTier = BattlePassItem.RewardTier.Free;
switch (reward.rewardType)
{
case RewardType.Icon:
tempPassItem.rewardType = BattlePassItem.RewardType.IconReward;
tempPassItem.iconReward = reward.iconRewards[0];
break;
case RewardType.Frame:
tempPassItem.rewardType = BattlePassItem.RewardType.FrameReward;
tempPassItem.frameReward = reward.frameRewards[0];
break;
case RewardType.Character:
tempPassItem.rewardType = BattlePassItem.RewardType.CharacterReward;
tempPassItem.characterData = new CharacterData[] { reward.characterRewards[0] };
break;
case RewardType.InventoryItem:
tempPassItem.rewardType = BattlePassItem.RewardType.InventoryItemReward;
tempPassItem.inventoryItems = new InventoryItem[] { reward.inventoryItems[0] };
break;
}
MonetizationManager.Singleton.BattlePassUnlockItem(tempPassItem);
break;
}
}
/// <summary>
/// Retrieves a translated string or returns the fallback if not found.
/// </summary>
public string GetTranslatedString(NameTranslatedByLanguage[] translations, string fallback, string currentLang)
{
if (translations != null)
{
foreach (var trans in translations)
{
if (!string.IsNullOrEmpty(trans.LanguageId)
&& trans.LanguageId.Equals(currentLang)
&& !string.IsNullOrEmpty(trans.Translate))
{
return trans.Translate;
}
}
}
return fallback;
}
/// <summary>
/// Retrieves a translated description string or returns the fallback if not found.
/// </summary>
public string GetTranslatedString(DescriptionTranslatedByLanguage[] translations, string fallback, string currentLang)
{
if (translations != null)
{
foreach (var trans in translations)
{
if (!string.IsNullOrEmpty(trans.LanguageId)
&& trans.LanguageId.Equals(currentLang)
&& !string.IsNullOrEmpty(trans.Translate))
{
return trans.Translate;
}
}
}
return fallback;
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: da920075f2f1e8a4f9ad88a4f9e512c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: fd7692836f72bef4f96f118bce19c72a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: c700776657e0bb34b81e9f4b04bef575
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 054a15360702db9439f4a29003cc3fa2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,85 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
using Firebase.Firestore;
using Firebase.Auth;
using Firebase.Extensions;
namespace BulletHellTemplate
{
/// <summary>
/// Handles saving and loading reward data from Firebase and PlayerPrefs.
/// Prevents multiple calls to Firebase during initialization.
/// </summary>
public static class RewardSaveManager
{
/// <summary>
/// Loads rewards from Firestore for a specified collection and invokes a callback with the data.
/// </summary>
public static void LoadRewards(string collectionName, System.Action<Dictionary<string, object>> onLoaded)
{
FirebaseUser user = FirebaseAuth.DefaultInstance.CurrentUser;
if (user == null) return;
FirebaseFirestore db = FirebaseFirestore.DefaultInstance;
DocumentReference docRef = db.Collection("Players").Document(user.UserId).Collection(collectionName).Document("RewardData");
docRef.GetSnapshotAsync().ContinueWithOnMainThread(task =>
{
if (task.IsCompleted && task.Result.Exists)
{
Dictionary<string, object> data = task.Result.ToDictionary();
onLoaded?.Invoke(data);
}
else
{
Debug.LogError("Failed to load reward data.");
onLoaded?.Invoke(null);
}
});
}
/// <summary>
/// Saves rewards to Firestore for a specified collection, and also stores them locally in PlayerPrefs.
/// </summary>
public static void SaveRewards(string collectionName, Dictionary<string, object> data)
{
FirebaseUser user = FirebaseAuth.DefaultInstance.CurrentUser;
if (user == null) return;
FirebaseFirestore db = FirebaseFirestore.DefaultInstance;
DocumentReference docRef = db.Collection("Players").Document(user.UserId).Collection(collectionName).Document("RewardData");
docRef.SetAsync(data).ContinueWithOnMainThread(task =>
{
if (task.IsCompleted)
{
Debug.Log($"{collectionName} rewards saved successfully.");
}
else
{
Debug.LogError($"Failed to save {collectionName} rewards: {task.Exception}");
}
});
// Save locally
SaveRewardsLocally(collectionName, data);
}
/// <summary>
/// Saves rewards data locally in PlayerPrefs as JSON.
/// </summary>
private static void SaveRewardsLocally(string collectionName, Dictionary<string, object> data)
{
string json = JsonUtility.ToJson(data);
PlayerPrefs.SetString(collectionName, json);
PlayerPrefs.Save();
}
/// <summary>
/// Loads rewards data from PlayerPrefs if available.
/// </summary>
public static Dictionary<string, object> LoadRewardsLocally(string collectionName)
{
string json = PlayerPrefs.GetString(collectionName, null);
return string.IsNullOrEmpty(json) ? null : JsonUtility.FromJson<Dictionary<string, object>>(json);
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 14a1f9538dc44a444b86779f45b4962e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: f804f902a78cff145807640e51740dbb
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 20e295e3dffe0ec4488423fbac55e740
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Armors/ArmorCommon.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Armors/ArmorEpic.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Armors/ArmorLegendary.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Armors/ArmorRare.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Armors/ArmorUncommon.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Hat/HatCommon.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Hat/HatEpic.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Hat/HatLegendary.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Hat/HatRare.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Hat/HatUncommon.asset
uploadId: 777582

View File

@ -5,3 +5,10 @@ PrefabImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/InventoryEntryPrefab.prefab
uploadId: 777582

View File

@ -5,3 +5,10 @@ PrefabImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/ItemApplyEntryPrefab.prefab
uploadId: 777582

View File

@ -5,3 +5,10 @@ PrefabImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/ItemSlotEntryPrefab.prefab
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Pants/PantsCommon.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Pants/PantsEpic.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Pants/PantsLegendary.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Pants/PantsRare.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Pants/PantsUncommon.asset
uploadId: 777582

View File

@ -5,3 +5,10 @@ PrefabImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/RuneEntryApply.prefab
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Runes/RuneAttackCommon.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Runes/RuneAttackEpic.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Runes/RuneDefenseRare.asset
uploadId: 777582

View File

@ -6,3 +6,10 @@ NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293814
packageName: BulletHell Elemental Template
packageVersion: 1.3.0
assetPath: Assets/BulletHellTemplate/Addons/Inventory/Resources/Runes/RuneSupportLegendary.asset
uploadId: 777582

Some files were not shown because too many files have changed in this diff Show More