193 lines
5.0 KiB
Markdown
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_met()`, `get_progress()`, `get_summary_text()`) have been moved to `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()`) have been moved to `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):
|
|
game_state.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 GameState
|
|
- Goals auto-discovered from `res://sandbox/goals/`
|
|
- 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:
|
|
GameState._try_unlock_buff_from_goal(buff)
|
|
```
|
|
|
|
### With Prestige
|
|
- Goal completion state persists through prestige
|
|
- Reset via `GameState.reset_for_prestige()`
|
|
|
|
## See Also
|
|
|
|
- `core/game_state.gd` - Goal state management
|
|
- `generator/currency_generator_data.gd` - Generator unlock goals
|
|
- `generator/generator_buff_data.gd` - Buff unlock goals
|