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

Prestige Module Documentation

Overview

The prestige/ subfolder implements the rebirth/reset system and the prestige buff graph — permanent upgrades purchasable with prestige currency that persist across resets.

Files

File Purpose
prestige_config.gd Configuration resource for prestige rules (PrestigeConfig)
prestige_manager.gd Core prestige state, gain calculation, and reset orchestration (PrestigeManager)
prestige_buff_node.gd Single node in the prestige buff graph (PrestigeBuffNode)
prestige_buff_catalogue.gd Flat list of all prestige buff nodes (PrestigeBuffCatalogue)
prestige_buff_graph_tile.gd Individual tile UI for a buff node (PrestigeBuffGraphTile)
prestige_buff_graph_tile.tscn Tile scene (icon, name, effect, cost, state)
prestige_buff_graph_panel.gd Full-screen panel displaying the buff graph (PrestigeBuffGraphPanel)
prestige_panel.gd UI panel for prestige reset interaction (PrestigePanel)
prestige_panel.tscn Prestige panel scene
prestige_progress_bar.gd Progress bar toward next prestige threshold
prestige_progress_bar.tscn Progress bar scene
TECH_SPEC.md Detailed technical specification (historical design doc)

Architecture

LevelGameState (Node)
├── PrestigeManager (Node)
│   ├── Tracks prestige currency & reset count
│   ├── Calculates pending gain from config
│   └── Orchestrates reset via GameState
├── PrestigePanel (UI)
│   ├── Shows current/pending prestige & multiplier
│   └── Reset button with confirmation dialog
├── PrestigeProgressBar (UI)
│   └── Visual progress toward next threshold
├── PrestigeBuffGraphPanel (UI)
│   ├── Ascension currency balance
│   └── Tiered rows of PrestigeBuffGraphTile nodes
└── prestige_buff_catalogue (PrestigeBuffCatalogue)
	└── Array of PrestigeBuffNode resources

PrestigeManager is not an autoload — it is a child node of LevelGameState. The game state finds it via find_child("PrestigeManager") in _ready().


Part 1 — Prestige Reset System

PrestigeConfig

Resource defining prestige rules.

Enums

enum BasisType {
	LIFETIME_TOTAL,  # All-time currency acquired (single currency)
	RUN_TOTAL,       # Currency this run (single currency)
	RUN_MAX,         # Peak currency this run (single currency)
	ALL_CURRENCIES   # Sum of all currencies (multi-currency tracking)
}

enum FormulaType {
	POWER,                # gain = scale * (ratio^exponent)
	TRIANGULAR_INVERSE    # gain based on triangular number inverse
}

enum RoundingMode {
	FLOOR, ROUND, CEIL
}

enum MultiplierMode {
	ADDITIVE,                    # +X per prestige
	MULTIPLICATIVE_POWER         # x^(multiplier_per_prestige)
}

Key Properties

@export var id: StringName = &"primary"
@export var display_name: String = "Ascension"
@export var prestige_currency_id: StringName = &"prestige"
@export var source_currency_id: StringName = &"magic"  # What to measure (ignored for ALL_CURRENCIES)
@export var basis: BasisType = LIFETIME_TOTAL
@export var formula: FormulaType = POWER

# Currency tracking
@export var include_currency_ids: Array[StringName] = []  # Empty = all currencies (for ALL_CURRENCIES basis)

# Threshold: minimum source currency needed
@export var threshold_mantissa: float = 1.0
@export var threshold_exponent: int = 4  # 1e4 = 10,000

# Formula parameters
@export var scale: float = 1.0
@export var exponent: float = 0.5  # Square root

# Multiplier growth
@export var multiplier_per_prestige: float = 0.05  # +5% per prestige
@export var multiplier_mode: MultiplierMode = ADDITIVE

PrestigeManager

Child node of LevelGameState managing prestige state.

Signals

