This commit is contained in:
2026-04-04 16:04:51 +02:00
parent eaa8f18a4a
commit cd23125739
9 changed files with 870 additions and 7 deletions

117
core/README.md Normal file
View File

@@ -0,0 +1,117 @@
# Core Module Documentation
## Overview
The `core/` directory contains the fundamental systems for the idle game. It provides:
- **Numeric Foundation**: `BigNumber` for handling huge idle game values
- **Game State Management**: `GameState` autoload for all persistent state
- **Currency System**: Database and tracking for multiple currency types
- **Generator System**: Automated resource production with scaling costs
- **Buff System**: Upgrades that modify generator behavior
- **Goal System**: Achievement tracking and unlock triggers
- **Prestige System**: Rebirth mechanics with permanent multipliers
## Architecture
```
core/
├── big_number.gd # Core numeric API
├── game_state.gd # Main state management autoload
├── buff_database.gd # Buff registration system
├── currency_database.gd # Currency registration system
├── currency/ # Currency data structures
├── generator/ # Generator logic and data
├── goals/ # Goal/achievement system
└── prestige/ # Prestige/rebirth system
```
## Data Flow
1. **Game Start**: `GameState._ready()` initializes currencies, loads save
2. **Player Action**: Generator bought → `GameState` updated → signals emitted
3. **UI Reaction**: Listeners respond to signals → update display
4. **Goal Check**: Currency changed → goals evaluated → buffs unlocked
5. **Save**: `GameState.save_game()` → JSON to `user://idle_save.json`
## Save Format
**Version**: 3
```json
{
"save_format_version": 3,
"currencies": {
"<currency_id>": {
"current": {"m": float, "e": int},
"total": {"m": float, "e": int},
"all_time": {"m": float, "e": int}
}
},
"generator_states": {
"<generator_id>": {
"owned": int,
"purchased_count": int,
"unlocked": bool,
"available": bool
}
},
"buff_levels": {
"<buff_id>": int
},
"buff_unlocked": {
"<buff_id>": true
},
"buff_active": {
"<buff_id>": true
},
"goals": {
"completed": ["<goal_id>", ...]
},
"last_save_time": unix_timestamp
}
```
## Key Signals
| Signal | Emitted When |
|--------|--------------|
| `currency_changed(currency_id, new_amount)` | Any currency balance changes |
| `generator_state_changed(generator_id, state)` | Generator owned/unlocked changes |
| `generator_buff_level_changed(gen_id, buff_id, level)` | Buff level increased |
| `buff_unlocked_changed(buff_id, unlocked)` | Buff unlock state changed |
| `goal_completed(goal_id)` | Goal criteria met |
## BigNumber Serialization
All large numbers use scientific notation internally:
```gdscript
# Value = mantissa * 10^exponent
var num = BigNumber.new(1.5, 24) # 1.5e24
var dict = num.serialize() # {"m": 1.5, "e": 24}
var restored = BigNumber.deserialize(dict)
```
## Extending the Core
### Adding a New Currency
1. Create `res://sandbox/currencies/<name>.tres` extending `Currency`
2. Set `id`, `display_name`, and `icon`
3. Auto-discovered by `CurrencyDatabase`
### Adding a New Generator
1. Create `res://sandbox/generators/<name>.tres` extending `CurrencyGeneratorData`
2. Configure costs, production, and unlock goals
3. Instance `CurrencyGenerator` scene with this data
### Adding a New Buff
1. Create `res://sandbox/buffs/<name>.tres` extending `GeneratorBuffData`
2. Set `target_ids`, `kind`, `effect_increment`, and `cost`
3. Auto-discovered by `BuffDatabase`
## See Also
- `currency/` - Currency data structures
- `generator/` - Generator production system
- `goals/` - Achievement system
- `prestige/` - Rebirth mechanics

84
core/currency/README.md Normal file
View File

