Files
idle/core/goals/README.md
Michele Rossi 81a4058b04 systems rework: buffs, prestige graph, research, modular architecture
Decouples core systems from centralized globals in favor of catalogue-based architecture and data-driven content.

   Key changes:
   - Prestige: node-based buff tech tree, graph panel, progress bar
   - Research: multi-worker system, per-generator research, xp generation
   - Ascension: meta-currency layer via multi-currency prestige resets
   - Buffs: refactored as generator-independent via catalogues
   - UI: currency panel, edge-scrolling camera + zoom, current-goal panel
   - Alchemy Tower: crafting building with recipe/cost system
   - Testing: 7 test suites (ascension, prestige, research, goals)
   - Content: migrated from hardcoded idles/ to gym format (tiny_sword)
2026-05-07 20:36:21 +00:00

193 lines
5.0 KiB
Markdown

# Goals Module Documentation
## Overview
The `goals/` subfolder contains the achievement system. Goals track player progress and can unlock generators or buffs when conditions are met.
## Files
| File | Purpose |
|------|---------|
| `goal_data.gd` | Goal definition resource |
| `goal_requirement_data.gd` | Individual requirement criteria |
## GoalData
Resource class defining an achievement.
### Properties
```gdscript
class_name GoalData
extends Resource
@export var id: StringName = &"" # Unique identifier
@export var requirements: Array[GoalRequirementData] # Conditions to complete
@export var unlock_behavior: UnlockBehavior = MANUAL # AUTOMATIC or MANUAL
```
### Enum
```gdscript
enum UnlockBehavior {
AUTOMATIC, # Goal completes immediately when met
MANUAL, # Goal requires player to click unlock button
}
```
### Methods
```gdscript
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_goal_met()`, `get_goal_progress()`, `get_goal_requirement_summary()`) are implemented in `LevelGameState` for proper decoupling.
## GoalRequirementData
Single condition within a goal.
### Properties
```gdscript
class_name GoalRequirementData
extends Resource
@export var currency: Currency # Currency to track
@export var amount_mantissa: float = 0.0 # Required amount (scientific notation)
@export var amount_exponent: int = 0
```
### Methods
```gdscript
func get_currency() -> Currency # Currency to track
func get_amount() -> BigNumber # Required amount
func has_valid_amount() -> bool # Amount is non-negative
```
**Note:** State-dependent methods (`is_met()`, `get_progress()`, `get_summary_text()`) are implemented in `LevelGameState` for proper decoupling.
### Progress Calculation
Uses **logarithmic scaling** for better visual feedback on huge numbers:
```gdscript
# Convert to log space
current_log = log(current_mantissa) / log(10) + current_exponent
required_log = log(required_mantissa) / log(10) + required_exponent
# Progress ratio
progress = clampf(current_log / required_log, 0.0, 1.0)
```
This means reaching 50% progress on a 1e100 goal requires ~1e50 currency.
## Goal Evaluation
### Automatic Checking
`LevelGameState` evaluates goals whenever currency changes:
```gdscript
func _on_currency_changed(currency_id, new_amount):
evaluate_all_goals()
```
### Completion Flow
1. Currency updated → signal emitted
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
### Manual Unlock vs Automatic
Goals control their own unlock behavior via `unlock_behavior`:
- **AUTOMATIC**: Goal completes immediately when requirements are met
- **MANUAL**: Goal stays incomplete until player clicks unlock button
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
Create `res://sandbox/goals/first_million.tres`:
```
[gd_resource type="Resource" script_class="GoalData"]
[resource]
id = &"first_million"
requirements = [
preload("res://sandbox/goals/first_million_req.tres")
]
```
### Defining a Requirement
Create `res://sandbox/goals/first_million_req.tres`:
```
[gd_resource type="Resource" script_class="GoalRequirementData"]
[resource]
currency = preload("res://sandbox/currencies/gold.tres")
amount_mantissa = 1.0
amount_exponent = 6 # 1,000,000
```
### Connecting to Generator
In `CurrencyGeneratorData`:
```gdscript
@export var unlock_goal: GoalData # Assign goal resource
```
The goal's `unlock_behavior` determines if the generator unlocks automatically or requires manual button click.
## Signals
```gdscript
signal goal_completed(goal_id: StringName)
signal goal_progress_changed(goal_id: StringName, progress: float)
```
## Integration Points
### With LevelGameState
- Goals registered from `GoalCatalogue` assigned to `LevelGameState`
- Completion state saved to JSON
- Buff unlock goals checked automatically
### With Buffs
```gdscript
# In GeneratorBuffData
@export var unlock_goal: GoalData
# When goal completes, buff auto-unlocks:
level_game_state._try_unlock_buff_from_goal(buff)
```
### With Prestige
- Goal completion state persists through prestige
- Reset via `LevelGameState.reset_for_prestige()`
## See Also
- `core/level_game_state.gd` - Goal state management
- `core/generator/currency_generator_data.gd` - Generator unlock goals
- `core/generator/generator_buff_data.gd` - Buff unlock goals