Mr. Phil Games’ Blog

Stellar Throne Devlog #10 — From Bug Fixes to Breakthroughs

Yesterday marked a massive surge of progress on Stellar Throne, my 4X strategy game. For those new to the project, Stellar Throne is built with Godot (or “go-DOH”) for the UI client and Zig for the high-performance simulation engine. After validating that the dual-engine architecture was correctly preserving visual data, yesterday’s focus was all about systematically fixing critical bugs and implementing major missing systems.

Fixing Fleet Movement: The Multi-Hop Breakthrough

The day began with a curious issue — fleets would stop at their first waypoint instead of continuing to their final destination. If I ordered a fleet to travel across five star systems, it would jump to the first and then just… stop.

There were two culprits:

  1. The Zig simulation backend was missing multi-hop continuation logic.

  2. The has_planned_route flag wasn’t being properly set during deserialization.

I fixed both by adding continuation logic to the Zig fleet arrival handler and correcting route flag inference from the route array. Fleets now correctly travel across all waypoints to reach distant colonies.


Event Manager Revival: “Salvage the Past” Returns

Next, I discovered that the “Salvage the Past” event — which provides narrative context and a small resource bonus at game start — wasn’t firing.
The cause? The Event Manager was still trying to load from a deprecated JSON file instead of the new TOML configuration system. After migrating all configs months ago, this subsystem had simply never been updated.

I replaced the hardcoded loader with a TOML-based one through the Config Manager, added automatic format conversion (snake_case → camelCase), and fixed an autoload order issue that caused Event Manager to initialize too early. The fix was simple but pivotal: moving Config Manager earlier in the autoload sequence. Events now trigger properly again.


Research System Overhaul: Persistence, Costs, and Effects

The research system was next — and it was a tangle. Research progress wasn’t persisting correctly after saving and loading, and techs were completing at incorrect cost values. Digging in revealed:

  • A serialization key mismatch (active_tech vs. active_tech_id)

  • Corrupted deserialization from nested research dictionaries

  • Over-accumulating progress past 100%

  • Flat serialization structures incompatible with the Godot side

I rebuilt the serialization/deserialization layer and restructured ResearchStateSimEmpire, and TurnSimulator to restore consistent persistence.

But research completion costs were still wrong — some techs completed at arbitrary thresholds. The problem was broken tier-matching logic: Zig was inferring tech costs from tiers instead of fetching them from the TOML configuration. After replacing it with proper ID-based lookups, research now respects real costs.

Finally, I discovered that tech effects weren’t being applied when research completed. The Zig backend had a “simplified” completeTech() function that unlocked techs but didn’t apply effects or send notifications. I implemented a temporary workaround in the TurnSimulationService, post-processing newly completed techs and calling the full Godot-side applyTechnologyEffects() method.

This fix is documented for a proper long-term solution in TODO_TECH_EFFECTS.md, which estimates a 15–20 hour implementation.


Planning for Parity: Building a Structured Roadmap

After the bug marathon, I shifted to long-term planning. The Zig backend currently includes 10 simplified systems — construction, combat, AI, events, and more. To bring full parity with Godot, I created a detailed roadmap across five new documents:

  1. Zig Implementation Plan — 13-week roadmap with system-by-system breakdowns, totaling 360–450 estimated hours.

  2. Zig Parity Roadmap — Executive summary showing 8 full systems, 10 simplified, and a move from 65% tolerance to near 0% parity.

  3. Parity Test Examples — Deterministic test specifications for construction, combat, AI, and events using exact RNG seeding.

  4. Zig Parity Progress — Session tracking, milestone checklists, and velocity metrics.

  5. Session Workflow Guide — Practical start/during/end checklists for ongoing development.


Phase Five Complete: Construction System Implemented

With the roadmap ready, I dove into Phase Five — Construction.

This system enables both turn-based building construction and production-based shipbuilding in Zig. I created:

  • SimConstruction.zig (with ConstructionOrder and ConstructionQueue)

  • Extended SimGalaxy for build and ship queues

  • Added processing to TurnSimulator

Each construction order tracks 13 fields — progress, turns, costs, and more — while queues handle FIFO operations. The logic now supports both building completion and ship production (logged for now, pending full ship creation parity).

The system clocked in at 8 hours — 33% ahead of the 12–16 hour estimate.
Current test results:

  • 339 Zig unit tests: ✅ Passing

  • 739/740 GUT integration tests: ✅ Passing

