Files
Godot-Experements/.idea/random/aurora4x_godot_design_doc.md
T
2026-05-16 14:15:17 +02:00

16 KiB
Raw Blame History

Aurora 4X — Godot 4.6.2 Remake

Game Design Document & Developer Preparation

Status: Pre-production / Architecture phase
Engine: Godot 4.6.2
Genre: Real-time 4X Space Strategy with time scaling
Reference: Aurora 4X by AuroraSteve


Table of Contents

  1. Game Vision
  2. Core Gameplay Loop
  3. Time System Design
  4. Resource System Design
  5. Planet & Body Design
  6. Mining System Design
  7. Architecture Overview
  8. Threading Model
  9. Class Reference
  10. Developer Checklist
  11. Open Questions

1. Game Vision

A faithful real-time reimagining of Aurora 4X in Godot 4. The goal is to preserve Aurora's depth — procedural star systems, mineral economics, ship design, exploration, and diplomacy — while modernizing the experience with a proper real-time engine, time scaling, and a cleaner UI.

Design pillars:

  • Depth over accessibility — Aurora's complexity is a feature, not a flaw
  • Simulation integrity — the numbers must hold up at any time scale
  • Modular systems — each simulation domain (economy, combat, diplomacy) is isolated and independently tickable
  • Performance budget awareness — late-game with dozens of colonies and hundreds of ships must remain playable

2. Core Gameplay Loop

Explore star systems
    → Survey planets and bodies for minerals
        → Colonize and deploy mines
            → Extract minerals → Build ships & installations
                → Expand to new systems
                    → Encounter alien races → Diplomacy or War
                        → Repeat at larger scale

Key differences from the original Aurora:

  • Real-time instead of turn-based increments
  • Time scaling (1x → 64x+) replaces "advance X days" buttons
  • Visual star map and orbit rendering instead of text tables
  • Event-driven pause system (game pauses automatically on important events)

3. Time System Design

Philosophy

All simulation math is rate-based, not tick-based. This means:

  • Mining produces X tons per second rather than X tons per turn
  • Any system consuming time receives a scaled_delta (real delta × time scale)
  • At 64x, scaled_delta is simply 64× larger — no special casing anywhere

Time Scale Ladder

Level Scale Use Case
1 1× Combat, diplomacy, precision decisions
2 2× Routine fleet movement
3 4× Short transit legs
4 8× Long transit legs
5 16× Waiting for construction
6 32× Long colonization phases
7 64× Early game exploration stretches
8+ 128×+ Optional / player settings

Auto-Pause Events

The game automatically drops to paused when:

  • A mineral deposit is fully depleted
  • An unknown contact appears on sensors
  • A construction order completes
  • A ship takes damage
  • A diplomatic message is received
  • A colony falls below minimum supply threshold

GameClock — Autoload

Single source of truth for all time in the game. Nothing reads delta directly — everything calls GameClock.get_scaled_delta(delta).

# Autoload: GameClock
const SCALES: Array[float] = [1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0]

var time_scale: float = 1.0
var paused: bool = false
var elapsed_game_time: float = 0.0  # total in-game seconds

func get_scaled_delta(delta: float) -> float
func set_scale(scale: float) -> void
func toggle_pause() -> void
func faster() -> void
func slower() -> void

4. Resource System Design

The 11 Minerals

Directly ported from Aurora 4X. Each has a distinct role in construction and technology.

# Mineral Primary Use
0 Duranium Hull plating, construction
1 Neutronium Armour, advanced hulls
2 Corbomite Electronic components
3 Tritanium Engines
4 Boronide Power plants
5 Mercassium Sensors
6 Vendarite Weapons
7 Sorium Fuel (found mainly on gas giants)
8 Uridium Shields
9 Corundium Ground forces equipment
10 Gallicite Missile components

Deposit Properties

Each deposit on a body has exactly two values:

Property Range Effect
amount 0 999,999 tons Total remaining; depletes over time
accessibility 0.1 1.0 Multiplier on mine output rate

Mining rate formula:

tons_per_second = mines_assigned × accessibility

At accessibility 1.0, one mine extracts 1 ton/sec of in-game time. At 0.1, ten mines are needed for the same throughput. These base values are tuned during balancing.

Deposit Distribution by Body Type

Not every mineral appears on every body. Spawn probability during procedural generation:

Body Type Spawn Chance per Mineral
Rocky planet 60%
Moon 45%
Asteroid 80% (low amount, high accessibility)
Gas giant 10% (Sorium-weighted)
Comet 25%

Survey Mechanic

