# 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