signal prestige_state_changed(total_prestige, pending_gain)
signal prestige_performed(gain, total_prestige)
signal prestige_threshold_changed(next_threshold)

State Variables

var total_prestige_earned: BigNumber    # Lifetime prestige
var current_prestige_unspent: BigNumber # Available to spend
var prestige_resets_count: int          # How many resets
var run_start_total_source_currency: BigNumber  # Track run start
var run_peak_source_currency: BigNumber         # Peak this run

Key Methods

# Query state
func get_total_prestige() -> BigNumber
func get_current_prestige_unspent() -> BigNumber
func calculate_pending_gain() -> BigNumber
func can_prestige() -> bool
func get_total_multiplier() -> float
func get_next_prestige_threshold() -> BigNumber

# Perform prestige
func perform_prestige() -> bool

Gain Calculation

Power Formula (default):

ratio = basis_value / threshold
raw_gain = scale * (ratio^exponent) + flat_bonus

# Cumulative basis subtracts already earned
if basis == LIFETIME_TOTAL:
	gain = raw_gain - total_prestige_earned

Triangular Inverse Formula:

# Based on inverse triangular number: n = (sqrt(1 + 8k) - 1) / 2
k = basis_value / threshold
target_total = (sqrt(1 + 8k) - 1) * 0.5

if basis == LIFETIME_TOTAL:
	gain = target_total - total_prestige_earned

Basis Value Calculation:

if basis == ALL_CURRENCIES:
	# Sum of all tracked currencies
	basis_value = sum(get_total_currency_acquired(currency_id) for currency_id in tracked_currencies)
elif basis == RUN_TOTAL:
	# Currency earned since last reset
	basis_value = lifetime_total - run_start_total
elif basis == RUN_MAX:
	# Peak currency reached this run
	basis_value = run_peak_currency
else:  # LIFETIME_TOTAL
	# All-time currency acquired
	basis_value = lifetime_total

Multiplier Application

Additive Mode:

multiplier = base_multiplier + (total_prestige * multiplier_per_prestige)
# e.g., 1.0 + (5 * 0.05) = 1.25 (25% bonus)

Multiplicative Power Mode:

growth_base = 1.0 + multiplier_per_prestige
multiplier = base_multiplier * (growth_base ^ total_prestige)
# e.g., 1.0 * (1.05 ^ 5) = 1.276 (27.6% bonus)

Prestige Reset Flow

  1. Player clicks "Prestige" button
  2. perform_prestige() called
  3. Gain calculated and added to total
  4. Prestige currency granted via game_state.add_currency_by_id()
  5. game_state.reset_for_prestige() called (preserves prestige currency, resets generators, currencies, buff levels, goals)
  6. _reset_all_buff_levels() — clears generator buff levels (NOT prestige buff unlocks)
  7. Generator runtime state reinitialized
  8. Run tracking reinitialized
  9. game_state.emit_currency_changed_for_all() — triggers UI refresh
  10. Save triggered

PrestigePanel

UI component for prestige interaction.

Displays

_total_value.text       # Total prestige earned (e.g. "123.45")
_pending_value.text     # Gain available now
_multiplier_value.text  # Current multiplier (e.g. "x2.34")
_basis_label.text       # Basis + value (e.g. "Total Currency (1.50e6)")

Buttons

  • Reset: Triggers perform_prestige() after confirmation dialog
  • Save: Manually saves game state

Generator Integration

Generators apply prestige multipliers via PrestigeManager.get_total_multiplier():

func _get_prestige_multiplier() -> float:
	var parent: Node = get_parent()
	var prestige_manager: PrestigeManager = parent.find_child("PrestigeManager", true, false)
	if prestige_manager:
		return prestige_manager.get_total_multiplier()
	return 1.0

This is called from get_effective_auto_run_multiplier() alongside research and buff multipliers. In the future, prestige buff multipliers from the graph system will replace or augment this.

Save Integration

Prestige state stored in LevelGameState external save:

