Files
idle/core
2026-05-03 19:30:11 +02:00
..
2026-04-25 12:26:27 +02:00
2026-04-20 21:57:11 +02:00
2026-05-03 19:00:16 +02:00
2026-05-03 19:00:16 +02:00
2026-04-20 21:57:11 +02:00
2026-03-19 22:18:34 +01:00
2026-03-19 22:18:34 +01:00
2026-05-03 15:20:14 +02:00
2026-04-23 12:46:01 +02:00
2026-05-03 19:30:11 +02:00

Core Module Documentation

Overview

The core/ directory contains the fundamental systems for the idle game. It provides:

  • Numeric Foundation: BigNumber for handling huge idle game values
  • Game State Management: LevelGameState for all persistent state
  • Currency System: CurrencyCatalogue and tracking for multiple currency types
  • Generator System: Automated resource production with scaling costs
  • Buff System: Upgrades that modify generator behavior
  • Goal System: Achievement tracking and unlock triggers
  • Prestige System: Rebirth mechanics with permanent multipliers
  • Research System: XP-based progression with production multipliers

Architecture

core/
├── big_number.gd           # Core numeric API
├── level_game_state.gd     # Main state management (Node)
├── currency/               # Currency data structures + CurrencyCatalogue
├── currency_catalogue.gd   # Currency catalogue resource
├── buff_catalogue.gd       # Buff catalogue resource
├── goal_catalogue.gd       # Goal catalogue resource
├── generator/              # Generator logic and data
├── goals/                  # Goal/achievement system
├── prestige/               # Prestige/rebirth system
└── research/               # Research XP and progression

Data Flow

  1. Game Start: LevelGameState._ready() initializes currencies, loads save
  2. Player Action: Generator bought → LevelGameState updated → signals emitted
  3. UI Reaction: Listeners respond to signals → update display
  4. Goal Check: Currency changed → goals evaluated → buffs unlocked
  5. Save: LevelGameState.save_game() → JSON to user://level_save.json

Production Multiplier Chain

All multipliers compose multiplicatively from callee to caller. The entry point is _grant_cycle_income() in CurrencyGenerator.

Entry: CurrencyGenerator._grant_cycle_income() (currency_generator.gd:134)

var effective = get_effective_auto_run_multiplier()
var per_cycle = data.production_per_cycle(owned, effective, purchased_count)
var produced  = per_cycle × cycle_count

Layer 1 — CurrencyGeneratorData.production_per_cycle() (currency_generator_data.gd:117)

production_per_cycle = initial_productivity × owned × run_multiplier × milestone_mult × purchased_mult
Sub-multiplier Method Formula
milestone_mult milestone_multiplier_total(owned) milestone_multiplier ^ floor(owned / milestone_step)
purchased_mult purchased_multiplier(purchased) 1.0 + purchased × (purchased_boost_percent / 100)

Layer 2 — CurrencyGenerator.get_effective_auto_run_multiplier() (currency_generator.gd:357)

This is the run_multiplier value passed into Layer 1 above:

effective_run_multiplier = run_multiplier × research_multiplier × auto_production_buffs × prestige_multiplier

2a. run_multiplier

Exported float on the CurrencyGenerator node. Default 1.0. A scene-level tweak.

2b. research_multiplierLevelGameState.get_research_multiplier(id) (level_game_state.gd:488) → ResearchData.get_multiplier_for_level(level) (research/research_data.gd:52)

research_multiplier = base_multiplier + (multiplier_per_level × research_level)

Level is auto-calculated from accumulated BigNumber XP. Both constants are exports on ResearchData.

2c. auto_production_buffsLevelGameState.get_effective_multiplier(generator_id, AUTO_PRODUCTION_MULTIPLIER) (level_game_state.gd:441)

# For every active buff of kind AUTO_PRODUCTION_MULTIPLIER targeting this generator:
mult = 1.0
for buff in buffs:
    if buff.active and buff.kind == AUTO_PRODUCTION_MULTIPLIER:
        mult *= buff.get_effect_multiplier(level)

GeneratorBuffData.get_effect_multiplier(level) (generator_buff_data.gd:97):

buff_effect = base_effect + (effect_increment × level)

Multiple buffs of the same kind stack multiplicatively: ∏ (base_effect_i + increment_i × level_i).

2d. prestige_multiplierPrestigeManager.get_total_multiplier() (prestige_manager.gd:107)

Configured via PrestigeConfig.multiplier_mode:

Mode Formula
ADDITIVE base + prestige_points × multiplier_per_prestige
MULTIPLICATIVE_POWER base × (1 + multiplier_per_prestige) ^ (prestige_points ^ exponent)

Layer 3 — Production per second

