Shrinking zone

A circular safe zone that closes in by phases, and what happens outside: damage every second or expulsion (battle royale storm, sumo ring).

Source: module-kits:kits/zone_shrink/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs zone-shrinkMCP tool: gripforge_docs { "topic": "zone-shrink" }

A shrinking zone in your game (zone.shrink) #

What it is for #

zone.shrink manages a circular safe zone that closes in by phases of data: it holds still, then shrinks to a radius over some seconds, around a centre that may slide. Every entity that carries one of the affects tags (and none of the ignore tags) is placed against the edge, on foot or in a vehicle. Outside, it either takes damage every damageEvery seconds (the storm of a battle royale), is thrown out with an event (a sumo ring, a party round), or nothing happens beyond the events. The kit draws the wall, the edge on the ground and the next circle; deciding who is eliminated is left to other kits.

Turn it on #

Install zone.shrink (it requires game.engine@^1) and give the project a zone_phases collection. Without phases the zone keeps its config radius, a fixed ring. With autoStart: false the zone waits for start().

[
  { "id": "first", "wait": 30, "shrink": 65, "radius": 140, "damage": 2 },
  { "id": "second", "wait": 20, "shrink": 65, "radius": 86, "damage": 4 },
  { "id": "last", "wait": 5, "shrink": 20, "radius": 0, "damage": 10 }
]

First example #

<!-- test: kits/game_engine/host/tests/parity.rs::rust_host_matches_the_typescript_traces (fixture zone.shrink.storm) --> The fixture starts a zone of radius: 8 with damage: 1.5, damageEvery: 0.5 and margin: 0.25, and two phases: first (hold 1 s, shrink 1 s to radius 5, damage 3) then second (shrink 0.5 s to radius 2, centre sliding to [1, 0.5]). A phase whose radius is not a number is skipped. The player (tag player) and an NPC (tag contestant) are watched; a prop without the tags and a contestant tagged dead are not. The trace checks state(), view(), outside(), isOutside, contains, distance, and the events and damage over the following ticks.

Settings and defaults #

SettingTypeDefaultMeaning
center[x, z][0, 0]Centre of the zone when it starts.
radiusnumber60Radius when it starts, before the first phase.
outsidestringdamagedamage, out or none.
damagenumber2Damage per second outside, until a phase says otherwise.
damageEverynumber1Seconds between two damage ticks.
marginnumber0Metres past the edge before an entity counts as outside (negative: before the edge).
affectslistplayer, contestantTags of watched entities (any of them).
ignorelistdead, eliminated, spectatorTags that take an entity out of the watch.
autoStartbooleantrueStart with the game; off: call start().
wallHeightnumber40Height of the wall at the edge (0: ground circle only).
colorstring#a177f5Colour of the wall and the edge.
showNextbooleantrueDraw the circle the zone is closing in to.
wallOpacitynumber0.15Opacity of the wall.
rimbooleanfalseA glowing rim along the edge (a sumo ring).
pylonsinteger0Lamp pylons around the starting circle (0 to 64).
halobooleanfalseA glowing halo far under the starting circle.

A phase { id, radius, wait?, shrink?, center?, damage? }: wait seconds of hold, then a linear shrink over shrink seconds to radius, sliding to center; damage replaces the outside damage from that phase on.

With outside: damage, a tick of damage x damageEvery goes through combat.damage when the entity has combat health, through vehicle.damage for a vehicle, else it removes hp from the entity's health component. combat.core is optional.

Services, events, variables #

  • zone.storm (service): state() (started, stage, index, phase, center, radius, target, targetCenter, timer, damage), start(), stop(), contains(x, z, margin?), distance(x, z) (negative inside), outside() (entity ids), isOutside(entity), view() (what the renderer draws).
  • Events: zone.started, zone.phase_started, zone.shrinking, zone.closed, zone.finished, zone.stopped, zone.left and zone.returned ({ entity }), zone.expelled ({ entity, radius, distance }), zone.damaged ({ entity, amount, applied }; applied is false when no health is known for the entity).
  • Variables: zone.stage (idle, wait, shrink, done), zone.phase, zone.radius, zone.timer, zone.player_outside.

Hooks and modding #

HookPayloadWhat a tap does
zone.phase{ phase, index, wait, shrink, radius, damage }wait, shrink, radius, damage are mutable; cancelled, the phase is skipped.
zone.expel{ entity, radius, distance }With outside: out; cancelled, nothing happens until the entity returns and leaves again.
zone.damage{ entity, amount, index }With outside: damage; amount is mutable; cancelled, no damage this tick.
{ "id": "shelter", "hook": "zone.damage", "match": { "entity": "e1" }, "mul": { "amount": 0.5 } }

A mod can override every config key above and change the zone_phases collection.

Engines #

The logic is the same Rust implementation on the web and in the native player, which draw the wall and circles. The kit is delivered for the web and Rust targets.

When it does not do what you expect #

  • Nothing is watched: the entity lacks every affects tag, or has an ignore tag.
  • The zone never moves: autoStart is off (call start()), or there are no valid phases.
  • No damage with outside: out: that mode only emits zone.expelled; another kit takes the entity out of the game.
  • zone.damaged with applied: false: no health is known for that entity.

Limits #

One zone per game, circular only. The kit does not eliminate anyone itself and does not keep score.

Versions #

Kit 1.1.0, service zone.storm 1.0.0. The ring dressing (wallOpacity, rim, pylons, halo) arrived in 1.1.0.