# Prestige System - Technical Specification ## Document Status 1. Version: 1.0 2. Date: 2026-03-21 3. Scope: Add a configurable prestige reset loop with editor-authored curve math and permanent run multipliers. ## Problem Statement The prototype currently has growth, buffs, and unlock goals, but no reset loop that converts late-run progress into long-term power. Without prestige, the economy has no designed "ladder climb" cycle and balancing eventually relies only on direct cost/production tuning. ## Design Goals 1. Support a prestige reset that grants a permanent currency based on player progress. 2. Keep prestige math data-driven and editable in the Godot inspector for balancing iteration. 3. Support both major prestige archetypes from Kongregate's analysis: - Lifetime or max-stat based curves (diminishing gains when resetting at the same point repeatedly). - Since-reset/run-only curves (same run depth gives similar rewards each reset). 4. Integrate with existing systems (`GameState`, `BigNumber`, generator/buff runtime) without hardcoding one formula. 5. Persist all prestige state in `user://idle_save.json` alongside existing runtime data. ## Non-Goals (For First Iteration) 1. Multiple prestige currencies. 2. Prestige skill tree/shop UI. 3. Offline simulation changes beyond applying the resulting permanent multiplier. ## Kongregate-Informed Curve Model From "The Math of Idle Games, Part III", prestige formulas can be modeled as transformed progress stats with fractional exponents. Core variables: 1. `delta_p`: prestige currency gained on reset. 2. `p`: total prestige currency owned. 3. `c_L`: lifetime currency earned. 4. `c_M`: max currency reached in a run. 5. `c_R`: currency earned since last reset. Supported formula families for this system: 1. Power curve (generic): `f(x) = scale * (x / threshold)^exponent`. 2. Triangular inverse sqrt (Realm Grinder style): solve `x = threshold * p(p+1)/2` for `p`. 3. Log-like compression (future option, disabled by default). Behavior groups: 1. Cumulative-stat formulas (`c_L`, `c_M`) compute target total prestige then grant `target_total - current_total`. 2. Run-only formulas (`c_R`) compute direct gain per run. ## Proposed Runtime Architecture 1. Add `PrestigeManager` autoload (`core/prestige/prestige_manager.gd`) as the single authority for prestige state and reset flow. 2. Add `PrestigeConfig` resource (`core/prestige/prestige_config.gd`) authored in inspector and loaded from data assets. 3. `GameState` remains owner of currencies/generator state; `PrestigeManager` orchestrates reset and applies permanent multiplier. 4. Generators read the active prestige multiplier through one accessor (`PrestigeManager.get_total_multiplier()`), then multiply existing `run_multiplier` behavior. ## Data Model ### PrestigeConfig (Resource) Editor-authored resource fields (all exported): 1. `id: StringName`. 2. `display_name: String`. 3. `prestige_currency_id: StringName` (logical id, not regular wallet currency). 4. `source_currency_id: StringName` (which gameplay currency powers prestige calculation). 5. `basis: BasisType` enum: - `LIFETIME_TOTAL` - `RUN_TOTAL` - `RUN_MAX` 6. `formula: FormulaType` enum: - `POWER` - `TRIANGULAR_INVERSE` 7. `threshold_mantissa: float` and `threshold_exponent: int` (BigNumber-like threshold). 8. `scale: float`. 9. `exponent: float` (used by `POWER`). 10. `flat_bonus: float`. 11. `minimum_gain: int`. 12. `rounding_mode: RoundingMode` enum (`FLOOR`, `ROUND`, `CEIL`). 13. `allow_prestige_without_gain: bool`. 14. Multiplier tuning: - `multiplier_mode: MultiplierMode` enum (`ADDITIVE`, `MULTIPLICATIVE_POWER`). - `base_multiplier: float`. - `multiplier_per_prestige: float`. - `multiplier_exponent: float`. ### Prestige Runtime State Persisted by `PrestigeManager`: 1. `total_prestige_earned: BigNumber`. 2. `current_prestige_unspent: BigNumber`. 3. `prestige_resets_count: int`. 4. `run_start_total_source_currency: BigNumber`. 5. `run_peak_source_currency: BigNumber`. 6. `last_reset_time: int`. ## Formula Specification ### 1) Basis Value Extraction Given configured `source_currency_id`: 1. `LIFETIME_TOTAL`: `basis_value = GameState.get_total_currency_acquired_by_id(source_currency_id)`. 2. `RUN_TOTAL`: `basis_value = lifetime_now - run_start_total_source_currency`. 3. `RUN_MAX`: `basis_value = run_peak_source_currency`. ### 2) Raw Gain 1. POWER: `raw = scale * pow(basis_value / threshold, exponent) + flat_bonus`. 2. TRIANGULAR_INVERSE: - `k = basis_value / threshold` - `target = (sqrt(1 + 8k) - 1) / 2` - if basis is cumulative (`LIFETIME_TOTAL`, `RUN_MAX` interpreted as cumulative target), `raw = target - total_prestige_earned` - if basis is run-only, `raw = target` ### 3) Post-Processing 1. Clamp negative to zero. 2. Apply configured rounding. 3. Enforce `minimum_gain` when basis passed threshold and gain > 0. ### 4) Multiplier Application `get_total_multiplier()` returns one of: 1. Additive mode: `base_multiplier + total_prestige_earned * multiplier_per_prestige`. 2. Multiplicative power mode: `base_multiplier * pow(1 + multiplier_per_prestige, pow(total_prestige_earned, multiplier_exponent))`. Implementation note: compute large-number powers via `log10` transforms against `BigNumber` to avoid float overflow. ## Reset Flow 1. Player presses prestige button. 2. `PrestigeManager.calculate_pending_gain()` computes gain from configured curve. 3. If gain is zero and `allow_prestige_without_gain` is false, block reset. 4. On confirm: - Add gain to `total_prestige_earned` and `current_prestige_unspent`. - Increment `prestige_resets_count`. - Reset runtime progression (`GameState` currencies, generator owned counts, generator availability that should restart locked). - Preserve permanent unlocks only if explicitly marked as persistent in future extensions. - Reinitialize run-tracking values (`run_start_total_source_currency`, `run_peak_source_currency`). - Emit `prestige_performed(gain, total_prestige)` signal. ## GameState Integration 1. Add APIs for reset-safe state initialization (single call from `PrestigeManager` instead of duplicating clear logic). 2. Keep total-acquired currency behavior explicit: - Option A (recommended): reset current currencies and generator ownership, keep lifetime totals for `LIFETIME_TOTAL` basis. - Option B: clear totals too and rely on dedicated lifetime accumulator in prestige state. 3. Ensure generator/buff registrations are idempotent after reset (`_ensure_registered` paths already support this pattern). ## Editor Authoring And Balancing Workflow 1. Create `idles/prestige/primary_prestige.tres` using `PrestigeConfig`. 2. Designers tune only exported fields in inspector (no code edits). 3. Provide presets matching Kongregate article examples: - Realm Grinder-like: `basis=RUN_MAX`, `formula=TRIANGULAR_INVERSE`, `threshold=1e12`. - AdCap-like: `basis=LIFETIME_TOTAL`, `formula=POWER`, `scale=150`, `threshold=1e15`, `exponent=0.5`. - Cookie Clicker-like: `basis=LIFETIME_TOTAL`, `formula=POWER`, `threshold=1e12`, `exponent=0.333333`. - Egg Inc-like: `basis=RUN_TOTAL`, `formula=POWER`, `threshold=1e6`, `exponent=0.14`. 4. Balance target guidance: - For sqrt-like curves, doubling prestige generally needs roughly 3x-4x deeper runs. - For cube-root curves, doubling prestige tends toward ~8x deeper runs. - For ~1/7 exponent run curves, doubling prestige needs ~128x deeper runs. ## UI Requirements 1. Prestige panel must display: - current prestige total - pending gain (`delta_p`) - resulting multiplier after reset - basis stat currently used (`c_L`, `c_R`, or `c_M` equivalent) 2. Reset CTA disabled when pending gain is zero and config disallows zero-gain resets. 3. Confirmation dialog explicitly lists what resets and what is kept. ## Persistence 1. Extend save payload with `prestige_state` object. 2. Serialize all `BigNumber` values with existing `BigNumber.serialize()` pattern. 3. During load, validate dictionaries and apply defaults if fields are missing. 4. Maintain backward compatibility with pre-prestige saves by creating default prestige state when absent. ## Validation Plan 1. Headless parse: `"$GODOT_BIN" --headless --path . --quit`. 2. Script checks once implemented: - `"$GODOT_BIN" --headless --check-only --script res://core/prestige/prestige_manager.gd` - `"$GODOT_BIN" --headless --check-only --script res://core/prestige/prestige_config.gd` 3. Manual smoke cases: - same reset point in cumulative mode yields reduced/zero incremental gain. - same reset point in run-only mode yields roughly same gain. - multiplier updates production immediately after reset. - prestige state survives save/load. ## Implementation Milestones 1. Introduce data classes (`PrestigeConfig`) and one authored `.tres` profile. 2. Implement `PrestigeManager` with gain math + persistence. 3. Add reset integration entrypoint in `GameState`. 4. Wire generator production multiplier to prestige. 5. Add initial prestige UI panel and confirmation flow.