production_per_second = production_per_cycle / maxf(initial_time, 0.0001)

(currency_generator_data.gd:128)

Full Formula (one production cycle)

produced = initial_productivity × owned

         # --- effective_run_multiplier ---
         × run_multiplier
         × (base_multiplier + multiplier_per_level × research_level)
         × ∏(base_effect_i + effect_increment_i × buff_level_i)   [auto-production buffs]
         × prestige_multiplier                                      [from PrestigeManager]

         # --- milestone & purchased ---
         × milestone_multiplier ^ floor(owned / milestone_step)
         × (1.0 + purchased_count × purchased_boost_percent / 100)

Manual Click Formula (separate path)

_try_grant_click_currency() (currency_generator.gd:460):

click_reward = (click_mantissa × 10^click_exponent) × ∏ buff_effect_i   [MANUAL_CLICK_MULTIPLIER buffs]

Uses the same get_effective_multiplier() loop filtered to BuffKind.MANUAL_CLICK_MULTIPLIER. Research, prestige, milestone, and purchased multipliers are not applied to clicks.

Research XP Awarded (production side-effect)

Awarded inside _grant_cycle_income() when the generator has an associated ResearchData:

base_xp   = produced_currency × xp_per_currency_produced
scaled_xp = base_xp × (1.0 + research_workers × worker_scaling_factor)
actual_xp = scaled_xp × ∏ buff_effect_i                              [RESEARCH_XP_MULTIPLIER buffs]

Buff multiplier for XP is applied later in LevelGameState.add_research_xp() via ResearchBuffCalculator.apply_buffs() (core/generator/research_buff_calculator.gd).

Cascade Diagram

                    ┌─────────────────────────────────┐
                    │  run_multiplier (node export)   │
                    │  research_multiplier (XP level) │─── effective_run_multiplier ──┐
                    │  auto-production buffs (∏)      │                               │
                    │  prestige_multiplier            │                               │
                    └─────────────────────────────────┘                               │
                                                                                       ▼
production_per_cycle = initial_productivity × owned × effective_run_multiplier × milestone × purchased
                                                                                       │
                                                                                       ▼
production_per_second = production_per_cycle / initial_time

Save Format

Version: 5

{
  "save_format_version": 5,
  "currencies": {
    "<currency_id>": {
      "current": {"m": float, "e": int},
      "total": {"m": float, "e": int},
      "all_time": {"m": float, "e": int}
    }
  },
  "generator_states": {
    "<generator_id>": {
      "owned": int,
      "purchased_count": int,
      "unlocked": bool,
      "available": bool
    }
  },
  "buff_levels": {
    "<buff_id>": int
  },
  "buff_unlocked": {
    "<buff_id>": true
  },
  "buff_active": {
    "<buff_id>": true
  },
  "goals": {
    "completed": ["<goal_id>", ...]
  },
  "research_xp": {
    "<research_id>": {"m": float, "e": int}
  },
  "research_levels": {
    "<research_id>": int
  },
  "last_save_time": unix_timestamp
}

Key Signals

Signal Emitted When
currency_changed(currency_id, new_amount) Any currency balance changes
generator_state_changed(generator_id, state) Generator owned/unlocked changes
generator_buff_level_changed(gen_id, buff_id, level) Buff level increased
buff_unlocked_changed(buff_id, unlocked) Buff unlock state changed
goal_completed(goal_id) Goal criteria met

BigNumber Serialization

All large numbers use scientific notation internally:

# Value = mantissa * 10^exponent
var num = BigNumber.new(1.5, 24)  # 1.5e24
var dict = num.serialize()         # {"m": 1.5, "e": 24}
var restored = BigNumber.deserialize(dict)

Extending the Core

Adding a New Currency

  1. Create res://sandbox/currencies/<name>.tres extending Currency
  2. Set id, display_name, and icon
  3. Add to CurrencyCatalogue resource assigned to LevelGameState

Adding a New Generator

  1. Create res://sandbox/generators/<name>.tres extending CurrencyGeneratorData
  2. Configure costs, production, and unlock goals
  3. Instance CurrencyGenerator scene with this data

Adding a New Buff

  1. Create res://sandbox/buffs/<name>.tres extending GeneratorBuffData
  2. Set target_ids, kind, effect_increment, and cost
  3. Add to BuffCatalogue resource assigned to LevelGameState

Adding New Research

  1. Create res://sandbox/research/<name>.tres extending ResearchData
  2. Link to a generator via generator_id
  3. Add to ResearchCatalogue resource assigned to LevelGameState

See Also

  • currency/ - Currency data structures
  • generator/ - Generator production system
  • goals/ - Achievement system
  • prestige/ - Rebirth mechanics