Cleanup
This commit is contained in:
@@ -1,122 +0,0 @@
|
||||
# Buff System Migration Guide
|
||||
|
||||
## Overview
|
||||
This guide walks you through migrating from per-generator buffs to the new global buff system.
|
||||
|
||||
## Prerequisites
|
||||
- Backup your existing save file: `user://idle_save.json`
|
||||
- Note: This is a **breaking change** - old saves (v1) will be rejected
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### Step 1: Update Buff Resources
|
||||
|
||||
Open each buff in `res://sandbox/buffs/` and set the `target_ids` field:
|
||||
|
||||
**For buffs that should apply to all generators:**
|
||||
- Set `target_ids = ["*"]`
|
||||
|
||||
**For buffs that target specific generators:**
|
||||
- Set `target_ids = ["generator_id_1", "generator_id_2"]`
|
||||
- Use the same IDs as defined in your generator data files
|
||||
|
||||
**Example:**
|
||||
```gdscript
|
||||
# farm_flux.tres
|
||||
target_ids = ["farm", "forestry"] # Multi-target buff
|
||||
|
||||
# global_boost.tres
|
||||
target_ids = ["*"] # Wildcard - all current and future generators
|
||||
```
|
||||
|
||||
### Step 2: Update Generator Resources
|
||||
|
||||
Open each generator in `res://sandbox/generators/` and:
|
||||
|
||||
1. **Remove** the `buffs` array field (or clear it)
|
||||
2. **(Optional)** Add `expected_buff_ids` for documentation purposes
|
||||
|
||||
This is safe because generators now query `GameState.get_buffs_for_generator()` at runtime.
|
||||
|
||||
### Step 3: Delete Old Save Files
|
||||
|
||||
Delete `user://idle_save.json` to start fresh with the new v2 format.
|
||||
|
||||
**Note:** There is no automatic migration from v1 to v2. Players will need to start new games.
|
||||
|
||||
### Step 4: Verify Auto-Discovery
|
||||
|
||||
The `BuffDatabase` autoload will automatically load all `.tres` files from `res://sandbox/buffs/` at runtime.
|
||||
|
||||
Verify in the output log:
|
||||
```
|
||||
BuffDatabase initialized with N buffs
|
||||
```
|
||||
|
||||
### Step 5: Test Key Scenarios
|
||||
|
||||
Run through these test cases:
|
||||
|
||||
1. **Single-target buff**: Buy a buff that targets only one generator
|
||||
- Verify only that generator's production increases
|
||||
|
||||
2. **Multi-target buff**: Buy a buff targeting multiple generators
|
||||
- Verify all targeted generators' production increases
|
||||
|
||||
3. **Wildcard buff**: Buy a buff with `target_ids = ["*"]`
|
||||
- Verify ALL generators (current and future) get the bonus
|
||||
|
||||
4. **Goal unlock**: Meet a buff unlock goal
|
||||
- Verify the buff automatically unlocks and activates
|
||||
|
||||
5. **Save/Load**: Save the game, close, and reopen
|
||||
- Verify buff levels and unlock states persist
|
||||
|
||||
6. **Prestige**: Perform a prestige
|
||||
- Verify all buff levels reset to 0
|
||||
- Verify buff unlock states reset to false
|
||||
|
||||
## Rollback (If Needed)
|
||||
|
||||
If issues arise:
|
||||
|
||||
1. Revert all code changes using git
|
||||
2. Restore old save file from backup
|
||||
3. Restore buff/generator `.tres` files to original state
|
||||
4. Remove `BuffDatabase` from `project.godot` autoloads
|
||||
|
||||
## Checking Your Work
|
||||
|
||||
After migration, verify:
|
||||
|
||||
- [ ] All buffs load automatically at startup
|
||||
- [ ] Buff UI displays correctly for each generator
|
||||
- [ ] Buying a buff level increases the GLOBAL level (not per-generator)
|
||||
- [ ] Multi-target buffs affect all specified generators
|
||||
- [ ] Wildcard buffs affect all generators
|
||||
- [ ] Save files include buff state (version 2)
|
||||
- [ ] Prestige resets all buff levels
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Issue: Buff not appearing in UI
|
||||
**Solution**: Check that the buff's `target_ids` includes the generator's ID or uses `["*"]`
|
||||
|
||||
### Issue: Wildcard buffs not applying to new generators
|
||||
**Solution**: Ensure new generators call `GameState.register_buff()` for each buff they should inherit
|
||||
|
||||
### Issue: Old save won't load
|
||||
**Solution**: This is expected - v1 saves are incompatible. Delete `user://idle_save.json` and start fresh.
|
||||
|
||||
### Issue: Buff multipliers not stacking
|
||||
**Solution**: Verify `get_effective_multiplier()` is being called and that buff levels > 0
|
||||
|
||||
## Success Criteria
|
||||
|
||||
Migration is complete when:
|
||||
- ✅ All generators produce correct amounts with active buffs
|
||||
- ✅ Buff levels are shared globally across all targets
|
||||
- ✅ Wildcard buffs automatically apply to new generators
|
||||
- ✅ Save/load preserves buff state correctly
|
||||
- ✅ Prestige resets all buff levels to 0
|
||||
- ✅ New buffs can be added by dropping `.tres` files in `res://sandbox/buffs/`
|
||||
325
PLANNING.md
325
PLANNING.md
@@ -1,325 +0,0 @@
|
||||
# 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
|
||||
@@ -14,12 +14,16 @@ extends PanelContainer
|
||||
var _is_waiting_for_confirm: bool = false
|
||||
|
||||
func _ready() -> void:
|
||||
var manager: Node = _get_prestige_manager()
|
||||
game_state.ready.connect(_on_game_state_ready)
|
||||
|
||||
func _on_game_state_ready() -> void:
|
||||
var manager: PrestigeManager = _get_prestige_manager()
|
||||
manager.prestige_state_changed.connect(_on_prestige_state_changed)
|
||||
manager.prestige_performed.connect(_on_prestige_performed)
|
||||
game_state.currency_changed.connect(_on_currency_changed)
|
||||
_refresh_ui()
|
||||
|
||||
|
||||
func _on_reset_button_pressed() -> void:
|
||||
var manager: PrestigeManager = _get_prestige_manager()
|
||||
|
||||
|
||||
@@ -12,7 +12,9 @@ func _process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
func _on_area_2d_mouse_entered() -> void:
|
||||
_prestige_panel.visible = true
|
||||
## TODO: remove this comment to enable prestige panel
|
||||
#_prestige_panel.visible = true
|
||||
pass
|
||||
|
||||
func _on_area_2d_mouse_exited() -> void:
|
||||
_prestige_panel.visible = false
|
||||
|
||||
@@ -23,6 +23,7 @@ position = Vector2(1, 22)
|
||||
shape = SubResource("RectangleShape2D_tgvch")
|
||||
|
||||
[node name="PrestigePanel" parent="." unique_id=245519778 instance=ExtResource("3_l7gct")]
|
||||
visible = false
|
||||
offset_left = 155.0
|
||||
offset_top = -80.0
|
||||
offset_right = 575.0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[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="Script" uid="uid://bfbp4mo8ys5p8" path="res://core/goals/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"]
|
||||
|
||||
@@ -43,6 +43,7 @@ anchors_preset = 0
|
||||
layout_mode = 1
|
||||
|
||||
[node name="GoalsDebugUI" parent="LevelGameState/CanvasLayer/UI" unique_id=1494700185 instance=ExtResource("10_qifrv")]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 1433.0
|
||||
offset_top = 5.0
|
||||
|
||||
Reference in New Issue
Block a user