creeps
The monsters. This module spawns them at the gates, walks them down their lane, stops them to swing at whoever is standing in the way, kills them for bounty, and leaks them into the nexus at the far end.
game.creeps — everything that marches
The monsters. This module spawns them at the gates, walks them down their lane, stops them to swing at whoever is standing in the way, kills them for bounty, and leaks them into the nexus at the far end.
local Creeps = require("game.creeps")
What a creep is
A creep is an entity hierarchy: a bare root that carries the march, a handful of
Model parts hung off it that give the tier its silhouette, and a floating
health bar. The root's scale is the tier's scale from Rules.K.creeps, so a
Magma Colossus is the same blueprint blown up. Every part is temporary, so no
creep is ever written into the saved scene.
The four tiers read apart at a glance, by size, shape and colour:
| Tier | Silhouette |
|---|---|
emberling | knee-high glowing blob on two stubs, bright orange |
hound | long, low, four-legged, spined, crimson |
brute | broad hunched biped, dark, molten chest and flank seams |
colossus | towering black figure, crowned, with a furnace for a heart |
The wave
On waveTick, every lane's sendQueue is drained into the foe's lane — that
is the whole game — and Rules.K.match.freeCreepsPerWave baseline creeps are
added to each lane so a lane nobody is buying into still has something walking
down it. The baseline tier escalates with the wave number, slowly enough that a
defended lane is never drowned in creeps it did not earn.
The whole batch is released from the gate one creep at a time, so a wave streams out instead of appearing in a lump.
March, fight, die, leak
A creep walks from its gate toward its nexus at def.speed, weaving gently
around its own line. A defender standing in front of it within reach stops it,
and it swings on its tier's cadence. Every player south of the lava rift defends
ember and every player north of it defends ash — one player, two, or none, no
lane-ownership lookup needed.
Killed, it pays Rules.grant(byLane, def.bounty) and sinks. Leaked, it calls
Rules.leak(lane, def.leak) and the nexus takes the lives.
Emits creepSpawned, creepKilled, creepLeaked on the rules bus.
The contract the hero calls
These four are fixed. by is the attacking lane ("ember" for the local
player) — a hero only hits the creeps marching down its own lane, which is also
the lane whose bounty it collects.
Creeps.damageInArc(origin, forward, range, arcDegrees, amount, by) -> hitCount
Creeps.damageInRadius(origin, radius, amount, by) -> hitCount
Creeps.nearest(position, lane) -> creep?
Creeps.countIn(lane) -> number
origin / forward / position take a {x, y, z} record, a three-element
array, or an entity position proxy — all three read the same.
Reading the live set
Creeps.live is every live creep, newest last. Each entry carries at least
{ id, def, lane, entity, health, maxHealth }; lane is the lane it is
marching down, so "ember" means it threatens the ember nexus.
Creeps.roster() -- Rules.K.creeps, in cost order
Creeps.spawnNow(lane, creepId, at?) -- one creep now, optionally partway down
How creeps hurt the hero
A creep hands damage to game.hero through the first of applyDamage,
damage, takeDamage or hurt that the hero slice exposes, and falls back to
the health / alive fields the spec pins if none of them is there.
Scoped to this part · feeds back into the world's score.