# Defense towers (defense.towers) ## What it is for `defense.towers` lets the player build towers on build spots with money, upgrade them and sell them. A tower is a document with tiers: range, cadence, damage, an instant hit or a homing shot, splash around the target, a status put on what it hits (a slow, a burn), and whether it hits the air and the ground. Each tower picks a target among the enemies in range by a mode the player sets (`first`, `last`, `strongest`, `nearest`) and fires through `combat.attacks`, so its shots are drawn and saved like any other. An upgrade costs the price of the next tier; a sold tower gives back a share of everything it cost. Clicking or tapping a spot or a tower opens its panel. Tower defence, base defence, any game with turrets the player places. ## Turn it on Install the kit in a project and set its settings with the tools below; `gripforge kit api defense.towers` lists every setting, and `gripforge docs defense-towers` prints this guide offline. ```text gripforge_gamekit_install { project_id: "gkp_…", id: "defense.towers" } gripforge_gamekit_configure { project_id: "gkp_…", id: "defense.towers", config: { "sellRefund": 0.5, "defaultTargeting": "strongest", "rangeMarker": true } } ``` ```json { "id": "bolt", "name": "Bolt", "tiers": [ { "cost": 50, "range": 6, "cadence": 0.5, "damage": 8 }, { "cost": 40, "damage": 14, "name": "Bolt II", "range": 7 }, { "cost": 60, "cadence": 0.3 } ] } { "id": "spot_1", "at": [4, 2] } ``` ## First example A `bolt` tower (tiers costing 50, 40 and 60) is built on a spot, upgraded and sold. Building spends the first tier price, each upgrade the next one, and selling refunds `sellRefund` of the total. A `cannon` that does not hit the air ignores flying enemies. ```text defense.towers.build("spot_1", "bolt") // defense.tower.built { tower: "bolt", cost: 50 } defense.towers.upgrade(tower) // defense.tower.upgraded { tier: 2, cost: 40 } defense.towers.setTargeting(tower, "nearest") defense.towers.sell(tower) // defense.tower.sold { refund: sellRefund x 90, rounded down } ``` ## Settings and defaults | Setting | Default | Meaning | | --- | --- | --- | | `team` | `player` | `combat.health` team of the towers; they shoot entities of every other team | | `targetTag` | `enemy` | {{s_targetTag}} | | `flyingTag` | `flying` | {{s_flyingTag}} | | `sellRefund` | 0.7 | {{s_sellRefund}} | | `defaultTargeting` | `first` | {{s_defaultTargeting}} | | `pickRadius` | 2.5 | {{s_pickRadius}} | | `spotSize` | 2.6 | {{s_spotSize}} | | `towerHealth` | 1000 | {{s_towerHealth}} | | `rangeMarker` | `false` | {{s_rangeMarker}} | ## Services - `defense.towers` (service): `types()`, `spots()`, `towers()`, `stats(entity)`, `build(spot, tower, opts?)`, `upgrade(entity)`, `sell(entity)`, `refund(entity)`, `setTargeting(entity, mode)`, `select(entity | null)`, `selected()` and `pick(point)`. - Content. `defense_towers`: `id` (lowercase letters, digits, `_`), `name`, `description`, `air` and `ground` (both default `true`), `targeting`, `size` (`[width, height, depth]`), and `tiers` (1 to 6): the built tower, then its upgrades in order; a tier keeps what it does not say from the tier before. A tier has `name`, `cost` (the build price for the first tier, the upgrade price for the next ones), `range` (m), `cadence` (seconds between shots), `damage`, `projectileSpeed` (0: an instant hit; otherwise a homing shot in m/s), `splash` (radius in m) with `splashDamage` (share of the damage the others take, default 0.5), `status` (a `combat.core` status put on the target) with `statusOnSplash`, `style` (`arrow`, `orb`, `heavy`) and `color`. `defense_spots`: `id`, `at` (`[x, z]` or `[x, y, z]`) and `towers` (only these towers can stand there). An instance with the component `tower_spot { towers }` is a build spot too. ## Events - `defense.tower.built`: a tower was built (`entity`, `tower`, `spot`, `cost`). - `defense.tower.upgraded`: a tower went up a tier (`entity`, `tower`, `tier` 1-based, `cost`). - `defense.tower.sold`: a tower was sold (`entity`, `tower`, `refund`). - `defense.tower.refused`: an action was refused (`action`, `reason` of `money`, `taken`, `unknown`, `max`, `spot`, `allowed`, or the reason of a tap). - `defense.tower.selected`: the selection changed (`entity`, `kind` of `spot`, `tower`, or empty when cleared); it listens to `combat.attack.impact` and `combat.attack.cancelled`. ## Hooks and modding - `tower.build`: a tower is about to be built on a free spot that allows it (`spot`, `tower`, `cost`, `free`), before the money is spent; `cost` is mutable (0 when free), and cancelling emits `defense.tower.refused` with the reason of the tap (`refused` by default) and spends nothing. - `tower.upgrade`: a tower is about to go up to `tier` (1-based) for `cost`, before the money is spent; `cost` is mutable, and cancelling refuses with the reason of the tap. - `tower.sell`: a tower is about to be sold for `refund` (`sellRefund` x everything it cost, rounded down); `refund` is mutable, and cancelling keeps the tower. - `tower.fire`: a tower whose cadence is ready picked a target and is about to shoot it for `damage` (the splash takes its share); `damage` is mutable, and cancelling makes it hold its fire and ask again on the next tick; the hit itself goes through the `combat.damage` hook. ```json { "id": "half_price_archers", "hook": "tower.build", "match": { "tower": "archer" }, "mul": { "cost": 0.5 } } ``` ## Using it from content Script verbs: the action `build_tower` (`spot`: an id of `defense_spots` or an entity, `tower`, `free`: no money taken) and the condition `towers_built` (`tower`, `count`, at least 1). The input `tower_select` (button `Mouse0`) selects a spot or a tower. Variables: `defense.selected`, `defense.towers`. ```json { "build_tower": { "spot": "spot_1", "tower": "bolt", "free": true } } { "type": "towers_built", "count": 3 } ``` ## When it does not do what you expect | Symptom | Cause and fix | | --- | --- | | A build is refused | Read `defense.tower.refused`: `money` (too poor), `taken` (the spot is occupied), `unknown` (no such tower or spot), `spot` (not a build spot), `allowed` (the spot does not allow that tower), `max` (no tier left to upgrade). | | A tower never fires at some enemies | They lack `targetTag` (default `enemy`) or share the tower team, are out of `range`, or are flying (`flyingTag`) while the tower has `air: false`, or on the ground while it has `ground: false`. | | Clicking does not select a spot | The click landed further than `pickRadius` from it; use the service `pick(point)` to test a point. | ## Limits - It does not spawn enemies or waves, nor award money for kills: pair it with a wave kit and `economy.money`. - Towers shoot through `combat.attacks`; the shot look is `style` and `color`, and a model per tower comes from the `defense_tower` asset slot (variant = tower id). - The tower panel is a web display module; on other targets the service, the verbs and the events work and your game draws its own panel. ## Versions Kit `defense.towers` 1.1.0 (2026-10-09: the hooks `tower.build`, `tower.upgrade`, `tower.sell`, `tower.fire` and `towerHealth`; 1.0.0 was the first release). It needs `combat.attacks`, `combat.health`, `combat.damage`, `economy.money`, `input.actions` and `game.engine` ^1.