Waves and spawning
Untouched. The mod never creates a zombie.
Reference Read in any order
The Emerald architecture, mapped onto this game, without the marketing.
This page is about the zombie AI mod. For the mystery box, see how the box thinks.
The stock horde walks at you in a straight line and swings on a timer. This mod replaces
that brain, per zombie, with a full port of the Emerald AI 2025 architecture
— same component layout, same modular ability objects, same combat action objects, same
events roster, same Type 1 / Type 2 weapon system — rebuilt from scratch on
this game’s own Zombie, NavMeshAgent, Animator,
DamageTrigger and Photon plumbing.
There is no teleportation of any kind in this mod
Not for stuck zombies, not for spawns, not for catch-up, not anywhere. Every zombie walks, climbs or crawls to where it is going through the game’s own movement systems, or it does not get there. Stuck recovery samples a flanking point on the navmesh and repaths — it is pure pathfinding, never a position write.
One hook
The entire replacement is a single Harmony prefix on Zombie.Update.
Each frame, for each zombie, it decides one thing: does the new brain drive this one, or
does the stock code run?
Nothing is injected into the zombies themselves. Each brain is a plain managed object keyed by instance id, living alongside the game rather than inside it — so the game’s pooling, networking and death handling never see anything unusual.
Untouched. The mod never creates a zombie.
Untouched. Kill credit flows through the game’s own damage path.
Untouched. Hits land through the stock DamageTrigger.
Untouched. Brains attach and detach around it.
Before and after
Finding you
Zombies sense you in every direction at any range, through walls. They smell you; they do not watch you.
Per-zombie target scoring with a detection radius, an optional field-of-view cone, budgeted line-of-sight rays, and a grace bubble so a zombie right behind a window frame does not forget you exist.
Losing you
Not really possible. Break contact and the horde keeps coming anyway.
Target memory. Break line of sight and the zombie heads for the “ghost” of your last known position for TargetMemorySeconds, then gives up.
Gunfire
Silent. Shooting attracts nothing.
Shots that land make noise. Idle zombies inside HearingGunshotRadius of a landing hit turn on the shooter. Firing into the sky stays silent.
Approaching you
A straight line, one behind another. A conga line.
Golden-angle crowd spreading, so packs arrive as an arc. Strafing orbits at mid range, weave offsets at long range, flank-and-repath when stuck.
Attacking
A swing on a timer, at whatever facing.
Two weapon types with six abilities between them, picked by Order, Random or Odds, gated by range, angle, height and cooldown.
Under fire
Walks politely into your iron sights.
Six combat actions: dodge, brace, strafe, random movement, switch target, and rush. Peeling for a teammate now has consequences.
Barricades
One plank per hit, on the stock cadence.
Configurable rip damage and interval — and when the boards are gone the stock code walks the zombie through the window, which is exactly why a teleport can never happen there.
Two movesets
Straight out of Emerald’s Type 1 / Type 2 weapon system. Every module of every ability — cooldown, condition, damage, crit, damage-over-time, knockback, slow, area-of-effect — is a live config entry with its own tab in the panel.
The close-in brawling kit.
Quick, using the game’s own attack animations and damage hitbox, cycling through the rig’s animation variants. Carries the crit module for extra damage and a camera kick.
Slower and rarer, hits harder, and carries the knockback module — the swing that actually moves you off your camping spot. The shove is a smooth collision-checked slide over about a quarter second, so you can be bounced into cover but never through it.
Lighter damage, because the point is the slow module: it drags your walk and run speed down for a moment. Getting tagged in a crowd suddenly matters.
The mobile pressure kit.
A genuine sprint burst along the ground toward where you are going to be — real movement on the navmesh with real collision, not a blink. If it arrives in reach it converts straight into a strike.
Emerald’s area-of-effect module done honestly: the howler buffs and retargets zombies that already exist inside its radius, and plays one of the zombie’s own aggressive voice clips so you hear it coming. Nothing is ever spawned, so wave counts, drops and co-op sync stay exactly as the game expects.
The zombie rears up and a glob flies with real travel time. If you are still near the aim point when it lands you take the hit plus a lingering burn, via the damage-over-time module. Strafing sideways genuinely dodges it.
How zombies switch between the two
WeaponSwitchMode offers three rules. None locks each
zombie to its starting type. Timed swaps stances every few seconds, with
each zombie rolling its own timer so the horde never swaps in unison.
Distance — the default, and the most natural-feeling — picks by
range: Frenzy when you are far, to close the gap, and Claws once it reaches you.
Emerald ActionsClass
Abilities are what a zombie does to you. Actions are what it does about you — running on Emerald’s ActionsClass loop, each with its own enable switch, odds and cooldown.
| Action | What it does |
|---|---|
| Dodge | When its target is aiming at it inside the detection radius, a zombie can juke sideways — a real sidestep along the ground — and takes reduced damage during the move. |
| Brace | Emerald’s Block action, translated for creatures with no shield: under aimed fire at close range a zombie hunkers into its advance, moving at 60% speed but shrugging off a share of every hit while it closes the last metres. |
| Strafe | At mid range a zombie sometimes circles you instead of approaching dead straight, arcing along the navmesh. Combined with crowd spread, this is what makes the horde envelop rather than queue. |
| Random movement | Distant zombies weave small offsets into their approach instead of tracking a laser-straight line, which makes long-range headshots earn their keep. |
| Switch target | A zombie being shot in the back by someone other than its current target can whirl on its attacker. |
| Rush | The custom action slot: far-away zombies periodically surge forward in short bursts, closing dead ground in pulses so the back of the horde stays threatening. |
Why hits feel consistent
The mod opens and closes the game’s damage hitbox itself, at configured fractions of
the swing — HitWindowStart and HitWindowEnd — rather
than relying on animation events. Each ability sets its own damage on the game’s
DamageTrigger immediately before the window opens.
The practical result: armour, downing, kill credit and co-op sync all behave exactly like a stock hit, just with per-ability numbers. And the gap between the two window fractions is literally your dodge window.
Not laziness
These are delegated on purpose, because the game already executes them perfectly and replacing them would risk breaking things for no gain.
OnDeath and steps aside.HandleZapStatus keeps the stock stun-and-tick running unmodified, so electric traps work exactly as designed.DriveSpecials. Off by default so scripted set-piece rounds play as the developers tuned them.The one boundary
Everyone in the lobby can keep the mod installed. It does nothing harmful on machines where it is not driving.
With OnlyDriveAsHost on — the default — only the host machine
runs the new brains. Zombies are host-authoritative in this game, so clients see the new
behaviour through completely normal sync. Movement, targeting, melee damage, downing, kill
credit, barricade health and climbing all flow through the game’s own networked
paths.
This is the same approach Emerald AI’s own multiplayer add-on takes: gate the brains to the server. Turning it off makes every machine fight over the zombies and causes rubber-banding.
Direct player-body effects — the Heavy Swipe shove, the Grave Grasp and spit slows, and the acid burn ticks — can only be applied by the machine that owns that player. That is an engine boundary, not a design choice.
Offline, that is everyone, and the full kit works with no restrictions. Online, it is
the host’s own character — or any client also running the mod, for their own
body. Damage itself is completely unaffected: it goes through the
game’s DamageTrigger and network path like any stock hit.
So board health never double-counts.
Every affected setting says so on its own row
The settings whose behaviour changes online carry that fact in their own description,
in the config file and on the panel row. Nothing about this boundary is hidden in a readme
you have to go find. And EnableInOnline set to off makes the mod stand down
entirely in online rooms.
Degrading gracefully
If the mod’s own code throws more than ErrorsBeforeSelfDisable
distinct errors, it returns the zombies to the stock AI for the rest of the level
and says so in the console and the overlay. You should never see this trip —
it exists so a surprise game patch degrades gracefully instead of ruining a
match.
This game’s build shipped with most of IMGUI’s convenience layer stripped out, so the panel is drawn entirely on the surviving primitives, with its own text input and scrolling. Same look, same behaviour, no dependency on anything the game removed.
For people who know Emerald
| Emerald AI 2025 | This mod |
|---|---|
EmeraldSystem (root) | One plain managed brain per zombie, keyed by instance id, dispatching components in Emerald’s order every driven frame |
| Detection Component | Scoring, FOV, line of sight, memory, hearing, ignore list, override target |
| Movement Component | Gaits, throttled pathing, spread / weave / strafe, flank recovery, precision facing |
| Combat Component | Engagement events, weapon types, swing machinery, barricades, spits in flight |
| Behaviors + Combat Action Objects | An action catalogue with an ActionsClass per brain per action |
| Ability Objects and their modules | An ability catalogue where every module parameter is a live config entry |
| Animation Component | Cached writes to the game’s own animator parameters |
| Sounds Component | Clips played from the zombie’s own banks through the game’s PlaySound |
| Health / modifiers | Buff, temp-speed and action-speed channels, plus dodge and brace mitigation |
| Emerald Events Component | UngodlyEvents — a static hub carrying the full roster |
EmeraldAPI | EventsManager — a public static control API keyed by Zombie |
| Optimization settings | The [Performance] section — shared per-frame think and raycast budgets, three distance-tier think rates, destination throttling, far-polish skip |
Optional
Reference UngodlyEmeraldZombieAI.dll from your own BepInEx plugin and you get the full
Emerald events roster, keyed by the game’s own Zombie. Events always
fire — the [Events] config section only controls console printing
— and a subscriber that throws can never take the AI down.
using UngodlyEmeraldZombieAI.Events;
UngodlyEvents.OnKilledTarget += (zombie, victim) =>
myLog.LogInfo("Zombie " + zombie.GetZombieId() + " downed a player!");
UngodlyEvents.OnWaveStart += wave => { if (wave == 10) SpawnMyBossEncounter(); };
The control API is a set of safe calls that return false or null
when a given zombie has no brain, so you never have to guard them:
EventsManager.SetCombatTarget(zombie, playerData); // point it at someone
EventsManager.OverrideCombatTarget(zombie, pd); // lock on, scoring suspended
EventsManager.ClearTargetOverride(zombie);
EventsManager.SetIgnoredPlayer(zombie, pd, true); // detection skips this player
EventsManager.StopMovement(zombie); // freeze pathing in place
EventsManager.ResumeMovement(zombie);
EventsManager.SetDestinationPosition(zombie, pos); // send it somewhere
EventsManager.KillAI(zombie); // via the game's networked damage
bool driven = EventsManager.IsDriven(zombie);
The full roster covers lifecycle, detection, combat, damage, movement, abilities and match
events. README.md inside the mod archive lists every signature.
Built for 150+ zombies
Brains do a tiny per-frame update. The expensive decision pass is scheduled by distance tier and rationed by a shared budget, so a huge endless wave costs a predictable amount rather than scaling linearly into a stutter.
MaxBrainThinksPerFrame caps expensive decision passes per frame; MaxRaycastsPerFrame is one pool every line-of-sight check in the system draws from. Anything that misses its budget simply retries next frame.The debug overlay shows both budget pools live, so if you are tuning for framerate you can watch the effect rather than guess at it.