Files
idle/core/goals

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

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

enum UnlockBehavior {
	AUTOMATIC,  # Goal completes immediately when met
	MANUAL,     # Goal requires player to click unlock button
}

Methods

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

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

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:

# 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:

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

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:

@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

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

# 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