@@ -0,0 +1,84 @@
# Currency Module Documentation
## Overview
The `currency/` subfolder contains the data structure for defining in-game currencies.
## Files
### `currency.gd`
Defines the `Currency` class - a Resource for currency metadata.
## Currency Class
```gdscript
class_name Currency
extends Resource
@export var id: StringName = &"" # Unique identifier (e.g., &"gold")
@export var display_name: String = "" # UI-friendly name
@export var icon: Texture2D # Display icon
```
## Usage
### Defining a Currency
Create a `.tres` resource file with:
- **id**: Lowercase unique key (used internally)
- **display_name**: What players see ("Gold", "Gems")
- **icon**: Texture for UI display
Example `res://sandbox/currencies/gold.tres`:
```
[gd_resource type="Resource" script_class="Currency"]
[resource]
id = &"gold"
display_name = "Gold"
icon = ExtResource("uid://...")
```
### Accessing Currencies
```gdscript
# Get all known currencies
var currencies: Array[Currency] = CurrencyDatabase.get_known_currencies()
# Get currency by ID
var gold: Currency = CurrencyDatabase.get_currency_resource(&"gold")
# Check if currency exists
var is_valid: bool = CurrencyDatabase.is_known_currency_id(&"gems")
# Get display name
var name: String = CurrencyDatabase.get_currency_name(&"gold")
```
## Integration with GameState
Currencies are tracked in three ways:
1. **current**: Current balance (resets on prestige)
2. **total**: Total acquired this run (resets on prestige)
3. **all_time**: Lifetime total (never resets)
```gdscript
# Add currency
GameState.add_currency(gold_resource, BigNumber.new(100.0, 0))
# Get balance
var balance: BigNumber = GameState.get_currency_amount_by_id(&"gold")
# Spend currency (returns false if insufficient)
var success: bool = GameState.spend_currency_by_id(&"gold", BigNumber.new(50.0, 0))
```
## Auto-Discovery
`CurrencyDatabase` automatically scans `res://sandbox/currencies/` for `.tres` and `.res` files on startup.
## See Also
- `core/game_state.gd` - Currency state management
- `core/big_number.gd` - Large number handling
- `generator/currency_generator_data.gd` - Generator purchase currency

View File

@@ -721,7 +721,10 @@ func _get_generator_state(generator_id: StringName) -> Dictionary:
var existing_raw: Variant = generator_states.get(key, _default_generator_state(false, false, 0))
if existing_raw is Dictionary:
var state: Dictionary = existing_raw
var sanitized: Dictionary = _sanitize_generator_state(state, false, false, 0)
var current_unlocked: bool = state.get(GENERATOR_UNLOCKED_KEY, false)
var current_available: bool = state.get(GENERATOR_AVAILABLE_KEY, false)
var current_owned: int = state.get(GENERATOR_OWNED_KEY, 0)
var sanitized: Dictionary = _sanitize_generator_state(state, current_unlocked, current_available, current_owned)
generator_states[key] = sanitized
return sanitized
@@ -734,7 +737,10 @@ func _set_generator_state(generator_id: StringName, state: Dictionary) -> void:
if key.is_empty():
return
var sanitized: Dictionary = _sanitize_generator_state(state, false, false, 0)
var current_unlocked: bool = state.get(GENERATOR_UNLOCKED_KEY, false)
var current_available: bool = state.get(GENERATOR_AVAILABLE_KEY, false)
var current_owned: int = state.get(GENERATOR_OWNED_KEY, 0)
var sanitized: Dictionary = _sanitize_generator_state(state, current_unlocked, current_available, current_owned)
generator_states[key] = sanitized
generator_state_changed.emit(StringName(key), sanitized.duplicate(true))
@@ -1098,9 +1104,30 @@ func _try_complete_goal(goal_id: StringName) -> void:
if _goal_completed[goal_id]:
return
if goal.is_met():
if _is_goal_manual_unlock(goal_id):
return
_goal_completed[goal_id] = true
goal_completed.emit(goal_id)
func _is_goal_manual_unlock(goal_id: StringName) -> bool:
var goal: GoalData = _goal_definitions.get(goal_id, null)
if goal == null or not goal.has_id():
return false
var scene_root: Node = get_tree().current_scene
if scene_root == null:
return false
var gen_nodes: Array[Node] = scene_root.find_children("*", "CurrencyGenerator", true, false)
for node in gen_nodes:
if node.has_method("get_generator_id") and node.has_method("data"):
var data: Variant = node.get("data")
if data != null and data.has_method("has_unlock_goal") and data.has_method("unlocks_automatically_from_goal"):
if data.has_unlock_goal() and data.get_unlock_goal_id() == goal_id:
return not data.unlocks_automatically_from_goal()
return false
func _serialize_goals() -> Dictionary:
var result: Dictionary = {}
var completed_goals: Array[StringName] = []