{
  "prestige_state": {
	"total_prestige_earned": {"m": 10.0, "e": 0},
	"current_prestige_unspent": {"m": 5.0, "e": 0},
	"prestige_resets_count": 3,
	"run_start_total_source_currency": {"m": 1000.0, "e": 0},
	"run_peak_source_currency": {"m": 5000.0, "e": 0},
	"last_reset_time": 1234567890
  }
}

Configuration Example

For a typical idle game with single currency:

id = &"primary"
display_name = "Ascension"
source_currency_id = &"magic"
basis = LIFETIME_TOTAL
threshold_mantissa = 1.0
threshold_exponent = 6   # 1e6
formula = POWER
exponent = 0.5           # Square root
multiplier_mode = ADDITIVE
multiplier_per_prestige = 0.10

For multi-currency tracking (ALL_CURRENCIES):

id = &"ascension"
display_name = "Ascension"
prestige_currency_id = &"ascension"
basis = ALL_CURRENCIES
include_currency_ids = []  # Empty = all currencies from catalogue
threshold_mantissa = 1.0
threshold_exponent = 4     # 1e4
formula = POWER
exponent = 0.5
multiplier_mode = ADDITIVE
multiplier_per_prestige = 0.05

Part 2 — Prestige Buff Graph

Overview

The prestige buff graph is a permanent upgrade system where players spend prestige currency to unlock nodes in a DAG-structured graph. Unlike generator buffs (which reset on prestige), prestige buff unlocks are permanent and survive resets.

PrestigeBuffNode

Single node in the graph. Each node is a one-shot unlock — either locked or unlocked, no repeatable levels.

EffectType Enum

enum EffectType {
	CURRENCY_PRODUCTION_MULTIPLIER,  # Multiply currency output by x%
	BUILDING_CREATION_BONUS,         # When a building is created, increase currency gain by x%
	WORKER_THRESHOLD_MULTIPLIER,     # When 10+ workers are assigned to a building, increase currency gain by x%
	CARRYOVER_BUILDINGS,             # After prestige, start with some buildings and workers already assigned
	UNLOCK_AUTO_BUY_WORKER,          # Unlock option to auto-buy workers
	HOVER_SPEED_BOOST,               # Hovering over a place speeds up workers instead of clicking
	UNLOCK_BUILDING,                 # Unlock a new building (generator)
	WORKER_PRODUCTIVITY,             # Increase worker productivity by x%
	RESEARCH_XP_MULTIPLIER,          # Increase research XP gain by x%
}

Exported Fields

Field Type Notes
id StringName Unique node identifier
display_name String UI label
description String (multiline) Tooltip
icon Texture2D Icon in graph UI
parent_ids Array[StringName] Prerequisites (DAG — multiple parents allowed)
effect_type EffectType What this buff does
effect_value float Effect magnitude (multiplier or count depending on type)
target_id StringName Target building/currency for type-specific effects (empty = global)
cost_mantissa float Prestige currency cost mantissa
cost_exponent int Prestige currency cost exponent
tier int Row in graph UI (visual grouping)
x_position float Column in graph UI

Key Methods

func get_cost() -> BigNumber                        # BigNumber from mantissa/exponent
func has_parents() -> bool                          # True if parent_ids is non-empty
func all_parents_unlocked(unlocked: Dictionary) -> bool  # All prerequisites met?
func is_valid() -> bool                             # Non-empty id and positive cost

PrestigeBuffCatalogue

Flat resource containing all buff nodes. The graph structure emerges from parent_ids.

Field Type
nodes Array[PrestigeBuffNode]

Key Methods

func get_node_by_id(id: StringName) -> PrestigeBuffNode
func get_all_ids() -> Array[StringName]
func get_root_nodes() -> Array[PrestigeBuffNode]    # Nodes with empty parent_ids
func get_children_of(parent_id: StringName) -> Array[PrestigeBuffNode]

State in LevelGameState