Zig parity stands at 1/7 phases complete (14%).


Construction Bugs & Fixes

The new construction system exposed three deeper issues:

  1. Disappearing Orders — Planets lacked system_id after deserialization. Fixed by setting it during StarSystem.from_dict().

  2. “Unknown Building” Labels — Type strings weren’t deserializing properly. Implemented a temporary queue-preservation workaround.

  3. Wrong Building Creation — Empty item types caused incorrect building spawns. Fixed via state preservation until full parity logic is in place.


Smarter Documentation: Resume Management Automation

Finally, I overhauled documentation. The massive RESUME.md file had grown to over 2,000 lines, bloating context windows. I:

  • Archived sessions 1–59 to a separate file

  • Reduced RESUME.md to 400 lines (80% reduction)

  • Created two new Claude agents:

    • Resume Writer Agent — auto-archives when context exceeds 150K tokens or on “done for today”

    • Resume Reader Agent — summarizes state at session start

Now, context is lean, searchable, and structured for long-term development.


Technical Summary

Yesterday’s session generated 13 commits and over 1,000 lines of code changes:

  • Fleet multi-hop fix — 34 lines

  • Event system TOML migration — 63 lines

  • Autoload dependency fix — 1 line

  • Research persistence & cost fixes — 128 lines

  • Tech effect workaround — 53 lines

  • Planning documents — 2,000+ lines (5 new files)

  • Construction system — 454 lines

  • Construction bug fixes — 108 lines

  • Resume refactor — 1,500 archived lines + 2 Claude agents


Current Status

✅ Fleets now travel through multi-hop routes.
✅ Events trigger properly at game start.
✅ Research completes at correct costs and applies tech effects.
✅ Construction system is fully implemented in Zig.
✅ One of seven parity phases complete (14%).
✅ Documentation system streamlined and automated.

This marks a shift from “fixing bugs in the Zig backend” to “systematically implementing full system parity.” The new roadmap provides the structure to reach 100% Zig simulation parity over the next 13 weeks.

Next up: Phase Nine — Combat Resolution, estimated at 24–32 hours.


You can follow ongoing updates and technical breakdowns at mrphilgames.com.
Thanks for reading — and for joining me on this journey to build Stellar Throne from the ground up.

— MrPhil

Stellar Throne Devlog #9: Visual Parity and Data Preservation

Tuesday was a critical follow-up to Monday’s validation milestone for Stellar Throne, my 4X strategy game built with Godot for the UI client and Zig for the high-performance simulation engine.

After Monday’s comprehensive parity testing confirmed that simulation data was correct, Tuesday revealed a new set of challenges: while the numbers lined up perfectly, the visuals didn’t. Stars were rendering with the wrong colors, planets were losing their visual properties, and entity names were disappearing entirely.


Fixing the Star Rendering Bug

The day began by finishing the star rendering investigation I started Monday night. The issue came down to a type serialization mismatch.

In Godot, star types are represented as integer enum values — zero for red dwarfs, one for yellow stars, two for blue giants, and so on. But when serializing that data to JSON for the Zig engine, those enum values weren’t being preserved correctly.

Zig expected string representations such as “red dwarf” or “yellow star,” but it was receiving integers instead. When the Zig deserializer tried to interpret those integers as strings, the type information became corrupted, resulting in invalid or default star types and incorrect colors.

The solution was to add proper enum-to-string conversion in the Godot serialization layer. Now, when star data flows between the two engines, it converts integer enums into readable strings and back again as needed. With this fix in place, stars once again display with the correct colors and visual properties.


Preserving Planet Visual Data

Fixing the stars revealed another issue — planets were losing their visual properties after each turn.

Each planet has an orbital angle, orbital speed, and visual size that define how it appears in the star system view. After running a turn through the Zig simulation, all planets reset to default positions and sizes.

The cause was simple: the Zig layer doesn’t use visual data, so those cosmetic fields weren’t being serialized. To fix it, I extended the planet serialization to include the orbital fields.

Now, when planet data round-trips through Zig, it preserves its orbital angle, orbital speed, and size. Zig doesn’t calculate or modify these fields — it just passes them through unchanged so the Godot rendering layer can display planets exactly as the player left them.


Restoring Entity Names

A more fundamental problem appeared next: entity names were disappearing. Stars, planets, empires, and fleets were all showing up as “unnamed” or “unknown” after simulation turns.