204
core/generator/README.md Normal file
View File

@@ -0,0 +1,204 @@
# Generator Module Documentation
## Overview
The `generator/` subfolder contains the core idle game mechanics: automated resource production with scaling costs, upgrades (buffs), and manual interaction.
## Files
| File | Purpose |
|------|---------|
| `currency_generator.gd` | Runtime generator logic (Node2D) |
| `currency_generator_data.gd` | Balancing data resource |
| `generator_buff_data.gd` | Buff/upgrade definitions |
| `generator_container.gd` | UI panel for generator stats |
## Architecture
```
CurrencyGenerator (Node2D)
├── Handles production cycles
├── Manages purchases
├── Processes clicks/hover
└── GeneratorPanel (UI)
├── Shows stats/costs
└── Buff upgrade buttons
```
## CurrencyGeneratorData
Resource class for generator balancing data.
### Key Properties
```gdscript
@export var id: StringName = &"" # Unique identifier
@export var name: String = "" # Display name
@export var starts_unlocked: bool = true # Visible at game start
@export var purchase_currency: Currency # What to spend
@export var unlock_goal: GoalData # Goal to unlock this
# Cost formula: cost = base * coefficient^owned
@export var initial_cost: float = 10.0 # Base cost
@export var coefficient: float = 1.15 # Growth factor
# Production
@export var initial_time: float = 1.0 # Seconds per cycle
@export var initial_productivity: float = 1.0 # Resources per cycle
# Milestones
@export var milestone_step: int = 25 # Every N units
@export var milestone_multiplier: float = 2.0 # x multiplier
# Manual click
@export var click_mantissa: float = 1.0
@export var click_exponent: int = 0
@export var click_cooldown_seconds: float = 0.2
```
### Cost Formula
**Bulk Buy Cost** (geometric series):
```
total_cost = base * r^owned * ((r^n - 1) / (r - 1))
```
Where:
- `base` = initial_cost
- `r` = coefficient
- `owned` = current count
- `n` = amount to buy
**Max Buy** (inverse formula):
```
max = floor(log((currency * (r-1) / (base * r^owned)) + 1) / log(r))
```
### Production Formula
```gdscript
production_per_cycle = initial_productivity * owned * run_multiplier * milestone_mult * purchased_mult
production_per_second = production_per_cycle / initial_time
```
## CurrencyGenerator
Runtime node instance attached to generator scenes.
### Signals
```gdscript
signal purchase_completed(amount, total_owned, total_purchased, total_cost)
signal production_tick(amount, cycle_count, total_owned)
signal buff_purchased(buff_id, new_level, cost, cost_currency_id)
signal goal_achieved(generator_id, goal_id)
```
### Key Methods
```gdscript
# Purchase
func buy(amount: int = 1) -> bool
func buy_max() -> int
func get_cost_for_amount(amount: int) -> BigNumber
func can_buy(amount: int) -> bool
# Production
func grant_currency(amount: BigNumber)
func get_effective_auto_run_multiplier() -> float
# Buffs
func buy_buff(buff_id: StringName) -> bool
func get_buff_level(buff_id: StringName) -> int
func is_buff_unlocked(buff_id: StringName) -> bool
```
### Production Cycle
1. Accumulate `cycle_progress_seconds` in `_process()`
2. When `>= initial_time`, grant production
3. Emit `production_tick` signal
4. Multiply by:
- Owned count
- Run multiplier (export)
- Buff multipliers (auto_production)
- Milestone bonuses
- Purchase boost
## GeneratorBuffData
Defines upgradeable modifiers for generators.
### Buff Kinds
```gdscript
enum BuffKind {
AUTO_PRODUCTION_MULTIPLIER, # x output per cycle
MANUAL_CLICK_MULTIPLIER, # x click reward
RESOURCE_PURCHASE # Instant currency grant
}
```
### Key Properties
```gdscript
@export var id: StringName = &""
@export var target_ids: Array[StringName] = [] # Generators affected
@export var kind: BuffKind
@export var max_level: int = -1 # -1 = unlimited
@export var base_effect: float = 1.0 # Starting multiplier
@export var effect_increment: float = 0.1 # +per level
@export var base_cost_mantissa: float = 10.0
@export var base_cost_exponent: int = 0
@export var cost_multiplier: float = 1.5 # Cost growth
# Effect: base_effect + (effect_increment * level)
func get_effect_multiplier(level: int) -> float
```
### Cost Formula
```gdscript
cost_for_level = base_cost * cost_multiplier^level
```
### Resource Purchase Buff
Special buff type that grants instant currency:
```gdscript
resource_purchase_amount = base_amount * (resource_purchase_increment_multiplier^level)
```
## GeneratorPanel
UI component showing generator information.
### Displays
- Generator name and owned count
- Next purchase cost
- Production per second
- Buff upgrade buttons with costs
### Signals Connected
- `purchase_completed` - Refresh UI
- `production_tick` - Update production display
- `generator_buff_level_changed` - Refresh buff rows
## Prestige Integration
Generators automatically apply prestige multipliers:
```gdscript
effective_multiplier = run_multiplier * buff_multiplier * prestige_multiplier
```
On prestige:
1. `reset_runtime_state_for_prestige()` called
2. `cycle_progress_seconds` reset
3. Buff levels reset to 0
4. Unlocks re-evaluated from goals
## See Also
- `core/game_state.gd` - Generator state storage
- `goals/goal_data.gd` - Unlock conditions
- `prestige/prestige_manager.gd` - Multiplier application

