Game/Idle/IdleSave.cs
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Persistent state for Reactor (Idle) mode.
/// Serialized as a nested object inside PlayerSave — see PlayerSave.Idle.
/// Everything here survives a Meltdown except what ResetForMeltdown() clears.
/// </summary>
public class IdleSave
{
// ── Unlock / onboarding ───────────────────────────────────────
public bool Unlocked { get; set; } = false; // granted after the first finished run
public bool IntroShown { get; set; } = false; // first-entry explainer
public bool MeltdownSeen { get; set; } = false; // has the prestige panel ever been opened
// ── Currencies ────────────────────────────────────────────────
public double Cores { get; set; } = 0; // spendable, wiped by Meltdown
public double CycleCores { get; set; } = 0; // earned since last Meltdown — drives Flux payout
public double AllTimeCores { get; set; } = 0; // never resets
public int Flux { get; set; } = 0; // unspent prestige currency
public int FluxEarned { get; set; } = 0; // lifetime, for milestones
public int Meltdowns { get; set; } = 0;
// ── Upgrade levels ────────────────────────────────────────────
// Keys are IdleUpgradeId.ToString(); missing key == level 0.
public Dictionary<string, int> Upgrades { get; set; } = new();
public Dictionary<string, int> Perks { get; set; } = new();
// ── Offline bookkeeping ───────────────────────────────────────
public long LastSeenUnix { get; set; } = 0;
public double MeasuredCps { get; set; } = 0; // smoothed cores/sec, used for offline payout
public double BestCps { get; set; } = 0;
// Pending offline haul, staged so the welcome-back panel can show it
public double PendingOfflineCores { get; set; } = 0;
public double PendingOfflineSeconds { get; set; } = 0;
public int PendingOfflineFrags { get; set; } = 0;
// ── Daily loop ────────────────────────────────────────────────
public string LastDailyDate { get; set; } = "";
public int DailyStreak { get; set; } = 0;
public bool DailyClaimed { get; set; } = false;
// ── Fragment drip (own bucket — does not touch the pet farm cap) ──
public int TodayReactorFragments { get; set; } = 0;
public string LastFragDate { get; set; } = "";
public double FragProgress { get; set; } = 0; // cores banked toward the next fragment
// ── Records / milestones ──────────────────────────────────────
public int TotalDetonations { get; set; } = 0;
public long TotalChestsBlown { get; set; } = 0;
public int BestChain { get; set; } = 0;
public double BestSingleBlast { get; set; } = 0;
public int GoldenGrabbed { get; set; } = 0;
public double SecondsRun { get; set; } = 0;
public List<string> Achievements { get; set; } = new();
// ── Upgrade helpers ───────────────────────────────────────────
public int Level( IdleUpgradeId id )
=> Upgrades.TryGetValue( id.ToString(), out int v ) ? v : 0;
public void SetLevel( IdleUpgradeId id, int level )
=> Upgrades[id.ToString()] = Math.Max( 0, level );
public int PerkLevel( IdlePerkId id )
=> Perks.TryGetValue( id.ToString(), out int v ) ? v : 0;
public void SetPerkLevel( IdlePerkId id, int level )
=> Perks[id.ToString()] = Math.Max( 0, level );
public bool HasAchievement( string id ) => Achievements.Contains( id );
// ── Meltdown (prestige) ───────────────────────────────────────
/// <summary>Wipes the run-scoped economy. Flux, perks, records and achievements stay.</summary>
public void ResetForMeltdown()
{
Cores = 0;
CycleCores = 0;
Upgrades.Clear();
Meltdowns++;
// Kickstart perk hands back a head start so the early grind shrinks each time
int kick = PerkLevel( IdlePerkId.Kickstart );
if ( kick > 0 )
{
SetLevel( IdleUpgradeId.ChestValue, kick * 6 );
SetLevel( IdleUpgradeId.SpawnRate, kick * 4 );
SetLevel( IdleUpgradeId.CycleSpeed, kick * 4 );
SetLevel( IdleUpgradeId.DroneCount, Math.Min( kick, 3 ) );
}
}
// ── Day rollover for the fragment bucket ──────────────────────
public void EnsureFragDay()
{
string today = DateTime.UtcNow.ToString( "yyyy-MM-dd" );
if ( LastFragDate == today ) return;
LastFragDate = today;
TodayReactorFragments = 0;
}
public int FragmentsToday
{
get { EnsureFragDay(); return TodayReactorFragments; }
}
}