Developing Eldertal's Electricity System: Circuit Simulator Prototype

So, welcome back to Inside RedKnack! Today we want to talk about something we've been working on that turned into a way bigger project than expected - a circuit simulator prototype for Eldertal's upcoming electricity system. Grab a coffee, because this is going to be a ride.

Initial Design

So the idea started simple enough: build a visual electronics simulator where players can wire up components and watch signals flow through circuits. You know, resistors, LEDs, logic gates - the whole deal. This would serve as the foundation for Eldertal's electricity mechanics, letting players build functional circuits in-game. Seemed straightforward for a team with a few years (idk, 15-18 or so?) of experience under our belts. Components go on a canvas, you draw wires between them, signals propagate, done. Right?

Wrong. So. incredibly wrong.

Signal System Architecture

The original version used boolean signals - everything was either HIGH or LOW, 1 or 0. Classic digital logic. And it worked! For about five minutes. Then we realized - wait, what if we want a resistor that can actually resist? What if signals should have variable strength? For Eldertal's gameplay, we needed that granularity.

So we made the call: convert everything from bools to floats. Signals would now be 0.0 to 1.0, giving us that beautiful analog behavior. Sounds simple? Just change the types, recompile, ship it?

Haha. No.

The conversion touched literally everything. Every single component had to be rewritten. Logic gates needed new math - AND gates went from boolean AND to min operations, OR gates to max operations. The NOT gate became 1.0 minus input. Twenty-one components, all needing their logic completely reimplemented.

But here's the fun part - GDScript(bless its heart thousand times) has some quirky inheritance behavior. We kept hitting "function not found in base self" errors. Helper functions we defined in the base Component class? Invisible to derived classes. Why? Who knows! It's one of those delightful engine quirks you learn to work around.

Solution? Inline everything. Yeah, it's code duplication, but when the alternative is fighting the engine's inheritance system, sometimes you just embrace the repetition and move on. "We can fix that later!"

Script Cache Issues

Oh boy, this one was special. After converting to floats, we started getting "Invalid operands 'bool' and 'float' in operator multiply" errors. But we'd changed everything to floats! What was going on?

Turns out, there were three different Component base classes floating around in the project. THREE. One was the new float-based version. One was a duplicate. And one was the old bool-based system that somehow still existed. Godot's script cache was randomly picking which one to use, giving us this beautiful non-deterministic behavior where sometimes components would work and sometimes they'd explode. Maybe it was incompetence, who knows?

The fix? Delete the old files, add runtime type checking everywhere, and never trust your cached scripts again. Every input now gets checked with typeof and converted if necessary. Does it add overhead? Sure. Does it prevent hours of debugging mysterious crashes? Absolutely worth it. Don't forget: "We can fix that later!"

Propagation System

Early versions had a delightful issue where complex circuits would immediately stack overflow. Why? Recursive signal propagation. Component A updates, triggers Component B, which triggers Component C, which somehow triggers Component A again, and suddenly you're 10,000 calls deep and Godot is having a bad time.

The solution was implementing a queue-based propagation system. Instead of immediate recursion, components mark themselves as "needs update" and get added to a processing queue. The queue processes everything in batches, preventing infinite loops and keeping the stack happy. This will be crucial for Eldertal when players start building massive power grids.

Parallel Wire Behavior

Here's a fun design question: what happens when two wires connect to the same input pin? Say you have two resistors at 50 percent each, both feeding into a probe. What should the probe read?

Our first implementation used MAX logic - it would just take the strongest signal. So MAX of 0.5 and 0.5 equals 0.5. Technically defensible from a "highest voltage wins" perspective, but incredibly unintuitive.

After somefeedback (thanks for catching that one!), we switched to additive logic. Now those signals sum up - 0.5 plus 0.5 equals 1.0, clamped at maximum. Way more intuitive, way more useful for building interesting circuits. This is the kind of decision where player experience trumps technical pedantry.

Visual Design

The original version had everything as colored rectangles. Functional? Yes. Inspiring? Not so much. So we went through and redesigned the visual representations.

Resistors now have proper color bands like real resistors. Transistors show the classic NPN symbol with a circular body. Logic gates got their standard shapes - AND gates with the curved edge, OR gates with their swoopy sides, NOT gates with the inversion bubble. The LED has a proper dome shape with glow effects.

Blog Image

But the star of the show? The oscilloscope probe. This thing shows a real-time waveform graph with a CRT-style green display and grid lines. Hook up a clock to it and you see an actual square wave scrolling across the screen. It's the kind of feature that transforms the whole experience from "functional simulator" to "oh this is actually cool." and HECK it IS!

Usability Refinements

Some fixes were tiny but made huge differences. The counter component? We couldn't figure out which pin was which without memorizing the layout. Solution: add labels. CLK, RST, UP, DN on the inputs, Q0 through Q3 on the outputs. Obvious in hindsight, crucial for usability.

The junction component was so small we could barely wire it up. Triple the size, problem solved. The RGB LED button didn't work because it was sending "RGB-LED" but the registry expected "RGB_LED". One character, completely broken feature.

These are the things you don't anticipate in design docs.

Technical Takeaways

If you're building something similar, here's what we'd tell you:

First, don't trust inheritance in game engines. It works until it doesn't, and debugging invisible functions is maddening. When in doubt, inline your code and save yourself the headache.

Second, always implement type checking at runtime when you're doing major refactors. Yes, the type system should catch everything. No, it won't. Build your safety nets.

Third, queue-based systems beat recursion every time in game dev. Recursion is elegant in theory, but when you're dealing with player-created content that might form cycles, you need that queue. (Trust us... you NEED it)


Eldertal Integration

The simulator is in great shape now as a standalone prototype. Twenty-one fully functional components, additive signal logic, oscilloscope displays, proper symbols - it's genuinely fun to play with. More importantly, it proves that the core mechanics work for Eldertal's electricity system.

We're already thinking about how to adapt this for the main game. Players will be able to build power grids, wire up automated systems, and create logic circuits to control machinery. The same principles apply - just integrated into Eldertal's survival gameplay loop.