View File

@@ -96,12 +96,15 @@ func _ready() -> void:
assert(data != null, "Data cannot be null")
_generator_id = _resolve_generator_id()
#_ensure_registered()
cycle_progress_seconds = 0.0
GameState.currency_changed.connect(_on_currency_changed)
GameState.generator_state_changed.connect(_on_generated_state_changed)
_evaluate_generator_unlock_goal(false)
# Register with default state first, then evaluate unlock goal
_ensure_registered()
if data != null and data.unlocks_automatically_from_goal():
_evaluate_generator_unlock_goal(false)
## Updates click cooldown/click grants and runs automatic production cycles.
func _process(delta: float) -> void:
@@ -319,7 +322,8 @@ func reset_runtime_state_for_prestige() -> void:
cycle_progress_seconds = 0.0
_remaining_click_cooldown_seconds = 0.0
_ensure_registered()
_evaluate_generator_unlock_goal(false)
if data != null and data.unlocks_automatically_from_goal():
_evaluate_generator_unlock_goal(false)
#_evaluate_buff_unlock_goals()
visible = is_available_to_player()
@@ -579,7 +583,8 @@ func _try_unlock_buff_from_goal(buff: GeneratorBuffData) -> void:
func _on_currency_changed(_changed_currency_id: StringName, _new_amount: BigNumber) -> void:
GameState.evaluate_all_goals()
_evaluate_generator_unlock_goal(false)
if data != null and data.unlocks_automatically_from_goal():
_evaluate_generator_unlock_goal(false)
func try_unlock_from_goal() -> bool:
return _evaluate_generator_unlock_goal(true)
@@ -595,12 +600,17 @@ func _evaluate_generator_unlock_goal(allow_manual_unlock: bool) -> bool:
return false
var goal_id: StringName = data.get_unlock_goal_id()
if goal_id.is_empty() or not GameState.is_goal_completed(goal_id):
if goal_id.is_empty():
return false
var goal_completed: bool = GameState.is_goal_completed(goal_id)
if not goal_completed:
return false
GameState.set_generator_unlocked(_generator_id, true)
GameState.set_generator_available(_generator_id, true)
goal_achieved.emit(_generator_id, goal_id)
_on_generated_state_changed(_generator_id, GameState._get_generator_state(_generator_id))
return true
## Default unlocked state loaded from generator data.
@@ -634,3 +644,5 @@ func _on_area_2d_mouse_exited() -> void:
func _on_generated_state_changed(generator_id: StringName, _state: Dictionary) -> void:
if generator_id == _generator_id:
visible = is_available_to_player()
if info_generator_container != null:
info_generator_container._refresh_generator_ui()

View File