This was another serialization gap. The name fields existed in Godot’s data structures and were sent to Zig correctly, but the Zig structs weren’t including them in their serialized output. As a result, when the game state returned to Godot, the name fields were missing.

The fix was straightforward: ensure every relevant Zig struct explicitly includes name serialization in its output. Star names, planet names, empire names, and fleet names are now preserved completely when the game state returns from the Zig engine.


Fixing Fleet Composition and Movement

The next discovery was serious: fleets were losing their ship composition data.

In Stellar Throne, fleets aren’t just a single number — they’re made up of multiple ship types, each with unique stats. For example, a fleet might include five destroyers, three cruisers, and two carriers, each with its own health and combat attributes.

While fleet data was being serialized to Zig correctly, the simulation layer only tracked total ship count. The specific ship types and their stats were discarded during processing, meaning fleets lost their internal structure by the time they returned to Godot.

I fixed this by expanding the Zig fleet struct to include a ship composition array, storing each ship group’s type, count, and health percentage. The Zig simulation now preserves these details throughout turn processing, and the data returns intact to the UI layer.

While testing this fix, I found another visual bug: fleets were parking themselves at the exact center of stars after arriving at their destinations. The arrival logic was setting fleet positions to the star’s coordinates instead of assigning proper orbital positions.

I added arrival positioning logic so fleets calculate and occupy realistic orbits around stars upon arrival. The result looks much more natural — fleets now glide into orbit rather than clustering in the star’s core.


Cleaning Up Unit Tests

Finally, I addressed a series of failing Zig unit tests. They were breaking because they tried to import Godot-specific modules that don’t exist in the standalone Zig build.

I made the imports conditional so that they only load when building the shared library for Godot. In standalone mode, the pure Zig tests now run cleanly without external dependencies.


Technical Breakdown

  • 8 commits total
  • Star type serialization: 3 files, 47 lines changed
  • Planet visual preservation: 2 modules, 32 lines
  • Entity name serialization: 4 structs, 69 lines
  • Fleet composition fix: 5 files, 143 lines
  • Fleet orbital positioning: 27 lines
  • Unit test cleanup: 12 lines of conditional imports

The parity framework now preserves all visual data through simulation round-trips.
✔ Stars render correctly
✔ Planets maintain their orbits and sizes
✔ Entity names persist
✔ Fleets retain full ship composition
✔ Unit tests pass cleanly in both build modes


Why This Matters

This work ensures that Stellar Throne’s dual-engine architecture isn’t just mathematically correct, but visually consistent. Players don’t care that the simulation runs fifty-two times faster if their stars are the wrong color or their fleet names vanish.

These fixes make the performance improvements completely invisible to the player — everything feels smooth, stable, and accurate.


Next Steps

  • Stress test visual data preservation across long playthroughs

  • Add automated tests to confirm that cosmetic data survives multiple turn cycles

  • Profile the Zig engine for serialization performance

  • Begin designing new simulation systems — including diplomacy and trade routes — that take advantage of the validated, high-speed backend


Thanks for reading this week’s update.

Follow along with development progress and technical insights at MrPhilGames.com.

Stellar Throne Devlog #8: Validation, Parity, and Precision

Monday was a critical day of validation and bug hunting for Stellar Throne, my 4X strategy game built with Godot for the UI client and Zig for the high-performance simulation engine. After enabling the Zig backend in production over the weekend, my goal was to make absolutely sure both engines stayed perfectly in sync.


Dual-Engine Validation

The big picture: the Zig simulation now runs about fifty-two times faster than the original Godot implementation. But speed means nothing if the results aren’t identical. Monday’s focus was validation, testing, and fixing any divergence between the two engines.

I started the day tracking down a fleet position serialization bug. When fleets moved between star systems, their positions were stored correctly in Zig, but once the data returned to Godot for rendering, the interpolation broke — fleets would jump around the screen instead of moving smoothly along their paths.

The culprit was the serialization layer. Zig and Godot were using different coordinate formats. I standardized the data structure and updated the movement interpolation code to properly handle the incoming data. With that fix, fleet movement became smooth and visually accurate again.


Building Systematic Validation

Ad-hoc bug fixes weren’t enough; I needed systematic validation. I built a comprehensive serialization parity test framework to catch any mismatch before it caused a production bug.

The new test infrastructure compares both engines after every operation. It serializes game state from Godot to JSON, deserializes it in Zig, runs a simulation step, then serializes Zig’s results back to JSON and compares every field — ensuring no corruption or drift between engines.