@export var prestige_buff_catalogue: PrestigeBuffCatalogue
var _prestige_buff_unlocked: Dictionary = {}  # {StringName: bool} — node_id → unlocked
  • Never cleared by reset_for_prestige() (unlike _buff_levels which IS cleared)
  • Normalized on load: missing catalogue node IDs are filled as false
  • On initialization, iterates the catalogue and sets any missing entries to false

LevelGameState API

# Purchase and query
func purchase_prestige_buff(buff_id: StringName) -> bool
func can_purchase_prestige_buff(buff_id: StringName) -> bool
func is_prestige_buff_unlocked(buff_id: StringName) -> bool
func get_prestige_buff_cost(buff_id: StringName) -> BigNumber
func get_prestige_currency_id() -> StringName           # Returns &"ascension"

# Multiplier computation — walks graph, returns combined product from unlocked nodes
func get_prestige_buff_multiplier(effect_type: int, target_id: StringName = &"") -> float

# Available nodes (prerequisites met, not yet unlocked)
func get_available_prestige_buffs() -> Array[PrestigeBuffNode]

Signal

signal prestige_buff_unlocked(buff_id: StringName)

Prestige currency balance changes use the existing currency_changed signal — no separate signal needed.

Purchase Flow

Player clicks unlock node
  → can_purchase_prestige_buff(node_id)
	→ Node exists in catalogue?
	→ Not already unlocked?
	→ All parent_ids unlocked?
	→ Enough prestige currency?
  → YES: spend_currency_by_id("ascension", cost)
  → _prestige_buff_unlocked[node_id] = true
  → emit prestige_buff_unlocked
  → save_game()

Multiplier Computation

get_prestige_buff_multiplier(effect_type, target_id) iterates all unlocked nodes matching the given effect type and multiplies their effect_value together. Results start at 1.0.

  • If target_id is empty: all matching nodes are included (global effect)
  • If target_id is set: only nodes with empty or matching target_id are included

Multiplier Usage In Generators

Generators should call get_prestige_buff_multiplier() with their generator ID:

var asc_mult: float = game_state.get_prestige_buff_multiplier(
	PrestigeBuffNode.EffectType.CURRENCY_PRODUCTION_MULTIPLIER,
	get_generator_id()
)

This replaces the old _get_prestige_multiplier() call for prestige buff-driven production bonuses.

Save Format

  • Save format version bumped to 8 when prestige buffs were introduced.
  • Key: "prestige_buff_unlocked" → array of unlocked node ID strings: ["gold_boost", "farm_boost"]
  • Load deserialization reads the array, sets matching entries to true, then calls _initialize_prestige_buffs() to fill in missing entries as false.

UI — PrestigeBuffGraphPanel

A PanelContainer-based separate screen.

  • Layout: Tiered rows (HBoxContainer per tier), sorted by tier then x_position
  • Tiles (PrestigeBuffGraphTile): show icon, name, effect description, cost, state
  • States:
    • Locked (greyed out): prerequisites not met
    • Available (highlighted, button enabled): all prerequisites met, can purchase
    • Unlocked (green check, button disabled): already owned
  • Balance: Shows current prestige currency balance at top, refreshes on currency_changed
  • Graph rebuild: Rebuilds entire graph on prestige_buff_unlocked to reflect newly available children

Content Files

Prestige buff content lives in docs/gyms/tiny_sword/prestige/:

File Purpose
prestige_buff_catalogue.tres Catalogue resource referencing all buff nodes
prestige_buff_gold_boost.tres Example: +50% gold mine production
prestige_buff_farm_boost.tres Example: +50% farm food production

Each .tres is a PrestigeBuffNode resource with exported fields set in the inspector.

Prestige Reset And Buffs

  • Generator buffs (_buff_levels, _buff_unlocked, _buff_active): reset to 0/false on prestige
  • Prestige buff unlocks (_prestige_buff_unlocked): never reset — permanent across all prestige resets