# 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_multiplier` → `LevelGameState.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_buffs` → `LevelGameState.get_effective_multiplier(generator_id, AUTO_PRODUCTION_MULTIPLIER)` (`level_game_state.gd:441`) ```gdscript # 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_multiplier` → `PrestigeManager.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 ```json { "save_format_version": 5, "currencies": { "": { "current": {"m": float, "e": int}, "total": {"m": float, "e": int}, "all_time": {"m": float, "e": int} } }, "generator_states": { "": { "owned": int, "purchased_count": int, "unlocked": bool, "available": bool } }, "buff_levels": { "": int }, "buff_unlocked": { "": true }, "buff_active": { "": true }, "goals": { "completed": ["", ...] }, "research_xp": { "": {"m": float, "e": int} }, "research_levels": { "": 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: ```gdscript # 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/.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/.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/.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/.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