@@ -147,7 +147,14 @@ func _refresh_buff_rows(can_interact: bool) -> void:
row.tile.set_runtime(level, effect_text, cost_text, can_buy, false)
func _refresh_generator_ui() -> void:
if _buy_button == null or _buy_max_button == null:
return
if _generator == null:
return
var can_interact: bool = _generator.is_available_to_player()
_buy_button.disabled = not can_interact
_buy_max_button.disabled = not can_interact

167
core/goals/README.md Normal file
View File

@@ -0,0 +1,167 @@
# Goals Module Documentation
## Overview
The `goals/` subfolder contains the achievement system. Goals track player progress and can unlock generators or buffs when conditions are met.
## Files
| File | Purpose |
|------|---------|
| `goal_data.gd` | Goal definition resource |
| `goal_requirement_data.gd` | Individual requirement criteria |
## GoalData
Resource class defining an achievement.
### Properties
```gdscript
class_name GoalData
extends Resource
@export var id: StringName = &"" # Unique identifier
@export var requirements: Array[GoalRequirementData] # Conditions to complete
```
### Methods
```gdscript
func is_valid() -> bool # Has id and valid requirements
func is_met() -> bool # All requirements satisfied
func get_progress() -> float # 0.0 to 1.0 based on closest requirement
func get_first_requirement_summary() -> String # "100 Gold" format
```
## GoalRequirementData
Single condition within a goal.
### Properties
```gdscript
class_name GoalRequirementData
extends Resource
@export var currency: Currency # Currency to track
@export var amount_mantissa: float = 0.0 # Required amount (scientific notation)
@export var amount_exponent: int = 0
```
### Methods
```gdscript
func get_amount() -> BigNumber # Required amount
func is_met() -> bool # Current >= required
func get_progress() -> float # Log-scaled 0.0-1.0
func get_summary_text() -> String # "100 Gold" format
```
### Progress Calculation
Uses **logarithmic scaling** for better visual feedback on huge numbers:
```gdscript
# Convert to log space
current_log = log(current_mantissa) / log(10) + current_exponent
required_log = log(required_mantissa) / log(10) + required_exponent
# Progress ratio
progress = clampf(current_log / required_log, 0.0, 1.0)
```
This means reaching 50% progress on a 1e100 goal requires ~1e50 currency.
## Goal Evaluation
### Automatic Checking
`GameState` evaluates goals whenever currency changes:
```gdscript
func _on_currency_changed(currency_id, new_amount):
GameState.evaluate_all_goals()
```
### Completion Flow
1. Currency updated → signal emitted
2. `GameState.evaluate_all_goals()` called
3. Each uncompleted goal checked via `is_met()`
4. If met → `goal_completed` signal emitted
5. Buffs with unlock goals checked → unlocked if goal complete
### Manual Unlock vs Automatic
Generators can be configured to:
- **Automatic**: Unlock immediately when goal completes
- **Manual**: Goal completion enables a button player must click
## Usage Example
### Defining a Goal
Create `res://sandbox/goals/first_million.tres`:
```
[gd_resource type="Resource" script_class="GoalData"]
[resource]
id = &"first_million"
requirements = [
preload("res://sandbox/goals/first_million_req.tres")
]
```
### Defining a Requirement
Create `res://sandbox/goals/first_million_req.tres`:
```
[gd_resource type="Resource" script_class="GoalRequirementData"]
[resource]
currency = preload("res://sandbox/currencies/gold.tres")
amount_mantissa = 1.0
amount_exponent = 6 # 1,000,000
```
### Connecting to Generator
In `CurrencyGeneratorData`:
```gdscript
@export var unlock_goal: GoalData # Assign goal resource
@export var unlock_goal_behavior: UnlockGoalBehavior = AUTOMATIC
```
## Signals
```gdscript
signal goal_completed(goal_id: StringName)
signal goal_progress_changed(goal_id: StringName, progress: float)
```
## Integration Points
### With GameState
- Goals auto-discovered from `res://sandbox/goals/`
- Completion state saved to JSON
- Buff unlock goals checked automatically
### With Buffs
```gdscript
# In GeneratorBuffData
@export var unlock_goal: GoalData
# When goal completes, buff auto-unlocks:
GameState._try_unlock_buff_from_goal(buff)
```
### With Prestige
- Goal completion state persists through prestige
- Reset via `GameState.reset_for_prestige()`
## See Also
- `core/game_state.gd` - Goal state management
- `generator/currency_generator_data.gd` - Generator unlock goals
- `generator/generator_buff_data.gd` - Buff unlock goals

