Waves of minions
Usage guide of minion.waves: the wave state machine, routes and teams, settings and defaults, minion types, recipes, hooks minion.spawn and minion.reward, events, service, save.
Waves of minions (minion.waves) #
AI units that spawn in waves, walk a route toward the other end, fight what they meet, die and pay a reward. One kit serves a MOBA, a tower defense, an RTS skirmish or a siege, with a different config. Readable offline: gripforge docs minion-waves, or the MCP tool gripforge_docs (topic minion-waves).
What it is for #
Every step is a named state and an event, and every rule is a config key. The kit holds no wallet and draws nothing itself: it sends units down routes, runs their state machine and announces rewards; health, damage and attacks come from the combat kits, the model comes from a slot of your game.
| Step | What happens | Rule (config) | Event or state |
|---|---|---|---|
| SpawnWave | each team sends the composition down every route | firstWave, interval, composition, maxActive, rowSize, spacing | minion.wave |
| FollowPath | walk the route toward the other end | type speed | state follow |
| SelectTarget | nearest enemy of the first wanted category within aggro | targetPriority, assistHeroes | |
| EnterCombat | chase the target into attack range | engage | state combat |
| Attack | one hit every cooldown | type damage, range, cooldown | minion.attack |
| LoseAggro | target dead, out of aggro x 1.3, or too far off the route | chaseDistance | state return |
| ReturnToPath | walk back to the route, or resume from the nearest point | returnToLane | return then follow |
| Die | death animation, then despawn | despawnDelay | state dead |
| GiveReward | gold to the last hit, experience split between enemy heroes nearby | rewards.* | minion.killed |
Every state change is minion.state { minion, from, to, target }; each minion carries the component minion.ai { state, target, type, route, aggro, range }.
Turn it on #
gripforge_gamekit_install { project_id: "gkp_…", id: "minion.waves" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "minion.waves",
config: { "interval": 45, "targetPriority": ["structure"] } }
It requires combat.attacks, combat.health, combat.damage and game.engine. The full reference is gripforge kit api minion.waves.
Routes and teams #
- A kit that provides
lanes.routes(moba.lanesdoes) gives the routes and the two teams. - Otherwise the collection
minion_routesholds{ id, points: [[x, z]…] }(ground points from the first team's end to the second's) andteamsnames the two teams. - A scene may carry its own routes in
data.minion_routes. Entering the scene starts its waves over (firstWaveafter the entry); leaving it removes its minions.
The first team walks a route forward, the second backward, so opposite waves leave together and meet midway.
Settings and defaults #
| Key | Default | Meaning |
|---|---|---|
teams | ["blue", "red"] | the two teams (ignored when a kit provides lanes.routes) |
firstWave, interval | 8, 30 | seconds before the first wave, between waves |
composition | melee x3, siege x1 every 3 waves, ranged x3 | front row first; { type, count, every? } |
maxActive | 80 | no wave is sent while that many minions are alive |
rowSize, spacing | 3, 2 | minions side by side; metres between minions |
scale | 1 | size factor over a 1.8 m standard character |
engage | true | false: they only walk (a tower defense) |
chaseDistance | 6 | metres from the route a chase may take a minion |
returnToLane | true | walk back to the route after a fight |
targetPriority | ["minion", "hero", "structure"] | most wanted first; a category left out is ignored (also monster, other) |
assistHeroes, assistRadius | true, 9 | turn on an enemy hero who hits an allied hero nearby |
avoidance | true | keep spacing x 0.7 apart instead of stacking |
formation | false | a wave walks as a group at the pace of its slowest minion |
pool | false | the look of a dead minion is handed to the next minion of the same model |
scaling | { mode: "time", every: 180, hp: 0.05, damage: 0.04, armor: 2 } | growth per step; mode is time (seconds) or wave |
rewards | { lastHitOnly: true, shareXp: true, shareRadius: 14, goldFactor: 1, xpFactor: 1 } | |
despawnDelay | 1.5 | seconds a dead minion stays |
entityKind, slotPrefix, tags | minion, minion_, [] | entity kind, model slot prefix, extra tags |
debug | false | draw routes, detection and attack ranges, the current target |
Types #
The collection minion_types holds { id, name?, hp, damage, armor?, range, cooldown, speed, aggro, scale?, slot?, windup?, projectileSpeed?, color?, reward?: { gold?, xp? } }. Built in: melee, ranged, siege, super. A range above 3 m is a ranged attack (a projectile is drawn); armor divides damage by (100 + armor) / 100. The model of a type is the slot slotPrefix + id with the team as variant (minion_melee:blue); the slot declares the animation clips it needs (idle, run, attack1, death).
Recipes #
- Minions ignore heroes and only attack towers:
targetPriority: ["structure"]. - A tower defense (creeps walk, never fight):
engage: false, with one empty team. - Bigger, slower waves:
composition: [{ "type": "melee", "count": 6 }, { "type": "siege", "count": 2 }],interval: 45. - Late-game pressure:
scaling: { "mode": "wave", "every": 2, "hp": 0.1, "damage": 0.08, "armor": 3 }. - A boss every fifth wave: a
minion_typesentrybruteand{ "type": "brute", "count": 1, "every": 5 }.
Hooks, mods, services #
| What | Name | Detail |
|---|---|---|
| Hook | minion.spawn | { wave, route, team, count, reinforced }, asked once per route and team of a wave. A tap changes count (fewer: the rear stays home; more: the composition is repeated from its head; 48 at most) or cancels (nobody leaves for that team on that route; the wave still counts) |
| Hook | minion.reward | { minion, type, team, route, by, gold, xp }, once per death, before minion.killed; a tap changes gold and xp, a cancel pays nothing (minion.killed is still sent with 0) |
| Registries | minion_types, minion_routes | a mod adds, patches or removes documents |
| Moddable config | all keys but teams, entityKind and tags | wave, chase, priority, scaling and reward keys |
| Events | minion.wave { wave, routes, count }, minion.state, minion.attack { source, target, ranged, synchronized, type }, minion.killed { minion, type, team, route, by, gold, xp, sharers } | an economy or match kit pays gold to by and splits xp between sharers |
| Listens | combat.killed, combat.damaged | |
| Service | minion.waves | spawnWave(), spawn(type, team, route, t?, side?), list(), get(id), front(route, team), count(team?), wave(), routes(), teams(), types(), priority(), setPriority(list), setReinforcements(route, team, entries) (super minions after an inhibitor), setDebug(on), debug() |
{ "id": "thin_red_waves", "hook": "minion.spawn", "match": { "team": "red" }, "add": { "count": -2 } }
Which enemy a minion may hit is a service another kit provides, combat.targeting { canTarget?(attacker, target), radius?(id), category?(id) } (a tower that cannot be hit yet, a neutral camp, a wide building). Without it the category is read from tags (minion, hero / player, structure / tower, monster). A kit that provides world.navigation { next(key, from, to) } lets minions bend around walls when a chase takes them off their route.
Variables: minion.wave, minion.count, minion.debug.
Save #
The wave count and clock, every minion (type, team, route, progress, state, target, cooldown), the reinforcements and the target priority. After a load the minions walk and fight on and the next wave leaves on time; a minion whose type, route or team is gone (a mod removed it) is dropped.
Engines #
Web and native player run the same rules (waves, rows, scaling, the state machine, rewards, hooks, save). Health, damage, targeting, attacks, navigation, water and ground are called from the modules that serve them.