This framework immediately revealed several subtle issues:

  • Inconsistent resource formatting across empires

  • Rounding errors in planet population data due to floating-point differences

  • Missing serialized fields in certain edge cases

I spent the rest of the day tightening the serialization code. Every numeric field now includes explicit precision validation. Optional fields have null checks. Missing legacy data gets default values. The result is a much more defensive and resilient serialization layer.


Achieving Population Growth Parity

Once the parity tests stabilized, a new challenge surfaced: population growth parity. The tests passed for most systems, but population numbers were diverging slightly between engines.

The root cause turned out to be integer math in Zig where floating-point calculations were required. After switching the intermediate values to floating-point, both engines produced identical results down to the last decimal place — restoring confidence in one of the game’s most important mechanics.


Fixing Test Infrastructure Race Conditions

The deeper I tested, the more subtle issues emerged. The asynchronous turn-processing code in the test framework was causing race conditions: some tests were checking results before a turn had actually finished.

I fixed this by adding proper sequencing and awaits. Each turn now fully completes before validation begins, eliminating false failures and making the test results rock-solid.


Floating-Point Precision in Max Population Calculations

Another precision issue appeared in maximum population calculations. Planets calculate their population limits from habitability scores. Godot used single-precision floats, while Zig used double-precision. Over long playthroughs, these tiny discrepancies added up.

I standardized both engines to use double-precision floating-point for all population math. Now, population caps match exactly — no more subtle drift.


Debugging Star Rendering

The day ended with a star rendering issue. After running turns through the Zig simulation, a few stars failed to render correctly on the galaxy map. The investigation pointed to a coordinate transformation problem in the rendering pipeline. This one’s still in progress, but it’s the next problem on my list.


Technical Breakdown

  • Ten commits completed

  • Three hundred twenty-seven lines added for the serialization parity framework

  • Eighty-one lines changed for population growth fixes across four files

  • Six functions updated to standardize floating-point precision

  • Fifty-three lines added to improve asynchronous test sequencing

The parity suite now runs fifteen validation scenarios, covering empire resources, fleet positions, planet populations, star system states, and cross-turn consistency. Every test executes both engines side by side and compares outputs field by field.


Current Status

The Zig backend is now fully running in production with comprehensive parity validation.
✔ Population growth matches exactly
✔ Serialization round-trips are verified
✔ Fleet movement is smooth and accurate
⚙️ Star rendering issue still under investigation

This validation work might not be flashy, but it’s essential. The dual-engine architecture only works if both engines produce identical results. These parity tests prove that the fifty-two-times performance gain isn’t coming at the cost of correctness.


Next Steps

  • Finish debugging the star rendering issue

  • Add new parity tests for edge cases like empire bankruptcy and fleet combat

  • Begin profiling the Zig simulation to find remaining performance bottlenecks

  • Explore new simulation features that take advantage of the speed boost


Thanks for following along with the development of Stellar Throne.

You can find more updates and technical breakdowns at MrPhilGames.com.

🚀 Stellar Throne Devlog #7: The Zig Simulation Goes Live

Hey everyone, Mister Phil here with another devlog update!

Today marks a major milestone for Stellar Throne, my sci-fi 4X strategy game. For those just joining: Stellar Throne runs on a dual-engine architecture, using Godot for the UI client and Zig for the high-performance simulation backend.

Yesterday’s work was all about one huge goal — finally enabling the Zig simulation backend in production and fixing the critical serialization bugs that had been blocking deployment for days.


🧩 The Context: A Fully Functional Engine That Couldn’t Run

At the start of the day, I had a complete Zig simulation engine — all tests passing, a 52× performance improvement validated, and everything ready to go.

Except… I couldn’t actually use it.

The bridge between Godot and Zig — serialization — was broken. The game state couldn’t properly flow between the two, leaving the new engine stranded behind a wall of data mismatches.


⚙️ Fixing the Turn Processing Hang

The first issue was a classic async race condition. The game was using await to wait for a “turn completed” signal, but the listener was being connected after the signal was emitted.

Result: the code waited forever for an event that had already happened.

Fix: I stored the signal reference before calling the turn coordination function. That ensured the listener was ready before any signals could fire. I also added yield calls in the turn sequencer to give the engine a frame to process signals between phases.

✅ Result: Turn processing is now stable and reliable.


🔄 The Real Challenge: Serialization Across Engines

The real battle was with serialization mismatches.