244
core/prestige/README.md Normal file
View File

@@ -0,0 +1,244 @@
# Prestige Module Documentation
## Overview
The `prestige/` subfolder implements the rebirth/reset system. Players can reset progress in exchange for a permanent multiplier that accelerates future runs.
## Files
| File | Purpose |
|------|---------|
| `prestige_config.gd` | Configuration resource for prestige rules |
| `prestige_manager.gd` | Core logic and state management |
| `prestige_panel.gd` | UI panel for prestige interaction |
| `TECH_SPEC.md` | Detailed technical specification |
## Architecture
```
PrestigeManager (autoload node)
├── Tracks prestige currency
├── Calculates pending gain
├── Applies multipliers to generators
└── PrestigePanel (UI)
├── Shows current/pending prestige
└── Trigger reset button
```
## PrestigeConfig
Resource defining prestige rules.
### Enums
```gdscript
enum BasisType {
LIFETIME_TOTAL, # All-time currency acquired
RUN_TOTAL, # Currency this run
RUN_MAX # Peak currency this run
}
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
```gdscript
@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
@export var basis: BasisType = LIFETIME_TOTAL
@export var formula: FormulaType = POWER
# 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
Autoload node managing prestige state.
### Signals
```gdscript
signal prestige_state_changed(total_prestige, pending_gain)
signal prestige_performed(gain, total_prestige)
```
### State Variables
```gdscript
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
```gdscript
# Query state
func get_total_prestige() -> BigNumber
func calculate_pending_gain() -> BigNumber
func can_prestige() -> bool
func get_total_multiplier() -> float
# Perform prestige
func perform_prestige() -> bool
```
### Gain Calculation
**Power Formula** (default):
```gdscript
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**:
```gdscript
# 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
```
### Multiplier Application
**Additive Mode**:
```gdscript
multiplier = base_multiplier + (total_prestige * multiplier_per_prestige)
# e.g., 1.0 + (5 * 0.05) = 1.25 (25% bonus)
```
**Multiplicative Power Mode**:
```gdscript
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. `GameState.reset_for_prestige()` called:
- Current currencies reset to 0
- Generator states cleared
- Buff levels reset to 0
- Goal completion cleared
- Lifetime totals preserved
5. Generators reinitialized
6. Save triggered
## PrestigePanel
UI component for prestige interaction.
### Displays
```gdscript
_total_value.text = "123.45" # Total prestige earned
_pending_value.text = "56.78" # Gain available now
_multiplier_value.text = "x2.34" # Current multiplier
_basis_label.text = "Lifetime Total (1.50e6 Magic)"
```
### Buttons
- **Reset**: Triggers `perform_prestige()` after confirmation
- **Save**: Manually saves game state
## Integration with Generators
Generators apply prestige multipliers automatically:
```gdscript
func get_effective_auto_run_multiplier() -> float:
return run_multiplier * buff_multiplier * _get_prestige_multiplier()
func _get_prestige_multiplier() -> float:
var manager = get_node_or_null("/root/PrestigeManager")
if manager:
return manager.get_total_multiplier()
return 1.0
```
## Save Integration
Prestige state stored in `GameState` external save:
```json
{
"prestige": {
"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:
```gdscript
# Primary prestige currency
id = &"primary"
display_name = "Ascension"
source_currency_id = &"magic"
# Based on lifetime total of Magic currency
basis = LIFETIME_TOTAL
# Need 1e6 Magic to get first prestige
threshold_mantissa = 1.0
threshold_exponent = 6
# Square root scaling (diminishing returns)
formula = POWER
exponent = 0.5
# +10% multiplier per prestige point
multiplier_mode = ADDITIVE
multiplier_per_prestige = 0.10
```
## See Also
- `core/game_state.gd` - State persistence
- `generator/currency_generator.gd` - Multiplier application
- `core/big_number.gd` - Large number math

View File

@@ -12,6 +12,7 @@ config_version=5
config/name="Idles"
config/version="0.0.1"
run/main_scene="uid://dadeowsvaywpp"
config/features=PackedStringArray("4.6", "Forward Plus")
config/icon="res://icon.svg"