# Rooms and bases (build.modular) ## What it is for Pieces that join become a place. A structure of `build.placement` carrying the `modular.room` component has an inside: the player standing in it is indoors, and the room does to the survival gauges what its document says (a supply per second, a drain multiplier) for as long as they stay. Structures joined through their sockets form a base as soon as one of them is a room; the kit tells when the player enters and leaves a room or a base, when a base gains or loses a room, and when two bases are merged by a joining piece. What a room is and what it does is data: shelters, habitats, stations, houses, ships. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "build.placement" } gripforge_gamekit_install { project_id: "gkp_…", id: "build.modular" } ``` A structure is a room when its buildable (a document of `build.placement`) carries `modular.room`: `label` (shown in the variable `base.room`, default the buildable name), `inside` (`[width, height, depth]` of the inner box, default the structure size; the box is centred, turned with the structure and rises from its floor), `needs` (for each `survival.needs` gauge, `supply` units per second and `drain` as a multiplier on the gauge’s own drain, 0 stops it; applied on entering, removed on leaving) and `tags` (free words, given back by `rooms()`). The player is inside while their position is in the box. A base is named after the smallest entity id among its pieces, so its id does not change as it grows. Make a corridor a room too, with the same `needs`, and the whole base is one inside. ```json { "id": "cabin", "name": "Cabin", "size": [4, 3, 4], "sockets": [{ "id": "door", "at": [0, 0, 2], "yaw": 0, "type": "door" }], "components": { "modular.room": { "label": "Cabin", "needs": { "air": { "supply": 8, "drain": 0 } } } } } ``` ## First example A corridor and a habitat (a room giving `warmth` supply 5) are placed and joined through their sockets: the base appears with its rooms and center, the player entering the habitat feeds the gauge, and a save and reload bring the room back without announcing it. ```text build.placement.place("corridor", [0,0,0]) build.placement.place("hab", [0,0,0], 0, { "snapTo": { "entity": , "socket": "e", "own": "w" } }) event: base.changed build.modular.bases() -> [{ "id": , "rooms": [], "structures": [, ], "center": [1.5, 0, 0] }] player moves to [3,0,0] events: base.entered variable base.room = "Habitat" survival.needs.value("warmth") rises ``` ## Settings and defaults The kit has no setting and no collection of its own: what a room is comes from the `buildables` of `build.placement`, which a mod may add to or patch. ## Services - `rooms()` lists the rooms as `{ entity, label, base, position, inside, tags }`. - `bases()` lists the bases as `{ id, rooms, structures, center }`; `baseOf(entity)` gives the base of a structure or null. - `inside()` gives the room the player stands in (or null) and `current()` the base they are in (or null). Variables: `base.inside` (the player is in a room) and `base.room` (its label). Conditions for scripts: `inside_base` (the player stands in a room) and `base_has_rooms { count }` (the largest base has at least that many rooms). ## Events | Name | Detail | |---|---| | `base.entered` | The player walked into a room; payload `{ room, base }`. | | `base.left` | The player walked out of a room; payload `{ room }`. | | `base.joined` | The player entered a base from outside; payload `{ base }`. Going from a room to another room of the same base does not tell it. | | `base.exited` | The player left a base for outside; payload `{ base }`. | | `base.changed` | A base gained or lost a room (a structure placed or taken down, two bases merged by a joining piece, one split in two); payload `{ base, rooms }` with the rooms now (empty: the base is gone). | ## Hooks and modding A game or a mod changes this decision with a document of its `hooks` list, without touching the kit: - `base.shelter`: asked when the player walked into a room (or came back in it after a load) and the room is about to act on one survival gauge: once per need of the room, when its effects are set, never while the player stays; payload `{ room, base, label, need, drain, supply }`; a tap may change `drain` (the multiplier, 1 when the room gives none) and `supply` (units per second, 0 none), or cancel so the room does nothing to that gauge. Not asked without `survival.needs`. ```json { "id": "thin_air", "hook": "base.shelter", "match": { "need": "air" }, "mul": { "supply": 0.5 } } ``` ## When it does not do what you expect - A room does nothing to the gauges: `survival.needs` is not installed, the `needs` name does not match a gauge, or the player is not inside the box (check `inside()` and the `inside` size). - Pieces do not form a base: no piece is a room yet, or their sockets are not joined (see `build.placement`: sockets join when their types match, they are at the same point and face each other). ## Limits - The kit creates and keeps nothing: rooms and bases are read from the structures, again after a load, and a load announces nothing. - It decides nothing about building (that is `build.placement`) and has no display module. Targets web and native players. ## Versions 1.1.0 (2026-10-10): the hook `base.shelter`; 1.0.0 (2026-10-04): first release. Requires `build.placement` 1.1 or later and `game.engine`; `survival.needs` is optional.