Godot properties used snake_case (star_system_idfleet_name), while Zig structs used camelCase (starSystemIdfleetName). Every deserialization failed because of these naming conflicts.

I added manual field mapping in Zig — tedious, error-prone, and partial. Empires loaded correctly, but fleets, planets, and star systems still broke.

The breakthrough.
I implemented bidirectional camelCase serialization across all four major data structures:

  • SimEmpire

  • SimFleet

  • SimPlanet

  • SimStarSystem

Each gained custom toJSON() and fromJSON() methods that output camelCase and accept both camelCase and snake_case.

Struct Lines Added
SimEmpire +210
SimFleet +274
SimPlanet +44
SimStarSystem +131

All test fixtures were also updated to camelCase, covering five scenarios:

  • Edge Case Deficit

  • Edge Case Empty Empire

  • Fleet Upkeep

  • Single Turn Minimal

  • Ten Turn Economy


🧠 Type System Fixes & Data Integrity

Once serialization worked, I enabled the Zig backend in production. Immediately, Godot’s type system rebelled.

Files like Building.gdEmpire.gd, and Fleet.gd had overly strict type annotations that didn’t match the Zig output. I went through each file and refined type declarations to handle the new data correctly.

Then came the fleet disappearance bug — fleets vanished after turn processing. Turns out:

  1. The location field was structured incorrectly.

  2. fleet_name sometimes wasn’t serialized at all.

Both fixed.

And finally, empire colors were lost during serialization. The culprit?
Godot’s Color type can appear as RGB, RGBA, hex, or even name strings. Zig wasn’t handling that variety.

Solution: standardize on RGBA objects — {r, g, b, a} — for full round-trip fidelity between engines.


📈 The Numbers

Category Changes
Zig serialization +659 lines
GDScript type fixes 6 files, +41 lines
Fleet & color bug fixes +89 lines across 5 files
Test framework +150 lines new assertions
Test fixtures +59 lines camelCase JSON

8 commits in total.
The Zig backend is now fully active in production.
Game state round-trips cleanly. Colors, fleets, and names all persist. Integration tests are green across the board.


⚡ The Results

The game now runs 52× faster with the Zig simulation engine enabled by default.
Large galaxies that once took seconds now resolve in milliseconds. The performance leap is live — and dramatic.


🧭 Next Steps

  • Monitor gameplay for any hidden serialization edge cases

  • Expand integration test coverage

  • Begin profiling the Zig engine for further optimization


That’s the devlog update!
Thanks for following along as Stellar Throne crosses another huge milestone.

👉 Visit mrphilgames.com for more updates, devlogs, and behind-the-scenes deep dives.

Stellar Throne Devlog #6 — Zig Parity and Production Deployment

Hey everyone, MrPhil here with another devlog update!

Today marks a critical production deployment milestone for Stellar Throne, my 4X strategy game set in a post-empire galaxy. For those new here, I’m building Stellar Throne with a dual-engine architecture — using Godot for the UI client and Zig for the high-performance simulation backend.

This week’s focus: validating that the Zig simulation engine isn’t just fast — but production-ready.


⚙️ Context: The Zig Engine Hits 52.7× Performance

After two weeks of development, the Zig simulation engine is complete — delivering a verified 52.7× speedup over the original Godot implementation. Every one of the 1,069 tests passes cleanly.

The question was no longer “Does it work?” but “Can I ship it?” And even more importantly: “Will it stay stable under real-world stress?”


🧪 Parity Testing and Stress Scenarios

To answer that, I built a comprehensive parity testing system — a harness that runs the exact same turn through both the Godot and Zig engines, then compares every field in the game state to ensure they produce identical results.

Test scenarios ranged from:

  • 10 empires with 10 systems each

  • Up to 100 empires with 250 star systems

That’s when things got interesting — and the bugs started surfacing.


🐛 Three Critical Bugs (and How I Fixed Them)

1. Negative Resource Spirals

Zig’s resource validation could create negative feedback loops if a colony’s food production went below zero — spiraling further negative each turn instead of clamping to zero.
Fix: Added strict validation to prevent negative resource values during calculations.

2. Fleet Upkeep Mismatch

The Godot version used a flat-rate upkeep (10 Energy + 5 Minerals per fleet), but Zig calculated per-ship upkeep — leading to wildly inconsistent costs.
Fix: Standardized both engines to use the flat-rate formula for base upkeep while keeping per-ship maintenance for hull and weapon costs.

