// Assets/_Project/Scripts/Gameplay/GoldConfig.cs using System; using UnityEngine; using UnityEngine.Serialization; namespace TD.Gameplay { /// /// Gold rules for one encounter slot, addressed by position in the run rather than by /// which wave happens to occupy it. /// /// /// Position, not identity. Under the 2.0 run structure the waves filling each slot are /// drawn at random from a phase pool, so "the gold for wave 3" means the run's third /// encounter, whatever spawns in it. Payouts are a property of how far the players have /// progressed — not of which enemy they happened to draw. /// [Serializable] public class EncounterGoldEntry { [Tooltip("Gold awarded to the killing player per enemy slain during this encounter. " + "Applies uniformly to every enemy type.")] public int GoldPerEnemy = 10; [Tooltip("Flat bonus gold awarded to every active player when this encounter is fully " + "cleared (all enemies dead or reached the goal).")] public int CompletionBonus = 50; [Tooltip("Extra bonus gold awarded to a player who cleared this encounter without any " + "enemy from their own zone escaping their leak volume. Stacks on top of " + "CompletionBonus.")] public int NoLeaksBonus = 50; /// /// Guaranteed gold for a player who clears this encounter: the two bonuses, with no /// kill income counted. /// /// /// Kill income can't be known here — the wave occupying this slot is drawn at runtime, so /// its enemy count varies per run. /// supplies an estimate for the inspector's projected totals. /// public int GuaranteedBonusGold => CompletionBonus + NoLeaksBonus; /// /// Projected best-case earnings for one player this encounter, given an assumed enemy /// count. Editor tuning aid only — nothing at runtime reads it. /// public int ProjectedTotal(int assumedEnemyCount) => GoldPerEnemy * Mathf.Max(0, assumedEnemyCount) + GuaranteedBonusGold; } /// /// Single source of truth for every gold-related tunable in the game. /// /// /// Wiring. Assign one GoldConfig asset to WaveManager.goldConfig in the Match /// scene. WaveManager seeds per-player starting gold from , reads /// for kill rewards, and awards the completion /// and no-leak bonuses as each encounter clears. /// /// No per-enemy-type bounties. Every enemy killed in a given encounter grants the /// same reward regardless of . With enemy types now drawn at /// random per phase, per-type bounties would make income depend on the luck of the draw /// rather than on progression. /// [CreateAssetMenu(fileName = "GoldConfig", menuName = "TD/Gold Config", order = 2)] public class GoldConfig : ScriptableObject { [Tooltip("Gold each player starts the match with. Same for every player.")] public int StartingGold = 100; [Tooltip("Gold rules per encounter, in run order. Element 0 = the run's first encounter. " + "Encounters are counted continuously — cycles do NOT restart the count — so one " + "phase of 5 waves × 3 cycles + a boss needs 16 entries. Missing entries pay " + "zero for that encounter.")] [FormerlySerializedAs("Waves")] public EncounterGoldEntry[] Encounters; [Header("Editor preview")] [Tooltip("Assumed enemies per encounter, used ONLY to project totals in this inspector. " + "Nothing at runtime reads it — the real count comes from whichever wave the run " + "draws into each slot.")] [Min(0)] public int PreviewEnemiesPerEncounter = 40; /// /// Returns the gold rules for the given 1-based encounter number /// (RunState.GlobalEncounterNumber), or null if out of range. /// /// /// Keyed by run position, not by wave slot within a cycle. The same five waves come /// round three times per phase, so a slot-keyed table would pay the same on cycle 3 as on /// cycle 1 while the enemies grew steadily stronger from the buffs voted onto them. A /// run-long counter lets payouts climb with the difficulty. /// public EncounterGoldEntry GetEncounterEntry(int encounterNumber) { if (Encounters == null) return null; int index = encounterNumber - 1; if (index < 0 || index >= Encounters.Length) return null; return Encounters[index]; } /// Number of authored encounter entries. public int EncounterCount => Encounters?.Length ?? 0; /// /// Editor tuning aid: cumulative projected earnings for one player from the run's first /// encounter through inclusive, assuming /// kills each time and no leaks. /// public int ProjectedCumulativeThrough(int encounterNumber) { if (Encounters == null) return StartingGold; int total = StartingGold; int last = Mathf.Min(encounterNumber, Encounters.Length); for (int i = 0; i < last; i++) { if (Encounters[i] == null) continue; total += Encounters[i].ProjectedTotal(PreviewEnemiesPerEncounter); } return total; } } }