Cleanup and planning

This commit is contained in:
2026-03-29 15:24:13 +02:00
parent ebc9325b53
commit 85bbe51956
25 changed files with 635 additions and 725 deletions

449
README.md
View File

@@ -1,261 +1,190 @@
# Idles - Systems Architecture Report # Idles - Godot 4.6 Idle Game
A modular idle/incremental game prototype featuring a decoupled buff system, multi-target buffs with wildcard support, and goal-based progression.
## Quick Start
```bash
# Run the project
"$GODOT_BIN" --path .
# Headless mode (lint/smoke test)
"$GODOT_BIN" --headless --path . --quit
# Check single script syntax
"$GODOT_BIN" --headless --check-only --script res://core/big_number.gd
```
## Architecture Overview
### Core Systems
| System | Location | Purpose |
|--------|----------|---------|
| **BigNumber** | `core/big_number.gd` | Handles huge numbers (mantissa + exponent) for idle game math |
| **Currency** | `core/currency/`, `core/currency_database.gd` | Catalog system for all in-game currencies |
| **GameState** | `core/game_state.gd` | Central state authority (currencies, generators, buffs, persistence) |
| **Generators** | `core/generator/` | Production mechanics (auto cycles, clicks, purchases) |
| **Buffs** | `core/generator/generator_buff_data.gd` | Global multipliers with multi-target support |
| **Goals** | `core/goals/` | Unlock conditions for generators and buffs |
| **Prestige** | `core/prestige/` | Reset-with-bonus mechanics |
### Autoload Singletons
| Autoload | Script | Description |
|----------|--------|-------------|
| `GameState` | `core/game_state.gd` | Central state management, save/load |
| `CurrencyDatabase` | `core/currency_database.gd` | Currency catalog discovery |
| `PrestigeManager` | `core/prestige/prestige_manager.gd` | Prestige system |
### Data Directory Structure
```
sandbox/
├── currencies/ # Currency resources (gold, food, wood, worker, etc.)
├── generators/ # Generator configuration (.tres files)
├── buffs/ # Buff definitions with target_ids
├── goals/ # Unlock goal definitions
└── prestige/ # Prestige configuration
```
## Current Architecture (Post-Refactoring)
### Decoupled Buff System
Buffs are now **globally registered** and **multi-target capable**:
- **Global Levels**: Buff levels are shared across all targets
- **Multi-Target**: A single buff resource can affect multiple generators
- **Wildcards**: `target_ids: ["*"]` applies to all current + future generators
- **Auto-Discovery**: Buffs loaded from `res://sandbox/buffs/` at runtime
- **Goal-Based Activation**: Buffs unlock automatically when goals are met
### Key Design Decisions
| Feature | Implementation |
|---------|----------------|
| **Buff Levels** | Global (shared across all targets) |
| **Buff Registry** | Auto-discovered from `res://sandbox/buffs/` |
| **Wildcards** | `["*"]` matches all current AND future generators |
| **Default State** | Inactive until goal met |
| **Persistence** | Buff definitions serialized to save |
| **Prestige** | All buff levels reset to 0 |
| **Activation** | Automatic via goals, no manual toggle |
### API Changes
**Old API (per-generator state):**
```gdscript
GameState.register_generator_buff(generator_id, buff_id, level)
GameState.get_generator_buff_level(generator_id, buff_id)
```
**New API (global buff state):**
```gdscript
GameState.register_buff(buff: GeneratorBuffData)
GameState.get_buff(buff_id) -> GeneratorBuffData
GameState.get_buffs_for_generator(generator_id) -> Array[GeneratorBuffData]
GameState.get_effective_multiplier(generator_id, kind) -> float
```
## Save Format
**Version 2 (current):**
```json
{
"save_format_version": 2,
"currencies": { ... },
"generator_states": { ... },
"buff_definitions": { ... },
"buff_levels": { "farm_flux": 5 },
"buff_unlocked": { "farm_flux": true },
"buff_active": { "farm_flux": true },
"last_save_time": 1234567890
}
```
**Note**: Breaking change from v1 - old saves are incompatible.
## Development
### Adding New Content
**New Currency:**
1. Create `res://sandbox/currencies/<name>.tres` (Currency resource)
2. Set `id`, `display_name`, `icon`
3. Auto-discovered at runtime
**New Generator:**
1. Create `res://sandbox/generators/<name>.tres` (CurrencyGeneratorData)
2. Configure production stats, costs, purchase currency
3. Optionally add `unlock_goal`
4. Add `CurrencyGenerator` node to scene
**New Buff:**
1. Create `res://sandbox/buffs/<name>.tres` (GeneratorBuffData)
2. Set `target_ids` (e.g., `["farm", "forestry"]` or `["*"]`)
3. Configure effect, cost, unlock goal
4. Auto-registered at runtime
### Testing
```bash
# Run project and test manually
"$GODOT_BIN" --path .
# No automated test framework committed yet
# (GUT not included in repository)
```
## Known Issues / TODOs
- [ ] Manual save triggers not implemented (auto-save needed)
- [ ] `CurrencyTile` references `GameState.GOLD_CURRENCY_ID` (constant missing)
- [ ] `GeneratorPanel` has missing `mouse_entered`/`mouse_exited` handlers
- [ ] Some buffs configured locked with no unlock path
- [ ] No automated test suite
## Refactoring Status
See `REFACTORING.md` for:
- Complete refactoring plan (8 phases)
- Implementation timeline (15-23 hours estimated)
- Testing checklist
- Rollback procedures
### Completed Phases
- Phase 1: GeneratorBuffData with target_ids support
### Pending Phases
- Phase 2-8: GameState extension, save format update, data migration
## Technical Details
### BigNumber System
- **Representation**: Mantissa (float) + Exponent (int)
- **Range**: Handles values up to ~10^308 (float precision limit)
- **Operations**: Add, subtract, multiply, divide, compare
- **Optimization**: `add_in_place()` for performance-critical loops
### Production Formulas
```
Cost for N items: base * ratio^k * ((ratio^n - 1) / (ratio - 1))
Max affordable: log((currency*(ratio-1)/(base*ratio^k)) + 1) / log(ratio)
Production: base_productivity * owned * run_mult * milestone_mult * purchased_mult
```
### Buff Multiplier Stacking
Multiple buffs of the same kind stack **multiplicatively**:
```
total_multiplier = buff1_effect * buff2_effect * buff3_effect * ...
```
## License
Internal prototype - no external license applied.
---
## Scope Covered *Last updated: Refactoring plan documented*
*Godot version: 4.6*
This report covers all tracked files in the repository (62 total at time of analysis), including:
1. Project configuration.
2. Core runtime scripts.
3. Gameplay/economy systems.
4. Goal/unlock systems.
5. UI adapter scripts and scenes.
6. Data resources (`.tres`).
7. Technical docs and metadata/import files.
The focus below is on behavior-bearing files and how they interact.
## Architecture Summary
1. Global startup and singleton wiring is defined in `project.godot`.
2. Two autoload services power the runtime: `CurrencyDatabase` and `GameState`.
3. `BigNumber` is the numeric backbone used by currency amounts, costs, goals, production, and persistence.
4. Generator behavior is runtime-driven by `CurrencyGenerator`, parameterized by `CurrencyGeneratorData` and `GeneratorBuffData` resources.
5. Goal-based progression uses reusable goal primitives (`GoalData`, `GoalRequirementData`) directly on generator data (`CurrencyGeneratorData.unlock_goal`).
6. UI scripts are thin adapters listening to `GameState` signals.
## Core Systems
### 1) Numeric System (`BigNumber`)
File: `core/big_number.gd`
`BigNumber` stores values as mantissa + exponent (scientific notation style), normalizes them, and provides:
1. Arithmetic operations (`add`, `subtract`, `multiply`, `divide`).
2. In-place fast accumulation (`add_in_place`) for frame-loop performance.
3. Safe comparisons (`compare_to`, `is_greater_than`, etc.).
4. Ratio conversion for progress UI (`get_ratio`).
5. Display formatting (`to_string_sci`, `to_string_suffix`).
6. JSON-friendly serialization/deserialization.
This class is used everywhere currency values move.
### 2) Currency Catalog System
Files: `core/currency/currency.gd`, `core/currency_database.gd`, `idles/currencies/*.tres`
`CurrencyDatabase` scans `res://idles/currencies`, loads typed `Currency` resources, normalizes IDs, and resolves names/icons.
Current data assets:
1. `idles/currencies/magic.tres` (`id = magic`).
2. `idles/currencies/knowledge.tres` (`id = knowledge`).
This creates a data-driven currency catalog that `GameState`, goals, and UI can query consistently.
### 3) Global State + Persistence (`GameState`)
File: `core/game_state.gd`
`GameState` is the central state authority and signal bus.
It owns:
1. Current currency map by currency ID.
2. Total acquired currency map by currency ID.
3. Generator states (`owned`, `purchased_count`, `unlocked`, `available`).
4. Generator buff levels.
5. Generator buff unlocked flags.
6. `last_save_time`.
Important signals:
1. `currency_changed`.
2. `generator_state_changed`.
3. `generator_buff_level_changed`.
4. `generator_buff_unlocked_changed`.
Persistence:
1. Save path: `user://idle_save.json`.
2. Save payload includes currencies, generator states, buff levels, buff unlocked map, and timestamp.
3. Load path includes defensive sanitization and backward-compatible defaulting.
### 4) Generator Economy Runtime
Files: `core/generator/currency_generator.gd`, `core/generator/currency_generator_data.gd`, `core/generator/currency_generator.tscn`
`CurrencyGenerator` implements active gameplay behavior:
1. Auto production cycles (`_process`, cycle accumulation).
2. Click/hover grants with cooldown.
3. Buy-one and buy-max generator purchases.
4. Buff purchase logic and buff effect application.
5. State registration and lookup through `GameState`.
`CurrencyGeneratorData` provides formulas and tuning knobs:
1. Exponential cost growth.
2. Bulk-buy geometric cost.
3. Max-affordable estimate.
4. Milestone multipliers.
5. Purchased-count multiplier.
6. Production/cycle and production/second helpers.
7. ROI-like helpers (`payback_seconds`, `income_to_cost_ratio`).
8. Optional generator unlock goal (`unlock_goal`).
### 5) Buff System
Files: `core/generator/generator_buff_data.gd`, `idles/buffs/*.tres`
Buff kinds:
1. Auto production multiplier.
2. Manual click multiplier.
3. Resource purchase (instant grant on buff buy).
Each buff resource defines:
1. Unlock rules (`unlocked` and optional `unlock_goal`).
2. Effect scaling (`base_effect`, `effect_increment`).
3. Cost scaling (`base_cost`, `cost_multiplier`).
4. Optional target currency and resource grant scaling.
### 6) Goals and Unlock Progression
Files:
1. `core/goals/goal_requirement_data.gd`
2. `core/goals/goal_data.gd`
3. `core/generator/currency_generator_data.gd`
4. `core/generator/currency_generator.gd`
5. `idles/goals/*.tres`
Key behavior:
1. Goal requirements validate currency + target amount.
2. Requirement completion checks **total acquired currency**, not current wallet.
3. Each `CurrencyGenerator` evaluates its own `data.unlock_goal` on startup and on `currency_changed`.
4. If the goal is met, it sets both `unlocked = true` and `available = true`, then emits `goal_achieved(generator_id, goal_id)`.
### 7) UI Adapter Layer
Files:
1. `generator_container.gd` + `generator_container.tscn`.
2. `generator_buff_tile.gd` + `generator_buff_tile.tscn`.
3. `currency_label.gd` + `currency_tile.tscn`.
4. `big_number_progress_bar.gd` + `big_number_progress_bar.tscn`.
5. `core/generator-unlock-goals/goals_debug_ui.gd` + `.tscn`.
These scripts primarily:
1. Subscribe to `GameState` and generator signals.
2. Convert runtime values to UI strings/states.
3. Trigger user actions (buy generator, buy buff, debug unlock).
The architecture is mostly event-driven and avoids polling for state refresh.
## Scene Composition
### Active Gameplay Prototype
File: `docs/museums/generator_museum.tscn`
It composes:
1. `MagicGenerator` instance.
2. `KnowledgeGenerator` instance (initially hidden and starts locked via data).
3. Currency tiles for `magic` and `knowledge`.
4. Goals debug UI panel.
### Legacy/Secondary Museum
Files: `docs/museums/big_number_museum.tscn`, `docs/museums/big_number_museum.gd`
This appears to be an older/placeholder scene and not the current gameplay focus.
## Data-Driven Content Snapshot
### Generators
1. `primary_generator.tres`: `Magic Orb`, active from start, includes 3 buffs.
2. `secondary_generator.tres`: `Library`, starts locked/unavailable and unlocks via `magic_total_30` goal.
### Buffs (Magic)
1. `magic_auto_flux.tres`: auto-production buff, unlocked by default.
2. `magic_click_focus.tres`: manual-click buff.
3. `magic_supply_cache.tres`: resource-purchase buff.
### Goals
1. `magic_total_30.tres`: reach total magic 30.
2. `magic_total_1300.tres`: reach total magic 1300.
## End-to-End Runtime Flow
1. `project.godot` autoloads `CurrencyDatabase` and `GameState`.
2. `CurrencyDatabase` builds currency catalog from `idles/currencies`.
3. `GameState` initializes per-currency maps and loads save data.
4. Scene nodes initialize and connect to `GameState` and local gameplay signals.
5. Generators produce currency automatically and via click/hover paths.
6. Currency updates emit `currency_changed` and drive UI refresh + unlock checks.
7. Goal unlock evaluator may transition generator state to unlocked/available.
8. Generator and buff purchases route all balance/state mutations through `GameState`.
## Signal Guide (Emitters and Listeners)
### Custom Gameplay Signals
| Signal | Declared In | Emitted By | Emitted When | Listeners |
| --- | --- | --- | --- | --- |
| `currency_changed(currency_id, new_amount)` | `core/game_state.gd` | `GameState.add_currency_by_id`, `GameState.spend_currency_by_id` | Any currency balance increases/decreases | `CurrencyGenerator._on_currency_changed`, `GeneratorPanel._on_currency_changed`, `CurrencyTile._on_currency_changed`, `BigNumberProgressBar._on_currency_changed`, `GoalsDebugUI._on_currency_changed` |
| `generator_state_changed(generator_id, state)` | `core/game_state.gd` | `GameState.register_generator`, `GameState._set_generator_state` | Generator state created or changed (`owned`, `purchased_count`, `unlocked`, `available`) | `CurrencyGenerator._on_generated_state_changed`, `GeneratorPanel._on_generator_state_changed`, `GoalsDebugUI._on_generator_state_changed` |
| `generator_buff_level_changed(generator_id, buff_id, new_level)` | `core/game_state.gd` | `GameState.register_generator_buff`, `GameState.set_generator_buff_level` | Buff level created/sanitized/updated | `GeneratorPanel._on_generator_buff_level_changed` |
| `generator_buff_unlocked_changed(generator_id, buff_id, unlocked)` | `core/game_state.gd` | `GameState.register_generator_buff_unlocked`, `GameState.set_generator_buff_unlocked` | Buff unlock state created/sanitized/updated | `GeneratorPanel._on_generator_buff_unlocked_changed` |
| `purchase_completed(amount, total_owned, total_purchased, total_cost)` | `core/generator/currency_generator.gd` | `CurrencyGenerator.buy` | Generator purchase succeeds | `GeneratorPanel._on_generator_updated` |
| `purchase_failed(requested_amount, required_cost, available_currency)` | `core/generator/currency_generator.gd` | `CurrencyGenerator.buy` | Generator purchase fails for insufficient funds | `GeneratorPanel._on_generator_updated` |
| `production_tick(amount, cycle_count, total_owned)` | `core/generator/currency_generator.gd` | `CurrencyGenerator._grant_cycle_income` | One or more automatic production cycles complete | `GeneratorPanel._on_generator_updated` |
| `buff_purchased(buff_id, new_level, cost, cost_currency_id)` | `core/generator/currency_generator.gd` | `CurrencyGenerator.buy_buff` | Buff purchase succeeds | `GeneratorPanel._on_generator_updated` |
| `buff_purchase_failed(buff_id, required_cost, cost_currency_id)` | `core/generator/currency_generator.gd` | `CurrencyGenerator.buy_buff` | Buff purchase fails for insufficient funds | `GeneratorPanel._on_generator_updated` |
| `goal_achieved(generator_id, goal_id)` | `core/generator/currency_generator.gd` | `CurrencyGenerator._evaluate_generator_unlock_goal` | Generator unlock goal transitions that generator to unlocked+available | None currently (available for gameplay/UI hooks) |
| `buy_pressed(buff_id)` | `generator_buff_tile.gd` | `GeneratorBuffTile._on_buy_button_pressed` | User presses buff buy button in a buff row | `GeneratorPanel._on_buy_buff_pressed` |
### Built-In Godot Signals Wired In This Project
| Source Signal | Connected In | Listener Method | Purpose |
| --- | --- | --- | --- |
| `Area2D.mouse_entered` | `core/generator/currency_generator.tscn` | `CurrencyGenerator._on_area_2d_mouse_entered` | Enter generator hover zone; mark hover true and show generator panel |
| `Area2D.mouse_exited` | `core/generator/currency_generator.tscn` | `CurrencyGenerator._on_area_2d_mouse_exited` | Exit generator hover zone; mark hover false and hide generator panel |
| `BuyOneButton.pressed` | `generator_container.tscn` | `GeneratorPanel._on_buy_pressed` | Buy one generator |
| `BuyMaxButton.pressed` | `generator_container.tscn` | `GeneratorPanel._on_buy_max_pressed` | Buy max affordable generators |
| `GeneratorContainer.mouse_entered` | `generator_container.tscn` | `GeneratorPanel._on_mouse_entered` | Intended hover hook on panel container (method currently missing) |
| `GeneratorContainer.mouse_exited` | `generator_container.tscn` | `GeneratorPanel._on_mouse_exited` | Intended hover hook on panel container (method currently missing) |
| `BuyButton.pressed` | `generator_buff_tile.tscn` | `GeneratorBuffTile._on_buy_button_pressed` | Emit tile-level `buy_pressed` custom signal |
| `DebugIncomeButton.pressed` | `currency_tile.tscn` | `CurrencyTile._on_debug_income_button_pressed` | Add debug income to the tile currency |
| `Button.pressed` (runtime-created unlock button) | `core/generator-unlock-goals/goals_debug_ui.gd` | `GoalsDebugUI._on_unlock_pressed` (bound with goal id) | Manual debug unlock when a goal is ready |
### Common Signal Chains
1. Currency gain/spend chain: generator or debug action mutates `GameState` currency, `GameState` emits `currency_changed`, UI widgets and unlock systems recompute and redraw.
2. Generator purchase chain: panel button triggers `CurrencyGenerator.buy`, generator updates `GameState` state, generator emits purchase signal, panel refreshes stats and buttons.
3. Buff purchase chain: buff tile emits `buy_pressed`, panel calls `CurrencyGenerator.buy_buff`, generator updates buff level/unlock and optionally grants currency, `GameState` emits buff/currency/state signals, panel and other listeners refresh.
4. Goal unlock chain: `currency_changed` triggers generator-local unlock evaluator, evaluator sets generator `unlocked/available`, emits `goal_achieved(generator_id, goal_id)`, `GameState` emits `generator_state_changed`, generator and UI become interactable/visible.
## Key Findings and Risks
1. `project.godot` has no explicit `run/main_scene` entry, so startup scene is not pinned in tracked config.
2. Save/load is only partially wired: `load_game()` runs at startup, but `save_game()` is not invoked anywhere else in the repository.
3. `currency_label.gd` and `big_number_progress_bar.gd` reference `GameState.GOLD_CURRENCY_ID`, but that constant does not exist in `core/game_state.gd`.
4. `generator_container.tscn` connects `mouse_entered`/`mouse_exited` to methods that do not exist in `generator_container.gd`.
5. Generator unlock goals now assume the target generator exists in-scene and owns its own `unlock_goal`.
6. `KnowledgeGenerator` visibility logic may be inconsistent because `currency_generator.gd` sets `visible = true` whenever its generator state changes.
7. Two buffs are configured locked with no unlock-goal path, so they remain permanently inaccessible under current logic.
8. `core/generator-unlock-goals/TECH_SPEC.md` contains some outdated assumptions relative to current `.tres`-based implementation.
## Additional Notes
1. `.uid` files are identity metadata used by Godot and contain no runtime logic.
2. `icon.svg` and `icon.svg.import` are standard icon/import metadata.
3. Root config files (`.editorconfig`, `.gitattributes`, `.gitignore`) are minimal and conventional.