3. Maintenance Persistence Loss

Maintenance costs were being overwritten during stat recalculation, resetting values to zero.
Fix: Preserved maintenance data during the recalculation phase.

After fixing these, I expanded the stress tests — pushing edge cases like 200+ fleets per empire, negative production chains, and zero-population planets. That led to 11 parity fixes across both engines — covering resources, fleets, population growth, and infrastructure costs.


🚀 Deployment Roadblocks and Race Conditions

With parity achieved, I turned to production deployment — enabling the Zig backend automatically during game initialization.

Two blockers emerged:

  1. Initialization Timing
    The Zig backend tried to serialize game state before empires existed — crashing when the empires array was still empty.
    Fix: Corrected property references (galaxy.galaxy_width → galaxy.width) and restructured initialization order.

  2. Async Signal Race Condition
    The system awaited a signal after it was emitted — a classic async race.
    Fix: Stored the signal reference before starting the turn, ensuring the listener was connected in time.

To prevent future issues, I implemented:

  • Helper functions in GameScreenCore and GameInitializer

  • Debug logging in TurnProcessor

  • 343-line deployment guide detailing rollback, monitoring, and troubleshooting procedures


🧩 Deployment Decision: Disabled by Default

After reviewing everything, I made the cautious call to keep the Zig backend disabled by default for now.
The timing between empire creation and backend initialization is delicate — and I’d rather avoid subtle production bugs.

That said, the system is fully integrated. Manual enablement is available for testers. The performance boost is real, and parity is 100% confirmed.


📈 Final Stats

  • 20 commits in one day

  • 4,243 new lines of parity infrastructure across 13 files

  • 343-line deployment guide completed

  • 11 parity bugs fixed

  • All 1,069 tests passing

  • 52.7× performance improvement validated

The Zig simulation backend is production-ready, complete with fallback to Godot if anything fails.


🧭 Next Steps

  1. Verify signal-timing fixes in live game sessions

  2. Test deployment in extended campaign scenarios

  3. Decide whether to enable Zig automatically or keep manual control

Either way, the hard part’s done — the Zig engine is live-ready and just one config flag away.


Thanks for following along — and as always, you can find more at MrPhilGames.com.

See you in the next devlog!

Stellar Throne Devlog #5 — All 21 Zig Simulation Phases Implemented + Parity Tester Online (52.7× Faster)

Hey everyone — MrPhil here with a major development update for Stellar Throne, my sci-fi 4X strategy game.

This week I finished implementing all 21 simulation phases in Zig and brought the parity testing framework online to compare Zig vs. Godot turn-by-turn. The Zig engine is feature-complete at the phase level, but there are still parity differences to resolve before it becomes the default runtime.


🧠 Dual-Engine Architecture (Why Two Engines?)

  • Godot handles the UI, visualization, and input.

  • Zig runs the high-performance simulation of empires, colonies, economies, and galactic systems.

The goal is to keep Godot flexible while Zig delivers raw simulation speed — both verified through automated parity testing.


⚙️ Track Two Wrap-Up: 21 Phases Implemented

At the start of the week, only a few phases were functional. By the end, all 21 simulation phases were implemented and validated at the unit level.

Highlights:

  • Population growth, colony development, infrastructure maintenance

  • Resource extraction and production chains

  • Fleet movement and logistics

  • Natural disasters and random events

  • Quest system and narrative progression

  • Diplomacy drift, relationship updates, and victory conditions

  • Military combat resolution and research progression

  • Final integration phase that ties everything together

Each phase includes validation tests to ensure predictable behavior.


🧩 Real Data via TOML (No More Magic Numbers)

I ported the TOML configuration system so the simulation now loads real game data:

  • load_gameplay() — balance parameters and constants

  • load_resources() — economy definitions and production chains

  • load_content() — events, quests, and narrative triggers

This added 800 + lines of Zig, replacing thousands of hard-coded values.
On the Godot side, the Research Manager migrated from JSON to TOML for consistency.


🧪 Test Suite Rehab + Fixes

The Zig test suite had bit-rotted (wrong layout, 91 compile errors).
I:

  • Reorganized tests under src/tests

  • Wrote a test health monitor script

  • Fixed 11 failing tests (RNG distribution, TOML table arrays, event weighting, colony growth, diplomacy drift)

✅ All Zig tests now pass.


🌉 Godot ↔ Zig Bridge + Parity Harness

