systems rework: buffs, prestige graph, research, modular architecture

Decouples core systems from centralized globals in favor of catalogue-based architecture and data-driven content.

   Key changes:
   - Prestige: node-based buff tech tree, graph panel, progress bar
   - Research: multi-worker system, per-generator research, xp generation
   - Ascension: meta-currency layer via multi-currency prestige resets
   - Buffs: refactored as generator-independent via catalogues
   - UI: currency panel, edge-scrolling camera + zoom, current-goal panel
   - Alchemy Tower: crafting building with recipe/cost system
   - Testing: 7 test suites (ascension, prestige, research, goals)
   - Content: migrated from hardcoded idles/ to gym format (tiny_sword)
This commit was merged in pull request #1.
This commit is contained in:
2026-05-07 20:36:21 +00:00
parent 2743fd314a
commit 81a4058b04
242 changed files with 11226 additions and 2242 deletions

546
README.md
View File

@@ -1,261 +1,313 @@
# 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, goal-based progression, prestige resets, and production-driven research.
## 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 |
| **LevelGameState** | `core/level_game_state.gd` | Central state authority — currencies, generators, buffs, goals, research, persistence |
| **Currency** | `core/currency/`, `core/currency_catalogue.gd` | Catalog system for all in-game currencies |
| **Generators** | `core/generator/` | Production mechanics (auto cycles, clicks, purchases) |
| **Buffs** | `core/generator/generator_buff_data.gd` | Global, multi-target buffs applied to generators |
| **Goals** | `core/goals/` | Unlock conditions and achievement tracking |
| **Prestige** | `core/prestige/` | Reset-with-bonus mechanics |
| **Research** | `core/research/` | XP-based progression with production multipliers |
### Key Nodes
| Node | Script | Description |
|------|--------|-------------|
| `LevelGameState` | `core/level_game_state.gd` | Central state management, save/load, catalogs, all signals |
| `PrestigeManager` | `core/prestige/prestige_manager.gd` | Prestige calculation, reset orchestration |
| `CurrencyGenerator` | `core/generator/currency_generator.gd` | Per-generator production, purchasing, buff interaction |
| `GeneratorPanel` | `core/generator/generator_container.gd` | UI panel for a single generator |
**Important:** There are **no autoload singletons**. `LevelGameState` and `PrestigeManager` are regular nodes instantiated in the scene (e.g., `docs/gyms/tiny_sword/tiny_sword.tscn`). All code accesses them through `@export` references or `get_node()`, never through an autoload name like `/root/GameState`.
## Scope Covered
### Data Directory Structure
This report covers all tracked files in the repository (62 total at time of analysis), including:
All gameplay resources (`.tres` files) live under:
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.
```
docs/gyms/tiny_sword/
├── currencies/ # Currency resources (gold, food, wood, worker, etc.)
├── buildings/ # Generator configuration data
│ ├── farm/ # farm_generator.tres
│ ├── forestry/ # forestry_generator.tres
│ ├── gold_mine/ # gold_mine_generator.tres
│ ├── monastery/ # monastery_generator.tres
│ └── alchemy_tower/ # alchemy_tower_data.tres + recipes/
├── buffs/ # Buff definitions with target_ids
├── goals/ # Unlock goal definitions
├── prestige/ # Prestige configuration (primary_prestige.tres)
├── research/ # Research track data
└── test/ # (reserved for tests)
```
Game art assets live separately in:
```
sandbox/tiny_swords/
├── Buildings/ # Building sprites
├── Terrain/ # Tilesets and decorations
├── UI Elements/ # UI assets
└── Units/ # Unit sprites
```
## Core Documentation
Each `core/` subsystem has its own detailed README:
| Module | Documentation |
|--------|--------------|
| Core overview | `core/README.md` |
| Currency system | `core/currency/README.md` |
| Generator system | `core/generator/README.md` |
| Goals system | `core/goals/README.md` |
| Prestige system | `core/prestige/README.md` |
| Research system | `core/research/README.md` |
## Buff System
Buffs are **globally registered** and **multi-target capable**:
- **Global Levels**: Each buff has a single level shared across all generators it targets.
- **Multi-Target**: A single buff resource can affect multiple generators. Controlled by the `target_ids` array on `GeneratorBuffData`.
- **Wildcards**: A `target_ids` value of `["*"]` applies the buff to all current AND future generators.
- **Catalog-Based Loading**: Buffs are defined in a `BuffCatalogue` resource (`@export var buff_catalogue: BuffCatalogue` on `LevelGameState`). No runtime directory scanning is performed.
- **Goal-Based Activation**: Buffs with an `unlock_goal` automatically unlock when their goal is completed.
- **Multiplicative Stacking**: Multiple buffs of the same kind on a generator stack multiplicatively (`buff1 × buff2 × …`).
- **Buff Kinds**: `AUTO_PRODUCTION_MULTIPLIER`, `MANUAL_CLICK_MULTIPLIER`, `RESOURCE_PURCHASE`, `RESEARCH_XP_MULTIPLIER`
### Lifecycle
| Event | What happens |
|-------|-------------|
| **Default state** | Inactive (locked) until its unlock goal is met |
| **Goal met** | Buff auto-unlocks, becomes purchasable |
| **Purchased** | Level increments, effect applies to all target generators |
| **Prestige reset** | All buff levels reset to 0, all buffs re-locked |
### API (on LevelGameState)
```gdscript
LevelGameState.register_buff(buff: GeneratorBuffData)
LevelGameState.get_buff(buff_id: StringName) -> GeneratorBuffData
LevelGameState.get_buff_level(buff_id: StringName) -> int
LevelGameState.get_buffs_for_generator(generator_id: StringName) -> Array[GeneratorBuffData]
LevelGameState.get_effective_multiplier(generator_id: StringName, kind: int) -> float
LevelGameState.set_buff_level(buff_id: StringName, level: int)
LevelGameState.is_buff_active(buff_id: StringName) -> bool
```
## Goals System
Goals track player progress and unlock generators or buffs when requirements are met.
- **Automatic or Manual**: Goals define their own `unlock_behavior` (`AUTOMATIC` or `MANUAL`).
- **Logarithmic Progress**: Progress bars use log-scale for visual feedback on huge numbers.
- **Prestige Reset**: All goals are reset to incomplete on prestige. Previously unlocked generators re-lock until their goal is met again.
### Signals on LevelGameState
```gdscript
goal_completed(goal_id: StringName)
goal_progress_changed(goal_id: StringName, progress: float)
```
## Prestige System
Prestige resets run progress in exchange for a permanent production multiplier.
- **Configuration**: `PrestigeConfig` resource at `docs/gyms/tiny_sword/prestige/primary_prestige.tres`
- **Basis types**: `LIFETIME_TOTAL` (single currency), `RUN_TOTAL`, `RUN_MAX`, `ALL_CURRENCIES` (sum of all tracked currencies)
- **Formula types**: `POWER` (`scale × ratio^exponent`) or `TRIANGULAR_INVERSE`
- **Multiplier modes**: `ADDITIVE` or `MULTIPLICATIVE_POWER`
### What gets reset on prestige
| State | Behavior |
|-------|----------|
| **Current currency balances** | Reset to 0 (except prestige currency if configured) |
| **Generator states** (owned, unlocked) | Cleared |
| **Buff levels** | All reset to 0, all re-locked |
| **Goals** | All reset to incomplete |
| **Research XP and levels** | All cleared to 0 |
| **Lifetime currency totals** | Preserved |
| **All-time currency acquired** | Preserved |
| **Prestige points earned** | Preserved (permanent) |
## Research System
XP-based progression tied to generator output. Research tracks level up automatically, granting permanent production multipliers to their associated generator.
- **XP Formula**: `XP = currency_produced × xp_per_currency_produced × buff_multiplier`
- **Level Formula**: `xp_required(n) = base_xp_required × growth_multiplier^(n-1)`
- **Buffs**: The `RESEARCH_XP_MULTIPLIER` buff kind increases XP gain for a research track.
- **Prestige Reset**: All research XP and levels reset to 0.
## Save Format
**Version 5 (current):**
```json
{
"save_format_version": 5,
"currencies": {
"<currency_id>": {
"current": {"m": 1.0, "e": 0},
"total": {"m": 1.0, "e": 0},
"all_time": {"m": 1.0, "e": 0}
}
},
"generator_states": {
"<generator_id>": {
"owned": 0,
"purchased_count": 0,
"unlocked": false,
"available": false
}
},
"buff_levels": { "<buff_id>": 0 },
"buff_unlocked": { "<buff_id>": false },
"buff_active": { "<buff_id>": false },
"goals": { "completed": ["<goal_id>"] },
"research_xp": { "<research_id>": {"m": 0.0, "e": 0} },
"research_levels": { "<research_id>": 0 },
"research_workers": 0,
"prestige_state": { },
"last_save_time": 1234567890
}
```
Notes:
- All large numbers are serialized as `{"m": mantissa_float, "e": exponent_int}` (BigNumber format).
- `prestige_state` is an external section managed by `PrestigeManager`, not by `LevelGameState` directly.
- `research_workers` tracks workers assigned to research across all tracks.
- Save file is written to `user://level_save.json`.
## Save/Load
- **Save**: `LevelGameState.save_game()` serializes all state to JSON. Called on prestige reset (via `PrestigeManager`) and when manually triggered.
- **Load**: `LevelGameState.load_game()` deserializes from disk during `_ready()`. Handles version migration from v2+.
- **External sections**: Other systems (e.g. `PrestigeManager`) can attach their data via `set_external_save_data()` / `get_external_save_data()`.
## Development
### Adding New Content
All gameplay resources go under `docs/gyms/tiny_sword/` (not `sandbox/` — that's for art assets only).
**New Currency:**
1. Create `res://docs/gyms/tiny_sword/currencies/<name>.tres` (extend `Currency`)
2. Set `id`, `display_name`, `icon`
3. Add to the `CurrencyCatalogue` resource (`ts_currency_catalogue.tres`)
**New Generator:**
1. Create `res://docs/gyms/tiny_sword/buildings/<name>/<name>_generator.tres` (extend `CurrencyGeneratorData`)
2. Configure production stats, costs, purchase currency, optional `unlock_goal`
3. Add a `CurrencyGenerator` node to the scene referencing this data
**New Buff:**
1. Create `res://docs/gyms/tiny_sword/buffs/<name>.tres` (extend `GeneratorBuffData`)
2. Set `target_ids` (e.g., `["farm", "forestry"]` or `["*"]` for all generators)
3. Configure `kind`, `effect_increment`, cost, and optional `unlock_goal`
4. Add to the `BuffCatalogue` resource (`ts_buff_catalogue.tres`)
**New Research:**
1. Create `res://docs/gyms/tiny_sword/research/<name>.tres` (extend `ResearchData`)
2. Link to a generator via `generator_id`
3. Configure `xp_per_currency_produced`, `base_xp_required`, `xp_growth_multiplier`, `multiplier_per_level`
4. Add to the `ResearchCatalogue` resource (`ts_research_catalogue.tres`)
### BigNumber System
- **Representation**: `mantissa: float` × `10^exponent: int`
- **Range**: Values up to ~10^308 before float precision loss
- **Operations**: `add()`, `subtract()`, `multiply()`, `divide()`, `compare_to()`, `add_in_place()`
- **Serialization**: `.serialize()``{"m": float, "e": int}`; `BigNumber.deserialize(dict)``BigNumber`
- **Display**: `.to_string_suffix(decimals: int)``"1.50K"`, `"3.21M"`, etc.
### Production Formulas
```
Cost for N items: base × coefficient^owned × (coefficient^n 1) / (coefficient 1)
Max affordable: floor(log(currency × (coefficient 1) / (base × coefficient^owned) + 1) / log(coefficient))
Production/cycle: initial_productivity × owned × run_mult × milestone_mult × purchased_mult
Production/second: production/cycle / initial_time
```
## Project Structure
```
core/ # Engine code (all .gd scripts)
├── big_number.gd # BigNumber class
├── level_game_state.gd # Central state manager (LevelGameState node)
├── buff_catalogue.gd # Buff catalogue resource
├── currency_catalogue.gd # Currency catalogue resource
├── goal_catalogue.gd # Goal catalogue resource
├── currency/ # Currency resource definition
├── generator/ # Generator, buff data, UI panel, research buff calculator
├── goals/ # Goal/requirement resources
├── prestige/ # Prestige manager, config, panel
└── research/ # Research catalogue, panel, row UI
docs/gyms/tiny_sword/ # Playable game content (.tres resources)
├── currencies/ # Currency definitions
├── buildings/ # Generator configs
├── buffs/ # Buff definitions
├── goals/ # Goal definitions
├── prestige/ # Prestige config
├── research/ # Research configs
└── test/ # (reserved for tests)
sandbox/tiny_swords/ # Art assets (sprites, tilesets, UI textures)
└── Buildings/, Terrain/, UI Elements/, Units/
docs/museums/ # Prototype/demo scenes (big_number_museum)
```
## Known Issues / TODOs
The focus below is on behavior-bearing files and how they interact.
- [ ] Periodic auto-save not implemented (saves only on prestige and manual trigger)
- [ ] No automated test suite
- [ ] Some buffs may be configured with no unlock path
## Architecture Summary
## Technical Details
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.
### Buff Multiplier Stacking
## Core Systems
Multiple buffs of the same kind on the same generator stack **multiplicatively**:
### 1) Numeric System (`BigNumber`)
```
total_multiplier = buff1_effect(level) × buff2_effect(level) × buff3_effect(level) ×
```
File: `core/big_number.gd`
### Prestige Multiplier Application
`BigNumber` stores values as mantissa + exponent (scientific notation style), normalizes them, and provides:
Generators apply prestige multipliers automatically. The prestige multiplier is fetched from `PrestigeManager.get_total_multiplier()` and multiplied into the effective production rate alongside research and buff multipliers.
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.
## License
This class is used everywhere currency values move.
Internal prototype — no external license applied.
### 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.
*Godot version: 4.6*