# Wave defense in your game (defense.waves) ## What it is for Enemies march along authored paths toward an exit, in scripted waves. A wave lists groups (which enemy, how many, how far apart, on which path). An enemy that gets through costs lives; no lives left is a defeat, every wave beaten is a victory. Waves come on a countdown and calling the next one early pays money. Enemies are `combat.core` entities, so any kit that deals damage kills them and slowing statuses slow them. Towers and money come from other kits (for example `defense.towers`, `economy.money`). ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "defense.waves" } gripforge_gamekit_configure { project_id: "gkp_…", id: "defense.waves", config: { "lives": 20, "prepTime": 20, "interval": 12 } } ``` Needs `combat.core`, `game.engine`; installing the kit brings them along. ## First example One road, one enemy type, and a first wave of three runners (config `{ "lives": 6, "prepTime": 1, "interval": 2 }`). The wave starts after `prepTime` seconds, a runner appears every 0.5 s, each one that reaches the end of the road costs one life, and the 15 money of the wave is paid when its last enemy is gone. ```json { "defense_paths": [{ "id": "main", "points": [[-10, 0], [0, 0], [0, 8]] }], "defense_enemies": [{ "id": "runner", "name": "Runner", "hp": 20, "speed": 5, "reward": 4 }], "defense_waves": [{ "id": "w1", "reward": 15, "groups": [{ "enemy": "runner", "count": 3, "every": 0.5 }] }] } ``` ## Settings and defaults | Setting | Default | Meaning | |---|---|---| | `lives` | 20 | Lives at the start. | | `prepTime` | 20 | Seconds before the first wave. | | `interval` | 12 | Seconds between the end of a wave's spawning and the next wave. | | `autoStart` | `true` | Waves come on their own when the countdown ends; off: only when called. | | `earlyBonus` | 2 | Money per second of countdown skipped when the next wave is called early. | | `flyHeight` | 4 | Height of flyers above the ground (m). | | `team` | `enemy` | `combat.core` team of the enemies. | | `despawnDelay` | 0.6 | Seconds a killed enemy stays (its death). | | `drawPaths` | `true` | Lay a flat road along every ground path. | | `pathWidth` | 3 | Width of that road (m). | | `finish` | `true` | Without `game.endings`, the kit ends the game itself on an end screen; with it, endings read `defense.result`. | | `winTitle`, `winText`, `loseTitle`, `loseText` | Victory, Every wave was held back., Defeat, Too many enemies got through. | Texts of the end screen. | | `hidePlayer` | `false` | A game without an avatar: the player entity is hidden and stands at the centre of the paths, as the camera anchor. | Three collections hold the content (all optional fields are in the manifest, `gripforge kit api defense.waves` lists them): | Collection | Fields | |---|---| | `defense_paths` | `id`, `points` ([x, z], 2 to 64), `air` | | `defense_enemies` | `id`, `name`, `hp`, `speed`, `armor`, `reward`, `lives`, `flying`, `scale`, `color` | | `defense_waves` | `id`, `name`, `groups` (`enemy`, `count`, `every`, `delay`, `path`), `hp`, `reward`, `next` | Armor divides every hit by (100 + armor) / 100. A flyer takes an `air` path, else it goes straight from the entry to the exit. A group without `path` takes the paths in turn. The enemy models are bound to the asset slot `defense_enemy` (variant = enemy id). ## Services, events, variables - Service `defense.waves`: `state()`, `wave()`, `waves()`, `lives()`, `maxLives()`, `countdown()`, `bonus()`, `callNext()`, `preview(wave?)`, `enemies()`, `remaining(entity)`, `paths()`, `addLives(n)`. `callNext()` starts the next wave now and pays the early bonus; it answers null when a hook cancels it. - Events: `defense.wave` (`wave`, `waves`, `count`, `early`, `bonus`), `defense.spawned` (`entity`, `enemy`, `path`, `wave`), `defense.leaked` (`entity`, `enemy`, `lives`, `left`), `defense.killed` (`entity`, `enemy`, `by`, `reward`), `defense.wave_cleared` (`wave`, `reward`), `defense.won`, `defense.lost`. It listens to `combat.killed`. - Variables: `defense.wave`, `defense.waves`, `defense.lives`, `defense.lives.max`, `defense.countdown`, `defense.enemies`, `defense.result` (won, lost or empty). The input action `defense.call_wave` (key T) calls the next wave. - Verbs and conditions: ```json { "type": "call_wave" } { "add_lives": 5 } { "wave_reached": 5 } { "lives_at_least": 10 } ``` ## Hooks and modding | Hook | Meaning | |---|---| | `wave.start` | The next wave is about to start (countdown over or called early); `bonus` is mutable; cancelled, it does not start. | | `wave.spawn` | An enemy is about to appear at the entry of a path; `hp`, `reward` and `lives` are mutable; cancelled, this one does not appear. | | `wave.leak` | An enemy reached the exit; `lives` is mutable (0: it gets through for free). | | `wave.clear` | A wave has spawned and none of its enemies is left; `reward` is mutable. | ```json { "hook": "wave.spawn", "match": { "enemy": "runner" }, "mul": { "hp": 2 } } ``` Every setting and the listed collections are moddable. ## Limits Runs on the web player and the native player; Godot, Unity and Unreal have no native delivery of this kit. It places no towers and keeps no money: pair it with a tower kit and an economy kit. Flyers ignore towers that cannot hit the air. ## Versions Kit 1.1.0 (the hooks arrived in 1.1.0); service `defense.waves` 1.0.0. It relies on the contract of `game.engine` ^1 and on the health and damage services of `combat.core` ^1.