Deposits are hidden until surveyed. A geological survey ship must complete a survey mission on the body before deposits appear. This preserves Aurora's exploration tension.

  • deposit.surveyed = false by default
  • get_active_deposits() filters out unsurveyed deposits
  • Survey progress is a time-based operation (affected by time scale)

5. Planet & Body Design

PlanetBody — Plain Class (not Node or Resource)

Critical: PlanetBody must NOT extend Resource or Node. It is mutated from the economy worker thread. Plain GDScript classes are thread-safe to read/write; Godot's built-in base classes are not.

Properties:

body_name: String
body_type: String       # "Rocky", "Moon", "Gas Giant", "Asteroid", "Comet"
gravity: float          # relative to Earth (1.0)
temperature: float      # Kelvin
deposits: Dictionary    # MineralDeposit.Type (int) → MineralDeposit

Save/Load: Serialized manually to JSON or a custom binary format. Do not rely on ResourceSaver.

Body Types and Gameplay Role

Type Mining Colonization Notes
Rocky planet Yes Yes Main colony targets
Moon Yes Limited Good early mining
Asteroid Yes No High accessibility, low total
Gas giant Sorium only No Fuel source
Comet Rare minerals No Worth hunting

6. Mining System Design

MiningOperation — One Per Colony

Each colonized body that has mines deployed has exactly one MiningOperation. It references the body and tracks the deployed mine count and local stockpile.

Tick behavior:

  1. Get all active (surveyed, non-depleted) deposits
  2. Divide mines_deployed evenly across active deposits
  3. Call deposit.extract(scaled_delta, mines_share) on each
  4. Accumulate extracted amounts into stockpile
  5. Collect and return any depletion events

Mine allocation (v1 — simple):
Mines split evenly across all active deposits. Future versions can allow player-directed allocation per mineral type.

Stockpile Flow

PlanetBody deposits
    → MiningOperation.stockpile (local buffer)
        → Colony inventory (global pool per empire)
            → Construction queues consume from colony inventory
            → Freighters transport between colonies

The local stockpile buffer means mining output doesn't instantly teleport to construction — freighters must move it, preserving Aurora's logistics gameplay.


7. Architecture Overview

Node Tree (Main Scene)

Main
├── GameClock          (Autoload)
├── EconomyManager     (Node — _process every frame)
├── StarSystemManager  (Node — owns all PlanetBody instances)
├── FleetManager       (Node)
├── UILayer            (CanvasLayer)
│   ├── StarMap
│   ├── ColonyPanel
│   ├── EventLog
│   └── TimeControls
└── EventBus           (Autoload — for cross-system signals)

System Boundaries

System Owns Communicates via
EconomyManager MiningOperations, EconomyWorker EventBus signals
StarSystemManager PlanetBody, star layout Direct reference
FleetManager Ship instances, movement EventBus signals
UI Display only Reads snapshots, listens to EventBus

Rule: UI never writes to simulation data. It reads snapshots and sends player commands through a command queue.


8. Threading Model

Why a Thread

At 16x64x time scale, the economy tick can process significant simulation work per real frame. Moving it off the main thread keeps rendering and input at full frame rate regardless of simulation load.

Thread Safety Rules

  1. Only plain GDScript classes (class_name Foo, no extends) are mutated from the worker thread
  2. Never call emit_signal() from the worker thread — use an event queue instead
  3. Never instantiate Nodes from the worker thread
  4. Always lock the mutex before reading OR writing shared data from either thread
  5. The main thread owns the UI — the worker thread never touches display state

Architecture

Main Thread                          Worker Thread
─────────────────────────────────    ──────────────────────────────────
GameClock._process(delta)
  → EconomyManager._process(delta)
      → worker.push_delta()  ──────→ Semaphore wakes _run()
      → worker.poll_events() ←──────   _process_tick(scaled_delta)
          → emit via EventBus             for each MiningOperation:
          → maybe pause clock               deposit.extract()
                                            accumulate stockpile
                                            collect events → queue

EconomyWorker Internals

_thread: Thread
_semaphore: Semaphore      # main posts, worker waits
_mutex: Mutex              # guards all shared state

_operations: Array         # MiningOperation list
_pending_delta: float      # accumulated scaled delta
_pending_events: Array     # event queue (drained by main thread)

Delta accumulation: If the main thread posts faster than the worker can process (unlikely but possible at extreme time scales), _pending_delta accumulates rather than dropping ticks. The worker drains the full accumulated delta on its next wake.

Reading Data from UI

The UI must never read stockpile or deposit amounts directly without a mutex-guarded snapshot:

# EconomyWorker — safe snapshot for UI
func get_stockpile_snapshot(op: MiningOperation) -> Dictionary:
    _mutex.lock()
    var snapshot := op.stockpile.duplicate()
    _mutex.unlock()
    return snapshot

