Remove dead code

This commit is contained in:
2026-04-18 10:50:44 +02:00
parent dfb1161854
commit 3f49bb42c8
11 changed files with 186 additions and 263 deletions

View File

@@ -10,30 +10,26 @@ specific generators and can be enhanced with purchasable buffs that increase XP
| File | Purpose |
|------|---------|
| `research_xp_tracker.gd` | Accumulates XP from production and handles auto-leveling (Node) |
| `research_catalogue.gd` | Resource holding all research configurations |
| `research_panel.gd` | UI panel showing all research tracks |
| `research_row.gd` | UI row component for individual research display |
| `../generator/research_buff_calculator.gd` | Static utility for buff multiplier calculations |
## Architecture
```
LevelGameState (Node2D)
├── ResearchXPTracker (child node)
│ ├── Receives production_tick signals
│ ├── Applies buff multipliers
│ ├── Awards XP to research tracks
│ └── Auto-levels when thresholds reached
├── research_xp: Dictionary
├── research_levels: Dictionary
── research_catalogue: ResearchCatalogue
└── ResearchPanel (UI, always visible)
├── ResearchRow (for each research track)
│ ├── Name & icon
│ ├── Level display
│ ├── Progress bar (percentage)
│ ├── Multiplier indicator
│ └── Active buff display
── research_catalogue: ResearchCatalogue
└── ResearchPanel (UI, always visible)
├── ResearchRow (for each research track)
│ ├── Name & icon
│ ├── Level display
│ ├── Progress bar (percentage)
│ ├── Multiplier indicator
│ └── Active buff display
└── (buff calculation delegated to ResearchBuffCalculator static methods)
```
## Data Flow
@@ -42,12 +38,13 @@ LevelGameState (Node2D)
Generator Production (currency_per_cycle)
ResearchXPTracker
LevelGameState.add_research_xp()
1. base_xp = currency_produced * xp_per_currency_produced
2. actual_xp = base_xp * buff_multiplier
3. research_xp += actual_xp
4. Check level threshold → auto-level
5. Emit research_level_up signal
2. buff_multiplier = ResearchBuffCalculator.apply_buffs(research, buffs)
3. actual_xp = base_xp * buff_multiplier
4. research_xp += actual_xp
5. Check level threshold → auto-level
6. Emit research_level_up signal
ResearchState {xp: 150.5, level: 3}
@@ -143,38 +140,61 @@ func get_all_research() -> Array[ResearchData]
Referenced directly by `LevelGameState` via `@export var research_catalogue: ResearchCatalogue`.
## ResearchXPTracker
## ResearchBuffCalculator
Child node of `LevelGameState` that accumulates XP from production and applies buff multipliers.
Static utility class for calculating research XP buff multipliers.
### Signals
### Location
```gdscript
signal research_xp_changed(research_id: StringName, new_xp: float)
signal research_level_up(research_id: StringName, old_level: int, new_level: int)
```
`res://core/generator/research_buff_calculator.gd`
### Key Methods
```gdscript
func add_xp_with_buffs(research_id: StringName, base_xp: BigNumber) -> BigNumber:
"""Apply buff multipliers and award XP. Returns actual XP after buffs (BigNumber)."""
var buff_multiplier: float = game_state.get_research_xp_multiplier(research_id)
var multiplier_bn: BigNumber = BigNumber.from_float(buff_multiplier)
var actual_xp: BigNumber = base_xp.multiply(multiplier_bn)
game_state.add_research_xp(research_id, actual_xp)
return actual_xp
static func apply_buffs(research: ResearchData, game_state: LevelGameState) -> float:
"""Calculate total buff multiplier for research XP gain. Returns multiplicative factor."""
var multiplier: float = 1.0
if research.associated_buff_id.is_empty():
return multiplier
var buff: GeneratorBuffData = game_state.get_buff(research.associated_buff_id)
if buff != null and game_state.is_buff_active(buff.id):
var buff_level: int = game_state.get_buff_level(buff.id)
multiplier *= ResearchBuffCalculator._calculate_buff_multiplier(buff, buff_level)
return multiplier
func _on_generator_produced(generator_id: StringName, amount: BigNumber) -> void:
var research: ResearchData = _get_research_for_generator(generator_id)
var amount_float: float = amount.mantissa * pow(10.0, float(amount.exponent))
var base_xp: BigNumber = BigNumber.from_float(research.xp_per_currency_produced * amount_float)
add_xp_with_buffs(research.id, base_xp)
static func _calculate_buff_multiplier(buff: GeneratorBuffData, level: int) -> float:
"""Calculate buff effect multiplier at given level."""
return buff.get_effect_multiplier(level)
```
### Connection
### Usage
Connected to `LevelGameState` via `generator_produced` signal.
Called from `LevelGameState.add_research_xp()`:
```gdscript
func add_research_xp(research_id: StringName, xp: BigNumber) -> void:
var research: ResearchData = research_catalogue.get_research_by_id(research_id)
if research == null:
return
# Apply buff multipliers
var buff_multiplier: float = ResearchBuffCalculator.apply_buffs(research, self)
var multiplier_bn: BigNumber = BigNumber.from_float(buff_multiplier)
var actual_xp: BigNumber = xp.multiply(multiplier_bn)
# Store XP and check for level-up
var old_level: int = get_research_level(research_id)
research_xp[research_id] = research_xp.get(research_id, BigNumber.new(0.0, 0)).add(actual_xp)
var new_level: int = research.get_level_for_xp(research_xp[research_id])
if new_level > old_level:
research_levels[research_id] = new_level
research_level_up.emit(research_id, old_level, new_level)
research_xp_changed.emit(research_id, research_xp[research_id])
```
## LevelGameState Extensions
@@ -182,7 +202,7 @@ Connected to `LevelGameState` via `generator_produced` signal.
```gdscript
var research_xp: Dictionary = {} # {research_id: BigNumber}
var research_levels: Dictionary = {} # {research_id: level_int}
var research_tracker: ResearchXPTracker
# research_tracker removed - buff calculation moved to static utility
```
### Signals
@@ -208,21 +228,27 @@ func get_research_multiplier(research_id: StringName) -> float:
return research.get_multiplier_for_level(level)
func add_research_xp(research_id: StringName, xp: BigNumber) -> void:
"""Add XP (BigNumber) and auto-level if threshold reached"""
"""Add XP (BigNumber) with buff multipliers applied, auto-level if threshold reached"""
var research: ResearchData = research_catalogue.get_research_by_id(research_id)
if research == null:
return
var old_level: int = get_research_level(research_id)
var current_xp: BigNumber = get_research_xp(research_id)
research_xp[research_id] = current_xp.add(xp)
new_level = research.get_level_for_xp(research_xp[research_id])
# Apply buff multipliers via static utility
var buff_multiplier: float = ResearchBuffCalculator.apply_buffs(research, self)
var multiplier_bn: BigNumber = BigNumber.from_float(buff_multiplier)
var actual_xp: BigNumber = xp.multiply(multiplier_bn)
research_xp[research_id] = current_xp.add(actual_xp)
var new_level: int = research.get_level_for_xp(research_xp[research_id])
if new_level > old_level:
research_levels[research_id] = new_level
research_level_up.emit(research_id, old_level, new_level)
func get_research_xp_multiplier(research_id: StringName) -> float:
"""Returns multiplicative buff for research XP gain"""
var multiplier: float = 1.0
var buff: GeneratorBuffData = get_buff(research.associated_buff_id)
if is_buff_active(buff.id):
multiplier *= buff.get_effect_multiplier(get_buff_level(buff.id))
return multiplier
research_xp_changed.emit(research_id, research_xp[research_id])
```
### Save/Load Integration
@@ -272,11 +298,11 @@ func _grant_cycle_income(cycle_count: int) -> void:
_add_currency(produced)
production_tick.emit(produced, cycle_count, owned)
# Award research XP (BigNumber)
# Award research XP (BigNumber) - buff multipliers applied in LevelGameState
if data.research_data != null:
var amount_float: float = produced.mantissa * pow(10.0, float(produced.exponent))
var xp: BigNumber = BigNumber.from_float(data.research_data.xp_per_currency_produced * amount_float)
research_tracker.add_xp_with_buffs(data.research_data.id, xp)
game_state.add_research_xp(data.research_data.id, xp)
```
### Research Multiplier
@@ -425,7 +451,7 @@ actual_xp = base_xp * xp_multiplier
| Decision | Value |
|----------|-------|
| ResearchCatalogue reference | Direct `@export var research_catalogue: ResearchCatalogue` in LevelGameState |
| ResearchXPTracker location | Child of `LevelGameState` (receives signals via connection) |
| Buff calculation | Static utility (`ResearchBuffCalculator`) - no dedicated tracker node |
| Progress bar display | Percentage format (e.g., "75%") |
| Buff display | Shows buff name and effect (e.g., "Mining Expert: +20% XP") |
| Research unlock | Starts unlocked |
@@ -438,8 +464,8 @@ actual_xp = base_xp * xp_multiplier
## System Behavior
1. **Generator produces currency**`production_tick` signal emitted
2. **ResearchXPTracker receives signal** → calculates base XP from production amount
3. **Buff multipliers applied** → checks for active RESEARCH_XP_MULTIPLIER buffs
2. **CurrencyGenerator calls**`game_state.add_research_xp()` with base XP
3. **LevelGameState applies buffs**`ResearchBuffCalculator.apply_buffs()` calculates multiplier
4. **XP awarded** → stored in `research_xp` dictionary
5. **Level check** → if XP exceeds threshold, auto-level and emit `research_level_up`
6. **Multiplier applied**`get_research_multiplier()` returns updated production bonus
@@ -458,4 +484,5 @@ On prestige reset:
- `core/level_game_state.gd` - Research state storage and management
- `core/generator/currency_generator_data.gd` - Links to ResearchData
- `core/generator/generator_buff_data.gd` - RESEARCH_XP_MULTIPLIER buff type
- `core/generator/research_buff_calculator.gd` - Static buff calculation utility
- `core/research/research_catalogue.gd` - Research configuration resource