433
REFACTORING.md Normal file
View File

@@ -0,0 +1,433 @@
# Refactoring Plan: Decoupled Buff System
## Overview
Decouple buffs from generators to enable:
- Global buff levels shared across all targets
- Multi-target buffs (including wildcards for all current + future generators)
- Automatic buff activation via goal achievement
- Clean separation between buff registry and generator state
- Flexible UI patterns (global buff shop, per-generator panels, debug controls)
## Current State (Problem)
- Buffs stored in `CurrencyGeneratorData.buffs` array (tight coupling)
- Buff levels tracked per-generator in `GameState.generator_buff_levels[generator_id][buff_id]`
- Buff resources have no `target_ids` - don't know which generators they affect
- Cannot create global buffs or multi-target buffs
- Buff logic scattered across `CurrencyGenerator` and `GameState`
## Target State (Solution)
- Buff registry in `GameState._buff_definitions` (global, auto-discovered from `res://sandbox/buffs/`)
- Buff levels stored globally: `GameState._buff_levels[buff_id]`
- Buff resources have `target_ids: Array[StringName]` (supports `["*"]` for wildcards)
- Generators query `GameState.get_buffs_for_generator(generator_id)` for applicable buffs
- Multipliers calculated in `GameState.get_effective_multiplier(generator_id, kind)`
---
## Implementation Phases
### Phase 1: Enhance GeneratorBuffData (Safe, Non-Breaking)
**File**: `core/generator/generator_buff_data.gd`
**Add:**
```gdscript
@export var target_ids: Array[StringName] = []
func targets_generator(generator_id: StringName) -> bool:
if target_ids.is_empty():
return false
if "*" in target_ids:
return true
return generator_id in target_ids
func get_target_generators() -> Array[StringName]:
return target_ids.duplicate()
```
**Timeline**: 1-2 hours
**Risk**: Low (additive change, backward compatible)
**Testing**: Verify existing buffs work with empty target_ids
---
### Phase 2: Extend GameState with Buff Registry
**File**: `core/game_state.gd`
**Add state variables:**
```gdscript
var _buff_definitions: Dictionary = {} # buff_id → GeneratorBuffData
var _buff_levels: Dictionary = {} # buff_id → int
var _buff_unlocked: Dictionary = {} # buff_id → bool
var _buff_active: Dictionary = {} # buff_id → bool (auto-set when unlocked)
signal buff_unlocked_changed(buff_id: StringName, unlocked: bool)
signal buff_level_changed(buff_id: StringName, new_level: int)
```
**Add registry methods:**
```gdscript
func register_buff(buff: GeneratorBuffData) -> void
func get_buff(buff_id: StringName) -> GeneratorBuffData
func get_all_buffs() -> Array[GeneratorBuffData]
func get_buff_level(buff_id: StringName) -> int
func set_buff_level(buff_id: StringName, level: int) -> void
func is_buff_unlocked(buff_id: StringName) -> bool
func set_buff_unlocked(buff_id: StringName, unlocked: bool) -> void
func is_buff_active(buff_id: StringName) -> bool
func get_generators_targeted_by(buff_id: StringName) -> Array[StringName]
func get_buffs_for_generator(generator_id: StringName) -> Array[GeneratorBuffData]
func get_effective_multiplier(generator_id: StringName, kind: int) -> float
```
**Add goal evaluation helper:**
```gdscript
func _try_unlock_buff_from_goal(buff: GeneratorBuffData) -> void
```
**Timeline**: 4-6 hours
**Risk**: High (core state management)
**Testing**: Unit tests for each method, verify goal-based unlocks
---
### Phase 3: Update Save/Load System
**File**: `core/game_state.gd`
**New save format (v2):**
```json
{
"save_format_version": 2,
"currencies": { ... },
"generator_states": { ... },
"buff_definitions": { ... },
"buff_levels": { ... },
"buff_unlocked": { ... },
"buff_active": { ... },
"last_save_time": 1234567890
}
```
**Add serialization helpers:**
```gdscript
func _serialize_buff_definitions() -> Dictionary
func _serialize_buff_data(buff: GeneratorBuffData) -> Dictionary
func _deserialize_buff_definitions(raw: Variant) -> void
```
**Version check in load_game():**
```gdscript
if version < 2:
push_error("Save file version %d too old. Please start fresh." % version)
return
```
**Timeline**: 3-4 hours
**Risk**: High (data loss if bugs exist)
**Testing**: Save/load cycles, verify buff state persists, test version rejection
---
### Phase 4: Update CurrencyGenerator
**File**: `core/generator/currency_generator.gd`
**Changes:**
```gdscript
# Replace:
func get_buffs() -> Array[GeneratorBuffData] # Now queries GameState
func _get_multiplier_for_buff_kind(kind: int) -> float: # Delegates to GameState
return GameState.get_effective_multiplier(_generator_id, kind)
# Update _register_buffs() to only evaluate unlock goals
func _register_buffs() -> void:
for buff in get_buffs():
if buff == null:
continue
var buff_id: StringName = buff.id
if not GameState.get_buff(buff_id):
GameState.register_buff(buff)
_try_unlock_buff_from_goal(buff)
```
**Timeline**: 2-3 hours
**Risk**: Medium (runtime behavior change)
**Testing**: Verify production multipliers, buff purchases, goal unlocks
---
### Phase 5: Update PrestigeManager
**File**: `core/prestige/prestige_manager.gd`
**Add buff reset logic:**
```gdscript
func perform_prestige() -> bool:
# ... existing logic ...
GameState.reset_for_prestige(true, false)
_reset_all_buff_levels()
# ... rest of logic ...
func _reset_all_buff_levels() -> void:
for buff_id in GameState._buff_levels.keys():
GameState.set_buff_level(buff_id, 0)
GameState.set_buff_unlocked(buff_id, false)
```
**Timeline**: 1-2 hours
**Risk**: Low
**Testing**: Verify buff levels reset after prestige
---
### Phase 6: Update UI Components
**File**: `core/generator/generator_container.gd`
**Minimal changes needed** - `GeneratorPanel._build_buff_rows()` should work automatically since it uses `_generator.get_buffs()`.
**Optional: Add Global Buff Panel**
Create `res://ui/global_buff_panel.gd` for testing/future UI:
```gdscript
extends PanelContainer
func _build_buff_list() -> void:
for buff in GameState.get_all_buffs():
if GameState.is_buff_hidden(buff.id):
continue
# Create UI row showing buff state
```
**Timeline**: 1-2 hours (optional)
**Risk**: Low
**Testing**: Verify buff UI updates on level/unlock changes
---
### Phase 7: Data Migration (One-Time Manual)
**Manual steps:**
1. **Update `res://sandbox/buffs/*.tres`:**
- Open each buff in Godot editor
- Add `target_ids` array (e.g., `["farm", "forestry"]` or `["*"]`)
2. **Update `res://sandbox/generators/*.tres`:**
- Remove `buffs` array field
- (Optional) Add `expected_buff_ids` for documentation
3. **Delete old saves:**
- `user://idle_save.json` - starts fresh with v2 format
**Timeline**: 1 hour
**Risk**: Low (data files only)
**Testing**: Load game, verify all buffs work correctly
---
### Phase 8: Buff Auto-Discovery System (Bonus Feature)
**New file**: `core/buff_database.gd`
```gdscript
extends Node
const BUFF_DIRECTORY_PATH: String = "res://sandbox/buffs"
var _buff_by_id: Dictionary = {}
var _initialized: bool = false
func _ready() -> void:
_initialize_buff_catalog()
func _initialize_buff_catalog() -> void:
_buff_by_id.clear()
var dir: DirAccess = DirAccess.open(BUFF_DIRECTORY_PATH)
if dir == null:
push_warning("Unable to open buff directory: %s" % BUFF_DIRECTORY_PATH)
return
dir.list_dir_begin()
var entry_name: String = dir.get_next()
while not entry_name.is_empty():
if not dir.current_is_dir() and entry_name.ends_with(".tres"):
var path: String = "%s/%s" % [BUFF_DIRECTORY_PATH, entry_name]
var buff: GeneratorBuffData = load(path) as GeneratorBuffData
if buff != null:
GameState.register_buff(buff)
entry_name = dir.get_next()
dir.list_dir_end()
func get_all_buffs() -> Array[GeneratorBuffData]:
return _buff_by_id.values()
func get_buff(buff_id: StringName) -> GeneratorBuffData:
return _buff_by_id.get(buff_id)
```
**Add `BuffDatabase` to project.godot autoloads**
**Timeline**: 2-3 hours
**Risk**: Low
**Testing**: Verify all buffs auto-load at runtime
---
## Timeline Summary
| Phase | Duration | Risk |
|-------|----------|------|
| Phase 1: GeneratorBuffData | 1-2h | Low |
| Phase 2: GameState Extension | 4-6h | High |
| Phase 3: Save/Load Update | 3-4h | High |
| Phase 4: CurrencyGenerator | 2-3h | Medium |
| Phase 5: PrestigeManager | 1-2h | Low |
| Phase 6: UI Updates | 1-2h | Low |
| Phase 7: Data Migration | 1h | Low |
| Phase 8: Buff Auto-Discovery | 2-3h | Low |
| **Total** | **15-23 hours** | |
---
## Testing Checklist
- [ ] Buff with `target_ids: ["farm", "forestry"]` applies to both
- [ ] Buff with `target_ids: ["*"]` applies to all current + future generators
- [ ] Buying a buff level increases global level (not per-generator)
- [ ] Buff unlock goal automatically activates buff when met
- [ ] Generator production reflects active buff multipliers
- [ ] Save/load preserves buff levels and unlock states
- [ ] Prestige resets all buff levels to 0
- [ ] Adding new generator auto-includes wildcard buffs
- [ ] Multiple buff kinds stack multiplicatively
- [ ] Inactive (locked) buffs don't apply multipliers
- [ ] Buff definitions auto-load from `res://sandbox/buffs/`
- [ ] New buff resources appear in registry without code changes
---
## Rollback Plan
If issues arise:
1. **Revert code changes** - Use git to restore previous state
2. **Delete user://idle_save.json** - Start fresh with v1 format
3. **Restore buff/generator .tres files** - Remove target_ids, restore buffs arrays
4. **Remove GameState buff methods** - Restore original API
**Note**: No save migration code - breaking change requires fresh starts
---
## Success Criteria
- [ ] All generators produce correct amounts with active buffs
- [ ] Buff UI can display global buff list or per-generator view
- [ ] Save files load correctly and preserve buff state
- [ ] Prestige resets buff levels as expected
- [ ] New buffs can be added by dropping .tres files in sandbox/buffs/
- [ ] Wildcard buffs auto-apply to new generators
- [ ] No performance degradation in _process loop
---
## Future Enhancements (Post-Implementation)
1. **Buff synergies**: "If you have X buff, Y buff gets +10%"
2. **Buff tiers**: Common/rare/legendary classification
3. **Conditional buffs**: "Only active if generator_ownership > 10"
4. **Buff stacking rules**: Additive vs multiplicative vs highest-only
5. **Prestige-only buffs**: Permanent buffs unlocked via prestige currency
6. **Buff UI shop**: Global buff purchase interface
7. **Buff preview**: Show "what-if" calculations before purchase
---
## Technical Notes
### Architecture Decisions
| Decision | Implementation |
|----------|----------------|
| **Buff Levels** | Global (shared across all targets) |
| **Buff Registry** | Auto-discovered from `res://sandbox/buffs/` |
| **Wildcards** | `["*"]` matches all current AND future generators |
| **Default State** | Inactive/unlocked = false, auto-activates on goal met |
| **Persistence** | Buff definitions serialized to save (by id) |
| **Prestige** | All buff levels reset to 0 |
| **Activation** | Automatic via goals, no manual toggle |
### Key API Changes
**Old API (per-generator state):**
```gdscript
GameState.register_generator_buff(generator_id, buff_id, level)
GameState.get_generator_buff_level(generator_id, buff_id)
GameState.is_generator_buff_unlocked(generator_id, buff_id)
```
**New API (global buff state):**
```gdscript
GameState.register_buff(buff: GeneratorBuffData)
GameState.get_buff(buff_id) -> GeneratorBuffData
GameState.get_buff_level(buff_id) -> int
GameState.is_buff_unlocked(buff_id) -> bool
GameState.get_buffs_for_generator(generator_id) -> Array[GeneratorBuffData]
GameState.get_effective_multiplier(generator_id, kind) -> float
```
### Save Format Migration
**Version 1 (old - incompatible):**
```json
{
"currencies": { ... },
"generator_states": { ... },
"generator_buff_levels": { "farm": { "farm_flux": 5 } },
"generator_buff_unlocked": { "farm": { "farm_flux": true } }
}
```
**Version 2 (new):**
```json
{
"save_format_version": 2,
"currencies": { ... },
"generator_states": { ... },
"buff_definitions": { "farm_flux": { ... } },
"buff_levels": { "farm_flux": 5 },
"buff_unlocked": { "farm_flux": true },
"buff_active": { "farm_flux": true }
}
```
---
## Dependencies
- Godot 4.6+ (current project version)
- Existing `BigNumber` system for currency math
- Existing `GameState` autoload
- Existing `GoalData` and `GoalRequirementData` for unlock conditions
---
## Risks & Mitigations
| Risk | Mitigation |
|------|------------|
| Save format incompatibility | Clear version check, error on old saves |
| Performance in _process loop | Cache multiplier calculations, only recalc on buff changes |
| Buff definition loading failures | Graceful degradation, warn but continue |
| Wildcard expansion at runtime | Explicit registration in generator _ready() |
| Multiple buffs of same kind | Multiplicative stacking (document clearly) |
---
## Approval Status
- [ ] Architecture approved
- [ ] Implementation phases reviewed
- [ ] Timeline accepted
- [ ] Risks understood
- [ ] Ready to begin Phase 1
---
*Last updated: Initial planning document*
*Status: Pending implementation approval*