Built the Turn Simulation Service in Godot:

  1. Serialize state to JSON

  2. Run a turn in Zig

  3. Deserialize results back into Godot

Added:

  • Runtime engine toggle (Godot ↔ Zig A/B testing)

  • Parity testing framework that runs identical turns in both engines and compares every field across 10–100 empires and 10–250 systems


🔎 What Parity Testing Found (Work in Progress)

  • Godot and Zig diverge slightly on population growth and stability formulas.

  • Some mismatches come from floating-point drift and minor logic differences.

So, while Zig has feature parity at the phase level, behavioral parity is not yet complete.


⚡ Performance Snapshot

In large galaxy tests, the Zig simulation ran ≈ 52.7× faster than Godot.


📈 Commits at a Glance

Area Lines Added Notes
Zig Simulation ~5 000 Turn simulator, data structures, config loader
Tests ~2 000 9 new test files
Godot Integration 376 Turn Simulation Service
Debug Toggle 139 Runtime engine swap
Parity Framework 864 Cross-engine verification
Docs Extensive Phase summaries + testing guides

✅ Current Status

  • ✅ All 21 phases implemented in Zig

  • ✅ All Zig tests passing

  • ✅ TOML data loader integrated

  • ✅ Parity testing framework online

  • 🚧 Parity differences remain to be resolved

  • 🚧 Zig engine not default yet


🔭 Next Steps

  • Fix remaining parity gaps (population, stability, diplomacy)

  • Reduce floating-point variance

  • Once identical outputs are confirmed, switch to Zig as default for full performance gain


That’s this week’s devlog — thanks for reading!
Follow progress and upcoming milestones at mrphilgames.com.

— MrPhil

Stellar Throne DEVLOG #4 — The Great Migration: GDScript to Zig Complete 🚀


🧠 Win • Challenge • Next

  • Win: The Godot → Zig port reached a huge milestone — gdzig is running smoothly and the simulation core is nearly 100 % native.
  • Challenge: The TOML port was incomplete and needed several file rewrites before integration could succeed.
  • Next: Continue the full simulation port to Zig, building on the completed manager migration.

⚙️ 100 % Zig Migration Complete (23 / 23 Managers)

All 23 game managers are now fully ported from GDScript to Zig.

  • Final additions: ZigAIDirectorManager, ZigAIEmpireManager, and ZigQuestManager
  • 333 tests passing (256 Zig + 77 GUT) — zero compiler warnings, zero leaks

📊 TOML Configuration Migration

Gameplay configuration has moved entirely to TOML:

  • 22 TOML files now act as the single source of truth for balance, events, and systems
  • Legacy JSON assets archived with complete documentation

🚀 Simulation System Track 2

The Turn Simulator foundation is now live:

  • 23 phases planned — 4 implemented (resource generation, upkeep, colony growth, research)
  • Economic feedback loops and performance benchmarks already show 20–50× speed gain

Track 2 Progress: 21 % complete and climbing.


🧩 Testing, Docs & Performance

  • 333/333 tests passing (100 %) across Zig + GUT
  • Documentation reorganized (72 → 57 files)
  • Hybrid Zig/GDScript architecture yielding major speed ups

6 000 lines of Zig code, 2 300 lines of tests, 2 500 lines of new docs — 31 commits in 24 hours.


🪐 The Big Picture

Track 1 (Migration): ✅ Complete
Track 2 (Simulation): 🚧 Underway (21 %)

The simulation framework is ready to handle complex turn-based economics and AI strategy at empire scale.


CTA: Learn more at MrPhilGames.com

Ground Combat & Territory Borders Revamped

This week’s updates brought big strides in polish and clarity for Stellar Throne. Ground combat now displays strength bars above their respective forces, making planetary invasions easier to read. Ownership now correctly updates after successful invasions, and the final phase of an invasion plays out as intended.

On the combat side, the battlefield grid now renders fully, fleets start in centered positions, and you can left-click + drag to move the map. Support ships have distinct plus-shaped icons, colony ships use circles, and troop ships have been fully renamed for clarity. Fleets with no ships are automatically cleaned up after combat, reducing clutter.

Outside battle, draggable UI panels (shipyard, diplomacy, bombardment) were unified under a new DraggablePanel base class, making the interface smoother. Loading screens are more responsive, and territory borders now update dynamically after colonization. The border system itself has been revamped with shaders, opening the door for smoother, more striking visuals.

This update brings Stellar Throne one step closer to feeling like a finished 4X experience.

