Skip to content
Northforge Interactive
← Back to journal
PULSE//ZERO

Devlog #008 — Grid Anomalies: When the Arena Turns on You

The Living Grid is the stage every fight happens on. Grid Anomalies are the moments that stage stops being scenery and becomes the threat — timed, arena-wide events that bend the rules of the floor for thirty to sixty seconds and then let go. Here is how they work, and why we built the room to attack you.

Back in Devlog #006 we made the case that the Living Grid isn’t a backdrop — it’s the stage the whole game is performed on. This entry is about the moments that stage stops behaving. Grid Anomalies are timed, arena-wide events that temporarily rewrite the rules of the floor: enemies move differently, the lights go out, the geometry bends, the ground itself tears open. For thirty to sixty seconds the room is the antagonist. Then it lets go, and the fight goes back to being about the enemies — until the next one.

A gravity well tears open on the grid, dragging the magenta lines and everything nearby toward a dark core

For a few seconds the room is the antagonist — space bends and your shots no longer go where you point them.

Why make the room a threat

A twin-stick arena can get into a rhythm fast. Once you’ve internalised how the enemies read (Devlog #002) and how the Adaptive Director paces them, a skilled run can settle into a groove. Grooves are comfortable, and comfortable is the enemy of a good arcade run.

Anomalies exist to break the routine. They don’t add a bigger health bar or a nastier enemy — they change the conditions you’re already fighting under, so the muscle memory you just built stops being reliable for a little while. The design rules we set ourselves were strict: no health-sponges, everything temporary, everything dramatic in and out, and nothing that rewards endurance over skill. An anomaly should make you adapt, not just wait it out.

What’s shipped

There are five anomalies in the build, each one a different kind of pressure:

  • Grid Storm — the grid scrolls beneath you. No combat change at all; it’s pure visual unease, a floor that won’t hold still while you’re trying to read it.
  • Overcharge — enemies move about 35% faster and the grid blazes brighter, but every kill is worth an extra multiplier. It’s the greedy one: more danger, more reward, your call on how hard to push.
  • Blackout — the grid dims to roughly 12% brightness. Only the neon objects — your ship, the enemies, the shots — still read clearly. The arena goes dark and you fight by the glow of the things trying to kill you.
  • Gravity Pulse — enemy and projectile paths bend along an invisible curl. Your shots no longer go where you point them, so you have to lead differently and re-learn your spacing on the fly.
  • Fracture — four lethal rifts tear open across the arena. Each one telegraphs before it goes hot; touching an active rift costs a life. Suddenly part of the floor is off-limits and you’re fighting in the space that’s left.

Each one telegraphs its arrival, runs for a randomised window, and then tweens back out over a second or so — the grid settles, the rifts close, the lights come back. Nothing snaps; the drama is in the transition as much as the effect.

How it actually works

The system is split the same way the rest of PULSE//ZERO is: a pure-logic brain and a scene-side bridge that touches the world. The brain is AnomalyManager, an autoload that owns no scene nodes at all. It selects anomalies, times them, ends them, and exposes a live “modifier surface” that gameplay systems read every frame. Enemies ask it for their speed multiplier; the combo system asks it for the bonus per kill; both enemies and projectiles run their velocity through its gravity curl. Because the manager is just state and logic, the same code runs in any scene — which is exactly what lets future bosses suspend anomalies without touching this file.

Crucially, anomalies are data, not code. Each one is a .tres resource that sets a handful of plain tuning params — duration range, earliest run-time it can appear, selection weight, and the effect knobs like enemy_speed_mult, gravity_strength, grid_fade, or fracture_count. The manager applies effects by which params are set, never by branching on the anomaly’s name. Recombine the params and you’ve got a new anomaly with no new code.

Left to its own devices, the manager self-schedules: a quiet gap, then a weighted roll over everything currently eligible, with no immediate repeats. And that gap isn’t fixed — it shrinks as the run goes on, so anomalies arrive more often the longer you survive:

func _current_gap() -> float:
    var t := clampf(_run_time / GAP_RAMP, 0.0, 1.0)
    return lerpf(GAP_EARLY, GAP_LATE, t)   # 34s early → 15s deep into a run

But it doesn’t have to drive itself. Flip external_control on and the Adaptive Director takes the wheel — the manager stops self-scheduling and waits for a request_anomaly() call, still timing the active anomaly to its natural end. That’s deliberate. The Director already reads the run and decides when to apply pressure; letting it own anomaly timing too means the room turning on you can be part of the same authored-feeling pacing curve rather than a separate metronome ticking in the background.

The lifecycle

Every anomaly runs the same arc. When one triggers, it fires a one-time “NEW GRID ANOMALY” intro card the first time you ever meet it — a brief slowdown and a fade-in card so the moment lands — then broadcasts anomaly_started. The scene-side EncounterDirector picks that up and drives the visible side: the Living Grid tweens into its new look, the music floor lifts, and any Fracture zones open. The modifiers stay live for the duration, then anomaly_ended settles everything back. And anomalies never overlap with mini-bosses — when a boss spawns, the active anomaly ends and scheduling suspends, because a boss owns the spotlight on its own.

Next time

That’s the room turning on you. Five anomalies, a brain that’s pure logic, and a clean handoff so the Adaptive Director can fold them into its pacing. Next we step away from the arena floor entirely and look at what you take between runs: progression and cosmetic unlocks — the systems that decide what carries forward when a run ends. That’s Devlog #009.

As always: we build in the open.