View File

@@ -1,58 +0,0 @@
# Generator-Embedded Unlock Goals - Technical Specification
## Document Status
- Version: 2.0
- Date: 2026-03-21
- Scope: Unlock generators from `CurrencyGeneratorData.unlock_goal` when goal thresholds are reached.
## Problem Statement
Generator unlock progression should be authored where generator behavior lives: `CurrencyGeneratorData`.
The previous scene-level target mapping layer increased setup complexity and created mismatch risk between goal targets and scene content.
## Current Baseline
1. Generator state persists in `GameState.generator_states` (`owned`, `purchased_count`, `unlocked`, `available`).
2. `CurrencyGenerator` already gates interactions using `is_available_to_player()`.
3. Goal primitives remain reusable and data-driven:
- `GoalRequirementData`
- `GoalData`
4. Generator data now owns unlock definition via `unlock_goal: GoalData`.
## Functional Requirements
1. A generator may define one optional unlock goal (`CurrencyGeneratorData.unlock_goal`).
2. Goal completion uses logical AND across requirements.
3. Requirement checks use total acquired currency (`GameState.get_total_currency_acquired_by_id`).
4. Unlock evaluation runs:
- once on generator `_ready()`
- on every `GameState.currency_changed`
5. When a goal is met, runtime sets:
- `GameState.set_generator_unlocked(generator_id, true)`
- `GameState.set_generator_available(generator_id, true)`
6. Unlock transitions are idempotent.
7. Unlock completion emits `CurrencyGenerator.goal_achieved(generator_id, goal_id)` for hooks/UI.
## Runtime Design
1. `CurrencyGeneratorData` exposes:
- `has_unlock_goal()`
- `is_unlock_goal_met()`
- `get_unlock_goal_id()`
2. `CurrencyGenerator` executes `_evaluate_generator_unlock_goal()`.
3. No scene-level unlock controller is required.
4. Goals debug UI discovers goals from scene generators instead of external target-mapping resources.
## Data Authoring Rules
1. Configure locked generators with `starts_unlocked = false` and `starts_available = false`.
2. Set `unlock_goal` directly on that generator `.tres` resource.
3. Keep `GoalData.id` unique and stable.
4. Goal data can still be shared across systems (generator unlocks, buff unlocks).
## Save/Load Behavior
1. Unlock persistence remains unchanged because `GameState` stores `unlocked/available`.
2. Re-evaluation after load is safe due to idempotent state writes.
## Validation
1. Run headless parse: `"$GODOT_BIN" --headless --path . --quit`
2. Manual smoke test in `generator_museum`:
- verify locked generator starts unavailable
- grant required currency
- verify generator unlocks automatically
- verify state persists after restart

