using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace BulletHellTemplate
{
///
/// Data class for saving recharge information.
///
[Serializable]
public class RechargeSaveData
{
public List entries = new List();
///
/// Sets the last recharge time for a given coin.
///
/// Identifier for the coin.
/// The Unix time to set as the last recharge time.
public void SetValue(string coinID, long unixTime)
{
for (int i = 0; i < entries.Count; i++)
{
if (entries[i].coinID == coinID)
{
entries[i].lastRechargeTime = unixTime;
return;
}
}
entries.Add(new RechargeKeyValue { coinID = coinID, lastRechargeTime = unixTime });
}
///
/// Gets the last recharge time for a given coin.
///
/// Identifier for the coin.
/// Last recharge time as Unix time. Returns 0 if not found.
public long GetValue(string coinID)
{
for (int i = 0; i < entries.Count; i++)
{
if (entries[i].coinID == coinID)
return entries[i].lastRechargeTime;
}
return 0L;
}
}
///
/// Represents a key-value pair for recharge data.
///
[Serializable]
public class RechargeKeyValue
{
public string coinID;
public long lastRechargeTime;
}
///
/// Manager class for handling recharge logic for rechargeable currencies.
///
public class RechargeablesManager : MonoBehaviour
{
public static RechargeablesManager Singleton;
[Header("Auto Save Interval (seconds)")]
[SerializeField] private float autoSaveInterval = 60f;
[SerializeField] private string saveFileName = "currencyRecharge.json";
private float saveTimer;
private RechargeSaveData localData = new RechargeSaveData();
///
/// Awake is called when the script instance is being loaded.
/// Implements the singleton pattern.
///
private void Awake()
{
if (Singleton == null)
{
Singleton = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
return;
}
}
///
/// Start is called before the first frame update.
///
private void Start()
{
StartCoroutine(WaitForBackendAndInitialize());
}
///
/// Waits for the backend to initialize before loading local data and updating all currencies recharge.
///
/// IEnumerator for coroutine.
private IEnumerator WaitForBackendAndInitialize()
{
while (!BackendManager.Singleton.CheckInitialized())
yield return null;
LoadLocalData();
UpdateAllCurrenciesRecharge(); // Applies offline recharges
}
///
/// Update is called once per frame.
/// Checks if the auto save interval has been reached.
///
private void Update()
{
saveTimer += Time.deltaTime;
if (saveTimer >= autoSaveInterval)
{
saveTimer = 0f;
SaveLocalData();
}
}
///
/// Called on application quit. Saves local recharge data.
///
private void OnApplicationQuit()
{
SaveLocalData();
}
///
/// Handles the offline recharge logic for all rechargeable currencies.
///
public void UpdateAllCurrenciesRecharge()
{
if (MonetizationManager.Singleton == null)
{
Debug.LogWarning("MonetizationManager.Singleton is null. Cannot update recharge.");
return;
}
foreach (Currency currency in MonetizationManager.Singleton.currencies)
{
if (!currency.isRechargeableCurrency)
continue;
CheckAndApplyRecharge(currency);
}
}
///
/// Applies all pending recharges (offline or delayed) in a single call to avoid multiple database calls.
/// Returns true if the maximum amount is reached.
///
/// The currency to apply pending charges.
/// True if the maximum amount is reached, false otherwise.
public bool ApplyAllPendingCharges(Currency currency)
{
if (currency == null || !currency.isRechargeableCurrency)
return false;
int oldAmount = MonetizationManager.GetCurrency(currency.coinID);
CheckAndApplyRecharge(currency); // Reuses the same logic
int newAmount = MonetizationManager.GetCurrency(currency.coinID);
// Returns true if the maximum amount is reached or exceeded.
if (currency.useMaxAmount && newAmount >= currency.maxAmount)
return true;
return false;
}
///
/// Base logic to calculate how many recharge ticks have occurred since the last recharge and apply them.
///
/// The currency to recharge.
private void CheckAndApplyRecharge(Currency currency)
{
int currentAmount = MonetizationManager.GetCurrency(currency.coinID);
// If the currency is at maximum, reset last recharge time to avoid infinite offline accumulation.
if (currency.useMaxAmount && currentAmount >= currency.maxAmount)
{
localData.SetValue(currency.coinID, 0);
return;
}
long lastTime = localData.GetValue(currency.coinID);
long now = GetCurrentUnixTime();
// If never set, initialize with the current time and exit.
if (lastTime == 0)
{
localData.SetValue(currency.coinID, now);
return;
}
long elapsed = now - lastTime;
if (elapsed <= 0) return;
float chunkDuration = ConvertToSeconds(currency.rechargeableTime, currency.rechargeableTimeScale);
// Calculate how many ticks occurred.
int ticks = Mathf.FloorToInt((float)elapsed / chunkDuration);
if (ticks > 0)
{
int totalRecharge = ticks * currency.rechargeAmount;
int newAmount = currentAmount + totalRecharge;
if (currency.useMaxAmount && newAmount > currency.maxAmount)
{
newAmount = currency.maxAmount;
localData.SetValue(currency.coinID, 0);
}
else
{
// Adjust last recharge time to now minus the unused time.
long totalSecondsUsed = (long)(ticks * chunkDuration);
long newLastTime = lastTime + totalSecondsUsed;
localData.SetValue(currency.coinID, newLastTime);
}
MonetizationManager.SetCurrency(currency.coinID, newAmount);
}
}
///
/// Saves local recharge data to a file.
///
public void SaveLocalData()
{
try
{
string path = Path.Combine(Application.persistentDataPath, saveFileName);
string json = JsonUtility.ToJson(localData, true);
File.WriteAllText(path, json);
}
catch (Exception e)
{
Debug.LogError($"Error saving local recharge data: {e.Message}");
}
}
///
/// Loads local recharge data from a file.
///
public void LoadLocalData()
{
try
{
string path = Path.Combine(Application.persistentDataPath, saveFileName);
if (!File.Exists(path))
{
localData = new RechargeSaveData();
return;
}
string json = File.ReadAllText(path);
localData = JsonUtility.FromJson(json);
if (localData == null)
localData = new RechargeSaveData();
}
catch (Exception e)
{
Debug.LogError($"Error loading local recharge data: {e.Message}");
localData = new RechargeSaveData();
}
}
///
/// Retrieves the current Unix time in seconds.
///
/// Current Unix time in seconds.
private long GetCurrentUnixTime()
{
return DateTimeOffset.UtcNow.ToUnixTimeSeconds();
}
///
/// Converts a time value based on the provided time scale to seconds.
///
/// The time value to convert.
/// The time scale (Seconds, Minutes, or Hours).
/// Equivalent time in seconds.
private float ConvertToSeconds(float timeValue, rechargeableTimeScale scale)
{
switch (scale)
{
case rechargeableTimeScale.Seconds: return timeValue;
case rechargeableTimeScale.Minutes: return timeValue * 60f;
case rechargeableTimeScale.Hours: return timeValue * 3600f;
default: return timeValue;
}
}
}
}