Install & Uninstall

install and uninstall are two commands defined on Generic Room. A player runs install <thing> (holding the thing) or uninstall <thing> (standing in the room), and the command routes the object to a shared timed action. Several unrelated object families ride the same two commands: door locks and security systems, cyberware, and furniture. This page describes the shared flow and the verbs you implement to make a new kind of thing installable.

The flow

flowchart TD CMD["install command on the room"] --> MATCH["match the object in inventory"] MATCH --> FAM{"which family?"} FAM -->|lock / security| DIR["resolve target door/exit"] FAM -->|cyberware| CRE["resolve target creature / parent ware"] FAM -->|furniture| ROOM["target = the room"] DIR --> PRED CRE --> PRED ROOM --> PRED PRED["why_cant_install(dude, target)"] -->|reason string| STOP["tell player, abort"] PRED -->|empty| QUEUE["queue_action(install)"] QUEUE --> START["action:start"] START --> WAIT["wait duration"] WAIT --> FINISH["action:finish"] FINISH --> PRED2["why_cant_install again"] PRED2 -->|empty| DO["do_install(dude, check, target)"]

The command verb only parses arguments, picks the target, and calls the object's why_cant_install predicate. If that returns a non-empty reason it is shown and nothing happens. Otherwise the command queues the install action; it does not perform the install itself.

The two commands

Both commands live on Generic Room, so they work in any location.

  • install <thing> matches <thing> in the player's inventory, then branches on the object family to work out what it is being installed onto:
    • A lock or security system needs install <thing> on <direction>; the target is the door in that direction.
    • Cyberware defaults to the player, or takes into <creature> (and options take into <creature>'s <ware>).
    • Furniture targets the current room; install <thing> in <container> is also accepted.
  • uninstall <thing> matches the already-installed object (on the door, in the room, or in a creature's cyberware list depending on the from target) and queues the uninstall action.

The shared action

Both commands queue a timed action (the install / uninstall entries in $actions). Actions run a two-phase start -> finish lifecycle over a fixed duration, so an install takes a few seconds of game time and can be interrupted. See the action system for the general pattern.

start re-validates with why_cant_install, announces "begins installing", and returns the duration plus a finish_state. finish re-validates again (state can change while the timer runs) and then calls do_install. Uninstall mirrors this with why_cant_uninstall and do_uninstall.

Checked vs. check-free installs

Some installs are a skill check and some are automatic:

  • If the object defines an install_difficulty verb, the action rolls a security skill check against that difficulty. On success it installs and improves the skill; on failure it announces a fumble and still grants some practice.
  • If the object does not define install_difficulty, the action skips the check entirely and installs automatically once the timer completes.

Furniture is the check-free case: there is no meaningful difficulty to putting a table down, so furniture omits the install_difficulty verb and installs with no roll. Locks, security systems, and cyberware define install_difficulty and go through the check.

The installable interface

To make a new kind of object installable, implement these verbs on it. The predicates return an empty string when the action is allowed, or a player-facing reason string when it is not.

Verb Contract
why_cant_install(dude, target) Reason install is disallowed, or "". Checked both when the command is issued and again when the timer finishes.
do_install(dude, check, target[, ...]) Commit the install: record where it is installed, move the object as needed, and announce via $you:do.
why_cant_uninstall(dude, target) Reason uninstall is disallowed, or "".
do_uninstall(dude, check, target) Commit the uninstall and announce it.
is_installed() Whether the object is currently installed.
install_difficulty() Optional. Define it to require a skill check; omit it for a check-free install.

Worked example: furniture

Generic Furniture is the simplest installable and a good template. It tracks where it is installed in an installed_in property, and is_installed() just reports whether that is set.

Installing. why_cant_install refuses if the piece is already installed, if the player is not carrying it, if the target is not a room the player is standing in, or if the piece is broken. do_install sets installed_in, moves the piece into the room, and announces it. Because furniture defines no install_difficulty, the action installs it with no skill check.

No pickup while installed. Installed furniture cannot be carried off. Furniture implements cant_take, the same hook the pickup path already consults, and returns a reason whenever is_installed() is true. Any attempt to get or take the piece is refused with that message until it is uninstalled.

No uninstall while in use. why_cant_uninstall refuses if the piece is not installed, is flagged permanent, is not installed in the room the player is standing in, or is currently in use. Before checking occupancy it calls validate_occupants to drop anyone who has wandered off, then inspects the sitter list: if the only occupant is the player, they are told to get up first; if anyone else is using it, it reports the piece is in use. Only when it is empty can it be removed. do_uninstall clears installed_in, moves the piece back to the player, and announces it.

Adding your own installable

  1. Give the object the six interface verbs above (skip install_difficulty for a check-free install).
  2. Add a branch to the room's install / uninstall commands for your object family, resolving the correct target and queueing the action. Existing branches for locks, cyberware, and furniture are the models.
  3. If the object should resist being picked up or moved while installed, implement cant_take (and any similar guard) keyed on is_installed().
  4. Keep every guard in the why_cant_* predicate, not in do_*. The action re-runs the predicate at finish time, so state that changes during the timer is caught.

See also

This page was last edited on 2026-07-19 13:59

Powered by Wiki|Docs

This page was last edited on 2026-07-19 13:59

bitMuse
Cognito-hazards provided without warranty

Powered by Wiki|Docs