View File

@@ -1,10 +1,6 @@
[gd_scene format=3 uid="uid://jeoiinukrrsp"] [gd_scene format=3 uid="uid://jeoiinukrrsp"]
[ext_resource type="Script" uid="uid://dtbxopw6ulhl8" path="res://core/generator/currency_generator.gd" id="1_4n4ca"] [ext_resource type="Script" uid="uid://dtbxopw6ulhl8" path="res://core/generator/currency_generator.gd" id="1_4n4ca"]
[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://idles/currencies/magic.tres" id="2_5tmvy"]
[ext_resource type="Resource" uid="uid://co0mcc2kvcpo5" path="res://idles/generators/orb.tres" id="3_wx13b"]
[node name="CurrencyGenerator" type="Node2D" unique_id=967969064] [node name="CurrencyGenerator" type="Node2D" unique_id=967969064]
script = ExtResource("1_4n4ca") script = ExtResource("1_4n4ca")
currency = ExtResource("2_5tmvy")
data = ExtResource("3_wx13b")

View File

@@ -10,6 +10,7 @@ enum BuffKind {
const HUGE_EXPONENT: int = 1000000 const HUGE_EXPONENT: int = 1000000
@export var id: StringName = &"" @export var id: StringName = &""
@export var target_ids: Array[StringName] = []
@export var kind: BuffKind = BuffKind.AUTO_PRODUCTION_MULTIPLIER @export var kind: BuffKind = BuffKind.AUTO_PRODUCTION_MULTIPLIER
@export_multiline var text: String = "" @export_multiline var text: String = ""
@export var icon: Texture2D @export var icon: Texture2D
@@ -35,6 +36,18 @@ const HUGE_EXPONENT: int = 1000000
@export var resource_purchase_base_exponent: int = 0 @export var resource_purchase_base_exponent: int = 0
@export var resource_purchase_increment_multiplier: float = 1.2 @export var resource_purchase_increment_multiplier: float = 1.2
func targets_generator(generator_id: StringName) -> bool:
if target_ids.is_empty():
return false
if target_ids.has("*"):
return true
return target_ids.has(generator_id)
func get_target_generators() -> Array[StringName]:
return target_ids.duplicate()
func has_unlock_goal() -> bool: func has_unlock_goal() -> bool:
if unlock_goal == null: if unlock_goal == null:
return false return false

View File

@@ -1,78 +0,0 @@
[gd_scene format=3 uid="uid://cfryecxmcg8hw"]
[ext_resource type="PackedScene" uid="uid://jeoiinukrrsp" path="res://core/generator/currency_generator.tscn" id="1_6pne4"]
[ext_resource type="Resource" uid="uid://bnhqk8b31mm4e" path="res://idles/currencies/knowledge.tres" id="2_8qilt"]
[ext_resource type="Resource" uid="uid://l0pn6mlcer7t" path="res://idles/currencies/spirit.tres" id="2_jlqd0"]
[ext_resource type="Resource" uid="uid://04pmc034qupd" path="res://idles/generators/library.tres" id="3_4ly0e"]
[ext_resource type="Resource" uid="uid://cythfovqgqlyh" path="res://idles/currencies/wood.tres" id="4_2xpf5"]
[ext_resource type="PackedScene" uid="uid://btkxru2gdjsgc" path="res://currency_tile.tscn" id="4_6ri4a"]
[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://idles/currencies/magic.tres" id="5_dl3gy"]
[ext_resource type="Resource" uid="uid://df5k58yu1g6rf" path="res://idles/generators/forestry.tres" id="5_u3cug"]
[ext_resource type="PackedScene" uid="uid://dirvi76rkoowf" path="res://goals_debug_ui.tscn" id="7_7i63j"]
[ext_resource type="PackedScene" path="res://core/prestige/prestige_panel.tscn" id="9_q5vce"]
[node name="GeneratorGym" type="Node2D" unique_id=1219373683]
[node name="MagicGenerator" parent="." unique_id=967969064 instance=ExtResource("1_6pne4")]
position = Vector2(262, 301)
[node name="KnowledgeGenerator" parent="." unique_id=2139088546 instance=ExtResource("1_6pne4")]
visible = false
position = Vector2(262, 626)
currency = ExtResource("2_8qilt")
data = ExtResource("3_4ly0e")
press_buys_generator = false
[node name="WoodGenerator" parent="." unique_id=29858558 instance=ExtResource("1_6pne4")]
visible = false
position = Vector2(1046, 626)
currency = ExtResource("4_2xpf5")
data = ExtResource("5_u3cug")
press_buys_generator = false
[node name="UI" type="Control" parent="." unique_id=452530906]
layout_mode = 3
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0
[node name="MagicCurrencyTile" parent="UI" unique_id=1440583137 instance=ExtResource("4_6ri4a")]
layout_mode = 0
offset_right = 160.0
offset_bottom = 31.0
currency = ExtResource("5_dl3gy")
[node name="SpiritCurrencyTile" parent="UI" unique_id=797237056 instance=ExtResource("4_6ri4a")]
layout_mode = 0
offset_top = 32.0
offset_right = 160.0
offset_bottom = 63.0
currency = ExtResource("2_jlqd0")
[node name="KnowledgeCurrencyTile" parent="UI" unique_id=1977342362 instance=ExtResource("4_6ri4a")]
layout_mode = 0
offset_top = 63.0
offset_right = 160.0
offset_bottom = 94.0
currency = ExtResource("2_8qilt")
[node name="WoodCurrencyTile" parent="UI" unique_id=1210103101 instance=ExtResource("4_6ri4a")]
layout_mode = 0
offset_top = 94.0
offset_right = 160.0
offset_bottom = 125.0
currency = ExtResource("4_2xpf5")
[node name="GoalsDebugUI" parent="UI" unique_id=4710697 instance=ExtResource("7_7i63j")]
layout_mode = 1
offset_left = 1253.0
offset_top = 817.0
offset_right = 1913.0
offset_bottom = 1076.0
[node name="PrestigePanel" parent="UI" unique_id=401213142 instance=ExtResource("9_q5vce")]
layout_mode = 0
offset_left = 12.0
offset_top = 822.0
offset_right = 432.0
offset_bottom = 1028.0

View File

@@ -1,18 +0,0 @@
[gd_resource type="Resource" script_class="GeneratorBuffData" format=3 uid="uid://mvgfe3nc7uwa"]
[ext_resource type="Resource" uid="uid://bnhqk8b31mm4e" path="res://idles/currencies/knowledge.tres" id="1_1db6v"]
[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://idles/currencies/magic.tres" id="2_pg1j1"]
[ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="3_t80he"]
[ext_resource type="Resource" uid="uid://bmlhoeasl7xor" path="res://idles/goals/magic_total_13k.tres" id="4_83vra"]
[resource]
script = ExtResource("3_t80he")
id = &"bigger_forest"
text = "Bigger Forest"
max_level = 10
unlock_goal = ExtResource("4_83vra")
effect_increment = 1.0
cost_currency = ExtResource("1_1db6v")
base_cost_mantissa = 250.0
cost_multiplier = 1.7
resource_target_currency = ExtResource("2_pg1j1")

View File

@@ -1,18 +0,0 @@
[gd_resource type="Resource" script_class="GeneratorBuffData" format=3 uid="uid://tu63jy51yigb"]
[ext_resource type="Resource" uid="uid://bnhqk8b31mm4e" path="res://idles/currencies/knowledge.tres" id="1_78qkq"]
[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://idles/currencies/magic.tres" id="2_b57xf"]
[ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="3_nc8nr"]
[ext_resource type="Resource" uid="uid://bmlhoeasl7xor" path="res://idles/goals/magic_total_13k.tres" id="4_78qkq"]
[resource]
script = ExtResource("3_nc8nr")
id = &"library_auto_flux"
text = "Library Dynamo"
max_level = 10
unlock_goal = ExtResource("4_78qkq")
effect_increment = 1.0
cost_currency = ExtResource("1_78qkq")
base_cost_mantissa = 100.0
cost_multiplier = 1.7
resource_target_currency = ExtResource("2_b57xf")

View File

@@ -1,18 +0,0 @@
[gd_resource type="Resource" script_class="GeneratorBuffData" format=3 uid="uid://ceugcxmassmpk"]
[ext_resource type="Resource" uid="uid://bnhqk8b31mm4e" path="res://idles/currencies/knowledge.tres" id="1_lnp8f"]
[ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="1_r7ak1"]
[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://idles/currencies/magic.tres" id="2_h3we5"]
[ext_resource type="Resource" uid="uid://qyxct5gbrxwa" path="res://idles/goals/magic_total_30.tres" id="4_000a0"]
[resource]
script = ExtResource("1_r7ak1")
id = &"magic_auto_flux"
text = "Arcane Dynamo"
max_level = 10
unlock_goal = ExtResource("4_000a0")
effect_increment = 1.0
cost_currency = ExtResource("1_lnp8f")
base_cost_mantissa = 25.0
cost_multiplier = 1.7
resource_target_currency = ExtResource("2_h3we5")

View File

@@ -1,19 +0,0 @@
[gd_resource type="Resource" script_class="GeneratorBuffData" format=3 uid="uid://6i3fcygusuqf"]
[ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="1_1wyaq"]
[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://idles/currencies/magic.tres" id="2_0j56j"]
[ext_resource type="Resource" uid="uid://qyxct5gbrxwa" path="res://idles/goals/magic_total_30.tres" id="3_bsqao"]
[resource]
script = ExtResource("1_1wyaq")
id = &"magic_click_focus"
kind = 1
text = "Apprentice Gloves"
max_level = 4
unlock_goal = ExtResource("3_bsqao")
effect_increment = 1.0
cost_currency = ExtResource("2_0j56j")
base_cost_mantissa = 2.5
base_cost_exponent = 1
cost_multiplier = 2.0
resource_target_currency = ExtResource("2_0j56j")

View File

@@ -1,20 +0,0 @@
[gd_resource type="Resource" script_class="GeneratorBuffData" format=3 uid="uid://coi7k1cx4p4hr"]
[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://idles/currencies/magic.tres" id="1_aawxd"]
[ext_resource type="Resource" uid="uid://l0pn6mlcer7t" path="res://idles/currencies/spirit.tres" id="2_0eqxt"]
[ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="3_0ov1n"]
[ext_resource type="Resource" uid="uid://qyxct5gbrxwa" path="res://idles/goals/magic_total_30.tres" id="4_ifsqd"]
[resource]
script = ExtResource("3_0ov1n")
id = &"summon_spirit"
kind = 2
text = "Summon Spirit"
unlock_goal = ExtResource("4_ifsqd")
effect_increment = 1.0
cost_currency = ExtResource("1_aawxd")
base_cost_mantissa = 200.0
cost_multiplier = 1.15
resource_target_currency = ExtResource("2_0eqxt")
resource_purchase_base_mantissa = 1.0
resource_purchase_increment_multiplier = 1.0

View File

@@ -1,10 +0,0 @@
[gd_resource type="Resource" script_class="Currency" format=3 uid="uid://bnhqk8b31mm4e"]
[ext_resource type="Script" uid="uid://dtgqjf3bl7pm8" path="res://core/currency/currency.gd" id="1_72iuq"]
[ext_resource type="Texture2D" uid="uid://bgtt3wu43tajh" path="res://icon.svg" id="2_x2h5x"]
[resource]
script = ExtResource("1_72iuq")
id = &"knowledge"
display_name = "Knowledge"
icon = ExtResource("2_x2h5x")

View File

@@ -1,10 +0,0 @@
[gd_resource type="Resource" script_class="Currency" format=3 uid="uid://brqaojindcxa5"]
[ext_resource type="Script" uid="uid://dtgqjf3bl7pm8" path="res://core/currency/currency.gd" id="1_x4uiu"]
[ext_resource type="Texture2D" uid="uid://bgtt3wu43tajh" path="res://icon.svg" id="2_52ar0"]
[resource]
script = ExtResource("1_x4uiu")
id = &"magic"
display_name = "Magic"
icon = ExtResource("2_52ar0")

View File

@@ -1,9 +0,0 @@
[gd_resource type="Resource" script_class="Currency" format=3 uid="uid://l0pn6mlcer7t"]
[ext_resource type="Script" uid="uid://dtgqjf3bl7pm8" path="res://core/currency/currency.gd" id="1_m14p5"]
[resource]
script = ExtResource("1_m14p5")
id = &"spirit"
display_name = "Spirit"
metadata/_custom_type_script = "uid://dtgqjf3bl7pm8"

View File

@@ -1,10 +0,0 @@
[gd_resource type="Resource" script_class="Currency" format=3 uid="uid://cythfovqgqlyh"]
[ext_resource type="Texture2D" uid="uid://bgtt3wu43tajh" path="res://icon.svg" id="1_t8m5x"]
[ext_resource type="Script" uid="uid://dtgqjf3bl7pm8" path="res://core/currency/currency.gd" id="2_137dc"]
[resource]
script = ExtResource("2_137dc")
id = &"wood"
display_name = "Wood"
icon = ExtResource("1_t8m5x")

View File

@@ -1,23 +0,0 @@
[gd_resource type="Resource" script_class="CurrencyGeneratorData" format=3 uid="uid://df5k58yu1g6rf"]
[ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="1_ffn26"]
[ext_resource type="Resource" uid="uid://mvgfe3nc7uwa" path="res://idles/buffs/bigger_forest.tres" id="2_rptf4"]
[ext_resource type="Resource" uid="uid://l0pn6mlcer7t" path="res://idles/currencies/spirit.tres" id="3_rptf4"]
[ext_resource type="Script" uid="uid://b00tqsuhxdy0d" path="res://core/generator/currency_generator_data.gd" id="5_43n1y"]
[ext_resource type="Resource" uid="uid://bmlhoeasl7xor" path="res://idles/goals/magic_total_13k.tres" id="6_ffn26"]
[resource]
script = ExtResource("5_43n1y")
id = &"Wood"
name = "Forestry"
starts_unlocked = false
starts_available = false
initial_owned = 1
purchase_currency = ExtResource("3_rptf4")
unlock_goal = ExtResource("6_ffn26")
unlock_goal_behavior = 1
initial_cost = 1.0
coefficient = 1.0
initial_productivity = 20.0
buffs = Array[ExtResource("1_ffn26")]([ExtResource("2_rptf4")])
metadata/_custom_type_script = "uid://b00tqsuhxdy0d"

View File

@@ -1,25 +0,0 @@
[gd_resource type="Resource" script_class="CurrencyGeneratorData" format=3 uid="uid://04pmc034qupd"]
[ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="1_jemvk"]
[ext_resource type="Resource" uid="uid://tu63jy51yigb" path="res://idles/buffs/library_auto_flux.tres" id="2_fcbji"]
[ext_resource type="Resource" uid="uid://l0pn6mlcer7t" path="res://idles/currencies/spirit.tres" id="3_fcbji"]
[ext_resource type="Script" uid="uid://b00tqsuhxdy0d" path="res://core/generator/currency_generator_data.gd" id="3_xj12v"]
[ext_resource type="Resource" uid="uid://c4mkxj4ubhsi0" path="res://idles/goals/magic_total_1300.tres" id="4_1h03m"]
[resource]
script = ExtResource("3_xj12v")
id = &"Knowledge"
name = "Library"
starts_unlocked = false
starts_available = false
initial_owned = 1
purchase_currency = ExtResource("3_fcbji")
unlock_goal = ExtResource("4_1h03m")
unlock_goal_behavior = 1
initial_cost = 1.0
coefficient = 1.0
initial_time = 3.0
initial_revenue = 60.0
initial_productivity = 20.0
buffs = Array[ExtResource("1_jemvk")]([ExtResource("2_fcbji")])
metadata/_custom_type_script = "uid://b00tqsuhxdy0d"

View File

@@ -1,12 +0,0 @@
[gd_resource type="Resource" script_class="CurrencyGeneratorData" format=3 uid="uid://nfq4erycp7vy"]
[ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="1_s017t"]
[ext_resource type="Script" uid="uid://b00tqsuhxdy0d" path="res://core/generator/currency_generator_data.gd" id="2_2rq43"]
[resource]
script = ExtResource("2_2rq43")
id = &"monastery"
name = "Monastery"
starts_unlocked = false
starts_available = false
metadata/_custom_type_script = "uid://b00tqsuhxdy0d"

View File

@@ -1,19 +0,0 @@
[gd_resource type="Resource" script_class="CurrencyGeneratorData" format=3 uid="uid://co0mcc2kvcpo5"]
[ext_resource type="Script" uid="uid://b00tqsuhxdy0d" path="res://core/generator/currency_generator_data.gd" id="1_c6y77"]
[ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="1_t6lg0"]
[ext_resource type="Resource" uid="uid://ceugcxmassmpk" path="res://idles/buffs/orb_auto_flux.tres" id="2_x505b"]
[ext_resource type="Resource" uid="uid://6i3fcygusuqf" path="res://idles/buffs/orb_click_focus.tres" id="3_fsxdm"]
[ext_resource type="Resource" uid="uid://coi7k1cx4p4hr" path="res://idles/buffs/orb_summon_spirit.tres" id="4_5v0af"]
[ext_resource type="Resource" uid="uid://l0pn6mlcer7t" path="res://idles/currencies/spirit.tres" id="4_jpwus"]
[resource]
script = ExtResource("1_c6y77")
id = &"Magic"
name = "Magic Orb"
purchase_currency = ExtResource("4_jpwus")
initial_cost = 1.0
coefficient = 1.0
initial_productivity = 20.0
buffs = Array[ExtResource("1_t6lg0")]([ExtResource("2_x505b"), ExtResource("3_fsxdm"), ExtResource("4_5v0af")])
metadata/_custom_type_script = "uid://b00tqsuhxdy0d"

View File

@@ -1,18 +0,0 @@
[gd_resource type="Resource" script_class="CurrencyGeneratorData" format=3 uid="uid://ri1ggb756753"]
[ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="1_tgm58"]
[ext_resource type="Script" uid="uid://b00tqsuhxdy0d" path="res://core/generator/currency_generator_data.gd" id="2_1qpvc"]
[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://idles/currencies/magic.tres" id="2_chq14"]
[ext_resource type="Resource" uid="uid://qyxct5gbrxwa" path="res://idles/goals/magic_total_30.tres" id="4_tj1lt"]
[resource]
script = ExtResource("2_1qpvc")
id = &"spirit_factory"
name = "Spirit Factory"
starts_unlocked = false
starts_available = false
purchase_currency = ExtResource("2_chq14")
unlock_goal = ExtResource("4_tj1lt")
unlock_goal_behavior = 1
initial_cost = 200.0
metadata/_custom_type_script = "uid://b00tqsuhxdy0d"

View File

@@ -1,24 +0,0 @@
[gd_resource type="Resource" script_class="GoalData" format=3 uid="uid://oh1a4tneuons"]
[ext_resource type="Script" uid="uid://r4js5eajolio" path="res://core/goals/goal_requirement_data.gd" id="1_pj6se"]
[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://idles/currencies/magic.tres" id="2_lsxf0"]
[ext_resource type="Script" uid="uid://dx4kmh33rrkpy" path="res://core/goals/goal_data.gd" id="3_hyry2"]
[ext_resource type="Resource" uid="uid://cythfovqgqlyh" path="res://idles/currencies/wood.tres" id="3_lsxf0"]
[sub_resource type="Resource" id="Resource_tvl3d"]
script = ExtResource("1_pj6se")
currency = ExtResource("2_lsxf0")
amount_mantissa = 3.5
amount_exponent = 5
[sub_resource type="Resource" id="Resource_hyry2"]
script = ExtResource("1_pj6se")
currency = ExtResource("3_lsxf0")
amount_mantissa = 2.0
amount_exponent = 4
metadata/_custom_type_script = "uid://r4js5eajolio"
[resource]
script = ExtResource("3_hyry2")
id = &"magic_350k_wood_20k"
requirements = Array[ExtResource("1_pj6se")]([SubResource("Resource_tvl3d"), SubResource("Resource_hyry2")])

View File

@@ -1,16 +0,0 @@
[gd_resource type="Resource" script_class="GoalData" format=3 uid="uid://c4mkxj4ubhsi0"]
[ext_resource type="Script" uid="uid://dx4kmh33rrkpy" path="res://core/goals/goal_data.gd" id="1_b11ou"]
[ext_resource type="Script" uid="uid://r4js5eajolio" path="res://core/goals/goal_requirement_data.gd" id="2_d507t"]
[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://idles/currencies/magic.tres" id="3_tu3fx"]
[sub_resource type="Resource" id="Resource_tvl3d"]
script = ExtResource("2_d507t")
currency = ExtResource("3_tu3fx")
amount_mantissa = 1.3
amount_exponent = 3
[resource]
script = ExtResource("1_b11ou")
id = &"magic_total_1300"
requirements = Array[ExtResource("2_d507t")]([SubResource("Resource_tvl3d")])

View File

@@ -1,16 +0,0 @@
[gd_resource type="Resource" script_class="GoalData" format=3 uid="uid://bmlhoeasl7xor"]
[ext_resource type="Script" uid="uid://r4js5eajolio" path="res://core/goals/goal_requirement_data.gd" id="1_185pw"]
[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://idles/currencies/magic.tres" id="2_ox6xg"]
[ext_resource type="Script" uid="uid://dx4kmh33rrkpy" path="res://core/goals/goal_data.gd" id="3_ef7ki"]
[sub_resource type="Resource" id="Resource_tvl3d"]
script = ExtResource("1_185pw")
currency = ExtResource("2_ox6xg")
amount_mantissa = 1.3
amount_exponent = 4
[resource]
script = ExtResource("3_ef7ki")
id = &"magic_total_13k"
requirements = Array[ExtResource("1_185pw")]([SubResource("Resource_tvl3d")])

View File

@@ -1,15 +0,0 @@
[gd_resource type="Resource" script_class="GoalData" format=3 uid="uid://qyxct5gbrxwa"]
[ext_resource type="Script" uid="uid://dx4kmh33rrkpy" path="res://core/goals/goal_data.gd" id="1_rh4uj"]
[ext_resource type="Script" uid="uid://r4js5eajolio" path="res://core/goals/goal_requirement_data.gd" id="2_58n5q"]
[ext_resource type="Resource" uid="uid://brqaojindcxa5" path="res://idles/currencies/magic.tres" id="3_qm5e7"]
[sub_resource type="Resource" id="Resource_v4v16"]
script = ExtResource("2_58n5q")
currency = ExtResource("3_qm5e7")
amount_mantissa = 30.0
[resource]
script = ExtResource("1_rh4uj")
id = &"magic_total_30"
requirements = Array[ExtResource("2_58n5q")]([SubResource("Resource_v4v16")])

View File

@@ -1,25 +0,0 @@
[gd_resource type="Resource" script_class="PrestigeConfig" format=3]
[ext_resource type="Script" path="res://core/prestige/prestige_config.gd" id="1_3tg3a"]
[resource]
script = ExtResource("1_3tg3a")
id = &"ascension"
display_name = "Ascension"
prestige_currency_id = &"ascension"
source_currency_id = &"magic"
basis = 0
formula = 0
threshold_mantissa = 1.0
threshold_exponent = 4
scale = 1.0
exponent = 0.5
flat_bonus = 0.0
minimum_gain = 0
rounding_mode = 0
allow_prestige_without_gain = false
multiplier_mode = 0
base_multiplier = 1.0
multiplier_per_prestige = 0.05
multiplier_exponent = 1.0
metadata/_custom_type_script = "res://core/prestige/prestige_config.gd"