Files
idle/core/prestige/TECH_SPEC.md
2026-03-22 10:50:37 +01:00

8.9 KiB

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.