Game/Idle/IdleUpgradeCatalog.cs
using System;
using System.Collections.Generic;
using System.Linq;
public enum IdleUpgradeCategory { Reactor, Chains, Systems }
public enum IdleUpgradeId
{
// Reactor — how much material the board produces
SpawnRate,
SpawnCount,
ChestValue,
GemChance,
BombChestChance,
GridSize,
// Chains — how hard the drones hit
DroneCount,
CycleSpeed,
BlastSize,
ChainPower,
CritChance,
CritPower,
// Systems — offline, manual play, meta payout
OfflineEfficiency,
OfflineCap,
OverloadCooldown,
ChargeGain,
GoldenChestRate,
FragmentYield,
}
public enum IdlePerkId
{
CoreEfficiency,
Kickstart,
DeepStorage,
NightShift,
FragmentCondenser,
ChainTheory,
AutoOverload,
Momentum,
PetLink,
}
public class IdleUpgrade
{
public IdleUpgradeId Id { get; set; }
public IdleUpgradeCategory Category { get; set; }
public string Name { get; set; }
public string Icon { get; set; }
public string Desc { get; set; }
public double BaseCost { get; set; }
public double CostGrowth { get; set; }
public int MaxLevel { get; set; }
/// <summary>Cores needed to buy the level after <paramref name="level"/>.</summary>
public double CostAt( int level ) => BaseCost * Math.Pow( CostGrowth, level );
/// <summary>Human readable effect at a given level, e.g. "1.8 /s".</summary>
public Func<int, string> Effect { get; set; }
/// <summary>Cores this upgrade only becomes visible at — keeps the early list short.</summary>
public double RevealAt { get; set; } = 0;
}
public class IdlePerk
{
public IdlePerkId Id { get; set; }
public string Name { get; set; }
public string Icon { get; set; }
public string Desc { get; set; }
public int BaseCost { get; set; }
public int CostStep { get; set; }
public int MaxLevel { get; set; }
public int CostAt( int level ) => BaseCost + CostStep * level;
public Func<int, string> Effect { get; set; }
}
public static class IdleUpgradeCatalog
{
// ── Core upgrades — bought with Cores, wiped on Meltdown ───────────────
public static readonly List<IdleUpgrade> All = new()
{
// ───────────── Reactor ─────────────
new IdleUpgrade {
Id = IdleUpgradeId.ChestValue, Category = IdleUpgradeCategory.Reactor,
Name = "Dense Loot", Icon = "🎁",
Desc = "Every chest is worth more Cores.",
BaseCost = 30, CostGrowth = 1.175, MaxLevel = 300,
Effect = lvl => $"×{IdleBalance.ChestValueMult( lvl ):F2} value",
},
new IdleUpgrade {
Id = IdleUpgradeId.SpawnRate, Category = IdleUpgradeCategory.Reactor,
Name = "Feed Line", Icon = "⚙️",
Desc = "Ambient chest drip between detonations.",
BaseCost = 120, CostGrowth = 1.60, MaxLevel = 40,
Effect = lvl => $"every {IdleBalance.SpawnInterval( lvl ):F2}s",
},
new IdleUpgrade {
Id = IdleUpgradeId.SpawnCount, Category = IdleUpgradeCategory.Reactor,
Name = "Bulk Hopper", Icon = "📦",
Desc = "Chests dumped onto the board right before every detonation. Chain length lives here.",
BaseCost = 1000, CostGrowth = 4.00, MaxLevel = 12,
Effect = lvl => $"{IdleBalance.SpawnCount( lvl )} per cycle",
RevealAt = 500,
},
new IdleUpgrade {
Id = IdleUpgradeId.GemChance, Category = IdleUpgradeCategory.Reactor,
Name = "Gem Seeding", Icon = "💎",
Desc = "Gem chests are worth 4× a normal chest.",
BaseCost = 2400, CostGrowth = 1.70, MaxLevel = 30,
Effect = lvl => $"{IdleFormat.Pct( IdleBalance.GemChance( lvl ) )} gems",
RevealAt = 500,
},
new IdleUpgrade {
Id = IdleUpgradeId.BombChestChance, Category = IdleUpgradeCategory.Reactor,
Name = "Volatile Cargo", Icon = "💣",
Desc = "Bomb chests detonate on death and extend chains.",
BaseCost = 6000, CostGrowth = 1.75, MaxLevel = 30,
Effect = lvl => $"{IdleFormat.Pct( IdleBalance.BombChestChance( lvl ) )} bomb chests",
RevealAt = 1600,
},
new IdleUpgrade {
Id = IdleUpgradeId.GridSize, Category = IdleUpgradeCategory.Reactor,
Name = "Reactor Frame", Icon = "🔲",
Desc = "Expands the reactor board. Everything you own scales with it.",
BaseCost = 500000, CostGrowth = 120, MaxLevel = 4,
Effect = lvl => $"{IdleBalance.GridSize( lvl )}×{IdleBalance.GridSize( lvl )} board",
RevealAt = 40000,
},
// ───────────── Chains ─────────────
new IdleUpgrade {
Id = IdleUpgradeId.CycleSpeed, Category = IdleUpgradeCategory.Chains,
Name = "Fuse Timing", Icon = "⏱️",
Desc = "Drones detonate more often.",
BaseCost = 240, CostGrowth = 1.60, MaxLevel = 40,
Effect = lvl => $"every {IdleBalance.CycleInterval( lvl ):F2}s",
},
new IdleUpgrade {
Id = IdleUpgradeId.DroneCount, Category = IdleUpgradeCategory.Chains,
Name = "Drone Bay", Icon = "🛰️",
Desc = "More bombs placed per detonation cycle — widens how much of the board you cover.",
BaseCost = 3000, CostGrowth = 8.0, MaxLevel = 9,
Effect = lvl => $"{IdleBalance.DroneCount( lvl )} drones",
RevealAt = 600,
},
new IdleUpgrade {
Id = IdleUpgradeId.ChainPower, Category = IdleUpgradeCategory.Chains,
Name = "Chain Theory", Icon = "⛓️",
Desc = "Steepens the chain multiplier. This is where the real numbers come from.",
BaseCost = 10000, CostGrowth = 3.20, MaxLevel = 25,
Effect = lvl => $"exponent {IdleBalance.ChainExponent( lvl, 0 ):F2}",
RevealAt = 3000,
},
new IdleUpgrade {
Id = IdleUpgradeId.BlastSize, Category = IdleUpgradeCategory.Chains,
Name = "Warhead Class", Icon = "💥",
Desc = "Upgrades the drone payload pattern.",
BaseCost = 50000, CostGrowth = 40, MaxLevel = 4,
Effect = lvl => IdleBalance.BlastName( lvl ),
RevealAt = 8000,
},
new IdleUpgrade {
Id = IdleUpgradeId.CritChance, Category = IdleUpgradeCategory.Chains,
Name = "Overpressure", Icon = "🎲",
Desc = "Chance for a detonation to crit.",
BaseCost = 5000, CostGrowth = 1.75, MaxLevel = 30,
Effect = lvl => $"{IdleFormat.Pct( IdleBalance.CritChance( lvl ) )} crit",
RevealAt = 1200,
},
new IdleUpgrade {
Id = IdleUpgradeId.CritPower, Category = IdleUpgradeCategory.Chains,
Name = "Critical Mass", Icon = "☢️",
Desc = "Crits pay out far harder.",
BaseCost = 10000, CostGrowth = 1.85, MaxLevel = 30,
Effect = lvl => $"×{IdleBalance.CritPower( lvl ):F0} on crit",
RevealAt = 2400,
},
// ───────────── Systems ─────────────
new IdleUpgrade {
Id = IdleUpgradeId.OverloadCooldown, Category = IdleUpgradeCategory.Systems,
Name = "Manual Override", Icon = "🔴",
Desc = "Shorter cooldown on the OVERLOAD button.",
BaseCost = 1000, CostGrowth = 1.70, MaxLevel = 20,
Effect = lvl => $"{IdleBalance.OverloadCooldown( lvl ):F1}s cooldown",
},
new IdleUpgrade {
Id = IdleUpgradeId.ChargeGain, Category = IdleUpgradeCategory.Systems,
Name = "Capacitor", Icon = "🔋",
Desc = "Overloads fill the Surge meter faster.",
BaseCost = 4000, CostGrowth = 1.90, MaxLevel = 20,
Effect = lvl => $"+{IdleFormat.Pct( IdleBalance.ChargeGain( lvl, 0 ) )} per overload",
RevealAt = 1200,
},
new IdleUpgrade {
Id = IdleUpgradeId.OfflineEfficiency, Category = IdleUpgradeCategory.Systems,
Name = "Night Crew", Icon = "🌙",
Desc = "Share of your output the reactor keeps producing while you are on another screen.",
BaseCost = 10000, CostGrowth = 2.60, MaxLevel = 14,
Effect = lvl => $"{IdleFormat.Pct( IdleBalance.OfflineEfficiency( lvl, 0 ) )} while unattended",
RevealAt = 3000,
},
new IdleUpgrade {
Id = IdleUpgradeId.OfflineCap, Category = IdleUpgradeCategory.Systems,
Name = "Core Storage", Icon = "🛢️",
Desc = "How many hours of unattended production the tanks can hold.",
BaseCost = 16000, CostGrowth = 3.00, MaxLevel = 10,
Effect = lvl => $"{IdleBalance.OfflineCapHours( lvl, 0 ):F0}h stored",
RevealAt = 4000,
},
new IdleUpgrade {
Id = IdleUpgradeId.GoldenChestRate, Category = IdleUpgradeCategory.Systems,
Name = "Lucky Manifest", Icon = "🌟",
Desc = "Golden Chests appear more often. Click them before they leave.",
BaseCost = 12000, CostGrowth = 2.30, MaxLevel = 15,
Effect = lvl => $"every ~{IdleBalance.GoldenInterval( lvl ):F0}s",
RevealAt = 3000,
},
new IdleUpgrade {
Id = IdleUpgradeId.FragmentYield, Category = IdleUpgradeCategory.Systems,
Name = "Condenser Coil", Icon = "💠",
Desc = "Cheapens every Fragment condensed today — worth several extra Fragments per level.",
BaseCost = 50000, CostGrowth = 3.20, MaxLevel = 12,
Effect = lvl => $"first 💠 at {IdleFormat.Short( IdleBalance.FragmentCost( lvl, 0, 0 ) )}",
RevealAt = 8000,
},
};
public static IdleUpgrade Get( IdleUpgradeId id ) => All.FirstOrDefault( u => u.Id == id );
public static IEnumerable<IdleUpgrade> InCategory( IdleUpgradeCategory cat )
=> All.Where( u => u.Category == cat );
// ── Flux perks — permanent, survive every Meltdown ─────────────────────
public static readonly List<IdlePerk> Perks = new()
{
new IdlePerk {
Id = IdlePerkId.CoreEfficiency, Name = "Core Efficiency", Icon = "⚛️",
Desc = "All Core income multiplied.",
BaseCost = 3, CostStep = 3, MaxLevel = 12,
Effect = lvl => $"×{1f + 0.25f * lvl:F2} income",
},
new IdlePerk {
Id = IdlePerkId.ChainTheory, Name = "Resonance", Icon = "⛓️",
Desc = "Permanently steepens the chain multiplier curve.",
BaseCost = 10, CostStep = 8, MaxLevel = 10,
Effect = lvl => $"+{0.02f * lvl:F2} chain exponent",
},
new IdlePerk {
Id = IdlePerkId.Kickstart, Name = "Kickstart", Icon = "🚀",
Desc = "Start every Meltdown with upgrade levels already bought.",
BaseCost = 8, CostStep = 7, MaxLevel = 6,
Effect = lvl => $"{lvl * 6} Dense Loot · {lvl * 4} Feed Line",
},
new IdlePerk {
Id = IdlePerkId.NightShift, Name = "Night Shift", Icon = "🌙",
Desc = "Extra unattended efficiency on top of Night Crew.",
BaseCost = 6, CostStep = 5, MaxLevel = 8,
Effect = lvl => $"+{IdleFormat.Pct( 0.08f * lvl )} unattended",
},
new IdlePerk {
Id = IdlePerkId.DeepStorage, Name = "Deep Storage", Icon = "🛢️",
Desc = "Extra hours of unattended storage.",
BaseCost = 5, CostStep = 4, MaxLevel = 8,
Effect = lvl => $"+{lvl * 2}h cap",
},
new IdlePerk {
Id = IdlePerkId.Momentum, Name = "Momentum", Icon = "🔥",
Desc = "Surge lasts longer and the meter fills faster.",
BaseCost = 12, CostStep = 9, MaxLevel = 6,
Effect = lvl => $"+{lvl * 4}s surge · +{IdleFormat.Pct( 0.15f * lvl )} charge",
},
new IdlePerk {
Id = IdlePerkId.FragmentCondenser, Name = "Condenser Array", Icon = "💠",
Desc = "Raises the daily Fragment ceiling and cheapens condensing.",
BaseCost = 10, CostStep = 10, MaxLevel = 6,
Effect = lvl => $"+{lvl * 60} 💠/day",
},
new IdlePerk {
Id = IdlePerkId.AutoOverload, Name = "Autopilot", Icon = "🤖",
Desc = "The reactor fires OVERLOAD by itself. True idle.",
BaseCost = 25, CostStep = 25, MaxLevel = 3,
Effect = lvl => lvl <= 0 ? "off" : $"auto every {IdleBalance.AutoOverloadInterval( lvl ):F0}s",
},
new IdlePerk {
Id = IdlePerkId.PetLink, Name = "Pet Link", Icon = "🐾",
Desc = "Your pet works the night shift too — doubles its reactor bonus.",
BaseCost = 40, CostStep = 0, MaxLevel = 1,
Effect = lvl => lvl > 0 ? "pet bonus ×2" : "—",
},
};
public static IdlePerk GetPerk( IdlePerkId id ) => Perks.FirstOrDefault( p => p.Id == id );
}