Stellar Throne Changelog Week of August 8–15, 2025

🌟 What’s New

This week brings major planetary warfare upgrades with the new Orbital Bombardment and Ground Invasion systems, a redesigned Ground Combat UI, smoother combat flow, and dozens of bug fixes and performance improvements. If you’ve been waiting to crush enemy worlds in style, now’s the time.


🆕 New Features

  • Orbital Bombardment System — Added comprehensive orbital bombardment mechanics with UI controls, allowing fleets to weaken planetary defenses before invasion.

  • Ground Invasion — Implemented ground invasion capability integrated with bombardment UI for a seamless planet conquest workflow.

  • Ground Combat UI Redesign — Enhanced battlefield visualization with army strength bars displayed above unit columns for better awareness.


⚔️ Combat Improvements

  • Damage Visualization — Incremental damage application synchronized with weapon effects for more engaging combat feedback.

  • Destroyed Ship Feedback — Clear visual indicators when ships are destroyed in combat.

  • Ship Detail Panel — Panel now closes automatically when combat begins.

  • Combat Flow — Smoother battle completion and input handling for consecutive battles.

  • Auto-Resolve — Fixed to jump directly to the final combat state.


🐞 Bug Fixes

  • Fixed multiple combat UI issues including scroll isolation, input handling, and grid cell highlighting.

  • Resolved ship targeting bug where destroyed ships could regenerate.

  • Fixed timing issues so destruction animations play at proper turn end.

  • Corrected ships having zero energy capacity in combat.

  • Fixed double damage and combat synchronization issues.

  • Resolved input handling problems when switching between combat and starmap views.

  • Fixed ground combat final screen to properly display complete phase results.


🎨 UI/UX Improvements

  • Enhanced scroll wheel isolation between UI panels and galaxy view.

  • Improved memory management in combat UI systems.

  • Better defender priority handling for grid cell movement conflicts.

  • Cleaner, modular architecture for combat UI components.


🛠 Technical Updates

  • Refactored project documentation into a modular structure for better maintainability.

  • Resolved SpatialGrid naming conflicts in CombatManager.

  • Reduced test suite orphan count and fixed various test failures.

  • Removed debug print statements and cleaned up temporary test files.


🚀 Performance & Stability

  • Improved combat animation system performance.

  • Enhanced memory management across UI systems.

  • Better error handling for bombardment operations involving Building resources.


When the Bug Breaks Before You Do

A couple days ago was… maddening.

I’d been wrestling with the same bug for days—the kind that sits in your code like a smug little goblin, daring you to come find it. Every time I thought I had it cornered—poof—it would slip away into another layer of logic.

The problem sounded simple enough: combat is simultaneous. Even if a ship gets destroyed, it should still get to fire its weapon that turn. But the UI shouldn’t mark it as destroyed until after all attacks are resolved.

Except in my game, ships weren’t showing their dramatic “destroyed” flair until the start of the next turn. By then, the moment was gone. It was like telling a joke and waiting five minutes for the punchline to land.

I threw everything at it:

  • The “outside consultant” trick—pretending Claude was a hired expert swooping in to save the day.

  • The “you’re a zookeeper” trick. (Don’t ask.)

  • Breaking the workflow into phases.

  • Having Claude explain the code back to me.

  • Running the debugger subagent.

  • Asking it to think hard… harder… ultra-think.

  • Asking Claude to improve my prompt.

  • Diagramming the problem like a detective on a conspiracy board.

  • Adding a ton of debug logs.

  • Even pulling in ChatGPT to craft a better Claude prompt.

  • Describing the issue in painstaking detail—right down to which variables changed on which frame.

Nothing. Worked.

And this wasn’t even a crash bug—the game ran fine. It was just wrong. The kind of subtle pacing flaw that players might not notice consciously, but would feel in their bones. The kind that makes everything feel just slightly “off.”

By about hour six that day, I was leaning back in my chair, staring at my code, wondering if the bug was less about the project and more about me.

Then—somewhere between frustration and surrender—I tried one more approach. Nothing special about it. No perfect galaxy-brain prompt. Just another attempt in a long line of attempts. And this time… it worked. The ships died exactly when they were supposed to, the UI updated cleanly, and the combat flow finally felt right.

I’d love to tell you it was some brilliant debugging insight or the magic words that unlocked Claude’s genius. But it wasn’t. It was just the luck of the dice—one more roll, one more try, and this time it came up in my favor.