From f671ab34196515d13e5d5d47d96893e18facbcb1 Mon Sep 17 00:00:00 2001 From: Michele Rossi Date: Fri, 10 Apr 2026 01:09:57 +0200 Subject: [PATCH] Decouple everything from globals --- .codex | 0 IMPLEMENTATION_SUMMARY.md | 230 ---------- PLANNING.md | 325 +++++++++++++ REFACTORING.md | 433 ------------------ big_number_progress_bar.gd | 12 +- core/buff_catalogue.gd | 24 + core/buff_catalogue.gd.uid | 1 + core/buff_database.gd | 47 -- core/buff_database.gd.uid | 1 - core/currency/currency_catalogue.gd | 17 + core/currency/currency_catalogue.gd.uid | 1 + core/currency_database.gd | 164 ------- core/currency_database.gd.uid | 1 - core/game_state.gd.uid | 1 - core/generator/currency_generator.gd | 168 ++++--- core/generator/generator_buff_data.gd | 12 +- core/generator/generator_container.gd | 21 +- core/goal_catalogue.gd | 17 + core/goal_catalogue.gd.uid | 1 + core/goals/README.md | 34 +- core/goals/goal_data.gd | 53 +-- core/goals/goal_requirement_data.gd | 56 +-- core/{game_state.gd => level_game_state.gd} | 341 +++++++++----- core/level_game_state.gd.uid | 1 + core/prestige/prestige_manager.gd | 56 ++- core/prestige/prestige_panel.gd | 11 +- core/prestige/prestige_panel.tscn | 2 +- currency_label.gd | 25 +- .../buffs/farm_auto_flux_buff.tres} | 6 +- .../buffs/forestry_auto_flux_buff.tres} | 6 +- .../buffs/gold_auto_flux_buff.tres} | 6 +- .../buffs/gold_click_buff.tres | 4 +- .../buffs/spawn_worker_buff.tres} | 6 +- .../tiny_sword/buffs/ts_buff_catalogue.tres | 14 + .../tiny_sword/buildings/castle/castle.tscn | 2 +- .../tiny_sword/buildings/castle/test_buff.gd | 28 +- docs/gyms/tiny_sword/buildings/farm/farm.tscn | 4 +- .../buildings/farm/farm_generator.tres} | 6 +- .../buildings/forestry/forestry.tscn | 4 +- .../forestry/forestry_generator.tres} | 6 +- .../buildings/gold_mine/gold_mine.tscn | 4 +- .../gold_mine/gold_mine_generator.tres} | 6 +- .../{ => tiny_sword}/currencies/food.tres | 0 .../{ => tiny_sword}/currencies/gold.tres | 0 .../currencies/ts_currency_catalogue.tres | 13 + .../{ => tiny_sword}/currencies/wood.tres | 0 .../{ => tiny_sword}/currencies/worker.tres | 0 .../goals/gold_350k_wood_20k_goal.tres} | 4 +- .../goals/gold_total_1300_goal.tres} | 2 +- .../goals/gold_total_13k_goal.tres} | 2 +- .../goals/gold_total_30_goal.tres} | 2 +- .../tiny_sword/goals/ts_goal_catalogue.tres | 12 + .../prestige/primary_prestige.tres | 0 docs/gyms/tiny_sword/tiny_sword.tscn | 38 +- goals_debug_ui.gd | 47 +- project.godot | 7 - 56 files changed, 993 insertions(+), 1291 deletions(-) create mode 100644 .codex delete mode 100644 IMPLEMENTATION_SUMMARY.md create mode 100644 PLANNING.md delete mode 100644 REFACTORING.md create mode 100644 core/buff_catalogue.gd create mode 100644 core/buff_catalogue.gd.uid delete mode 100644 core/buff_database.gd delete mode 100644 core/buff_database.gd.uid create mode 100644 core/currency/currency_catalogue.gd create mode 100644 core/currency/currency_catalogue.gd.uid delete mode 100644 core/currency_database.gd delete mode 100644 core/currency_database.gd.uid delete mode 100644 core/game_state.gd.uid create mode 100644 core/goal_catalogue.gd create mode 100644 core/goal_catalogue.gd.uid rename core/{game_state.gd => level_game_state.gd} (85%) create mode 100644 core/level_game_state.gd.uid rename docs/gyms/{buffs/farm_auto_flux.tres => tiny_sword/buffs/farm_auto_flux_buff.tres} (78%) rename docs/gyms/{buffs/forestry_auto_flux.tres => tiny_sword/buffs/forestry_auto_flux_buff.tres} (78%) rename docs/gyms/{buffs/gold_auto_flux.tres => tiny_sword/buffs/gold_auto_flux_buff.tres} (79%) rename docs/gyms/{ => tiny_sword}/buffs/gold_click_buff.tres (84%) rename docs/gyms/{buffs/spawn_worker.tres => tiny_sword/buffs/spawn_worker_buff.tres} (80%) create mode 100644 docs/gyms/tiny_sword/buffs/ts_buff_catalogue.tres rename docs/gyms/{generators/farm.tres => tiny_sword/buildings/farm/farm_generator.tres} (82%) rename docs/gyms/{generators/forestry.tres => tiny_sword/buildings/forestry/forestry_generator.tres} (82%) rename docs/gyms/{generators/gold_mine.tres => tiny_sword/buildings/gold_mine/gold_mine_generator.tres} (81%) rename docs/gyms/{ => tiny_sword}/currencies/food.tres (100%) rename docs/gyms/{ => tiny_sword}/currencies/gold.tres (100%) create mode 100644 docs/gyms/tiny_sword/currencies/ts_currency_catalogue.tres rename docs/gyms/{ => tiny_sword}/currencies/wood.tres (100%) rename docs/gyms/{ => tiny_sword}/currencies/worker.tres (100%) rename docs/gyms/{goals/gold_350k_wood_20k.tres => tiny_sword/goals/gold_350k_wood_20k_goal.tres} (89%) rename docs/gyms/{goals/gold_total_1300.tres => tiny_sword/goals/gold_total_1300_goal.tres} (91%) rename docs/gyms/{goals/gold_total_13k.tres => tiny_sword/goals/gold_total_13k_goal.tres} (91%) rename docs/gyms/{goals/gold_total_30.tres => tiny_sword/goals/gold_total_30_goal.tres} (91%) create mode 100644 docs/gyms/tiny_sword/goals/ts_goal_catalogue.tres rename docs/gyms/{ => tiny_sword}/prestige/primary_prestige.tres (100%) diff --git a/.codex b/.codex new file mode 100644 index 0000000..e69de29 diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md deleted file mode 100644 index a6054fa..0000000 --- a/IMPLEMENTATION_SUMMARY.md +++ /dev/null @@ -1,230 +0,0 @@ -# Global Buff System Implementation Summary - -## Status: ✅ COMPLETE - -All phases from the refactoring plan have been implemented. - -## Implemented Phases - -### ✅ Phase 1: Enhanced GeneratorBuffData -**File**: `core/generator/generator_buff_data.gd` - -**Changes:** -- Added `target_ids: Array[StringName]` field -- Added `targets_generator(generator_id: StringName) -> bool` method -- Added `get_target_generators() -> Array[StringName]` method - -**Risk**: Low (backward compatible) - ---- - -### ✅ Phase 2: GameState Buff Registry -**File**: `core/game_state.gd` - -**New State Variables:** -```gdscript -var _buff_definitions: Dictionary = {} -var _buff_levels: Dictionary = {} -var _buff_unlocked: Dictionary = {} -var _buff_active: Dictionary = {} -``` - -**New Signals:** -```gdscript -signal buff_level_changed(buff_id: StringName, new_level: int) -signal buff_unlocked_changed(buff_id: StringName, unlocked: bool) -``` - -**New Methods:** -- `register_buff(buff: GeneratorBuffData)` - Register buff definition -- `get_buff(buff_id: StringName)` - Get buff by ID -- `get_all_buffs()` - Get all registered buffs -- `get_buff_level(buff_id: StringName)` - Get global buff level -- `set_buff_level(buff_id: StringName, level: int)` - Set global buff level -- `is_buff_unlocked(buff_id: StringName)` - Check unlock state -- `set_buff_unlocked(buff_id: StringName, unlocked: bool)` - Set unlock state -- `is_buff_active(buff_id: StringName)` - Check if buff is active -- `get_generators_targeted_by(buff_id: StringName)` - Get targeted generators -- `get_buffs_for_generator(generator_id: StringName)` - Get buffs for specific generator -- `get_effective_multiplier(generator_id: StringName, kind: int)` - Calculate multipliers -- `_try_unlock_buff_from_goal(buff: GeneratorBuffData)` - Auto-unlock from goals - -**Risk**: High (core state management) - **Tested** - ---- - -### ✅ Phase 3: Save/Load System Update -**File**: `core/game_state.gd` - -**New Save Format (v2):** -```json -{ - "save_format_version": 2, - "currencies": { ... }, - "generator_states": { ... }, - "buff_levels": { "farm_flux": 5 }, - "buff_unlocked": { "farm_flux": true }, - "buff_active": { "farm_flux": true }, - "last_save_time": 1234567890 -} -``` - -**New Serialization Methods:** -- `_serialize_buff_levels()` -- `_serialize_buff_unlocked()` -- `_serialize_buff_active()` -- `_deserialize_buff_states()` - -**Version Check:** Old saves (v1) are rejected with error message - -**Risk**: High (breaking change) - **Tested** - ---- - -### ✅ Phase 4: CurrencyGenerator Update -**File**: `core/generator/currency_generator.gd` - -**Changes:** -- `get_buffs()` now queries `GameState.get_buffs_for_generator()` -- `get_buff_level()` delegates to `GameState.get_buff_level()` -- `is_buff_unlocked()` delegates to `GameState.is_buff_unlocked()` -- `buy_buff()` uses global buff level -- `_get_multiplier_for_buff_kind()` calls `GameState.get_effective_multiplier()` -- `_register_buffs()` registers buffs globally and evaluates unlock goals - -**Risk**: Medium (runtime behavior change) - **Tested** - ---- - -### ✅ Phase 5: PrestigeManager Update -**File**: `core/prestige/prestige_manager.gd` - -**Changes:** -- Added `_reset_all_buff_levels()` method -- `perform_prestige()` now resets all buff levels and unlock states - -**Risk**: Low - **Tested** - ---- - -### ✅ Phase 6: UI Components -**Status**: No changes required - -The existing `GeneratorPanel._build_buff_rows()` should work automatically since it uses `_generator.get_buffs()`, which now queries the global registry. - -**Optional**: Can add `GlobalBuffPanel` later for a dedicated buff UI - ---- - -### ✅ Phase 7: Data Migration Guide -**File**: `BUFF_MIGRATION.md` - -**Manual Steps Required:** -1. Update buff resources to set `target_ids` -2. Clear `buffs` array from generator resources -3. Delete old `user://idle_save.json` - -**Risk**: Low (data files only) - ---- - -### ✅ Phase 8: Buff Auto-Discovery -**File**: `core/buff_database.gd` - -**Features:** -- Auto-loads all `.tres` files from `res://sandbox/buffs/` -- Registered as autoload in `project.godot` -- Prints initialization log message - -**Added to Autoloads:** -``` -BuffDatabase="*res://core/buff_database.gd" -``` - -**Risk**: Low - **Tested** - ---- - -## Example Buff Files Created - -### `res://sandbox/buffs/farm_flux.tres` -- **ID**: `farm_flux` -- **Target**: `["farm", "forestry"]` (multi-target) -- **Effect**: +10% auto production per level - -### `res://sandbox/buffs/global_boost.tres` -- **ID**: `global_boost` -- **Target**: `["*"]` (wildcard - all generators) -- **Effect**: +5% auto production per level - ---- - -## 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 - ---- - -## API Changes Summary - -### 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 -``` - ---- - -## Next Steps - -1. **Run the project** to verify auto-discovery works -2. **Create more buff resources** in `res://sandbox/buffs/` -3. **Update existing generator resources** to remove `buffs` arrays -4. **Test buff UI** in `big_number_museum.tscn` -5. **Verify production calculations** with active buffs - ---- - -## Files Modified - -- ✅ `core/generator/generator_buff_data.gd` -- ✅ `core/game_state.gd` -- ✅ `core/generator/currency_generator.gd` -- ✅ `core/prestige/prestige_manager.gd` -- ✅ `core/buff_database.gd` (new) -- ✅ `project.godot` (added BuffDatabase autoload) - -## Files Created - -- ✅ `BUFF_MIGRATION.md` (migration guide) -- ✅ `IMPLEMENTATION_SUMMARY.md` (this file) -- ✅ `sandbox/buffs/farm_flux.tres` (example) -- ✅ `sandbox/buffs/global_boost.tres` (example) - ---- - -**Implementation Date**: March 29, 2026 -**Total Implementation Time**: ~4 hours -**Status**: Ready for testing diff --git a/PLANNING.md b/PLANNING.md new file mode 100644 index 0000000..1fd8491 --- /dev/null +++ b/PLANNING.md @@ -0,0 +1,325 @@ +# Per-Level GameState Architecture Refactoring Plan + +## Overview + +Convert the current autoload-based global GameState system to a per-level instance-based architecture where each level scene has its own `LevelGameState` node with references to catalogues (resources) and its own save file. + +## Current Architecture (To Be Replaced) + +``` +project.godot autoloads: +├─ BuffDatabase (autoload) +├─ CurrencyDatabase (autoload) +├─ GameState (autoload) +└─ PrestigeManager (autoload) + +tiny_sword.tscn: +└─ Uses GameState autoload directly from any node +``` + +**Problems:** +- Level scenes implicitly depend on global autoloads +- Cannot have multiple levels with independent state +- Catalogues auto-discover from filesystem (tightly coupled) +- Save file path is hardcoded globally + +## Target Architecture + +``` +tiny_sword.tscn (root node) +├─ LevelGameState (Node) +│ ├─ @export var currency_catalogue: CurrencyCatalogue (Resource) +│ ├─ @export var buff_catalogue: BuffCatalogue (Resource) +│ ├─ @export var goal_catalogue: GoalCatalogue (Resource) +│ ├─ @export var save_file_path: String = "user://tiny_sword_save.json" +│ └─ PrestigeManager (Node child) +│ └─ @export var game_state: LevelGameState +├─ World (Node2D) +│ ├─ GoldMine (CurrencyGenerator) +│ │ └─ @onready var game_state = find_parent("LevelGameState") +│ └─ ...other generators +└─ UI (Control) + ├─ CurrencyTile (each: @export var game_state: LevelGameState) + └─ ... +``` + +## Catalogue Resources + +All catalogues are `Resource` subclasses with explicit arrays populated in the editor: + +### CurrencyCatalogue +```gdscript +class_name CurrencyCatalogue +extends Resource + +@export var currencies: Array[Currency] = [] + +func get_currency_by_id(id: StringName) -> Currency +func get_all_ids() -> Array[StringName] +``` + +**Location:** `res://docs/gyms/tiny_sword/currencies/tiny_sword_catalogue.tres` + +### BuffCatalogue +```gdscript +class_name BuffCatalogue +extends Resource + +@export var buffs: Array[GeneratorBuffData] = [] + +func get_buff_by_id(id: StringName) -> GeneratorBuffData +func get_all_ids() -> Array[StringName] +func get_buffs_for_generator(gen_id: StringName) -> Array[GeneratorBuffData] +``` + +**Location:** `res://docs/gyms/tiny_sword/buffs/tiny_sword_buffs.tres` + +### GoalCatalogue +```gdscript +class_name GoalCatalogue +extends Resource + +@export var goals: Array[GoalData] = [] + +func get_goal_by_id(id: StringName) -> GoalData +func get_all_ids() -> Array[StringName] +``` + +**Location:** `res://docs/gyms/tiny_sword/goals/tiny_sword_goals.tres` + +--- + +## Implementation Steps + +### Step 1: Create Catalogue Classes + +**Files to create:** + +1. `core/currency_catalogue.gd` +2. `core/buff_catalogue.gd` +3. `core/goal_catalogue.gd` + +**Purpose:** Resource classes that hold arrays of game data and provide lookup methods. + +--- + +### Step 2: Create LevelGameState + +**File to create:** `core/level_game_state.gd` + +**Key changes from current `game_state.gd`:** + +| Current | New | +|---------|-----| +| `extends Node` (autoload) | `extends Node` (instance) | +| `CurrencyDatabase.get_known_currency_ids()` | `currency_catalogue.get_all_ids()` | +| `BuffDatabase` calls | `buff_catalogue` calls | +| Static `file_name` | `@export var save_file_path: String` | +| Filesystem goal discovery | `goal_catalogue.goals` array | +| Buff auto-registration from BuffDatabase | Buff registration from `buff_catalogue` | + +**New properties:** +```gdscript +@export var currency_catalogue: CurrencyCatalogue +@export var buff_catalogue: BuffCatalogue +@export var goal_catalogue: GoalCatalogue +@export var save_file_path: String = "user://level_save.json" +``` + +**Methods to port:** +- All currency methods (`add_currency`, `spend_currency`, `get_currency_amount_by_id`, etc.) +- All generator methods (`register_generator`, `get_generator_state`, etc.) +- All buff methods (`register_buff`, `get_buff_level`, `get_effective_multiplier`, etc.) +- All goal methods (`register_goal`, `is_goal_completed`, `evaluate_all_goals`, etc.) +- Save/load (`save_game()`, `load_game()`) with configurable path +- Prestige methods (`reset_for_prestige`, `emit_currency_changed_for_all`) + +--- + +### Step 3: Convert PrestigeManager + +**File to modify:** `core/prestige/prestige_manager.gd` + +**Changes:** +- Remove autoload registration from `project.godot` +- Add `@export var game_state: LevelGameState` +- Change `/root/PrestigeManager` lookup to use direct reference +- Make it a child node of `LevelGameState` in scenes + +**Key fix in `_get_prestige_multiplier()`:** +```gdscript +# Old: get_node_or_null("/root/PrestigeManager") +# New: Direct access through injected reference +``` + +--- + +### Step 4: Update CurrencyGenerator + +**File to modify:** `core/generator/currency_generator.gd` + +**Changes:** +- Replace all `GameState.` calls with `game_state.` calls +- Add `@onready var game_state: LevelGameState = find_parent("LevelGameState")` +- Update signal connections: `game_state.currency_changed.connect(...)` +- Update `_get_prestige_multiplier()` to use injected reference + +--- + +### Step 5: Update CurrencyTile + +**File to modify:** `currency_label.gd` + +**Changes:** +```gdscript +@export var game_state: LevelGameState # ← NEW + +func _ready() -> void: + if game_state == null: + push_error("CurrencyTile '%s' missing game_state reference" % name) + return + + _currency_id = game_state.get_currency_id(currency) + # ... rest of initialization +``` + +--- + +### Step 6: Update project.godot + +**Remove autoloads:** +```ini +# REMOVE these lines: +# BuffDatabase="*res://core/buff_database.gd" +# CurrencyDatabase="*res://core/currency_database.gd" +# GameState="*uid://d2j7tvlgxr2jp" +# PrestigeManager="*res://core/prestige/prestige_manager.gd" +``` + +--- + +### Step 7: Update tiny_sword.tscn + +**Add LevelGameState node:** +```ini +[node name="LevelGameState" type="Node" parent="."] +currency_catalogue = ExtResource("currency_catalogue_tres") +buff_catalogue = ExtResource("buff_catalogue_tres") +goal_catalogue = ExtResource("goal_catalogue_tres") +save_file_path = "user://tiny_sword_save.json" +``` + +**Add PrestigeManager as child:** +```ini +[node name="PrestigeManager" type="Node" parent="LevelGameState"] +game_state = NodePath("..") # Reference to LevelGameState +``` + +**Wire up children:** +- Each `CurrencyTile`: `game_state = LevelGameState` +- Each `CurrencyGenerator`: auto-discovers via `find_parent()` + +--- + +### Step 8: Create Catalogue Resources + +**Create `.tres` files via Godot editor:** + +1. `res://docs/gyms/tiny_sword/currencies/tiny_sword_catalogue.tres` + - `currencies = [gold, worker, food, wood]` + +2. `res://docs/gyms/tiny_sword/buffs/tiny_sword_buffs.tres` + - `buffs = [all relevant buffs for this level]` + +3. `res://docs/gyms/tiny_sword/goals/tiny_sword_goals.tres` + - `goals = [all relevant goals for this level]` + +--- + +### Step 9: Clean Up + +**Files to delete:** +- `core/currency_database.gd` (replaced by `CurrencyCatalogue`) +- `core/buff_database.gd` (replaced by `BuffCatalogue`) +- `core/game_state.gd` (replaced by `level_game_state.gd`) + +--- + +## File Summary + +### Files to Create (4) +| File | Purpose | +|------|---------| +| `core/currency_catalogue.gd` | Resource with `Array[Currency]` + lookup methods | +| `core/buff_catalogue.gd` | Resource with `Array[GeneratorBuffData]` + lookup methods | +| `core/goal_catalogue.gd` | Resource with `Array[GoalData]` + lookup methods | +| `core/level_game_state.gd` | Complete GameState implementation without autoload dependencies | + +### Files to Modify (4) +| File | Changes | +|------|---------| +| `core/prestige/prestige_manager.gd` | Remove autoload, add `@export var game_state` | +| `core/generator/currency_generator.gd` | Add `find_parent()` lookup, replace `GameState.` | +| `currency_label.gd` | Add `@export var game_state: LevelGameState` | +| `project.godot` | Remove 4 autoloads | + +### Files to Delete (3) +| File | Reason | +|------|--------| +| `core/currency_database.gd` | Replaced by `CurrencyCatalogue` | +| `core/buff_database.gd` | Replaced by `BuffCatalogue` | +| `core/game_state.gd` | Replaced by `level_game_state.gd` | + +### Scene Updates +| File | Changes | +|------|---------| +| `tiny_sword.tscn` | Add `LevelGameState` node, add `PrestigeManager` child, wire catalogues | +| Create `.tres` resources | `tiny_sword_catalogue.tres`, `tiny_sword_buffs.tres`, `tiny_sword_goals.tres` | + +--- + +## Benefits + +1. **Decoupled levels**: Each level can exist independently without global dependencies +2. **Multiple levels**: Can support multiple levels with independent state (future feature) +3. **Explicit dependencies**: All dependencies are visible in the inspector +4. **Resource-based catalogues**: Content is defined as editor-authorable resources +5. **Per-level saves**: Each level has its own save file path +6. **Better testing**: Easier to test individual levels in isolation + +--- + +## Risks and Considerations + +1. **Signal connections**: UI components must be manually wired to `LevelGameState` instance +2. **PrestigeManager access**: Must ensure proper reference injection +3. **Catalogue population**: Catalogue resources must be created and populated in editor +4. **Migration**: Existing save files may need migration logic + +--- + +## Implementation Order + +1. Create catalogue classes (3 files) +2. Create level_game_state.gd (the core refactor) +3. Update prestige_manager.gd (remove autoload dependency) +4. Update currency_generator.gd (add game_state injection) +5. Update currency_label.gd (add game_state export) +6. Update project.godot (remove autoloads) +7. Create catalogue resources (.tres files via editor) +8. Update tiny_sword.tscn (wire everything together) +9. Delete old files (cleanup) + +--- + +## Status + +- [ ] Step 1: Create catalogue classes +- [ ] Step 2: Create level_game_state.gd +- [ ] Step 3: Update prestige_manager.gd +- [ ] Step 4: Update currency_generator.gd +- [ ] Step 5: Update currency_label.gd +- [ ] Step 6: Update project.godot +- [ ] Step 7: Create catalogue resources (manual editor work) +- [ ] Step 8: Update tiny_sword.tscn (manual editor work) +- [ ] Step 9: Delete old files diff --git a/REFACTORING.md b/REFACTORING.md deleted file mode 100644 index 83a36ae..0000000 --- a/REFACTORING.md +++ /dev/null @@ -1,433 +0,0 @@ -# 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* diff --git a/big_number_progress_bar.gd b/big_number_progress_bar.gd index 773a2e0..ccda872 100644 --- a/big_number_progress_bar.gd +++ b/big_number_progress_bar.gd @@ -1,6 +1,7 @@ extends HBoxContainer @export var currency: Resource +@export var game_state: LevelGameState @onready var _label_start = $LabelStart @onready var _label_end = $LabelEnd @@ -22,15 +23,16 @@ func set_limits(start: BigNumber, end: BigNumber) -> void: _progress_bar.max_value = _end.get_ratio(_start) func _ready() -> void: - if currency == null: - currency = CurrencyDatabase.get_currency_resource(GameState.GOLD_CURRENCY_ID) if currency == null: push_warning("BigNumberProgressBar '%s' has no currency configured." % String(name)) return - _currency_id = GameState.get_currency_id(currency) + if game_state == null: + push_warning("BigNumberProgressBar '%s' has no game_state configured." % String(name)) + return + _currency_id = game_state.get_currency_id(currency) - GameState.currency_changed.connect(_on_currency_changed) - _current = GameState.get_currency_amount_by_id(_currency_id) + game_state.currency_changed.connect(_on_currency_changed) + _current = game_state.get_currency_amount_by_id(_currency_id) set_limits(_current, _current.add(BigNumber.new(1, 1))) diff --git a/core/buff_catalogue.gd b/core/buff_catalogue.gd new file mode 100644 index 0000000..63ecb2f --- /dev/null +++ b/core/buff_catalogue.gd @@ -0,0 +1,24 @@ +class_name BuffCatalogue +extends Resource + +@export var buffs: Array[GeneratorBuffData] = [] + +func get_buff_by_id(id: StringName) -> GeneratorBuffData: + for buff in buffs: + if buff.id == id: + return buff + return null + +func get_all_ids() -> Array[StringName]: + var ids: Array[StringName] = [] + for buff in buffs: + if buff.id: + ids.append(buff.id) + return ids + +func get_buffs_for_generator(generator_id: StringName) -> Array[GeneratorBuffData]: + var result: Array[GeneratorBuffData] = [] + for buff in buffs: + if buff.targets_generator(generator_id): + result.append(buff) + return result diff --git a/core/buff_catalogue.gd.uid b/core/buff_catalogue.gd.uid new file mode 100644 index 0000000..7021592 --- /dev/null +++ b/core/buff_catalogue.gd.uid @@ -0,0 +1 @@ +uid://ctc5yjlnvi0ok diff --git a/core/buff_database.gd b/core/buff_database.gd deleted file mode 100644 index 39b4fb9..0000000 --- a/core/buff_database.gd +++ /dev/null @@ -1,47 +0,0 @@ -extends Node - -## Auto-discovers and registers all buff resources from res://docs/gyms/buffs/ - -const BUFF_DIRECTORY_PATH: String = "res://docs/gyms/buffs" - -var _buff_by_id: Dictionary = {} -var _initialized: bool = false - -func _ready() -> void: - _initialize_buff_catalog() - -func _initialize_buff_catalog() -> void: - if _initialized: - return - - _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) - _initialized = true - 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: - _buff_by_id[buff.id] = buff - GameState.register_buff(buff) - entry_name = dir.get_next() - dir.list_dir_end() - - _initialized = true - print("BuffDatabase initialized with %d buffs" % _buff_by_id.size()) - -func get_all_buffs() -> Array[GeneratorBuffData]: - var result: Array[GeneratorBuffData] = [] - for buff in _buff_by_id.values(): - if buff is GeneratorBuffData: - result.append(buff) - return result - -func get_buff(buff_id: StringName) -> GeneratorBuffData: - return _buff_by_id.get(buff_id, null) diff --git a/core/buff_database.gd.uid b/core/buff_database.gd.uid deleted file mode 100644 index 01a70a7..0000000 --- a/core/buff_database.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cjm87u4xh0718 diff --git a/core/currency/currency_catalogue.gd b/core/currency/currency_catalogue.gd new file mode 100644 index 0000000..7a2d0c0 --- /dev/null +++ b/core/currency/currency_catalogue.gd @@ -0,0 +1,17 @@ +class_name CurrencyCatalogue +extends Resource + +@export var currencies: Array[Currency] = [] + +func get_currency_by_id(id: StringName) -> Currency: + for currency in currencies: + if currency.id == id: + return currency + return null + +func get_all_ids() -> Array[StringName]: + var ids: Array[StringName] = [] + for currency in currencies: + if currency.id != &"": + ids.append(currency.id) + return ids diff --git a/core/currency/currency_catalogue.gd.uid b/core/currency/currency_catalogue.gd.uid new file mode 100644 index 0000000..4eaf29c --- /dev/null +++ b/core/currency/currency_catalogue.gd.uid @@ -0,0 +1 @@ +uid://621tus0uvbrd diff --git a/core/currency_database.gd b/core/currency_database.gd deleted file mode 100644 index ba268a7..0000000 --- a/core/currency_database.gd +++ /dev/null @@ -1,164 +0,0 @@ -extends Node - -#const GOLD_CURRENCY_ID: StringName = &"gold" -#const GEMS_CURRENCY_ID: StringName = &"gems" -#const LEGACY_CURRENCY_INDEX_TO_ID: Dictionary = { - #0: GOLD_CURRENCY_ID, - #1: GEMS_CURRENCY_ID, -#} - -const CURRENCY_DIRECTORY_PATH: String = "res://docs/gyms/currencies" -const CURRENCY_RESOURCE_EXTENSIONS: PackedStringArray = ["tres", "res"] - -var _currency_by_id: Dictionary = {} -var _catalog_initialized: bool = false - -func _ready() -> void: - _initialize_currency_catalog() - -func get_known_currencies() -> Array[Currency]: - _initialize_currency_catalog_if_needed() - - var result: Array[Currency] = [] - for raw_currency in _currency_by_id.values(): - var currency: Currency = raw_currency as Currency - if currency != null: - result.append(currency) - return result - -func get_known_currency_ids() -> Array[StringName]: - _initialize_currency_catalog_if_needed() - - var ids: Array[StringName] = [] - for raw_currency_id in _currency_by_id.keys(): - var currency_id: StringName = _normalize_currency_id(StringName(String(raw_currency_id))) - if currency_id == &"": - continue - ids.append(currency_id) - return ids - -func get_currency_id(currency: Currency) -> StringName: - if currency == null: - return &"" - - var raw_id: Variant = currency.id - if raw_id is StringName: - return _normalize_currency_id(raw_id) - return _normalize_currency_id(StringName(String(raw_id))) - -func parse_currency_id(raw_currency: Variant) -> StringName: - #if raw_currency is int: - #return currency_id_from_legacy_index(raw_currency) - - var currency_text: String = String(raw_currency).to_lower().strip_edges() - if currency_text == "gem": - currency_text = "gems" - return _normalize_currency_id(StringName(currency_text)) - -#func currency_id_from_legacy_index(currency_index: int) -> StringName: - #return LEGACY_CURRENCY_INDEX_TO_ID.get(currency_index, &"") - -func is_known_currency_id(currency_id: StringName) -> bool: - _initialize_currency_catalog_if_needed() - - var normalized_currency_id: StringName = _normalize_currency_id(currency_id) - if normalized_currency_id == &"": - return false - return _currency_by_id.has(normalized_currency_id) - -func get_currency_resource(currency_id: StringName) -> Currency: - _initialize_currency_catalog_if_needed() - - var normalized_currency_id: StringName = _normalize_currency_id(currency_id) - if normalized_currency_id == &"": - return null - - var raw_currency: Variant = _currency_by_id.get(normalized_currency_id) - if raw_currency is Currency: - return raw_currency - return null - -func get_currency_name(currency_id: StringName) -> String: - var currency: Currency = get_currency_resource(currency_id) - if currency != null: - var display_name: String = String(currency.display_name).strip_edges() - if not display_name.is_empty(): - return display_name - return _humanize_currency_id(currency_id) - -func get_currency_icon(currency_id: StringName) -> Texture2D: - var currency: Resource = get_currency_resource(currency_id) - if currency == null: - return null - - var raw_icon: Variant = currency.icon - if raw_icon is Texture2D: - return raw_icon - return null - -func _initialize_currency_catalog_if_needed() -> void: - if _catalog_initialized: - return - _initialize_currency_catalog() - -func _initialize_currency_catalog() -> void: - _currency_by_id.clear() - _catalog_initialized = true - - var currency_resource_paths: PackedStringArray = _discover_currency_resource_paths() - for resource_path in currency_resource_paths: - var loaded_resource: Resource = load(resource_path) - if loaded_resource == null: - push_warning("Failed to load currency resource at '%s'." % resource_path) - continue - if not (loaded_resource is Currency): - continue - - var currency_id: StringName = get_currency_id(loaded_resource) - if currency_id == &"": - push_warning("Skipping currency with missing id: %s" % resource_path) - continue - if _currency_by_id.has(currency_id): - push_warning("Duplicate currency id '%s' found at %s. Keeping first occurrence." % [String(currency_id), resource_path]) - continue - - _currency_by_id[currency_id] = loaded_resource - - if _currency_by_id.is_empty(): - push_warning("No currency resources discovered under %s. Ensure currency .tres files exist and are included in export presets." % CURRENCY_DIRECTORY_PATH) - -func _discover_currency_resource_paths() -> PackedStringArray: - var result: PackedStringArray = [] - var directory: DirAccess = DirAccess.open(CURRENCY_DIRECTORY_PATH) - if directory == null: - push_warning("Unable to open currency directory: %s" % CURRENCY_DIRECTORY_PATH) - return result - - directory.list_dir_begin() - var entry_name: String = directory.get_next() - while not entry_name.is_empty(): - if directory.current_is_dir() or entry_name.begins_with("."): - entry_name = directory.get_next() - continue - - var extension: String = entry_name.get_extension().to_lower() - if CURRENCY_RESOURCE_EXTENSIONS.has(extension): - result.append("%s/%s" % [CURRENCY_DIRECTORY_PATH, entry_name]) - - entry_name = directory.get_next() - directory.list_dir_end() - - result.sort() - return result - -func _normalize_currency_id(currency_id: StringName) -> StringName: - var normalized: String = String(currency_id).to_lower().strip_edges() - if normalized.is_empty(): - return &"" - return StringName(normalized) - -func _humanize_currency_id(currency_id: StringName) -> String: - var normalized: String = String(_normalize_currency_id(currency_id)) - if normalized.is_empty(): - return "Unknown" - return normalized.replace("_", " ").capitalize() diff --git a/core/currency_database.gd.uid b/core/currency_database.gd.uid deleted file mode 100644 index 27c1cd2..0000000 --- a/core/currency_database.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dmv83fnq26xao diff --git a/core/game_state.gd.uid b/core/game_state.gd.uid deleted file mode 100644 index fdee5b0..0000000 --- a/core/game_state.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d2j7tvlgxr2jp diff --git a/core/generator/currency_generator.gd b/core/generator/currency_generator.gd index 79907a0..02680cf 100644 --- a/core/generator/currency_generator.gd +++ b/core/generator/currency_generator.gd @@ -44,42 +44,45 @@ var _mouse_entered: bool = false ## Time left until next click/hover grant is allowed. var _remaining_click_cooldown_seconds: float = 0.0 +## Reference to LevelGameState instance. +@onready var game_state: LevelGameState = find_parent("LevelGameState") + ## Number of currently owned generators. -## Backed by GameState via this generator's resolved id. +## Backed by LevelGameState via this generator's resolved id. var owned: int: get: _ensure_registered() - return GameState.get_generator_owned(_generator_id) + return game_state.get_generator_owned(_generator_id) set(value): _ensure_registered() - GameState.set_generator_owned(_generator_id, value) + game_state.set_generator_owned(_generator_id, value) ## Lifetime purchased generators (does not decrease if ownership drops). var purchased_count: int: get: _ensure_registered() - return GameState.get_generator_purchased_count(_generator_id) + return game_state.get_generator_purchased_count(_generator_id) set(value): _ensure_registered() - GameState.set_generator_purchased_count(_generator_id, value) + game_state.set_generator_purchased_count(_generator_id, value) ## Whether this generator is unlocked for the player. var is_unlocked: bool: get: _ensure_registered() - return GameState.is_generator_unlocked(_generator_id) + return game_state.is_generator_unlocked(_generator_id) set(value): _ensure_registered() - GameState.set_generator_unlocked(_generator_id, value) + game_state.set_generator_unlocked(_generator_id, value) ## Whether this unlocked generator is currently visible/usable to the player. var is_available: bool: get: _ensure_registered() - return GameState.is_generator_available(_generator_id) + return game_state.is_generator_available(_generator_id) set(value): _ensure_registered() - GameState.set_generator_available(_generator_id, value) + game_state.set_generator_available(_generator_id, value) ## Accumulated elapsed time toward the next automatic production cycle. var cycle_progress_seconds: float = 0.0 @@ -98,9 +101,10 @@ func _ready() -> void: _generator_id = _resolve_generator_id() cycle_progress_seconds = 0.0 - GameState.currency_changed.connect(_on_currency_changed) - GameState.generator_state_changed.connect(_on_generated_state_changed) - GameState.goal_completed.connect(_on_goal_completed) + if game_state: + game_state.currency_changed.connect(_on_currency_changed) + game_state.generator_state_changed.connect(_on_generated_state_changed) + game_state.goal_completed.connect(_on_goal_completed) _ensure_registered() @@ -220,18 +224,27 @@ func buy_max() -> int: func get_buffs() -> Array[GeneratorBuffData]: if data == null: return [] - return GameState.get_buffs_for_generator(_generator_id) + if game_state == null: + return [] + return game_state.get_buffs_for_generator(_generator_id) func get_buff_level(buff_id: StringName) -> int: _ensure_registered() - return GameState.get_buff_level(buff_id) + if game_state == null: + return 0 + return game_state.get_buff_level(buff_id) func is_buff_unlocked(buff_id: StringName) -> bool: _ensure_registered() - return GameState.is_buff_unlocked(buff_id) + if game_state == null: + return false + return game_state.is_buff_unlocked(buff_id) func get_buff_cost(buff_id: StringName) -> BigNumber: - var buff: GeneratorBuffData = GameState.get_buff(buff_id) + if game_state == null: + return BigNumber.from_float(0.0) + + var buff: GeneratorBuffData = game_state.get_buff(buff_id) if buff == null: return BigNumber.from_float(0.0) if not is_buff_unlocked(buff.id): @@ -241,7 +254,10 @@ func get_buff_cost(buff_id: StringName) -> BigNumber: return buff.get_cost_for_level(level) func get_buff_cost_currency_id(buff_id: StringName) -> StringName: - var buff: GeneratorBuffData = GameState.get_buff(buff_id) + if game_state == null: + return &"" + + var buff: GeneratorBuffData = game_state.get_buff(buff_id) if buff == null: return &"" return _resolve_buff_cost_currency_id(buff) @@ -250,7 +266,10 @@ func can_buy_buff(buff_id: StringName) -> bool: if not is_available_to_player(): return false - var buff: GeneratorBuffData = GameState.get_buff(buff_id) + if game_state == null: + return false + + var buff: GeneratorBuffData = game_state.get_buff(buff_id) if buff == null: return false @@ -266,21 +285,24 @@ func can_buy_buff(buff_id: StringName) -> bool: return false var cost: BigNumber = buff.get_cost_for_level(current_level) - var current_amount: BigNumber = GameState.get_currency_amount_by_id(cost_currency_id) + var current_amount: BigNumber = game_state.get_currency_amount_by_id(cost_currency_id) return current_amount.is_greater_than(cost) or current_amount.is_equal_to(cost) func buy_buff(buff_id: StringName) -> bool: if not is_available_to_player(): return false - var buff: GeneratorBuffData = GameState.get_buff(buff_id) + if game_state == null: + return false + + var buff: GeneratorBuffData = game_state.get_buff(buff_id) if buff == null: return false - if not GameState.is_buff_unlocked(buff.id): + if not game_state.is_buff_unlocked(buff.id): return false - var current_level: int = GameState.get_buff_level(buff.id) + var current_level: int = game_state.get_buff_level(buff.id) if not buff.can_purchase_next_level(current_level): return false @@ -294,19 +316,22 @@ func buy_buff(buff_id: StringName) -> bool: return false var next_level: int = current_level + 1 - GameState.set_buff_level(buff.id, next_level) + game_state.set_buff_level(buff.id, next_level) _apply_resource_purchase_buff(buff, next_level) buff_purchased.emit(buff.id, next_level, cost, cost_currency_id) return true func is_buff_maxed(buff_id: StringName) -> bool: - var buff: GeneratorBuffData = GameState.get_buff(buff_id) + if game_state == null: + return true + + var buff: GeneratorBuffData = game_state.get_buff(buff_id) if buff == null: return true - if not GameState.is_buff_unlocked(buff.id): + if not game_state.is_buff_unlocked(buff.id): return false - return not buff.can_purchase_next_level(GameState.get_buff_level(buff.id)) + return not buff.can_purchase_next_level(game_state.get_buff_level(buff.id)) func get_automatic_production_multiplier() -> float: return _get_multiplier_for_buff_kind(GeneratorBuffData.BuffKind.AUTO_PRODUCTION_MULTIPLIER) @@ -384,13 +409,18 @@ func _grants_click_while_hovering() -> bool: return grants_click_while_hovering func _get_prestige_multiplier() -> float: - var manager: PrestigeManager = get_node_or_null("/root/PrestigeManager") - if manager == null: + if game_state == null: return 1.0 - if not manager.get_total_multiplier(): + + var parent: Node = get_parent() + if parent == null: return 1.0 - - var raw_multiplier: Variant = manager.get_total_multiplier() + + var prestige_manager: PrestigeManager = parent.find_child("PrestigeManager", true, false) + if prestige_manager == null: + return 1.0 + + var raw_multiplier: Variant = prestige_manager.get_total_multiplier() if raw_multiplier is float: return maxf(raw_multiplier, 0.0) if raw_multiplier is int: @@ -405,14 +435,16 @@ func get_generator_id() -> StringName: ## Returns the configured currency id. func get_currency_id() -> StringName: - return GameState.get_currency_id(currency) + if game_state == null: + return &"" + return game_state.get_currency_id(currency) func get_purchase_currency_id() -> StringName: var purchase_currency: Currency = _resolve_purchase_currency() if purchase_currency == null: return &"" - return GameState.get_currency_id(purchase_currency) + return game_state.get_currency_id(purchase_currency) ## True when the generator is both unlocked and available. func is_available_to_player() -> bool: @@ -424,13 +456,17 @@ func _add_currency(amount: BigNumber) -> void: push_error("CurrencyGenerator '%s' cannot add currency: currency resource is null." % String(name)) return - GameState.add_currency(currency, amount) + if game_state == null: + return + game_state.add_currency(currency, amount) func _add_currency_by_id(currency_id: StringName, amount: BigNumber) -> void: if currency_id == &"": return - GameState.add_currency_by_id(currency_id, amount) + if game_state == null: + return + game_state.add_currency_by_id(currency_id, amount) ## Attempts to spend target currency; returns false when insufficient. func _spend_currency(cost: BigNumber) -> bool: @@ -438,25 +474,33 @@ func _spend_currency(cost: BigNumber) -> bool: push_error("CurrencyGenerator '%s' cannot spend currency: currency resource is null." % String(name)) return false - return GameState.spend_currency(currency, cost) + if game_state == null: + return false + return game_state.spend_currency(currency, cost) func _spend_currency_by_id(currency_id: StringName, cost: BigNumber) -> bool: if currency_id == &"": return false - return GameState.spend_currency_by_id(currency_id, cost) + if game_state == null: + return false + return game_state.spend_currency_by_id(currency_id, cost) ## Returns the current amount for the configured currency type. func _get_currency_amount() -> BigNumber: if currency == null: return BigNumber.from_float(0.0) - return GameState.get_currency_amount(currency) + if game_state == null: + return BigNumber.from_float(0.0) + return game_state.get_currency_amount(currency) func _get_currency_amount_by_id(currency_id: StringName) -> BigNumber: if currency_id == &"": return BigNumber.from_float(0.0) - return GameState.get_currency_amount_by_id(currency_id) + if game_state == null: + return BigNumber.from_float(0.0) + return game_state.get_currency_amount_by_id(currency_id) ## Safely converts finite float values into BigNumber costs. func _float_to_big_number(value: float) -> BigNumber: @@ -484,7 +528,9 @@ func _resolve_buff_cost_currency_id(buff: GeneratorBuffData) -> StringName: if cost_currency == null: return &"" - return GameState.get_currency_id(cost_currency) + if game_state == null: + return &"" + return game_state.get_currency_id(cost_currency) func _resolve_purchase_currency() -> Currency: if data == null: @@ -500,10 +546,14 @@ func _resolve_buff_target_currency_id(buff: GeneratorBuffData) -> StringName: if target_currency == null: return &"" - return GameState.get_currency_id(target_currency) + if game_state == null: + return &"" + return game_state.get_currency_id(target_currency) func _get_multiplier_for_buff_kind(kind: GeneratorBuffData.BuffKind) -> float: - return GameState.get_effective_multiplier(_generator_id, kind) + if game_state == null: + return 1.0 + return game_state.get_effective_multiplier(_generator_id, kind) func _apply_resource_purchase_buff(buff: GeneratorBuffData, level: int) -> void: if buff == null: @@ -533,7 +583,7 @@ func _resolve_generator_id() -> StringName: return &"generator" -## Ensures this generator has a state entry in GameState. +## Ensures this generator has a state entry in LevelGameState. func _ensure_registered() -> void: if _is_registered or _is_registering: return @@ -543,8 +593,11 @@ func _ensure_registered() -> void: if _generator_id == &"": return + if game_state == null: + return + _is_registering = true - GameState.register_generator( + game_state.register_generator( _generator_id, _default_unlocked_state(), _default_available_state(), @@ -555,6 +608,9 @@ func _ensure_registered() -> void: _is_registering = false func _register_buffs() -> void: + if game_state == null: + return + for buff in get_buffs(): if buff == null: continue @@ -564,20 +620,24 @@ func _register_buffs() -> void: continue var buff_id: StringName = StringName(buff_id_text) - GameState.register_buff(buff) + game_state.register_buff(buff) - if not GameState.is_buff_unlocked(buff_id): - var persisted_level: int = GameState.get_buff_level(buff_id) + if not game_state.is_buff_unlocked(buff_id): + var persisted_level: int = game_state.get_buff_level(buff_id) var starts_unlocked: bool = buff.unlocked or persisted_level > 0 - GameState.set_buff_unlocked(buff_id, starts_unlocked) + game_state.set_buff_unlocked(buff_id, starts_unlocked) _try_unlock_buff_from_goal(buff) func _try_unlock_buff_from_goal(buff: GeneratorBuffData) -> void: - GameState._try_unlock_buff_from_goal(buff) + if game_state == null: + return + game_state._try_unlock_buff_from_goal(buff) func _on_currency_changed(_changed_currency_id: StringName, _new_amount: BigNumber) -> void: - GameState.evaluate_all_goals() + if game_state == null: + return + game_state.evaluate_all_goals() func _on_goal_completed(goal_id: StringName) -> void: if data == null or not data.has_unlock_goal(): @@ -587,10 +647,12 @@ func _on_goal_completed(goal_id: StringName) -> void: if is_available_to_player(): return - GameState.set_generator_unlocked(_generator_id, true) - GameState.set_generator_available(_generator_id, true) + if game_state == null: + return + game_state.set_generator_unlocked(_generator_id, true) + game_state.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)) + _on_generated_state_changed(_generator_id, game_state._get_generator_state(_generator_id)) ## Default unlocked state loaded from generator data. func _default_unlocked_state() -> bool: diff --git a/core/generator/generator_buff_data.gd b/core/generator/generator_buff_data.gd index c92d936..09cdcc9 100644 --- a/core/generator/generator_buff_data.gd +++ b/core/generator/generator_buff_data.gd @@ -58,10 +58,10 @@ func get_unlock_goal_amount() -> BigNumber: if unlock_goal == null: return BigNumber.from_float(0.0) - var valid_requirements: Array[GoalRequirementData] = unlock_goal.get_valid_requirements() - if valid_requirements.is_empty(): + var requirements: Array[GoalRequirementData] = unlock_goal.get_requirements() + if requirements.is_empty(): return BigNumber.from_float(0.0) - var requirement_resource: GoalRequirementData = valid_requirements[0] + var requirement_resource: GoalRequirementData = requirements[0] if requirement_resource == null: return BigNumber.from_float(0.0) @@ -75,10 +75,10 @@ func get_unlock_goal_currency() -> Currency: if unlock_goal == null: return null - var valid_requirements: Array[GoalRequirementData] = unlock_goal.get_valid_requirements() - if valid_requirements.is_empty(): + var requirements: Array[GoalRequirementData] = unlock_goal.get_requirements() + if requirements.is_empty(): return null - var requirement_resource: GoalRequirementData = valid_requirements[0] + var requirement_resource: GoalRequirementData = requirements[0] if requirement_resource == null: return null diff --git a/core/generator/generator_container.gd b/core/generator/generator_container.gd index d3e6969..3aec810 100644 --- a/core/generator/generator_container.gd +++ b/core/generator/generator_container.gd @@ -37,11 +37,12 @@ func _ready() -> void: _generator.purchase_failed.connect(_on_generator_updated) _generator.buff_purchased.connect(_on_generator_updated) _generator.buff_purchase_failed.connect(_on_generator_updated) - GameState.currency_changed.connect(_on_currency_changed) - - GameState.generator_state_changed.connect(_on_generator_state_changed) - GameState.generator_buff_level_changed.connect(_on_generator_buff_level_changed) - GameState.generator_buff_unlocked_changed.connect(_on_generator_buff_unlocked_changed) + + if _generator.game_state: + _generator.game_state.currency_changed.connect(_on_currency_changed) + _generator.game_state.generator_state_changed.connect(_on_generator_state_changed) + _generator.game_state.generator_buff_level_changed.connect(_on_generator_buff_level_changed) + _generator.game_state.generator_buff_unlocked_changed.connect(_on_generator_buff_unlocked_changed) _name_label.text = _generator.data.name if _generator.data != null else "Generator" _build_buff_rows() @@ -123,9 +124,9 @@ func _refresh_buff_rows(can_interact: bool) -> void: if not buff_unlocked: var locked_hint: String = "Locked" var unlock_goal_currency: Resource = buff.get_unlock_goal_currency() - if buff.has_unlock_goal() and unlock_goal_currency != null: - var goal_currency_id: StringName = GameState.get_currency_id(unlock_goal_currency) - var currency_name: String = GameState.get_currency_name(goal_currency_id) + if buff.has_unlock_goal() and unlock_goal_currency != null and _generator.game_state: + var goal_currency_id: StringName = _generator.game_state.get_currency_id(unlock_goal_currency) + var currency_name: String = _generator.game_state.get_currency_name(goal_currency_id) var required_text: String = buff.get_unlock_goal_amount().to_string_suffix(2) locked_hint = "Unlock at %s %s" % [required_text, currency_name] @@ -141,7 +142,7 @@ func _refresh_buff_rows(can_interact: bool) -> void: var cost: BigNumber = _generator.get_buff_cost(buff.id) var cost_currency_id: StringName = _generator.get_buff_cost_currency_id(buff.id) - var currency_label: String = GameState.get_currency_name(cost_currency_id) + var currency_label: String = _generator.game_state.get_currency_name(cost_currency_id) if _generator.game_state else "Unknown" var cost_text: String = "%s %s" % [cost.to_string_suffix(2), currency_label] var can_buy: bool = can_interact and _generator.can_buy_buff(buff.id) row.tile.set_runtime(level, effect_text, cost_text, can_buy, false) @@ -161,7 +162,7 @@ func _refresh_generator_ui() -> void: _owned_value.text = str(_generator.owned) if can_interact: var purchase_currency_id: StringName = _generator.get_purchase_currency_id() - var purchase_currency_name: String = GameState.get_currency_name(purchase_currency_id) + var purchase_currency_name: String = _generator.game_state.get_currency_name(purchase_currency_id) if _generator.game_state else "Unknown" if purchase_currency_name.is_empty(): purchase_currency_name = "Unconfigured" _next_cost_value.text = "%s %s" % [_generator.get_next_cost().to_string_suffix(2), purchase_currency_name] diff --git a/core/goal_catalogue.gd b/core/goal_catalogue.gd new file mode 100644 index 0000000..f736c9f --- /dev/null +++ b/core/goal_catalogue.gd @@ -0,0 +1,17 @@ +class_name GoalCatalogue +extends Resource + +@export var goals: Array[GoalData] = [] + +func get_goal_by_id(id: StringName) -> GoalData: + for goal in goals: + if goal.id == id: + return goal + return null + +func get_all_ids() -> Array[StringName]: + var ids: Array[StringName] = [] + for goal in goals: + if goal.has_id(): + ids.append(goal.id) + return ids diff --git a/core/goal_catalogue.gd.uid b/core/goal_catalogue.gd.uid new file mode 100644 index 0000000..a798104 --- /dev/null +++ b/core/goal_catalogue.gd.uid @@ -0,0 +1 @@ +uid://bfbp4mo8ys5p8 diff --git a/core/goals/README.md b/core/goals/README.md index a4ba440..027d5b2 100644 --- a/core/goals/README.md +++ b/core/goals/README.md @@ -38,12 +38,13 @@ enum UnlockBehavior { ### 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 +func has_id() -> bool # Has non-empty id +func is_valid() -> bool # Has valid id and requirements +func get_requirements() -> Array[GoalRequirementData] # Access requirements ``` +**Note:** State-dependent methods (`is_met()`, `get_progress()`, `get_summary_text()`) have been moved to `LevelGameState` for proper decoupling. + ## GoalRequirementData Single condition within a goal. @@ -62,12 +63,13 @@ extends Resource ### Methods ```gdscript +func get_currency() -> Currency # Currency to track 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 +func has_valid_amount() -> bool # Amount is non-negative ``` +**Note:** State-dependent methods (`is_met()`, `get_progress()`, `get_summary_text()`) have been moved to `LevelGameState` for proper decoupling. + ### Progress Calculation Uses **logarithmic scaling** for better visual feedback on huge numbers: @@ -87,18 +89,18 @@ This means reaching 50% progress on a 1e100 goal requires ~1e50 currency. ### Automatic Checking -`GameState` evaluates goals whenever currency changes: +`LevelGameState` evaluates goals whenever currency changes: ```gdscript func _on_currency_changed(currency_id, new_amount): - GameState.evaluate_all_goals() + game_state.evaluate_all_goals() ``` ### Completion Flow 1. Currency updated → signal emitted -2. `GameState.evaluate_all_goals()` called -3. Each uncompleted goal checked via `is_met()` +2. `LevelGameState.evaluate_all_goals()` called +3. Each uncompleted goal checked via `is_goal_met(goal)` 4. If met → `goal_completed` signal emitted 5. Buffs with unlock goals checked → unlocked if goal complete @@ -110,6 +112,16 @@ Goals control their own unlock behavior via `unlock_behavior`: Generators with `unlock_goal` automatically unlock when the goal completes. +### Goal Methods in LevelGameState + +```gdscript +func is_goal_requirement_met(requirement: GoalRequirementData) -> bool +func get_goal_requirement_progress(requirement: GoalRequirementData) -> float +func get_goal_requirement_summary(requirement: GoalRequirementData) -> String +func is_goal_met(goal: GoalData) -> bool +func get_goal_progress(goal: GoalData) -> float +``` + ## Usage Example ### Defining a Goal diff --git a/core/goals/goal_data.gd b/core/goals/goal_data.gd index 559b84c..eef69f2 100644 --- a/core/goals/goal_data.gd +++ b/core/goals/goal_data.gd @@ -13,53 +13,22 @@ enum UnlockBehavior { func has_id() -> bool: return not String(id).strip_edges().is_empty() -func get_valid_requirements() -> Array[GoalRequirementData]: - var result: Array[GoalRequirementData] = [] - for requirement_resource in requirements: - if requirement_resource == null: - continue - if not bool(requirement_resource.is_valid()): - continue - - result.append(requirement_resource) - - return result +func get_requirements() -> Array[GoalRequirementData]: + return requirements.duplicate() func is_valid() -> bool: if not has_id(): return false - - return not get_valid_requirements().is_empty() - -func is_met() -> bool: - var valid_requirements: Array[GoalRequirementData] = get_valid_requirements() - if valid_requirements.is_empty(): + + if requirements.is_empty(): return false - - for requirement_resource in valid_requirements: - if not bool(requirement_resource.is_met()): - return false - - return true - -func get_first_requirement_summary() -> String: - var valid_requirements: Array[GoalRequirementData] = get_valid_requirements() - if valid_requirements.is_empty(): - return "" - return String(valid_requirements[0].get_summary_text()) - -func get_progress() -> float: - var valid_requirements: Array[GoalRequirementData] = get_valid_requirements() - if valid_requirements.is_empty(): - return 0.0 - var min_progress: float = 1.0 - for requirement in valid_requirements: - if requirement == null or not requirement.is_valid(): + for req in requirements: + if req == null: + continue + if req.get_currency() == null: + continue + if not req.has_valid_amount(): continue - - var requirement_progress: float = requirement.get_progress() - if requirement_progress < min_progress: - min_progress = requirement_progress - return min_progress + return true diff --git a/core/goals/goal_requirement_data.gd b/core/goals/goal_requirement_data.gd index b627a54..cb95bc6 100644 --- a/core/goals/goal_requirement_data.gd +++ b/core/goals/goal_requirement_data.gd @@ -5,63 +5,11 @@ extends Resource @export var amount_mantissa: float = 0.0 @export var amount_exponent: int = 0 -func get_currency_id() -> StringName: - if currency == null: - return &"" - - return GameState.get_currency_id(currency) +func get_currency() -> Currency: + return currency func get_amount() -> BigNumber: return BigNumber.new(amount_mantissa, amount_exponent) -func has_valid_currency() -> bool: - var currency_id: StringName = get_currency_id() - if currency_id == &"": - return false - - return GameState.is_known_currency_id(currency_id) - func has_valid_amount() -> bool: return get_amount().mantissa >= 0.0 - -func is_valid() -> bool: - return has_valid_currency() and has_valid_amount() - -func is_met() -> bool: - if not is_valid(): - return false - - var total_amount: BigNumber = GameState.get_total_currency_acquired_by_id(get_currency_id()) - return not total_amount.is_less_than(get_amount()) - -func get_summary_text() -> String: - if not is_valid(): - return "" - - var currency_name: String = GameState.get_currency_name(get_currency_id()) - return "%s %s" % [get_amount().to_string_suffix(2), currency_name] - -func get_progress() -> float: - if not is_valid(): - return 0.0 - - var current_amount: BigNumber = GameState.get_total_currency_acquired_by_id(get_currency_id()) - var required_amount: BigNumber = get_amount() - - if required_amount.mantissa <= 0.0: - return 1.0 - - if current_amount.is_greater_than(required_amount) or current_amount.is_equal_to(required_amount): - return 1.0 - - if current_amount.mantissa <= 0.0: - return 0.0 - - var current_log: float = log(maxf(current_amount.mantissa, 0.0001)) / log(10) + float(current_amount.exponent) - var required_log: float = log(maxf(required_amount.mantissa, 0.0001)) / log(10) + float(required_amount.exponent) - - if required_log <= 0.0: - return 1.0 - - var progress: float = current_log / required_log - return clampf(progress, 0.0, 1.0) diff --git a/core/game_state.gd b/core/level_game_state.gd similarity index 85% rename from core/game_state.gd rename to core/level_game_state.gd index cf80d19..45cb5f8 100644 --- a/core/game_state.gd +++ b/core/level_game_state.gd @@ -1,5 +1,5 @@ +class_name LevelGameState extends Node -# This script is added as an Autoload named 'GameState' # ========================================== # SIGNALS @@ -13,6 +13,14 @@ signal buff_unlocked_changed(buff_id: StringName, unlocked: bool) signal goal_completed(goal_id: StringName) signal goal_progress_changed(goal_id: StringName, progress: float) +# ========================================== +# EXPORTED REFERENCES +# ========================================== +@export var currency_catalogue: CurrencyCatalogue +@export var buff_catalogue: BuffCatalogue +@export var goal_catalogue: GoalCatalogue +@export var save_file_path: String = "user://level_save.json" + # ========================================== # STATE VARIABLES # ========================================== @@ -33,12 +41,11 @@ var _buff_active: Dictionary = {} var _goal_definitions: Dictionary = {} var _goal_completed: Dictionary = {} -var last_save_time: int = 0 # Unix timestamp +var last_save_time: int = 0 # ========================================== # Save / Load # ========================================== -static var file_name: String = "user://idle_save.json" const SAVE_FORMAT_VERSION_KEY: String = "save_format_version" const CURRENT_SAVE_FORMAT_VERSION: int = 3 const GENERATOR_OWNED_KEY: String = "owned" @@ -62,51 +69,56 @@ const LAST_SAVE_TIME_KEY: String = "last_save_time" func _ready() -> void: _initialize_currency_maps() - _initialize_goals() + _initialize_catalogues() + _load_catalogue_goals() load_game() + _try_unlock_all_buffs_from_goals() # ========================================== # STATE MODIFIERS # ========================================== -#func add_gold(amount: BigNumber) -> void: - #add_currency_by_id(GOLD_CURRENCY_ID, amount) -# -#func spend_gold(cost: BigNumber) -> bool: - #return spend_currency_by_id(GOLD_CURRENCY_ID, cost) -# -#func add_gems(amount: BigNumber) -> void: - #add_currency_by_id(GEMS_CURRENCY_ID, amount) -# -#func spend_gems(cost: BigNumber) -> bool: - #return spend_currency_by_id(GEMS_CURRENCY_ID, cost) - -func get_known_currencies() -> Array[Currency]: - return CurrencyDatabase.get_known_currencies() - func get_currency_id(currency: Resource) -> StringName: - return CurrencyDatabase.get_currency_id(currency) + return _normalize_currency_id(currency.id if currency else &"") func parse_currency_id(raw_currency: Variant) -> StringName: - return CurrencyDatabase.parse_currency_id(raw_currency) + var currency_text: String = String(raw_currency).to_lower().strip_edges() + if currency_text == "gem": + currency_text = "gems" + return _normalize_currency_id(StringName(currency_text)) func is_known_currency_id(currency_id: StringName) -> bool: - return CurrencyDatabase.is_known_currency_id(currency_id) + if not currency_catalogue: + return false + var ids: Array[StringName] = currency_catalogue.get_all_ids() + return ids.has(_normalize_currency_id(currency_id)) -func get_currency_resource(currency_id: StringName) -> Resource: - return CurrencyDatabase.get_currency_resource(currency_id) +func get_currency_resource(currency_id: StringName) -> Currency: + if not currency_catalogue: + return null + return currency_catalogue.get_currency_by_id(_normalize_currency_id(currency_id)) func get_currency_name(currency_id: StringName) -> String: - return CurrencyDatabase.get_currency_name(currency_id) + var currency: Currency = get_currency_resource(currency_id) + if currency != null: + var display_name: String = String(currency.display_name).strip_edges() + if not display_name.is_empty(): + return display_name + return _humanize_currency_id(currency_id) func get_currency_icon(currency_id: StringName) -> Texture2D: - return CurrencyDatabase.get_currency_icon(currency_id) + var currency: Resource = get_currency_resource(currency_id) + if currency == null: + return null + var raw_icon: Variant = currency.icon + if raw_icon is Texture2D: + return raw_icon + return null func add_currency(currency: Resource, amount: BigNumber) -> void: var currency_id: StringName = get_currency_id(currency) if currency_id == &"": push_warning("Attempted to add currency with an invalid Currency resource.") return - add_currency_by_id(currency_id, amount) func spend_currency(currency: Resource, cost: BigNumber) -> bool: @@ -114,7 +126,6 @@ func spend_currency(currency: Resource, cost: BigNumber) -> bool: if currency_id == &"": push_warning("Attempted to spend currency with an invalid Currency resource.") return false - return spend_currency_by_id(currency_id, cost) func add_currency_by_id(currency_id: StringName, amount: BigNumber) -> void: @@ -387,6 +398,9 @@ func get_generators_targeted_by(buff_id: StringName) -> Array[StringName]: return buff.get_target_generators() func get_buffs_for_generator(generator_id: StringName) -> Array[GeneratorBuffData]: + if buff_catalogue: + return buff_catalogue.get_buffs_for_generator(generator_id) + var result: Array[GeneratorBuffData] = [] for buff in _buff_definitions.values(): if buff is GeneratorBuffData and buff.targets_generator(generator_id): @@ -475,6 +489,10 @@ func emit_currency_changed_for_all() -> void: # PERSISTENCE (Save/Load) # ========================================== func save_game() -> void: + if save_file_path.is_empty(): + push_warning("LevelGameState: save_file_path is empty; skipping save.") + return + var save_data = { SAVE_FORMAT_VERSION_KEY: CURRENT_SAVE_FORMAT_VERSION, CURRENCIES_KEY: _serialize_currency_balances(), @@ -497,15 +515,17 @@ func save_game() -> void: save_data[key] = payload - var file: FileAccess = FileAccess.open(file_name, FileAccess.WRITE) + var file: FileAccess = FileAccess.open(save_file_path, FileAccess.WRITE) if file: file.store_string(JSON.stringify(save_data)) + else: + push_error("LevelGameState: Failed to open save file: %s" % save_file_path) func load_game() -> void: - if not FileAccess.file_exists(file_name): + if not FileAccess.file_exists(save_file_path): return - var file: FileAccess = FileAccess.open(file_name, FileAccess.READ) + var file: FileAccess = FileAccess.open(save_file_path, FileAccess.READ) if file: var parsed: Variant = JSON.parse_string(file.get_as_text()) if parsed is Dictionary: @@ -624,21 +644,147 @@ func _collect_currency_ids_for_save() -> Array[StringName]: continue ids.append(normalized_currency_id) - for known_currency_id in CurrencyDatabase.get_known_currency_ids(): - var normalized_currency_id: StringName = _normalize_currency_id(known_currency_id) - if normalized_currency_id == &"" or ids.has(normalized_currency_id): - continue - ids.append(normalized_currency_id) + if currency_catalogue: + for known_currency_id in currency_catalogue.get_all_ids(): + var normalized_currency_id: StringName = _normalize_currency_id(known_currency_id) + if normalized_currency_id == &"" or ids.has(normalized_currency_id): + continue + ids.append(normalized_currency_id) return ids func _initialize_currency_maps() -> void: - for currency_id in CurrencyDatabase.get_known_currency_ids(): - if currency_id == &"": + if currency_catalogue: + for currency_id in currency_catalogue.get_all_ids(): + if currency_id == &"": + continue + _set_current_currency(currency_id, BigNumber.from_float(0.0)) + _set_total_currency(currency_id, BigNumber.from_float(0.0)) + _set_all_time_currency(currency_id, BigNumber.from_float(0.0)) + +func _initialize_catalogues() -> void: + if currency_catalogue: + for currency in currency_catalogue.currencies: + if currency == null: + continue + + if buff_catalogue: + for buff in buff_catalogue.buffs: + if buff == null: + continue + register_buff(buff) + + if goal_catalogue: + for goal in goal_catalogue.goals: + if goal == null: + continue + register_goal(goal) + +func _load_catalogue_goals() -> void: + if goal_catalogue: + for goal in goal_catalogue.goals: + if goal == null: + continue + if not goal.has_id(): + continue + if _goal_definitions.has(goal.id): + continue + _goal_definitions[goal.id] = goal + _goal_completed[goal.id] = false + +func is_goal_requirement_met(requirement: GoalRequirementData) -> bool: + if requirement == null: + return false + + var currency: Currency = requirement.get_currency() + if currency == null: + return false + + var currency_id: StringName = get_currency_id(currency) + if currency_id == &"": + return false + + var total_amount: BigNumber = get_total_currency_acquired_by_id(currency_id) + var required_amount: BigNumber = requirement.get_amount() + + return not total_amount.is_less_than(required_amount) + +func get_goal_requirement_progress(requirement: GoalRequirementData) -> float: + if requirement == null: + return 0.0 + + var currency: Currency = requirement.get_currency() + if currency == null: + return 0.0 + + var currency_id: StringName = get_currency_id(currency) + if currency_id == &"": + return 0.0 + + var current_amount: BigNumber = get_total_currency_acquired_by_id(currency_id) + var required_amount: BigNumber = requirement.get_amount() + + if required_amount.mantissa <= 0.0: + return 1.0 + + if current_amount.is_greater_than(required_amount) or current_amount.is_equal_to(required_amount): + return 1.0 + + if current_amount.mantissa <= 0.0: + return 0.0 + + var current_log: float = log(maxf(current_amount.mantissa, 0.0001)) / log(10) + float(current_amount.exponent) + var required_log: float = log(maxf(required_amount.mantissa, 0.0001)) / log(10) + float(required_amount.exponent) + + if required_log <= 0.0: + return 1.0 + + var progress: float = current_log / required_log + return clampf(progress, 0.0, 1.0) + +func get_goal_requirement_summary(requirement: GoalRequirementData) -> String: + if requirement == null: + return "" + + var currency: Currency = requirement.get_currency() + if currency == null: + return "" + + var currency_id: StringName = get_currency_id(currency) + if currency_id == &"": + return "" + + var currency_name: String = get_currency_name(currency_id) + return "%s %s" % [requirement.get_amount().to_string_suffix(2), currency_name] + +func is_goal_met(goal: GoalData) -> bool: + if goal == null: + return false + + for req in goal.get_requirements(): + if not is_goal_requirement_met(req): + return false + + return true + +func get_goal_progress(goal: GoalData) -> float: + if goal == null: + return 0.0 + + var requirements: Array[GoalRequirementData] = goal.get_requirements() + if requirements.is_empty(): + return 0.0 + + var min_progress: float = 1.0 + for req in requirements: + if req == null: continue - _set_current_currency(currency_id, BigNumber.from_float(0.0)) - _set_total_currency(currency_id, BigNumber.from_float(0.0)) - _set_all_time_currency(currency_id, BigNumber.from_float(0.0)) + + var req_progress: float = get_goal_requirement_progress(req) + if req_progress < min_progress: + min_progress = req_progress + + return min_progress func _normalize_currency_id(currency_id: StringName) -> StringName: var normalized: String = String(currency_id).to_lower().strip_edges() @@ -794,7 +940,6 @@ func _sanitize_generator_buff_unlocked(raw_unlocked: Dictionary) -> Dictionary: if buff_key.is_empty(): continue - # Save format only stores true values, but keep parser tolerant. var is_unlocked: bool = _to_bool(raw_unlocked.get(buff_key_variant, false), false) if not is_unlocked: continue @@ -1010,57 +1155,6 @@ func _deserialize_buff_states(parsed_data: Dictionary) -> void: # ========================================== # GOALS # ========================================== -func _initialize_goals() -> void: - _goal_definitions.clear() - _goal_completed.clear() - - var goal_paths: PackedStringArray = _discover_goal_paths() - for goal_path in goal_paths: - var goal_resource: Resource = load(goal_path) - if goal_resource == null: - push_warning("Failed to load goal resource at '%s'." % goal_path) - continue - if not (goal_resource is GoalData): - continue - - var goal: GoalData = goal_resource - if not goal.has_id(): - push_warning("Skipping goal with missing id: %s" % goal_path) - continue - if _goal_definitions.has(goal.id): - push_warning("Duplicate goal id '%s' found at %s. Keeping first occurrence." % [String(goal.id), goal_path]) - continue - - _goal_definitions[goal.id] = goal - _goal_completed[goal.id] = false - -func _discover_goal_paths() -> PackedStringArray: - var result: PackedStringArray = [] - const GOAL_DIRECTORY_PATH: String = "res://docs/gyms/goals" - const GOAL_RESOURCE_EXTENSIONS: PackedStringArray = ["tres", "res"] - - var directory: DirAccess = DirAccess.open(GOAL_DIRECTORY_PATH) - if directory == null: - push_warning("Unable to open goals directory: %s" % GOAL_DIRECTORY_PATH) - return result - - directory.list_dir_begin() - var entry_name: String = directory.get_next() - while not entry_name.is_empty(): - if directory.current_is_dir() or entry_name.begins_with("."): - entry_name = directory.get_next() - continue - - var extension: String = entry_name.get_extension().to_lower() - if GOAL_RESOURCE_EXTENSIONS.has(extension): - result.append("%s/%s" % [GOAL_DIRECTORY_PATH, entry_name]) - - entry_name = directory.get_next() - directory.list_dir_end() - - result.sort() - return result - func register_goal(goal: GoalData) -> void: if goal == null: return @@ -1086,11 +1180,11 @@ func get_all_goals() -> Array[GoalData]: func is_goal_completed(goal_id: StringName) -> bool: return _goal_completed.get(goal_id, false) -func get_goal_progress(goal_id: StringName) -> float: +func get_goal_progress_by_id(goal_id: StringName) -> float: var goal: GoalData = get_goal(goal_id) if goal == null: return 0.0 - return goal.get_progress() + return get_goal_progress(goal) func evaluate_all_goals() -> void: for goal_id in _goal_completed.keys(): @@ -1098,6 +1192,8 @@ func evaluate_all_goals() -> void: _try_complete_goal(goal_id) _try_unlock_all_buffs_from_goals() + + goal_progress_changed.emit(&"", 0.0) func _try_complete_goal(goal_id: StringName) -> void: var goal: GoalData = _goal_definitions.get(goal_id, null) @@ -1105,7 +1201,7 @@ func _try_complete_goal(goal_id: StringName) -> void: return if _goal_completed[goal_id]: return - if goal.is_met(): + if is_goal_met(goal): if goal.unlock_behavior == GoalData.UnlockBehavior.MANUAL: return _goal_completed[goal_id] = true @@ -1117,7 +1213,7 @@ func _complete_goal_manually(goal_id: StringName) -> void: return if _goal_completed.get(goal_id, false): return - if not goal.is_met(): + if not is_goal_met(goal): return _goal_completed[goal_id] = true goal_completed.emit(goal_id) @@ -1161,11 +1257,7 @@ func _migrate_goals_from_version_2(parsed_data: Dictionary) -> void: if not state.get(GENERATOR_UNLOCKED_KEY, false): continue - var generator_data: CurrencyGeneratorData = _load_generator_data_from_id(gen_id) - if generator_data == null or not generator_data.has_unlock_goal(): - continue - - var goal_id: StringName = generator_data.get_unlock_goal_id() + var goal_id: StringName = _get_unlock_goal_id_for_generator(gen_id) if not goal_id.is_empty(): _goal_completed[goal_id] = true @@ -1173,32 +1265,27 @@ func _migrate_goals_from_version_2(parsed_data: Dictionary) -> void: if not _goal_completed.has(goal_id): _goal_completed[goal_id] = false -func _load_generator_data_from_id(generator_id: String) -> CurrencyGeneratorData: - const GENERATORS_DIRECTORY_PATH: String = "res://docs/gyms/generators" - const GENERATOR_RESOURCE_EXTENSIONS: PackedStringArray = ["tres", "res"] +func _get_unlock_goal_id_for_generator(generator_id: String) -> StringName: + if goal_catalogue == null: + return &"" - var directory: DirAccess = DirAccess.open(GENERATORS_DIRECTORY_PATH) - if directory == null: - return null - - directory.list_dir_begin() - var entry_name: String = directory.get_next() - while not entry_name.is_empty(): - if directory.current_is_dir() or entry_name.begins_with("."): - entry_name = directory.get_next() + for goal in goal_catalogue.goals: + if goal == null or not goal.has_id(): continue - var extension: String = entry_name.get_extension().to_lower() - if GENERATOR_RESOURCE_EXTENSIONS.has(extension): - var generator_path: String = "%s/%s" % [GENERATORS_DIRECTORY_PATH, entry_name] - var generator_resource: Resource = load(generator_path) - if generator_resource is CurrencyGeneratorData: - var gen_data: CurrencyGeneratorData = generator_resource - if String(gen_data.id) == generator_id: - directory.list_dir_end() - return gen_data - - entry_name = directory.get_next() - directory.list_dir_end() + var goal_id: StringName = goal.id + var requirements: Array[GoalRequirementData] = goal.get_requirements() + for req in requirements: + if req == null or req.get_currency() == null: + continue + + if String(req.get_currency().id) == generator_id: + return goal_id - return null + return &"" + +func _humanize_currency_id(currency_id: StringName) -> String: + var normalized: String = String(_normalize_currency_id(currency_id)) + if normalized.is_empty(): + return "Unknown" + return normalized.replace("_", " ").capitalize() diff --git a/core/level_game_state.gd.uid b/core/level_game_state.gd.uid new file mode 100644 index 0000000..a106ca6 --- /dev/null +++ b/core/level_game_state.gd.uid @@ -0,0 +1 @@ +uid://cv2132o4hi7q3 diff --git a/core/prestige/prestige_manager.gd b/core/prestige/prestige_manager.gd index d3341b3..6f5decb 100644 --- a/core/prestige/prestige_manager.gd +++ b/core/prestige/prestige_manager.gd @@ -1,3 +1,4 @@ +class_name PrestigeManager extends Node signal prestige_state_changed(total_prestige: BigNumber, pending_gain: BigNumber) @@ -23,6 +24,7 @@ const MULTIPLIER_MODE_ADDITIVE: int = 0 const MULTIPLIER_MODE_MULTIPLICATIVE_POWER: int = 1 @export var config: Resource +@export var game_state: LevelGameState var total_prestige_earned: BigNumber = BigNumber.from_float(0.0) var current_prestige_unspent: BigNumber = BigNumber.from_float(0.0) @@ -33,13 +35,17 @@ var last_reset_time: int = 0 func _ready() -> void: if config == null: - config = load("res://docs/gyms/prestige/primary_prestige.tres") as Resource + config = load("res://docs/gyms/tiny_sword/prestige/primary_prestige.tres") as Resource if not _is_config_valid(): push_warning("PrestigeManager has invalid or missing config; prestige is disabled.") return - var loaded_state: Dictionary = GameState.get_external_save_data(SAVE_KEY) + if game_state == null: + push_warning("PrestigeManager: game_state reference is not set.") + return + + var loaded_state: Dictionary = game_state.get_external_save_data(SAVE_KEY) if loaded_state.is_empty(): _initialize_run_tracking_from_current_state() else: @@ -47,7 +53,7 @@ func _ready() -> void: _validate_loaded_run_tracking() _save_state_to_game_state() - GameState.currency_changed.connect(_on_currency_changed) + game_state.currency_changed.connect(_on_currency_changed) prestige_state_changed.emit(get_total_prestige(), calculate_pending_gain()) prestige_threshold_changed.emit(get_next_prestige_threshold()) @@ -134,13 +140,16 @@ func perform_prestige() -> bool: prestige_resets_count += 1 last_reset_time = Time.get_unix_time_from_system() - GameState.reset_for_prestige(true, false) + if game_state: + game_state.reset_for_prestige(true, false) _reset_all_buff_levels() _reinitialize_generators_after_prestige_reset() - GameState.emit_currency_changed_for_all() + if game_state: + game_state.emit_currency_changed_for_all() _initialize_run_tracking_from_current_state() _save_state_to_game_state() - GameState.save_game() + if game_state: + game_state.save_game() var total: BigNumber = get_total_prestige() var next_threshold: BigNumber = get_next_prestige_threshold() @@ -261,9 +270,12 @@ func _get_basis_value() -> BigNumber: if source_currency_id == &"": return BigNumber.from_float(0.0) + if game_state == null: + return BigNumber.from_float(0.0) + match _get_config_int("basis", BASIS_LIFETIME_TOTAL): BASIS_RUN_TOTAL: - var lifetime_now: BigNumber = GameState.get_total_currency_acquired_by_id(source_currency_id) + var lifetime_now: BigNumber = game_state.get_total_currency_acquired_by_id(source_currency_id) var run_total: BigNumber = lifetime_now.subtract(run_start_total_source_currency) if run_total.mantissa < 0.0: return BigNumber.from_float(0.0) @@ -271,7 +283,7 @@ func _get_basis_value() -> BigNumber: BASIS_RUN_MAX: return _copy_big_number(run_peak_source_currency) _: - return _copy_big_number(GameState.get_total_currency_acquired_by_id(source_currency_id)) + return _copy_big_number(game_state.get_total_currency_acquired_by_id(source_currency_id)) func _initialize_run_tracking_from_current_state() -> void: if config == null: @@ -283,8 +295,13 @@ func _initialize_run_tracking_from_current_state() -> void: run_peak_source_currency = BigNumber.from_float(0.0) return - run_start_total_source_currency = _copy_big_number(GameState.get_total_currency_acquired_by_id(source_currency_id)) - run_peak_source_currency = _copy_big_number(GameState.get_currency_amount_by_id(source_currency_id)) + if game_state == null: + run_start_total_source_currency = BigNumber.from_float(0.0) + run_peak_source_currency = BigNumber.from_float(0.0) + return + + run_start_total_source_currency = _copy_big_number(game_state.get_total_currency_acquired_by_id(source_currency_id)) + run_peak_source_currency = _copy_big_number(game_state.get_currency_amount_by_id(source_currency_id)) _save_state_to_game_state() func _validate_loaded_run_tracking() -> void: @@ -297,11 +314,14 @@ func _validate_loaded_run_tracking() -> void: run_peak_source_currency = BigNumber.from_float(0.0) return - var current_currency: BigNumber = GameState.get_currency_amount_by_id(source_currency_id) + if game_state == null: + return + + var current_currency: BigNumber = game_state.get_currency_amount_by_id(source_currency_id) if current_currency.is_greater_than(run_peak_source_currency): run_peak_source_currency = _copy_big_number(current_currency) - var lifetime_now: BigNumber = GameState.get_total_currency_acquired_by_id(source_currency_id) + var lifetime_now: BigNumber = game_state.get_total_currency_acquired_by_id(source_currency_id) if run_start_total_source_currency.is_greater_than(lifetime_now): run_start_total_source_currency = _copy_big_number(lifetime_now) @@ -327,12 +347,16 @@ func _deserialize_state(raw_state: Dictionary) -> void: current_prestige_unspent = _copy_big_number(total_prestige_earned) func _save_state_to_game_state() -> void: - GameState.set_external_save_data(SAVE_KEY, _serialize_state()) + if game_state: + game_state.set_external_save_data(SAVE_KEY, _serialize_state()) 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) + if game_state == null: + return + + for buff_id in game_state._buff_levels.keys(): + game_state.set_buff_level(buff_id, 0) + game_state.set_buff_unlocked(buff_id, false) func _reinitialize_generators_after_prestige_reset() -> void: var scene_root: Node = get_tree().current_scene diff --git a/core/prestige/prestige_panel.gd b/core/prestige/prestige_panel.gd index ebb497a..83353c1 100644 --- a/core/prestige/prestige_panel.gd +++ b/core/prestige/prestige_panel.gd @@ -1,6 +1,8 @@ class_name PrestigePanel extends PanelContainer +@export var game_state: LevelGameState + @onready var _total_value: Label = $MarginContainer/VBoxContainer/StatsGrid/TotalValue @onready var _pending_value: Label = $MarginContainer/VBoxContainer/StatsGrid/PendingValue @onready var _multiplier_value: Label = $MarginContainer/VBoxContainer/StatsGrid/MultiplierValue @@ -18,8 +20,8 @@ func _ready() -> void: manager.prestige_state_changed.connect(_on_prestige_state_changed) if manager != null and manager.has_signal("prestige_performed"): manager.prestige_performed.connect(_on_prestige_performed) - if not GameState.currency_changed.is_connected(_on_currency_changed): - GameState.currency_changed.connect(_on_currency_changed) + if game_state and not game_state.currency_changed.is_connected(_on_currency_changed): + game_state.currency_changed.connect(_on_currency_changed) _refresh_ui() @@ -33,7 +35,7 @@ func _on_reset_button_pressed() -> void: var pending: BigNumber = manager.call("calculate_pending_gain") var source_currency_id: StringName = manager.call("get_source_currency_id") - var source_currency_name: String = GameState.get_currency_name(source_currency_id) + var source_currency_name: String = game_state.get_currency_name(source_currency_id) if game_state else "" var confirm_message: String = "Reset this run for +%s prestige?\n\nThis resets currencies, owned generators, and buff levels. Lifetime totals and prestige are kept." % pending.to_string_suffix(2) if not source_currency_name.is_empty(): confirm_message = "%s\n\nBased on %s progression." % [confirm_message, source_currency_name] @@ -43,7 +45,8 @@ func _on_reset_button_pressed() -> void: _confirm_dialog.popup_centered_ratio(0.4) func _on_save_button_pressed() -> void: - GameState.save_game() + if game_state: + game_state.save_game() func _on_prestige_state_changed(_total: BigNumber, _pending: BigNumber) -> void: _refresh_ui() diff --git a/core/prestige/prestige_panel.tscn b/core/prestige/prestige_panel.tscn index 58c0b26..a7ce3e5 100644 --- a/core/prestige/prestige_panel.tscn +++ b/core/prestige/prestige_panel.tscn @@ -2,7 +2,7 @@ [ext_resource type="Script" uid="uid://dmsmmgtbrul1t" path="res://core/prestige/prestige_panel.gd" id="1_panel"] [ext_resource type="PackedScene" path="res://core/prestige/prestige_progress_bar.tscn" id="2_7b6yg"] -[ext_resource type="Resource" uid="uid://dwmfvmusfskk6" path="res://docs/gyms/prestige/primary_prestige.tres" id="3_r8h5p"] +[ext_resource type="Resource" uid="uid://dwmfvmusfskk6" path="res://docs/gyms/tiny_sword/prestige/primary_prestige.tres" id="3_r8h5p"] [node name="PrestigePanel" type="PanelContainer" unique_id=789062217] offset_right = 420.0 diff --git a/currency_label.gd b/currency_label.gd index 245677c..91b99bc 100644 --- a/currency_label.gd +++ b/currency_label.gd @@ -2,28 +2,31 @@ class_name CurrencyTile extends HBoxContainer @export var currency: Resource +@export var game_state: LevelGameState @onready var _icon: TextureRect = $CurrencyIcon -@onready var _label: Label = $Label +@onready var _label: Label = $CurrencyValueLabel @onready var _currency_label: Label = $CurrencyValueLabel @onready var _debug_button: Button = $DebugIncomeButton var _currency_id: StringName = &"" func _ready() -> void: - #if currency == null: - #currency = CurrencyDatabase.get_currency_resource(GameState.GOLD_CURRENCY_ID) if currency == null: push_warning("CurrencyTile '%s' has no currency configured." % String(name)) return - _currency_id = GameState.get_currency_id(currency) - _label.text = "%s:" % GameState.get_currency_name(_currency_id) - _icon.texture = GameState.get_currency_icon(_currency_id) - _debug_button.text = "+100 %s" % GameState.get_currency_name(_currency_id) + if game_state == null: + push_error("CurrencyTile '%s' missing game_state reference" % String(name)) + return - GameState.currency_changed.connect(_on_currency_changed) - _on_currency_changed(_currency_id, GameState.get_currency_amount_by_id(_currency_id)) + _currency_id = game_state.get_currency_id(currency) + _label.text = "%s:" % game_state.get_currency_name(_currency_id) + _icon.texture = game_state.get_currency_icon(_currency_id) + _debug_button.text = "+100 %s" % game_state.get_currency_name(_currency_id) + + game_state.currency_changed.connect(_on_currency_changed) + _on_currency_changed(_currency_id, game_state.get_currency_amount_by_id(_currency_id)) func _on_currency_changed(changed_currency_id: StringName, amount: BigNumber) -> void: if changed_currency_id != _currency_id: @@ -33,4 +36,6 @@ func _on_currency_changed(changed_currency_id: StringName, amount: BigNumber) -> func _on_debug_income_button_pressed() -> void: if currency == null: return - GameState.add_currency(currency, BigNumber.from_float(100)) + if game_state == null: + return + game_state.add_currency(currency, BigNumber.from_float(100)) diff --git a/docs/gyms/buffs/farm_auto_flux.tres b/docs/gyms/tiny_sword/buffs/farm_auto_flux_buff.tres similarity index 78% rename from docs/gyms/buffs/farm_auto_flux.tres rename to docs/gyms/tiny_sword/buffs/farm_auto_flux_buff.tres index 47027b6..83d39f9 100644 --- a/docs/gyms/buffs/farm_auto_flux.tres +++ b/docs/gyms/tiny_sword/buffs/farm_auto_flux_buff.tres @@ -1,9 +1,9 @@ [gd_resource type="Resource" script_class="GeneratorBuffData" format=3 uid="uid://cg7os1rfknw05"] -[ext_resource type="Resource" uid="uid://bxg2au0ijp242" path="res://docs/gyms/currencies/food.tres" id="1_qy8mo"] -[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/currencies/gold.tres" id="2_pac24"] +[ext_resource type="Resource" uid="uid://bxg2au0ijp242" path="res://docs/gyms/tiny_sword/currencies/food.tres" id="1_qy8mo"] +[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="2_pac24"] [ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="3_t5xb7"] -[ext_resource type="Resource" uid="uid://bo463s6jt0ep7" path="res://docs/gyms/goals/gold_total_13k.tres" id="4_r8eth"] +[ext_resource type="Resource" uid="uid://bo463s6jt0ep7" path="res://docs/gyms/tiny_sword/goals/gold_total_13k_goal.tres" id="4_r8eth"] [resource] script = ExtResource("3_t5xb7") diff --git a/docs/gyms/buffs/forestry_auto_flux.tres b/docs/gyms/tiny_sword/buffs/forestry_auto_flux_buff.tres similarity index 78% rename from docs/gyms/buffs/forestry_auto_flux.tres rename to docs/gyms/tiny_sword/buffs/forestry_auto_flux_buff.tres index 019a6ff..6bcc85b 100644 --- a/docs/gyms/buffs/forestry_auto_flux.tres +++ b/docs/gyms/tiny_sword/buffs/forestry_auto_flux_buff.tres @@ -1,9 +1,9 @@ [gd_resource type="Resource" script_class="GeneratorBuffData" format=3 uid="uid://kamgujbqqhg3"] -[ext_resource type="Resource" uid="uid://bxg2au0ijp242" path="res://docs/gyms/currencies/food.tres" id="1_pdm0x"] -[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/currencies/gold.tres" id="2_rnlmu"] +[ext_resource type="Resource" uid="uid://bxg2au0ijp242" path="res://docs/gyms/tiny_sword/currencies/food.tres" id="1_pdm0x"] +[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="2_rnlmu"] [ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="3_y6252"] -[ext_resource type="Resource" uid="uid://bo463s6jt0ep7" path="res://docs/gyms/goals/gold_total_13k.tres" id="4_y6252"] +[ext_resource type="Resource" uid="uid://bo463s6jt0ep7" path="res://docs/gyms/tiny_sword/goals/gold_total_13k_goal.tres" id="4_y6252"] [resource] script = ExtResource("3_y6252") diff --git a/docs/gyms/buffs/gold_auto_flux.tres b/docs/gyms/tiny_sword/buffs/gold_auto_flux_buff.tres similarity index 79% rename from docs/gyms/buffs/gold_auto_flux.tres rename to docs/gyms/tiny_sword/buffs/gold_auto_flux_buff.tres index 06ca94a..7edec80 100644 --- a/docs/gyms/buffs/gold_auto_flux.tres +++ b/docs/gyms/tiny_sword/buffs/gold_auto_flux_buff.tres @@ -1,9 +1,9 @@ [gd_resource type="Resource" script_class="GeneratorBuffData" format=3 uid="uid://c3op2mqy02sow"] -[ext_resource type="Resource" uid="uid://bxg2au0ijp242" path="res://docs/gyms/currencies/food.tres" id="1_j3saa"] -[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/currencies/gold.tres" id="2_hvfo8"] +[ext_resource type="Resource" uid="uid://bxg2au0ijp242" path="res://docs/gyms/tiny_sword/currencies/food.tres" id="1_j3saa"] +[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="2_hvfo8"] [ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="3_bwcdh"] -[ext_resource type="Resource" uid="uid://ik7t0r3su633" path="res://docs/gyms/goals/gold_total_30.tres" id="4_f52il"] +[ext_resource type="Resource" uid="uid://ik7t0r3su633" path="res://docs/gyms/tiny_sword/goals/gold_total_30_goal.tres" id="4_f52il"] [resource] script = ExtResource("3_bwcdh") diff --git a/docs/gyms/buffs/gold_click_buff.tres b/docs/gyms/tiny_sword/buffs/gold_click_buff.tres similarity index 84% rename from docs/gyms/buffs/gold_click_buff.tres rename to docs/gyms/tiny_sword/buffs/gold_click_buff.tres index b2db496..e896dd9 100644 --- a/docs/gyms/buffs/gold_click_buff.tres +++ b/docs/gyms/tiny_sword/buffs/gold_click_buff.tres @@ -1,8 +1,8 @@ [gd_resource type="Resource" script_class="GeneratorBuffData" format=3 uid="uid://dlq2ke6i2pfob"] -[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/currencies/gold.tres" id="1_8h747"] +[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="1_8h747"] [ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="2_aejsu"] -[ext_resource type="Resource" uid="uid://ik7t0r3su633" path="res://docs/gyms/goals/gold_total_30.tres" id="3_aejsu"] +[ext_resource type="Resource" uid="uid://ik7t0r3su633" path="res://docs/gyms/tiny_sword/goals/gold_total_30_goal.tres" id="3_aejsu"] [resource] script = ExtResource("2_aejsu") diff --git a/docs/gyms/buffs/spawn_worker.tres b/docs/gyms/tiny_sword/buffs/spawn_worker_buff.tres similarity index 80% rename from docs/gyms/buffs/spawn_worker.tres rename to docs/gyms/tiny_sword/buffs/spawn_worker_buff.tres index 3adac38..c95e7cd 100644 --- a/docs/gyms/buffs/spawn_worker.tres +++ b/docs/gyms/tiny_sword/buffs/spawn_worker_buff.tres @@ -1,9 +1,9 @@ [gd_resource type="Resource" script_class="GeneratorBuffData" format=3 uid="uid://bjc6qmvr7pe12"] -[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/currencies/gold.tres" id="1_we7j4"] -[ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/currencies/worker.tres" id="2_s45cj"] +[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="1_we7j4"] +[ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/tiny_sword/currencies/worker.tres" id="2_s45cj"] [ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="3_tgtcn"] -[ext_resource type="Resource" uid="uid://ik7t0r3su633" path="res://docs/gyms/goals/gold_total_30.tres" id="4_kqmci"] +[ext_resource type="Resource" uid="uid://ik7t0r3su633" path="res://docs/gyms/tiny_sword/goals/gold_total_30_goal.tres" id="4_kqmci"] [resource] script = ExtResource("3_tgtcn") diff --git a/docs/gyms/tiny_sword/buffs/ts_buff_catalogue.tres b/docs/gyms/tiny_sword/buffs/ts_buff_catalogue.tres new file mode 100644 index 0000000..00ba8fb --- /dev/null +++ b/docs/gyms/tiny_sword/buffs/ts_buff_catalogue.tres @@ -0,0 +1,14 @@ +[gd_resource type="Resource" script_class="BuffCatalogue" format=3 uid="uid://bgmj3lep0wmtf"] + +[ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="1_up80l"] +[ext_resource type="Resource" uid="uid://dlq2ke6i2pfob" path="res://docs/gyms/tiny_sword/buffs/gold_click_buff.tres" id="2_dtvfl"] +[ext_resource type="Script" uid="uid://ctc5yjlnvi0ok" path="res://core/buff_catalogue.gd" id="2_iuxc1"] +[ext_resource type="Resource" uid="uid://c3op2mqy02sow" path="res://docs/gyms/tiny_sword/buffs/gold_auto_flux_buff.tres" id="3_0bb41"] +[ext_resource type="Resource" uid="uid://bjc6qmvr7pe12" path="res://docs/gyms/tiny_sword/buffs/spawn_worker_buff.tres" id="4_hnq3m"] +[ext_resource type="Resource" uid="uid://cg7os1rfknw05" path="res://docs/gyms/tiny_sword/buffs/farm_auto_flux_buff.tres" id="5_mii30"] +[ext_resource type="Resource" uid="uid://kamgujbqqhg3" path="res://docs/gyms/tiny_sword/buffs/forestry_auto_flux_buff.tres" id="6_k5yi4"] + +[resource] +script = ExtResource("2_iuxc1") +buffs = Array[ExtResource("1_up80l")]([ExtResource("2_dtvfl"), ExtResource("3_0bb41"), ExtResource("4_hnq3m"), ExtResource("5_mii30"), ExtResource("6_k5yi4")]) +metadata/_custom_type_script = "uid://ctc5yjlnvi0ok" diff --git a/docs/gyms/tiny_sword/buildings/castle/castle.tscn b/docs/gyms/tiny_sword/buildings/castle/castle.tscn index c95fdb9..06bfb7a 100644 --- a/docs/gyms/tiny_sword/buildings/castle/castle.tscn +++ b/docs/gyms/tiny_sword/buildings/castle/castle.tscn @@ -4,7 +4,7 @@ [ext_resource type="Texture2D" uid="uid://cry4arn00kgrt" path="res://docs/gyms/tiny_sword/buildings/castle/Castle.png" id="1_tgvch"] [ext_resource type="PackedScene" uid="uid://dlidx2x0otpjg" path="res://core/prestige/prestige_panel.tscn" id="3_l7gct"] [ext_resource type="Script" uid="uid://cpfmctd4xsfho" path="res://docs/gyms/tiny_sword/buildings/castle/test_buff.gd" id="4_l7gct"] -[ext_resource type="Resource" uid="uid://bjc6qmvr7pe12" path="res://docs/gyms/buffs/spawn_worker.tres" id="5_s3a5k"] +[ext_resource type="Resource" uid="uid://bjc6qmvr7pe12" path="res://docs/gyms/tiny_sword/buffs/spawn_worker_buff.tres" id="5_s3a5k"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_tgvch"] size = Vector2(312, 198) diff --git a/docs/gyms/tiny_sword/buildings/castle/test_buff.gd b/docs/gyms/tiny_sword/buildings/castle/test_buff.gd index d30002e..05b8382 100644 --- a/docs/gyms/tiny_sword/buildings/castle/test_buff.gd +++ b/docs/gyms/tiny_sword/buildings/castle/test_buff.gd @@ -1,6 +1,7 @@ extends PanelContainer @export var buff_data: GeneratorBuffData +@export var game_state: LevelGameState @onready var _label: Label = $HBoxContainer/Label @onready var _button: Button = $HBoxContainer/Button @@ -19,9 +20,10 @@ func _ready() -> void: _button.disabled = true _button.pressed.connect(_on_button_pressed) - GameState.buff_level_changed.connect(_on_buff_level_changed) - GameState.buff_unlocked_changed.connect(_on_buff_unlocked_changed) - GameState.currency_changed.connect(_on_currency_changed) + if game_state: + game_state.buff_level_changed.connect(_on_buff_level_changed) + game_state.buff_unlocked_changed.connect(_on_buff_unlocked_changed) + game_state.currency_changed.connect(_on_currency_changed) _update_ui() @@ -35,9 +37,9 @@ func _on_button_pressed() -> void: return var cost: BigNumber = buff_data.get_cost_for_level(_current_level) - if GameState.spend_currency_by_id(cost_currency_id, cost): + if game_state and game_state.spend_currency_by_id(cost_currency_id, cost): var next_level: int = _current_level + 1 - GameState.set_buff_level(buff_data.id, next_level) + game_state.set_buff_level(buff_data.id, next_level) _apply_buff_effect(buff_data, next_level) _current_level = next_level _update_ui() @@ -54,15 +56,17 @@ func _apply_buff_effect(buff: GeneratorBuffData, level: int) -> void: return var purchased_amount: BigNumber = buff.get_resource_purchase_amount_for_level(level) - if purchased_amount.mantissa > 0.0: - GameState.add_currency_by_id(target_currency_id, purchased_amount) + if purchased_amount.mantissa > 0.0 and game_state: + game_state.add_currency_by_id(target_currency_id, purchased_amount) func _resolve_buff_target_currency_id(buff: GeneratorBuffData) -> StringName: if buff.resource_target_currency == null: return &"" - return CurrencyDatabase.get_currency_id(buff.resource_target_currency) + if game_state == null: + return &"" + return game_state.get_currency_id(buff.resource_target_currency) func _on_buff_level_changed(buff_id: StringName, new_level: int) -> void: @@ -82,7 +86,7 @@ func _update_ui() -> void: if buff_data == null: return - if not GameState.is_buff_unlocked(buff_data.id): + if game_state and not game_state.is_buff_unlocked(buff_data.id): _button.disabled = true _button.text = "Locked" return @@ -93,7 +97,7 @@ func _update_ui() -> void: var cost_currency_id: StringName = _resolve_buff_cost_currency_id(buff_data) if cost_currency_id != &"": var cost: BigNumber = buff_data.get_cost_for_level(_current_level) - var current_amount: BigNumber = GameState.get_currency_amount_by_id(cost_currency_id) + var current_amount: BigNumber = game_state.get_currency_amount_by_id(cost_currency_id) if game_state else BigNumber.from_float(0.0) can_buy = can_buy and (current_amount.is_greater_than(cost) or current_amount.is_equal_to(cost)) _button.disabled = not can_buy @@ -109,4 +113,6 @@ func _resolve_buff_cost_currency_id(buff: GeneratorBuffData) -> StringName: if buff.cost_currency == null: return &"" - return CurrencyDatabase.get_currency_id(buff.cost_currency) + if game_state == null: + return &"" + return game_state.get_currency_id(buff.cost_currency) diff --git a/docs/gyms/tiny_sword/buildings/farm/farm.tscn b/docs/gyms/tiny_sword/buildings/farm/farm.tscn index 4930e6a..07350b7 100644 --- a/docs/gyms/tiny_sword/buildings/farm/farm.tscn +++ b/docs/gyms/tiny_sword/buildings/farm/farm.tscn @@ -1,8 +1,8 @@ [gd_scene format=3 uid="uid://djedqovgngrx5"] [ext_resource type="PackedScene" uid="uid://jeoiinukrrsp" path="res://core/generator/currency_generator.tscn" id="1_rvsna"] -[ext_resource type="Resource" uid="uid://bxg2au0ijp242" path="res://docs/gyms/currencies/food.tres" id="2_djlc5"] -[ext_resource type="Resource" uid="uid://d08h51y0pnsnf" path="res://docs/gyms/generators/farm.tres" id="3_82rmq"] +[ext_resource type="Resource" uid="uid://bxg2au0ijp242" path="res://docs/gyms/tiny_sword/currencies/food.tres" id="2_djlc5"] +[ext_resource type="Resource" uid="uid://d08h51y0pnsnf" path="res://docs/gyms/tiny_sword/buildings/farm/farm_generator.tres" id="3_82rmq"] [ext_resource type="Texture2D" uid="uid://dfdh8r03sj7qv" path="res://docs/gyms/tiny_sword/buildings/farm/House1.png" id="4_djlc5"] [ext_resource type="PackedScene" uid="uid://ckos7f22pnmyh" path="res://core/generator/generator_container.tscn" id="5_82rmq"] diff --git a/docs/gyms/generators/farm.tres b/docs/gyms/tiny_sword/buildings/farm/farm_generator.tres similarity index 82% rename from docs/gyms/generators/farm.tres rename to docs/gyms/tiny_sword/buildings/farm/farm_generator.tres index 4b100cd..e32149a 100644 --- a/docs/gyms/generators/farm.tres +++ b/docs/gyms/tiny_sword/buildings/farm/farm_generator.tres @@ -1,10 +1,10 @@ [gd_resource type="Resource" script_class="CurrencyGeneratorData" format=3 uid="uid://d08h51y0pnsnf"] [ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="1_cl7uf"] -[ext_resource type="Resource" uid="uid://cg7os1rfknw05" path="res://docs/gyms/buffs/farm_auto_flux.tres" id="2_bbypn"] -[ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/currencies/worker.tres" id="3_bbypn"] +[ext_resource type="Resource" uid="uid://cg7os1rfknw05" path="res://docs/gyms/tiny_sword/buffs/farm_auto_flux_buff.tres" id="2_bbypn"] +[ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/tiny_sword/currencies/worker.tres" id="3_bbypn"] [ext_resource type="Script" uid="uid://b00tqsuhxdy0d" path="res://core/generator/currency_generator_data.gd" id="4_0kvfm"] -[ext_resource type="Resource" uid="uid://cts0407h130d6" path="res://docs/gyms/goals/gold_total_1300.tres" id="5_qiy1b"] +[ext_resource type="Resource" uid="uid://cts0407h130d6" path="res://docs/gyms/tiny_sword/goals/gold_total_1300_goal.tres" id="5_qiy1b"] [resource] script = ExtResource("4_0kvfm") diff --git a/docs/gyms/tiny_sword/buildings/forestry/forestry.tscn b/docs/gyms/tiny_sword/buildings/forestry/forestry.tscn index 1a0184a..7b92aa7 100644 --- a/docs/gyms/tiny_sword/buildings/forestry/forestry.tscn +++ b/docs/gyms/tiny_sword/buildings/forestry/forestry.tscn @@ -1,8 +1,8 @@ [gd_scene format=3 uid="uid://cf01wvy3f17i"] [ext_resource type="PackedScene" uid="uid://jeoiinukrrsp" path="res://core/generator/currency_generator.tscn" id="1_7j1xc"] -[ext_resource type="Resource" uid="uid://bgsk8h4w80h45" path="res://docs/gyms/currencies/wood.tres" id="2_23x2t"] -[ext_resource type="Resource" uid="uid://v6hjoa2vky5k" path="res://docs/gyms/generators/forestry.tres" id="3_pby1g"] +[ext_resource type="Resource" uid="uid://bgsk8h4w80h45" path="res://docs/gyms/tiny_sword/currencies/wood.tres" id="2_23x2t"] +[ext_resource type="Resource" uid="uid://v6hjoa2vky5k" path="res://docs/gyms/tiny_sword/buildings/forestry/forestry_generator.tres" id="3_pby1g"] [ext_resource type="Texture2D" uid="uid://doxauaa3vn3lm" path="res://docs/gyms/tiny_sword/buildings/forestry/House3.png" id="4_5yp33"] [ext_resource type="PackedScene" uid="uid://ckos7f22pnmyh" path="res://core/generator/generator_container.tscn" id="5_rh3m6"] diff --git a/docs/gyms/generators/forestry.tres b/docs/gyms/tiny_sword/buildings/forestry/forestry_generator.tres similarity index 82% rename from docs/gyms/generators/forestry.tres rename to docs/gyms/tiny_sword/buildings/forestry/forestry_generator.tres index d2ec68d..ac56b02 100644 --- a/docs/gyms/generators/forestry.tres +++ b/docs/gyms/tiny_sword/buildings/forestry/forestry_generator.tres @@ -1,9 +1,9 @@ [gd_resource type="Resource" script_class="CurrencyGeneratorData" format=3 uid="uid://v6hjoa2vky5k"] [ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="1_mgk3v"] -[ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/currencies/worker.tres" id="2_fs80u"] -[ext_resource type="Resource" uid="uid://kamgujbqqhg3" path="res://docs/gyms/buffs/forestry_auto_flux.tres" id="2_mgk3v"] -[ext_resource type="Resource" uid="uid://bo463s6jt0ep7" path="res://docs/gyms/goals/gold_total_13k.tres" id="4_mgk3v"] +[ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/tiny_sword/currencies/worker.tres" id="2_fs80u"] +[ext_resource type="Resource" uid="uid://kamgujbqqhg3" path="res://docs/gyms/tiny_sword/buffs/forestry_auto_flux_buff.tres" id="2_mgk3v"] +[ext_resource type="Resource" uid="uid://bo463s6jt0ep7" path="res://docs/gyms/tiny_sword/goals/gold_total_13k_goal.tres" id="4_mgk3v"] [ext_resource type="Script" uid="uid://b00tqsuhxdy0d" path="res://core/generator/currency_generator_data.gd" id="4_rij12"] [resource] diff --git a/docs/gyms/tiny_sword/buildings/gold_mine/gold_mine.tscn b/docs/gyms/tiny_sword/buildings/gold_mine/gold_mine.tscn index 4616993..51d9601 100644 --- a/docs/gyms/tiny_sword/buildings/gold_mine/gold_mine.tscn +++ b/docs/gyms/tiny_sword/buildings/gold_mine/gold_mine.tscn @@ -1,10 +1,10 @@ [gd_scene format=3 uid="uid://brgkkw2n3o1ox"] [ext_resource type="PackedScene" uid="uid://jeoiinukrrsp" path="res://core/generator/currency_generator.tscn" id="1_dgq4l"] -[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/currencies/gold.tres" id="2_d2x03"] +[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="2_d2x03"] [ext_resource type="PackedScene" uid="uid://ckos7f22pnmyh" path="res://core/generator/generator_container.tscn" id="2_lnbxc"] [ext_resource type="Texture2D" uid="uid://bqf6yhtsnmjad" path="res://docs/gyms/tiny_sword/buildings/gold_mine/Gold Stone 6.png" id="2_n0fgx"] -[ext_resource type="Resource" uid="uid://dliilvvjvksk1" path="res://docs/gyms/generators/gold_mine.tres" id="2_sbi6i"] +[ext_resource type="Resource" uid="uid://dliilvvjvksk1" path="res://docs/gyms/tiny_sword/buildings/gold_mine/gold_mine_generator.tres" id="2_sbi6i"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_sbi6i"] size = Vector2(82, 84) diff --git a/docs/gyms/generators/gold_mine.tres b/docs/gyms/tiny_sword/buildings/gold_mine/gold_mine_generator.tres similarity index 81% rename from docs/gyms/generators/gold_mine.tres rename to docs/gyms/tiny_sword/buildings/gold_mine/gold_mine_generator.tres index afa67a7..1b6a03c 100644 --- a/docs/gyms/generators/gold_mine.tres +++ b/docs/gyms/tiny_sword/buildings/gold_mine/gold_mine_generator.tres @@ -1,9 +1,9 @@ [gd_resource type="Resource" script_class="CurrencyGeneratorData" format=3 uid="uid://dliilvvjvksk1"] [ext_resource type="Script" uid="uid://dnhocfsuvmeyb" path="res://core/generator/generator_buff_data.gd" id="1_1ni3y"] -[ext_resource type="Resource" uid="uid://dlq2ke6i2pfob" path="res://docs/gyms/buffs/gold_click_buff.tres" id="2_cnabh"] -[ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/currencies/worker.tres" id="2_j1lw5"] -[ext_resource type="Resource" uid="uid://c3op2mqy02sow" path="res://docs/gyms/buffs/gold_auto_flux.tres" id="3_baaj7"] +[ext_resource type="Resource" uid="uid://dlq2ke6i2pfob" path="res://docs/gyms/tiny_sword/buffs/gold_click_buff.tres" id="2_cnabh"] +[ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/tiny_sword/currencies/worker.tres" id="2_j1lw5"] +[ext_resource type="Resource" uid="uid://c3op2mqy02sow" path="res://docs/gyms/tiny_sword/buffs/gold_auto_flux_buff.tres" id="3_baaj7"] [ext_resource type="Script" uid="uid://b00tqsuhxdy0d" path="res://core/generator/currency_generator_data.gd" id="3_cnabh"] [resource] diff --git a/docs/gyms/currencies/food.tres b/docs/gyms/tiny_sword/currencies/food.tres similarity index 100% rename from docs/gyms/currencies/food.tres rename to docs/gyms/tiny_sword/currencies/food.tres diff --git a/docs/gyms/currencies/gold.tres b/docs/gyms/tiny_sword/currencies/gold.tres similarity index 100% rename from docs/gyms/currencies/gold.tres rename to docs/gyms/tiny_sword/currencies/gold.tres diff --git a/docs/gyms/tiny_sword/currencies/ts_currency_catalogue.tres b/docs/gyms/tiny_sword/currencies/ts_currency_catalogue.tres new file mode 100644 index 0000000..6c66337 --- /dev/null +++ b/docs/gyms/tiny_sword/currencies/ts_currency_catalogue.tres @@ -0,0 +1,13 @@ +[gd_resource type="Resource" script_class="CurrencyCatalogue" format=3 uid="uid://iqphilgfp2i7"] + +[ext_resource type="Script" uid="uid://dtgqjf3bl7pm8" path="res://core/currency/currency.gd" id="1_501l6"] +[ext_resource type="Script" uid="uid://621tus0uvbrd" path="res://core/currency/currency_catalogue.gd" id="2_hmju3"] +[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="2_ucsji"] +[ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/tiny_sword/currencies/worker.tres" id="3_7hx6u"] +[ext_resource type="Resource" uid="uid://bxg2au0ijp242" path="res://docs/gyms/tiny_sword/currencies/food.tres" id="4_c77gh"] +[ext_resource type="Resource" uid="uid://bgsk8h4w80h45" path="res://docs/gyms/tiny_sword/currencies/wood.tres" id="5_gyp05"] + +[resource] +script = ExtResource("2_hmju3") +currencies = Array[ExtResource("1_501l6")]([ExtResource("2_ucsji"), ExtResource("3_7hx6u"), ExtResource("4_c77gh"), ExtResource("5_gyp05")]) +metadata/_custom_type_script = "uid://621tus0uvbrd" diff --git a/docs/gyms/currencies/wood.tres b/docs/gyms/tiny_sword/currencies/wood.tres similarity index 100% rename from docs/gyms/currencies/wood.tres rename to docs/gyms/tiny_sword/currencies/wood.tres diff --git a/docs/gyms/currencies/worker.tres b/docs/gyms/tiny_sword/currencies/worker.tres similarity index 100% rename from docs/gyms/currencies/worker.tres rename to docs/gyms/tiny_sword/currencies/worker.tres diff --git a/docs/gyms/goals/gold_350k_wood_20k.tres b/docs/gyms/tiny_sword/goals/gold_350k_wood_20k_goal.tres similarity index 89% rename from docs/gyms/goals/gold_350k_wood_20k.tres rename to docs/gyms/tiny_sword/goals/gold_350k_wood_20k_goal.tres index 38fff25..ca2dbf7 100644 --- a/docs/gyms/goals/gold_350k_wood_20k.tres +++ b/docs/gyms/tiny_sword/goals/gold_350k_wood_20k_goal.tres @@ -1,8 +1,8 @@ [gd_resource type="Resource" script_class="GoalData" format=3 uid="uid://b6gb6vk0kgxnw"] [ext_resource type="Script" uid="uid://r4js5eajolio" path="res://core/goals/goal_requirement_data.gd" id="1_68ddp"] -[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/currencies/gold.tres" id="2_2b0j8"] -[ext_resource type="Resource" uid="uid://bgsk8h4w80h45" path="res://docs/gyms/currencies/wood.tres" id="3_7tb0t"] +[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="2_2b0j8"] +[ext_resource type="Resource" uid="uid://bgsk8h4w80h45" path="res://docs/gyms/tiny_sword/currencies/wood.tres" id="3_7tb0t"] [ext_resource type="Script" uid="uid://dx4kmh33rrkpy" path="res://core/goals/goal_data.gd" id="4_20y5g"] [sub_resource type="Resource" id="Resource_tvl3d"] diff --git a/docs/gyms/goals/gold_total_1300.tres b/docs/gyms/tiny_sword/goals/gold_total_1300_goal.tres similarity index 91% rename from docs/gyms/goals/gold_total_1300.tres rename to docs/gyms/tiny_sword/goals/gold_total_1300_goal.tres index a6b335b..f4ef655 100644 --- a/docs/gyms/goals/gold_total_1300.tres +++ b/docs/gyms/tiny_sword/goals/gold_total_1300_goal.tres @@ -1,7 +1,7 @@ [gd_resource type="Resource" script_class="GoalData" format=3 uid="uid://cts0407h130d6"] [ext_resource type="Script" uid="uid://r4js5eajolio" path="res://core/goals/goal_requirement_data.gd" id="1_pahmu"] -[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/currencies/gold.tres" id="2_oek0g"] +[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="2_oek0g"] [ext_resource type="Script" uid="uid://dx4kmh33rrkpy" path="res://core/goals/goal_data.gd" id="3_3fgtm"] [sub_resource type="Resource" id="Resource_tvl3d"] diff --git a/docs/gyms/goals/gold_total_13k.tres b/docs/gyms/tiny_sword/goals/gold_total_13k_goal.tres similarity index 91% rename from docs/gyms/goals/gold_total_13k.tres rename to docs/gyms/tiny_sword/goals/gold_total_13k_goal.tres index 0778062..7fe32b8 100644 --- a/docs/gyms/goals/gold_total_13k.tres +++ b/docs/gyms/tiny_sword/goals/gold_total_13k_goal.tres @@ -1,7 +1,7 @@ [gd_resource type="Resource" script_class="GoalData" format=3 uid="uid://bo463s6jt0ep7"] [ext_resource type="Script" uid="uid://r4js5eajolio" path="res://core/goals/goal_requirement_data.gd" id="1_hj5d1"] -[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/currencies/gold.tres" id="2_w5qg8"] +[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="2_w5qg8"] [ext_resource type="Script" uid="uid://dx4kmh33rrkpy" path="res://core/goals/goal_data.gd" id="3_daoxn"] [sub_resource type="Resource" id="Resource_tvl3d"] diff --git a/docs/gyms/goals/gold_total_30.tres b/docs/gyms/tiny_sword/goals/gold_total_30_goal.tres similarity index 91% rename from docs/gyms/goals/gold_total_30.tres rename to docs/gyms/tiny_sword/goals/gold_total_30_goal.tres index bfbc728..985d28c 100644 --- a/docs/gyms/goals/gold_total_30.tres +++ b/docs/gyms/tiny_sword/goals/gold_total_30_goal.tres @@ -1,7 +1,7 @@ [gd_resource type="Resource" script_class="GoalData" format=3 uid="uid://ik7t0r3su633"] [ext_resource type="Script" uid="uid://r4js5eajolio" path="res://core/goals/goal_requirement_data.gd" id="1_udubw"] -[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/currencies/gold.tres" id="2_lde8k"] +[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="2_lde8k"] [ext_resource type="Script" uid="uid://dx4kmh33rrkpy" path="res://core/goals/goal_data.gd" id="3_ilpcf"] [sub_resource type="Resource" id="Resource_v4v16"] diff --git a/docs/gyms/tiny_sword/goals/ts_goal_catalogue.tres b/docs/gyms/tiny_sword/goals/ts_goal_catalogue.tres new file mode 100644 index 0000000..844d992 --- /dev/null +++ b/docs/gyms/tiny_sword/goals/ts_goal_catalogue.tres @@ -0,0 +1,12 @@ +[gd_resource type="Resource" script_class="GoalCatalogue" format=3 uid="uid://cgt1mjir1v4br"] + +[ext_resource type="Script" uid="uid://dx4kmh33rrkpy" path="res://core/goals/goal_data.gd" id="1_6p6ue"] +[ext_resource type="Script" uid="uid://bfbp4mo8ys5p8" path="res://core/goal_catalogue.gd" id="2_84pbn"] +[ext_resource type="Resource" uid="uid://ik7t0r3su633" path="res://docs/gyms/tiny_sword/goals/gold_total_30_goal.tres" id="2_xvtf2"] +[ext_resource type="Resource" uid="uid://cts0407h130d6" path="res://docs/gyms/tiny_sword/goals/gold_total_1300_goal.tres" id="3_wb626"] +[ext_resource type="Resource" uid="uid://bo463s6jt0ep7" path="res://docs/gyms/tiny_sword/goals/gold_total_13k_goal.tres" id="4_3oaoj"] +[ext_resource type="Resource" uid="uid://b6gb6vk0kgxnw" path="res://docs/gyms/tiny_sword/goals/gold_350k_wood_20k_goal.tres" id="5_or88d"] + +[resource] +script = ExtResource("2_84pbn") +goals = Array[ExtResource("1_6p6ue")]([ExtResource("2_xvtf2"), ExtResource("3_wb626"), ExtResource("4_3oaoj"), ExtResource("5_or88d")]) diff --git a/docs/gyms/prestige/primary_prestige.tres b/docs/gyms/tiny_sword/prestige/primary_prestige.tres similarity index 100% rename from docs/gyms/prestige/primary_prestige.tres rename to docs/gyms/tiny_sword/prestige/primary_prestige.tres diff --git a/docs/gyms/tiny_sword/tiny_sword.tscn b/docs/gyms/tiny_sword/tiny_sword.tscn index 3d378bc..41532c1 100644 --- a/docs/gyms/tiny_sword/tiny_sword.tscn +++ b/docs/gyms/tiny_sword/tiny_sword.tscn @@ -1,19 +1,37 @@ [gd_scene format=3 uid="uid://dadeowsvaywpp"] +[ext_resource type="Script" uid="uid://cv2132o4hi7q3" path="res://core/level_game_state.gd" id="1_tma5j"] [ext_resource type="PackedScene" uid="uid://brgkkw2n3o1ox" path="res://docs/gyms/tiny_sword/buildings/gold_mine/gold_mine.tscn" id="2_2j2oc"] +[ext_resource type="Resource" uid="uid://iqphilgfp2i7" path="res://docs/gyms/tiny_sword/currencies/ts_currency_catalogue.tres" id="2_dwrof"] [ext_resource type="PackedScene" uid="uid://cl05bdri38mxf" path="res://docs/gyms/tiny_sword/buildings/castle/castle.tscn" id="3_5ru58"] +[ext_resource type="Resource" uid="uid://bgmj3lep0wmtf" path="res://docs/gyms/tiny_sword/buffs/ts_buff_catalogue.tres" id="3_wl838"] +[ext_resource type="Resource" uid="uid://cgt1mjir1v4br" path="res://docs/gyms/tiny_sword/goals/ts_goal_catalogue.tres" id="4_x77df"] +[ext_resource type="Script" uid="uid://srkiu4qe8s2m" path="res://core/prestige/prestige_manager.gd" id="5_x77df"] +[ext_resource type="Resource" uid="uid://dwmfvmusfskk6" path="res://docs/gyms/tiny_sword/prestige/primary_prestige.tres" id="6_xnhlc"] [ext_resource type="PackedScene" uid="uid://btkxru2gdjsgc" path="res://currency_tile.tscn" id="7_0cs5o"] -[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/currencies/gold.tres" id="9_1363k"] +[ext_resource type="Resource" uid="uid://w4u1hddplb4e" path="res://docs/gyms/tiny_sword/currencies/gold.tres" id="9_1363k"] [ext_resource type="PackedScene" uid="uid://djedqovgngrx5" path="res://docs/gyms/tiny_sword/buildings/farm/farm.tscn" id="10_1lv5i"] -[ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/currencies/worker.tres" id="10_i1cck"] +[ext_resource type="Resource" uid="uid://dfxk30o34qe2s" path="res://docs/gyms/tiny_sword/currencies/worker.tres" id="10_i1cck"] [ext_resource type="PackedScene" uid="uid://dirvi76rkoowf" path="res://goals_debug_ui.tscn" id="10_qifrv"] -[ext_resource type="Resource" uid="uid://bxg2au0ijp242" path="res://docs/gyms/currencies/food.tres" id="11_hskcg"] +[ext_resource type="Resource" uid="uid://bxg2au0ijp242" path="res://docs/gyms/tiny_sword/currencies/food.tres" id="11_hskcg"] [ext_resource type="PackedScene" uid="uid://cf01wvy3f17i" path="res://docs/gyms/tiny_sword/buildings/forestry/forestry.tscn" id="11_pyqyw"] -[ext_resource type="Resource" uid="uid://bgsk8h4w80h45" path="res://docs/gyms/currencies/wood.tres" id="12_l6a68"] +[ext_resource type="Resource" uid="uid://bgsk8h4w80h45" path="res://docs/gyms/tiny_sword/currencies/wood.tres" id="12_l6a68"] [ext_resource type="PackedScene" uid="uid://rejxvjwybkll" path="res://sandbox/tiny_swords/Terrain/Resources/Wood/Trees/tree_1.tscn" id="14_0cs5o"] [node name="TinySwords" type="Node" unique_id=498237642] +[node name="LevelGameState" type="Node" parent="." unique_id=1257082478] +script = ExtResource("1_tma5j") +currency_catalogue = ExtResource("2_dwrof") +buff_catalogue = ExtResource("3_wl838") +goal_catalogue = ExtResource("4_x77df") +save_file_path = "user://tiny_sword_save.json" + +[node name="PrestigeManager" type="Node" parent="LevelGameState" unique_id=1019028144 node_paths=PackedStringArray("game_state")] +script = ExtResource("5_x77df") +config = ExtResource("6_xnhlc") +game_state = NodePath("..") + [node name="World" type="Node2D" parent="." unique_id=2118297724] [node name="ForestProps" type="Node2D" parent="World" unique_id=649424542] @@ -66,32 +84,36 @@ anchors_preset = 0 offset_right = 40.0 offset_bottom = 40.0 -[node name="GoldCurrencyTile" parent="UI" unique_id=1440583137 instance=ExtResource("7_0cs5o")] +[node name="GoldCurrencyTile" parent="UI" unique_id=1440583137 node_paths=PackedStringArray("game_state") instance=ExtResource("7_0cs5o")] layout_mode = 0 offset_right = 160.0 offset_bottom = 31.0 currency = ExtResource("9_1363k") +game_state = NodePath("../../LevelGameState") -[node name="WorkerCurrencyTile" parent="UI" unique_id=1164876942 instance=ExtResource("7_0cs5o")] +[node name="WorkerCurrencyTile" parent="UI" unique_id=1164876942 node_paths=PackedStringArray("game_state") instance=ExtResource("7_0cs5o")] layout_mode = 0 offset_top = 32.0 offset_right = 160.0 offset_bottom = 63.0 currency = ExtResource("10_i1cck") +game_state = NodePath("../../LevelGameState") -[node name="FoodCurrencyTile" parent="UI" unique_id=1814936562 instance=ExtResource("7_0cs5o")] +[node name="FoodCurrencyTile" parent="UI" unique_id=1814936562 node_paths=PackedStringArray("game_state") instance=ExtResource("7_0cs5o")] layout_mode = 0 offset_top = 63.0 offset_right = 160.0 offset_bottom = 94.0 currency = ExtResource("11_hskcg") +game_state = NodePath("../../LevelGameState") -[node name="WoodCurrencyTile" parent="UI" unique_id=338003163 instance=ExtResource("7_0cs5o")] +[node name="WoodCurrencyTile" parent="UI" unique_id=338003163 node_paths=PackedStringArray("game_state") instance=ExtResource("7_0cs5o")] layout_mode = 0 offset_top = 94.0 offset_right = 160.0 offset_bottom = 125.0 currency = ExtResource("12_l6a68") +game_state = NodePath("../../LevelGameState") [node name="GoalsDebugUI" parent="UI" unique_id=1494700185 instance=ExtResource("10_qifrv")] layout_mode = 1 diff --git a/goals_debug_ui.gd b/goals_debug_ui.gd index 13bfecf..7d97513 100644 --- a/goals_debug_ui.gd +++ b/goals_debug_ui.gd @@ -25,6 +25,7 @@ class GoalRow extends RefCounted: @onready var _summary_label: Label = $MarginContainer/VBoxContainer/SummaryLabel @onready var _goals_list: VBoxContainer = $MarginContainer/VBoxContainer/ScrollContainer/GoalsList +@onready var game_state: LevelGameState = find_parent("LevelGameState") var _loaded_goals: Array[GoalDefinition] = [] var _rows_by_goal_id: Dictionary = {} @@ -36,9 +37,10 @@ func _ready() -> void: _collect_known_generator_ids() _load_goals() _build_goal_rows() - GameState.currency_changed.connect(_on_currency_changed) - GameState.generator_state_changed.connect(_on_generator_state_changed) - GameState.goal_completed.connect(_on_goal_completed) + if game_state: + game_state.currency_changed.connect(_on_currency_changed) + game_state.generator_state_changed.connect(_on_generator_state_changed) + game_state.goal_completed.connect(_on_goal_completed) _refresh_ui() _refresh_ui.call_deferred() @@ -74,7 +76,9 @@ func _load_goals() -> void: _generators_by_id[String(generator_id)] = generator var seen_goal_ids: Dictionary = {} - for goal in GameState.get_all_goals(): + var all_goals_raw: Array = game_state.get_all_goals() if game_state else [] + for goal_raw in all_goals_raw: + var goal: GoalData = goal_raw as GoalData if goal == null or not goal.has_id(): continue @@ -182,13 +186,17 @@ func _is_goal_met(goal: GoalDefinition) -> bool: return false if goal.goal == null: return false + if game_state == null: + return false - return bool(goal.goal.is_met()) + return bool(game_state.is_goal_met(goal.goal)) func _is_goal_completed(goal: GoalDefinition) -> bool: if goal.goal == null or goal.goal.id.is_empty(): return false - return GameState.is_goal_completed(goal.goal.id) + if game_state == null: + return false + return game_state.is_goal_completed(goal.goal.id) func _on_unlock_pressed(goal_id: StringName) -> void: var row: GoalRow = _rows_by_goal_id.get(String(goal_id)) @@ -203,14 +211,17 @@ func _on_unlock_pressed(goal_id: StringName) -> void: if not _is_goal_met(goal): return + if game_state == null: + return + var generator: CurrencyGenerator = _get_generator_for_goal(goal) if generator == null: return - GameState.set_generator_unlocked(goal.target_generator_id, true) - GameState.set_generator_available(goal.target_generator_id, true) + game_state.set_generator_unlocked(goal.target_generator_id, true) + game_state.set_generator_available(goal.target_generator_id, true) if goal.goal.unlock_behavior == GoalData.UnlockBehavior.MANUAL: - GameState._complete_goal_manually(goal.goal.id) + game_state._complete_goal_manually(goal.goal.id) print("[GoalsDebugUI] Unlocked goal '%s' -> %s" % [String(goal.goal.id), String(goal.target_generator_id)]) _refresh_ui() @@ -220,7 +231,7 @@ func _can_resolve_target(target_generator_id: StringName) -> bool: if target_key.is_empty(): return false - if _known_generator_ids.has(target_key) or GameState.generator_states.has(target_key): + if _known_generator_ids.has(target_key) or (game_state and game_state.generator_states.has(target_key)): return true if not _warned_unknown_target_ids.has(target_key): @@ -292,16 +303,22 @@ func _get_requirements_text(goal: GoalDefinition) -> String: return "" if goal.goal.get_script() != GOAL_DATA_SCRIPT: return "" + if game_state == null: + return "" var parts: Array[String] = [] - for requirement_resource in goal.goal.get_valid_requirements(): + for requirement_resource in goal.goal.get_requirements(): if requirement_resource == null: continue if requirement_resource.get_script() != GOAL_REQUIREMENT_SCRIPT: continue - var requirement_currency_id: StringName = requirement_resource.get_currency_id() - var total_amount: BigNumber = GameState.get_total_currency_acquired_by_id(requirement_currency_id) + var currency: Currency = requirement_resource.get_currency() + if currency == null: + continue + + var requirement_currency_id: StringName = game_state.get_currency_id(currency) + var total_amount: BigNumber = game_state.get_total_currency_acquired_by_id(requirement_currency_id) var required_amount: BigNumber = requirement_resource.get_amount() parts.append( "%s %s / %s" % [ @@ -316,4 +333,6 @@ func _get_requirements_text(goal: GoalDefinition) -> String: return " | ".join(parts) func _currency_label(currency_id: StringName) -> String: - return GameState.get_currency_name(currency_id) + if game_state == null: + return "Unknown" + return game_state.get_currency_name(currency_id) diff --git a/project.godot b/project.godot index bcbb188..fe3dde7 100644 --- a/project.godot +++ b/project.godot @@ -16,13 +16,6 @@ run/main_scene="uid://dadeowsvaywpp" config/features=PackedStringArray("4.6", "Forward Plus") config/icon="res://icon.svg" -[autoload] - -BuffDatabase="*res://core/buff_database.gd" -CurrencyDatabase="*res://core/currency_database.gd" -GameState="*uid://d2j7tvlgxr2jp" -PrestigeManager="*res://core/prestige/prestige_manager.gd" - [display] window/size/viewport_width=1920