UI refresh timer: every 0.5 real seconds — not every frame.


9. Class Reference

MineralDeposit

Base None (plain class)
Thread-safe
File mineral_deposit.gd
Member Type Notes
type Type (enum) One of 11 mineral types
amount float Tons remaining
accessibility float 0.1 1.0
surveyed bool Hidden until geological survey
get_rate(mines) float Tons/sec at given mine count
extract(scaled_delta, mines) float Mutates amount, returns extracted
is_depleted() bool True when amount ≤ 0

PlanetBody

Base None (plain class)
Thread-safe
File planet_body.gd
Member Type Notes
body_name String
body_type String Rocky / Moon / Gas Giant / etc.
gravity float Earth = 1.0
temperature float Kelvin
deposits Dictionary int → MineralDeposit
add_deposit(d) void
get_deposit(type) MineralDeposit
get_active_deposits() Array Surveyed + non-depleted only

MiningOperation

Base None (plain class)
Thread-safe
File mining_operation.gd
Member Type Notes
body PlanetBody
mines_deployed int
stockpile Dictionary Type → float (tons)
tick(scaled_delta) Array Returns events (depletions)
get_total_rate() Dictionary Type → tons/sec, for UI

EconomyWorker

Base None (plain class)
Thread-safe (internally managed)
File economy_worker.gd
Member Type Notes
start() void Launches thread
stop() void Graceful shutdown, waits for thread
push_delta(scaled_delta) void Called from main thread each frame
add_operation(op) void Register a colony
remove_operation(op) void Deregister a colony
poll_events() Array Drain event queue (main thread)
get_stockpile_snapshot(op) Dictionary Safe UI read

EconomyManager

Base Node
Thread-safe Main thread only
File economy_manager.gd

Thin coordinator. Drives EconomyWorker each frame, handles events from the worker, emits signals on EventBus.


GameClock

Base Node (Autoload)
Thread-safe Main thread only
File game_clock.gd

All time scaling lives here. Never read delta directly anywhere else in the project.


ResourceGenerator

Base None (static methods)
Thread-safe (no state)
File resource_generator.gd
Member Notes
generate_deposits(body, rng) Procedurally populates a PlanetBody
_spawn_chance(body_type) Per-type probability table

10. Developer Checklist

Phase 0 — Foundation

  • Set up Godot 4.6.2 project structure (Core/, Systems/, UI/, Data/)
  • Implement GameClock autoload
  • Implement EventBus autoload (for cross-system signals)
  • Write unit tests for MineralDeposit.extract() — verify math at 1x, 16x, 64x scale
  • Write unit tests for delta accumulation in EconomyWorker

Phase 1 — Resource System

  • MineralDeposit — full implementation + tests
  • PlanetBody — full implementation
  • ResourceGenerator — procedural deposit generation
  • MiningOperation — tick logic + event return
  • EconomyWorker — thread, mutex, semaphore
  • EconomyManager — main thread coordinator
  • Verify thread shutdown is clean on scene change and game exit

Phase 2 — Star System

  • Procedural star system generation (stars, planets, moons, asteroid belts)
  • Orbital mechanics (visual only — positions, not physics sim)
  • StarSystemManager node
  • Survey mission system (time-based, uses GameClock)

Phase 3 — Colony & UI

  • Colony creation flow (ship delivers colony module)
  • Mine deployment UI
  • Stockpile display (snapshot-based, 0.5s refresh)
  • Event log panel (depletion notices, auto-pause triggers)
  • Time controls UI (pause, scale ladder, keyboard shortcuts)

Phase 4 — Construction

  • Construction queue system
  • Mineral consumption from colony stockpile
  • Freighter / logistics system (move stockpile between colonies)

Phase 5 — Ships & Fleets

  • Ship component / design system
  • Fleet movement (real-time, scaled)
  • Basic sensor / detection system

Phase 6 — Expansion

  • Jump point discovery and inter-system travel
  • Non-Player Race (NPR) basic AI
  • Diplomacy framework

11. Open Questions

# Question Priority
1 What is the base mining rate (tons/sec per mine at 1x)? Needs balancing pass. High
2 Should mine allocation be automatic (even split) or player-directed per mineral? High
3 At what time scales should auto-pause be suppressible by the player? Medium
4 Save file format — JSON (human-readable) or custom binary (performance)? Medium
5 Is Sorium (fuel) part of the same mining pipeline or a separate extraction type? Medium
6 Should asteroid belts be individual bodies or a single aggregate body? Low
7 How many colonies realistically before the thread model needs profiling? Low

Document version 0.1 — Initial architecture session
Last updated: May 2026