View File

@@ -18,16 +18,10 @@ A production-based research system where:
```
┌─────────────────────────────────────────────────────────────────┐
│ LevelGameState │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ResearchXPTracker (child node) │ │
│ │ - Receives production_tick signals │ │
│ │ - Applies buff multipliers │ │
│ │ - Awards XP to research tracks │ │
│ │ - Auto-levels when thresholds reached │ │
│ └─────────────────────────────────────────────────────────┘ │
│ - research_xp: Dictionary │
│ - research_levels: Dictionary │
│ - research_catalogue: ResearchCatalogue │
│ - add_research_xp() applies buffs via ResearchBuffCalculator │
└─────────────────────────────────────────────────────────────────┘
┌───────────────────┼───────────────────┐
@@ -39,6 +33,12 @@ A production-based research system where:
│ - emits signal │ │ - Multiplier │ │ research │
└─────────────────┘ └─────────────────┘ │ - Progress bars │
└─────────────────┘
┌───────────────────────────────────────────────────────────────┐
│ ResearchBuffCalculator (static utility) │
│ - apply_buffs() calculates XP multiplier from active buffs │
└───────────────────────────────────────────────────────────────┘
```
---
@@ -50,15 +50,22 @@ Generator Production (currency_per_cycle)
┌──────────────────────┐
ResearchXPTracker
1. base_xp = currency_produced * xp_per_currency_produced
│ 2. actual_xp = base_xp * buff_multiplier
│ 3. research_xp += actual_xp
│ 4. Check level threshold → auto-level
│ 5. Emit research_level_up signal
CurrencyGen
emits production_tick │
└──────────────────────┘
┌─────────────────────────────────────────┐
│ LevelGameState.add_research_xp() │
│ 1. base_xp = currency_produced * rate │
│ 2. buff_mult = ResearchBuffCalculator.apply_buffs()
│ 3. actual_xp = base_xp * buff_mult │
│ 4. research_xp += actual_xp │
│ 5. Check level threshold → auto-level │
│ 6. Emit research_level_up signal │
└─────────────────────────────────────────┘
┌──────────────────────┐
│ ResearchState │
│ {xp: 150.5, level: 3}│
@@ -188,53 +195,33 @@ func get_all_research() -> Array[ResearchData]:
---
### 3. ResearchXPTracker (core/research/research_xp_tracker.gd)
### 3. ResearchBuffCalculator (core/generator/research_buff_calculator.gd)
**Purpose:** Child of LevelGameState; accumulates XP from production and applies buff multipliers
**Purpose:** Static utility class for calculating research XP buff multipliers
**Location:** `res://core/research/research_xp_tracker.gd`
**Signals:**
```gdscript
signal research_xp_changed(research_id: StringName, new_xp: float)
signal research_level_up(research_id: StringName, old_level: int, new_level: int)
```
**Location:** `res://core/generator/research_buff_calculator.gd`
**Key Methods:**
```gdscript
func add_xp_with_buffs(research_id: StringName, base_xp: float) -> float:
"""Apply buff multipliers and award XP. Returns actual XP after buffs."""
if game_state == null:
return 0.0
static func apply_buffs(research: ResearchData, game_state: LevelGameState) -> float:
"""Calculate total buff multiplier for research XP gain. Returns multiplicative factor."""
var multiplier: float = 1.0
if research.associated_buff_id.is_empty():
return multiplier
var actual_xp: float = base_xp
var buff: GeneratorBuffData = game_state.get_buff(research.associated_buff_id)
if buff != null and game_state.is_buff_active(buff.id):
var buff_level: int = game_state.get_buff_level(buff.id)
multiplier *= _calculate_buff_multiplier(buff, buff_level)
# Apply research-specific buff multiplier
var buff_multiplier: float = game_state.get_research_xp_multiplier(research_id)
actual_xp *= buff_multiplier
# Award XP (handles auto-leveling internally)
game_state.add_research_xp(research_id, actual_xp)
return actual_xp
return multiplier
func _on_generator_produced(generator_id: StringName, amount: BigNumber) -> void:
var research: ResearchData = _get_research_for_generator(generator_id)
if research == null:
return
var amount_float: float = amount.to_float()
if amount_float <= 0.0:
return
var base_xp: float = research.xp_per_currency_produced * amount_float
if base_xp <= 0.0:
return
add_xp_with_buffs(research.id, base_xp)
static func _calculate_buff_multiplier(buff: GeneratorBuffData, level: int) -> float:
"""Calculate buff effect multiplier at given level."""
return buff.get_effect_multiplier(level)
```
**Connection:** Connected to `LevelGameState` via `generator_produced` signal
**Usage:** Called from `LevelGameState.add_research_xp()` to apply buffs before storing XP
---
@@ -242,14 +229,14 @@ func _on_generator_produced(generator_id: StringName, amount: BigNumber) -> void
**New State Variables:**
```gdscript
var research_xp: Dictionary = {} # {research_id: xp_float}
var research_xp: Dictionary = {} # {research_id: BigNumber}
var research_levels: Dictionary = {} # {research_id: level_int}
var research_tracker: ResearchXPTracker
# research_tracker removed - buff calculation moved to static utility
```
**New Signals:**
```gdscript
signal research_xp_changed(research_id: StringName, new_xp: float)
signal research_xp_changed(research_id: StringName, new_xp: BigNumber)
signal research_level_up(research_id: StringName, old_level: int, new_level: int)
```
@@ -262,12 +249,12 @@ signal research_level_up(research_id: StringName, old_level: int, new_level: int
```gdscript
func register_research(research_id: StringName) -> void:
if not research_xp.has(research_id):
research_xp[research_id] = 0.0
research_xp[research_id] = BigNumber.new(0.0, 0)
if not research_levels.has(research_id):
research_levels[research_id] = 0
func get_research_xp(research_id: StringName) -> float:
return research_xp.get(research_id, 0.0)
func get_research_xp(research_id: StringName) -> BigNumber:
return research_xp.get(research_id, BigNumber.new(0.0, 0))
func get_research_level(research_id: StringName) -> int:
return research_levels.get(research_id, 0)
@@ -279,11 +266,22 @@ func get_research_multiplier(research_id: StringName) -> float:
return 1.0
return research.get_multiplier_for_level(level)
func add_research_xp(research_id: StringName, xp: float) -> void:
var old_level: int = get_research_level(research_id)
research_xp[research_id] = research_xp.get(research_id, 0.0) + xp
func add_research_xp(research_id: StringName, xp: BigNumber) -> void:
"""Add XP with buff multipliers applied, auto-level if threshold reached"""
var research: ResearchData = research_catalogue.get_research_by_id(research_id)
if research == null:
return
var new_level: int = _get_research_data(research_id).get_level_for_xp(research_xp[research_id])
var old_level: int = get_research_level(research_id)
var current_xp: BigNumber = get_research_xp(research_id)
# Apply buff multipliers via static utility
var buff_multiplier: float = ResearchBuffCalculator.apply_buffs(research, self)
var multiplier_bn: BigNumber = BigNumber.from_float(buff_multiplier)
var actual_xp: BigNumber = xp.multiply(multiplier_bn)
research_xp[research_id] = current_xp.add(actual_xp)
var new_level: int = research.get_level_for_xp(research_xp[research_id])
if new_level > old_level:
research_levels[research_id] = new_level
@@ -291,22 +289,6 @@ func add_research_xp(research_id: StringName, xp: float) -> void:
research_xp_changed.emit(research_id, research_xp[research_id])
func get_research_xp_multiplier(research_id: StringName) -> float:
"""Returns multiplicative buff for research XP gain"""
var multiplier: float = 1.0
var research: ResearchData = _get_research_data(research_id)
if research == null:
return 1.0
# Find associated buff and apply its multiplier
if not research.associated_buff_id.is_empty():
var buff: GeneratorBuffData = get_buff(research.associated_buff_id)
if buff != null and is_buff_active(buff.id):
var level: int = get_buff_level(buff.id)
multiplier *= buff.get_effect_multiplier(level)
return multiplier
func _get_research_data(research_id: StringName) -> ResearchData:
if research_catalogue == null:
return null
@@ -338,11 +320,6 @@ research_levels.clear()
**Initialization in _ready():**
```gdscript
# After initializing other state, create research tracker
research_tracker = ResearchXPTracker.new()
add_child(research_tracker)
research_tracker.game_state = self
# Initialize research state from catalogue
if research_catalogue:
for research in research_catalogue.get_all_research():
@@ -377,10 +354,10 @@ func _grant_cycle_income(cycle_count: int) -> void:
_add_currency(produced)
production_tick.emit(produced, cycle_count, owned)
# NEW: Award research XP
# Award research XP - buff multipliers applied in LevelGameState
if data.research_data != null:
var xp: float = data.research_data.xp_per_currency_produced * produced.to_float()
research_tracker.add_xp_with_buffs(data.research_data.id, xp)
game_state.add_research_xp(data.research_data.id, BigNumber.from_float(xp))
```
**New Method:**
@@ -399,13 +376,6 @@ func get_effective_auto_run_multiplier() -> float:
return run_multiplier * get_research_multiplier() * get_automatic_production_multiplier() * _get_prestige_multiplier()
```
**In `_ready()`:**
```gdscript
# Get reference to research tracker
if game_state:
research_tracker = game_state.research_tracker
```
---
### 7. GeneratorBuffData Extensions (core/generator/generator_buff_data.gd)
@@ -549,11 +519,11 @@ func _on_research_level_up(research_id: StringName, _old_level: int, _new_level:
|------|------|-------|----------|--------|
| 1 | Create `ResearchData` resource class | `core/generator/research_data.gd` | High | ⬜ |
| 2 | Create `ResearchCatalogue` resource class | `core/research/research_catalogue.gd` | High | ⬜ |
| 3 | Create `ResearchXPTracker` node class | `core/research/research_xp_tracker.gd` | High | |
| 4 | Add research state to `LevelGameState` | `core/level_game_state.gd` | High | |
| 3 | Create `ResearchBuffCalculator` static utility | `core/generator/research_buff_calculator.gd` | High | ✅ Done |
| 4 | Add research state to `LevelGameState` | `core/level_game_state.gd` | High | ✅ Done |
| 5 | Add `RESEARCH_XP_MULTIPLIER` buff kind | `core/generator/generator_buff_data.gd` | High | ⬜ |
| 6 | Add `research_data` export to `CurrencyGeneratorData` | `core/generator/currency_generator_data.gd` | High | ⬜ |
| 7 | Connect production to research XP in `CurrencyGenerator` | `core/generator/currency_generator.gd` | High | |
| 7 | Connect production to research XP in `CurrencyGenerator` | `core/generator/currency_generator.gd` | High | ✅ Done |
| 8 | Create `ResearchRow` scene & script | `core/research/research_row.tscn`, `.gd` | Medium | ⬜ |
| 9 | Create `ResearchPanel` scene & script | `core/research/research_panel.tscn`, `.gd` | Medium | ⬜ |
| 10 | Add save/load support for research state | `core/level_game_state.gd` | Medium | ⬜ |
@@ -569,7 +539,7 @@ func _on_research_level_up(research_id: StringName, _old_level: int, _new_level:
|----------|-------|
| ResearchCatalogue reference | Direct `@export var research_catalogue: ResearchCatalogue` in LevelGameState |
| ResearchCatalogue location | `core/research/research_catalogue.gd` (resource, referenced directly) |
| ResearchXPTracker location | Child of `LevelGameState` (receives signals via connection) |
| Buff calculation | Static utility (`ResearchBuffCalculator`) - no dedicated tracker node |
| Progress bar display | Percentage format (e.g., "75%") |
| Buff display | Shows buff name and effect (e.g., "Mining Expert: +20% XP") |
| Research unlock | Starts unlocked |
@@ -619,7 +589,8 @@ actual_xp = base_xp * xp_multiplier
- **Associated buffs** should be created as `GeneratorBuffData` with `BuffKind.RESEARCH_XP_MULTIPLIER`
- **Buff names** are configured in the `text` field of each `GeneratorBuffData`
- **ResearchPanel** should be added as a child node in the main game scene (always visible)
- **Save format version** should be incremented to 4 when adding research support
- **Save format version** should be incremented to 5 when adding research support (BigNumber XP)
- **ResearchXPTracker removed** - buff calculation moved to static `ResearchBuffCalculator` utility
---

View File

@@ -1,72 +0,0 @@
class_name ResearchXPTracker
extends Node
@export var game_state: LevelGameState
signal research_xp_changed(research_id: StringName, new_xp: BigNumber)
signal research_level_up(research_id: StringName, old_level: int, new_level: int)
func add_xp_with_buffs(research_id: StringName, base_xp: BigNumber) -> BigNumber:
if game_state == null:
return BigNumber.new(0.0, 0)
var buff_multiplier: float = game_state.get_research_xp_multiplier(research_id)
var multiplier_bn: BigNumber = BigNumber.from_float(buff_multiplier)
var actual_xp: BigNumber = base_xp.multiply(multiplier_bn)
game_state.add_research_xp(research_id, actual_xp)
return actual_xp
func _on_generator_produced(generator_id: StringName, _amount: Variant) -> void:
var research: ResearchData = _get_research_for_generator(generator_id)
if research == null:
return
var research_catalogue: ResearchCatalogue = game_state.research_catalogue
if research_catalogue == null:
return
research = research_catalogue.get_research_by_generator_id(generator_id)
if research == null:
return
if not _is_research_active(research.id):
return
var amount_big: BigNumber = _amount
var amount_float: float = amount_big.mantissa * pow(10.0, float(amount_big.exponent))
if amount_float <= 0.0:
return
var base_xp: BigNumber = BigNumber.from_float(research.xp_per_currency_produced * amount_float)
if base_xp.mantissa <= 0.0:
return
add_xp_with_buffs(research.id, base_xp)
func _get_research_for_generator(generator_id: StringName) -> ResearchData:
if game_state == null or game_state.research_catalogue == null:
return null
return game_state.research_catalogue.get_research_by_generator_id(generator_id)
func _is_research_active(research_id: StringName) -> bool:
if game_state == null:
return false
var research_panel: Node = game_state.find_child("ResearchPanel", true, false)
if research_panel == null:
return true
if research_panel.has_method("is_research_active"):
return research_panel.is_research_active(research_id)
return true
func activate_research(research_id: StringName) -> void:
if game_state == null:
return
var research_panel: Node = game_state.find_child("ResearchPanel", true, false)
if research_panel != null and research_panel.has_method("activate_research"):
research_panel.activate_research(research_id)

View File

@@ -1 +0,0 @@
uid://bv3a4utlnn2fg