aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-07-01 03:21:52 -0400
committerhistoria <[not public]>2026-07-01 03:21:52 -0400
commitfbe5090ac3325ff8ea6989cdf4515c7f077c975b (patch)
tree1811ad9c6a596e7177bb2c42944c389c0a120066
parent5f37dc9b6f6b79da04da70ae0c33ec8d02998587 (diff)
downloadthehouseoficarus-fbe5090ac3325ff8ea6989cdf4515c7f077c975b.tar.gz
feat: item messaging standardized, custom messages for production paths implemented
-rw-r--r--building_guide/admin.md69
-rw-r--r--building_guide/behaviors.md2
-rw-r--r--building_guide/hazards.md1
-rw-r--r--building_guide/items.md120
-rw-r--r--building_guide/recipes.md24
-rw-r--r--building_guide/rooms.md18
-rw-r--r--data/.admin_history.json2
-rw-r--r--data/items/ammo/headless_arrow.yaml2
-rw-r--r--data/items/consumables/bread.yaml2
-rw-r--r--data/items/consumables/bread_dough.yaml2
-rw-r--r--data/items/consumables/guam_potion_unf.yaml2
-rw-r--r--data/items/consumables/pie_dish.yaml2
-rw-r--r--data/items/materials/ball_of_wool.yaml2
-rw-r--r--data/items/materials/bowstring.yaml2
-rw-r--r--data/items/materials/diamond.yaml2
-rw-r--r--data/items/materials/emerald.yaml2
-rw-r--r--data/items/materials/mahogany_planks.yaml2
-rw-r--r--data/items/materials/oak_planks.yaml2
-rw-r--r--data/items/materials/planks.yaml2
-rw-r--r--data/items/materials/ruby.yaml2
-rw-r--r--data/items/materials/sapphire.yaml2
-rw-r--r--data/items/materials/teak_planks.yaml2
-rw-r--r--data/items/tools/empty_pot.yaml2
-rw-r--r--data/items/tools/plant_pot.yaml2
-rw-r--r--data/items/tools/unfired_pie_dish.yaml2
-rw-r--r--data/items/tools/unfired_plant_pot.yaml2
-rw-r--r--data/items/tools/unfired_pot.yaml2
-rw-r--r--data/objects/adamantite_rock.yaml2
-rw-r--r--data/objects/anglerfish_spot.yaml2
-rw-r--r--data/objects/bait_fishing_spot.yaml6
-rw-r--r--data/objects/cage_fishing_spot.yaml2
-rw-r--r--data/objects/clay_rock.yaml2
-rw-r--r--data/objects/coal_rock.yaml2
-rw-r--r--data/objects/copper_rock.yaml4
-rw-r--r--data/objects/fly_fishing_spot.yaml6
-rw-r--r--data/objects/gold_rock.yaml2
-rw-r--r--data/objects/harpoon_fishing_spot.yaml6
-rw-r--r--data/objects/iron_rock.yaml2
-rw-r--r--data/objects/magic_tree.yaml2
-rw-r--r--data/objects/mahogany_tree.yaml2
-rw-r--r--data/objects/maple_tree.yaml2
-rw-r--r--data/objects/mithril_rock.yaml2
-rw-r--r--data/objects/net_fishing_spot.yaml10
-rw-r--r--data/objects/oak_tree.yaml2
-rw-r--r--data/objects/redwood_tree.yaml2
-rw-r--r--data/objects/runite_rock.yaml2
-rw-r--r--data/objects/sacred_eel_spot.yaml2
-rw-r--r--data/objects/scrap_pile.yaml2
-rw-r--r--data/objects/sheep.yaml2
-rw-r--r--data/objects/silver_rock.yaml2
-rw-r--r--data/objects/swamp_fishing_spot.yaml4
-rw-r--r--data/objects/teak_tree.yaml2
-rw-r--r--data/objects/tin_rock.yaml2
-rw-r--r--data/objects/tree.yaml2
-rw-r--r--data/objects/willow_tree.yaml2
-rw-r--r--data/objects/yew_tree.yaml2
-rw-r--r--data/rooms/intro/2001.yaml4
-rw-r--r--internal/admin/static/itemeditor.js48
-rw-r--r--internal/behavior/behavior.go26
-rw-r--r--internal/behavior/store.go8
-rw-r--r--internal/behavior/types.go47
-rw-r--r--internal/game/act.go10
-rw-r--r--internal/game/act_clean.go2
-rw-r--r--internal/game/act_combine.go157
-rw-r--r--internal/game/act_fletch.go2
-rw-r--r--internal/game/act_gather.go25
-rw-r--r--internal/game/act_state.go8
-rw-r--r--internal/game/act_use.go28
-rw-r--r--internal/game/act_use_interaction.go23
-rw-r--r--internal/game/cmd_use.go32
-rw-r--r--internal/game/core_production.go7
-rw-r--r--internal/game/production_engine.go31
-rw-r--r--internal/item/item.go4
73 files changed, 569 insertions, 251 deletions
diff --git a/building_guide/admin.md b/building_guide/admin.md
index 1c7db93..fbe15d5 100644
--- a/building_guide/admin.md
+++ b/building_guide/admin.md
@@ -40,7 +40,7 @@ etc.) and they receive a notification. Case-insensitive name matching.
```
dig <direction> [name]
```
-Creates a new room in the given direction (north/south/east/west/up/down) from your
+Creates a new room in the given direction (north/south/east/west/ne/nw/se/sw/up/down) from your
current room. The new room ID is auto-generated as the lowest available integer at-or-above
the minimum room ID in the same directory as your current room's YAML file.
@@ -61,7 +61,7 @@ Examples:
undig <direction>
```
Deletes the room in the given direction and removes all exits leading to it from
-every room. Requires confirmation — type `UNDIG <direction>` to proceed, anything
+every room. Requires confirmation — type `y`/`yes` to proceed, anything
else to cancel. The target room's name and ID are shown before deletion. A warning
is displayed if deleting the room would create orphaned (unreachable) rooms.
@@ -196,21 +196,62 @@ Dumps detailed diagnostic information about your current room:
- Connected players in the room
- Live mob instances with HP
+### Web Admin GUI
+
+The server includes a browser-based admin panel for managing all game data visually. It runs alongside
+the game server and is configured in `config.yaml` under `admin_http` (plain HTTP) or `admin_https`
+(TLS with auto-generated self-signed certificate).
+
+#### Setup
+
+1. Ensure your account YAML has `admin: true` (same as in-game admin).
+2. Configure the admin server in `config.yaml`:
+ ```yaml
+ admin_http: ":8080" # listens on port 8080, HTTP
+ # or for HTTPS:
+ admin_https: ":8443"
+ ```
+3. Start the game server. The admin panel is available at `http://<host>:8080` (or `https://<host>:8443`).
+4. Log in with your account name and password (same credentials as in-game).
+
+#### Features
+
+| Page | URL | Description |
+|------|-----|-------------|
+| Map | `/` | Interactive SVG map showing rooms on a 3D grid. Zoom/pan, click rooms to edit, create/delete rooms and exits, relink exits. Up/down links shown alongside horizontal connections. |
+| Items | `/editor/items` | Full CRUD editor with sections for equipment (type/slot/attack/defense/other), tool, craft (type/level/XP/ingredients/stations/tool/steps), firemaking, farming, potions, and requirements. Undo/redo supported. |
+| Objects | `/editor/objects` | CRUD editor with sections for gather (tools/drops/success formula), use (message/wait/consumes), talk (visual talk tree editor), safespot, steal, use_interactions, and on_look. |
+| Mobs | `/editor/mobs` | CRUD for mob definitions — stats, drops, talk, behavior, assassin/steal/task fields. |
+| Drops | `/editor/drops` | CRUD for shared drop tables (gem tables, bird's nests, etc.). |
+| Hazards | `/editor/hazards` | CRUD for hazard definitions. |
+| Techs | `/editor/techs` | CRUD for technology definitions (buffs, drain rates, categories). |
+| Courses | `/editor/courses` | CRUD for agility courses (obstacle sequences, XP, fail damage). |
+| Modules | `/editor/modules` | CRUD for science modules (combat/utility/transport/enchant/processing). |
+| Players | `/editor/players` | Browse character YAML files and view player state. |
+| Flags | `/editor/flags` | View and edit world flags in real time. |
+| Files | `/editor/files` | Raw file tree browser for all YAML files under `data/`. |
+
+All CRUD editors support undo/redo (persisted to `data/.admin_history.json`), so changes can be reverted
+even across sessions.
+
+#### Map Interface
+
+The map page (`/`) is the main navigation hub:
+- Rooms are displayed as colored nodes on a 3D grid, connected by links (bars for two-way, arrows for one-way, X for blocked).
+- Click a room node to edit its properties (name, description, color, exits, objects, mobs, spawns).
+- Drag connections between rooms to create or relink exits.
+- Right-click (or long-press) a room to delete it or access advanced options.
+- The map uses the same BFS-based grid layout as the in-game map, showing all rooms on the current z-plane.
+- Up/down links are shown with special glyphs when connecting rooms on different z-levels.
+
### Workflow
The typical admin worldbuilding workflow:
-1. **Lay out rooms:** Use `dig` to create connected rooms quickly. If `dig` would create a
- grid overlap, a two-way link is created instead — no duplicate rooms. Use `room insert`
- to place a new room between two already-connected rooms without breaking the map grid.
-2. **Flesh out rooms:** Use `room` commands to set names, descriptions, add objects/mobs/spawns
- directly from inside the game. For deeper edits, edit the YAML files directly in
- `data/rooms/` to add on-enter scripts, triggers, hazards, etc.
-3. **Rearrange:** Use `swapid` to renumber rooms and `undig` to delete rooms you no longer want
- (with full exit cleanup). Use `close` to remove individual exits.
+1. **Lay out rooms:** Use `dig` to create connected rooms quickly, or use the web map to create and link rooms visually. If `dig` would create a grid overlap, a two-way link is created instead — no duplicate rooms. Use `room insert` to place a new room between two already-connected rooms without breaking the map grid.
+2. **Flesh out rooms:** Use `room` commands to set names, descriptions, add objects/mobs/spawns directly from inside the game. Use the web admin editors for richer CRUD with undo/redo. For deeper edits, edit the YAML files directly in `data/rooms/` to add on-enter scripts, triggers, hazards, etc.
+3. **Rearrange:** Use `swapid` to renumber rooms and `undig` to delete rooms you no longer want (with full exit cleanup). Use `close` to remove individual exits.
4. **Reload:** Run `reload` to pick up YAML changes without restarting (rooms auto-reload).
5. **Test:** Use `goto` to jump around while debugging room connections and content.
-6. **God mode:** Use `god` to test dangerous areas safely — full stats, no aggro, pass through
- blocked exits. `ungod` restores normal state.
-7. **Debug:** Use `setflag` / `setplayerflag` to test quest logic and conditional content.
- Use `inspect` to check room state, flags, and triggers.
+6. **God mode:** Use `god` to test dangerous areas safely — full stats, no aggro, pass through blocked exits. `ungod` restores normal state.
+7. **Debug:** Use `setflag` / `setplayerflag` to test quest logic and conditional content. Use `inspect` to check room state, flags, and triggers.
diff --git a/building_guide/behaviors.md b/building_guide/behaviors.md
index 0fc6516..3d35036 100644
--- a/building_guide/behaviors.md
+++ b/building_guide/behaviors.md
@@ -510,7 +510,7 @@ Item-specific interaction:
```yaml
name: bookshelf
use_interactions:
- - item: dusty_tome
+ - item_id: dusty_tome
message: "The bookshelf slides aside, revealing a secret passage!"
action:
set_flags:
diff --git a/building_guide/hazards.md b/building_guide/hazards.md
index 26fdccc..5db7ceb 100644
--- a/building_guide/hazards.md
+++ b/building_guide/hazards.md
@@ -33,7 +33,6 @@ required_item: "" # optional: an equipped item that NEGATES the haza
| Field | Description |
|---|---|
-| `id` | Unique identifier (matches the filename) |
| `name` | Display name used in warnings and default messages |
| `description` | Flavour text (for a future `examine`) |
| `attack_type` | `stab`/`slash`/`crush`/`ranged`/`science` — selects which player defense bonus applies and which protection tech reduces the damage (empty = `crush`) |
diff --git a/building_guide/items.md b/building_guide/items.md
index eefdf9c..e03622d 100644
--- a/building_guide/items.md
+++ b/building_guide/items.md
@@ -5,6 +5,10 @@
put an `id:` field in the file — it is ignored. Anything that references this item (drop
tables, craft `ingredients`, room `item_spawns`) uses that filename.
+### Equipment & Tool Blocks
+
+Combat and equipment stats go under an `equipment:` block. Tool stats go under a `tool:` block.
+
```yaml
name: bronze pickaxe
color: "B2" # xterm-256 color index (00-FF)
@@ -12,88 +16,108 @@ aliases: ["pick", "pickaxe"]
description: "A sturdy bronze pickaxe."
value: 10
stackable: false
-equip_slot: main_hand # optional — where it equips
-weapon_type: melee # optional — melee, ranged, or science
-attack_type: stab # stab/slash/crush/ranged/science — determines accuracy type
-stats: # optional — per-type combat bonuses
- stab_attack: 4
- slash_attack: -2
- crush_attack: 2
- strength_bonus: 3
-speed: 5 # ticks between attacks
-tool_type: pickaxe # used by gather behaviors that require "tool: pickaxe"
-tool_speed: 2 # reduces gather wait time
+equipment: # optional — combat/equip behavior
+ type: melee_weapon # melee_weapon / ranged_weapon / tech_weapon / armor / ammo / tool
+ slot: main_hand # head / neck / torso / legs / hands / feet / back /
+ # ammo / main_hand / off_hand / ring
+ attack_type: stab # stab/slash/crush/ranged/science — determines accuracy type
+ speed: 5 # ticks between attacks
+ attack: # accuracy bonuses per type
+ stab: 4
+ slash: -2
+ crush: 2
+ other:
+ strength: 3 # melee max hit bonus
+tool: # optional — tool behavior
+ type: pickaxe # used by gather behaviors that require "tool: pickaxe"
+ speed: 2 # reduces gather wait time
requirements: # optional — skill levels needed to equip
accuracy: 1
```
-### Equipment Stats (ItemStats)
-
-Weapons have per-type **attack bonuses** — these determine accuracy based on the weapon's `attack_type`:
+### Equipment Block Fields
| Field | Description |
|---|---|
-| `stab_attack` | Accuracy bonus for stab attacks |
-| `slash_attack` | Accuracy bonus for slash attacks |
-| `crush_attack` | Accuracy bonus for crush attacks |
-| `science_attack` | Accuracy bonus for science attacks |
-| `ranged_attack` | Accuracy bonus for ranged attacks |
-
-Armor has per-type **defense bonuses** — the mob's `attack_type` determines which defense is used:
-
-| Field | Description |
+| `type` | Equipment type: `melee_weapon` / `ranged_weapon` / `tech_weapon` / `armor` / `ammo` / `tool` |
+| `slot` | Equip slot (see below) |
+| `attack_type` | `stab` / `slash` / `crush` / `ranged` / `science` — determines which defense the target uses |
+| `speed` | Ticks between attacks |
+| `junk` | Junk type this item gives when recycled (for Science decks) |
+| `attack` | Attack bonuses (accuracy) — sub-fields: `stab`, `slash`, `crush`, `ranged`, `science` |
+| `defense` | Defense bonuses — sub-fields: `stab`, `slash`, `crush`, `ranged`, `science` |
+| `other` | Other bonuses — `strength` (melee max hit), `ranged` (ranged max hit on ammo), `science` (science max hit), `technology` (reduces tech battery drain) |
+
+### Equipment Slots
+
+| Slot | Description |
|---|---|
-| `stab_defense` | Defense vs stab attacks |
-| `slash_defense` | Defense vs slash attacks |
-| `crush_defense` | Defense vs crush attacks |
-| `science_defense` | Defense vs science attacks (negative on metal armor) |
-| `ranged_defense` | Defense vs ranged attacks |
-
-Other damage/utility bonuses:
-
-| Field | Description |
-|---|---|
-| `strength_bonus` | Melee max hit bonus |
-| `ranged_strength` | Ranged max hit bonus (on ammo) |
-| `science_damage` | Science max hit bonus |
-| `technology_bonus` | Reduces tech battery drain rate |
+| `head` | Helmets, hats |
+| `neck` | Amulets, necklaces |
+| `torso` | Platebodies, chest armor |
+| `legs` | Platelegs, chaps |
+| `hands` | Gloves, vambraces |
+| `feet` | Boots |
+| `back` | Capes, back items |
+| `ammo` | Arrows, bolts |
+| `main_hand` | Weapons, tools, shields |
+| `off_hand` | Shields, secondary items |
+| `ring` | Rings |
### Attack Type
-Weapons should always set `attack_type`. Common mappings:
+`attack_type` determines which defense stat the target rolls against:
-| Category | attack_type |
+| Weapon | attack_type |
|---|---|
| Swords | slash |
| Daggers | stab |
| Axes | slash |
| Pickaxes | stab |
| Bows | ranged |
+| Science decks | science |
| Unarmed | crush (default) |
-### Equipment Requirements
+### Requirements
Higher-tier equipment requires skill levels to equip:
```yaml
requirements:
accuracy: 20 # melee weapons
- defense: 20 # armor
- ranged: 30 # ranged weapons/armor
+ defense: 20 # armor
+ ranged: 30 # ranged weapons/armor
```
Standard tiers: bronze/iron (none), steel (5), black (10), mithril (20), adamant (30), rune (40), dragon (60).
### Ranged Ammo
-Arrows and bolts use `ranged_strength` for damage. Accuracy comes from the bow:
+Arrows and bolts use `other.ranged` for damage. Accuracy comes from the bow:
```yaml
name: iron arrow
stackable: true
-equip_slot: ammo
-stats:
- ranged_strength: 10
+equipment:
+ other:
+ ranged: 10
+ slot: ammo
+ type: ammo
+```
+
+### Science Decks
+
+Decks use `tech_weapon` type and `science` attack_type:
+
+```yaml
+name: basic deck
+equipment:
+ type: tech_weapon
+ slot: main_hand
+ attack_type: science
+ speed: 5
+ attack:
+ science: 5
```
### Color
@@ -102,8 +126,8 @@ The `color` field accepts xterm-256 palette indices (00-FF, hex) with optional m
```yaml
color: "B2" # bronze/gold
-color: "4B bold" # bold steel blue
-color: "g:C4,D0,E2" # gradient red → orange → gold
+color: "4B bold" # bold steel blue
+color: "g:C4,D0,E2" # gradient red → orange → gold
```
In ANSI mode, extended colors (10-FF) automatically downgrade to the nearest ANSI color. Use `colortable` in-game to see all 256 colors.
diff --git a/building_guide/recipes.md b/building_guide/recipes.md
index e865fa6..96bf0f9 100644
--- a/building_guide/recipes.md
+++ b/building_guide/recipes.md
@@ -18,7 +18,7 @@ Crafting information lives on the **output item's YAML file** via the `craft:` b
| `ingredients` | []IngredientEntry | Ingredients consumed (see below) |
| `output_qty` | int | Quantity produced (default 1, for stackables) |
| `fail` | string | ItemID produced on failure (optional) |
-| `message` | string | Success message per cycle (optional, see message defaults) |
+| `success_message` | string | Success message per cycle (optional, see message defaults) |
| `fail_message` | string | Failure message (optional, empty = silent fail) |
| `start_message`| string | Message when action begins (optional, see message defaults) |
| `end_message` | string | Message when action completes (optional, see message defaults) |
@@ -29,7 +29,7 @@ The output is the item itself — no `output` field needed. The item ID IS the c
### Message Variables
-All message fields (`message`, `fail_message`, `start_message`, `end_message`, and `steps[].message`) support variables that expand to colorized item names:
+All message fields (`success_message`, `fail_message`, `start_message`, `end_message`, and `steps[].message`) support variables that expand to colorized item names:
| Variable | Expands to |
| -------- | ----------------------------------------------- |
@@ -47,7 +47,7 @@ Variables work alongside inline color tags (`{C4}text{/}`) which are expanded af
If a message field is omitted from the YAML, the game uses a skill-level default based on the craft `type`:
-| Type | Default `message` | Default `start_message` |
+| Type | Default `success_message` | Default `start_message` |
|------|-------------------|------------------------|
| `cooking` | `"Cooked to perfection. %n looks great!"` | `"You start cooking %i1."` |
| `smelt` | `"You remove a white hot %n!"` | `"You place the %i1 into the furnace."` |
@@ -81,7 +81,7 @@ craft:
message: "You try to puzzle how the pieces fit together."
- tick: 3
message: "Aha, this edge lines up here!"
- message: "You assemble the map!"
+ success_message: "You assemble the map!"
```
### IngredientEntry — Multi-Item Ingredient Slots
@@ -120,7 +120,7 @@ craft:
quantity: 1
- items: [eye_of_newt]
quantity: 1
- message: "You mix a %n." # %n expands to "stim potion" colored
+ success_message: "You mix a %n." # %n expands to "stim potion" colored
```
### Station-Based Production
@@ -139,8 +139,8 @@ craft:
quantity: 1
- items: [tin_ore]
quantity: 1
- message: "You smelt a %n." # default is "You remove a white hot %n!"
- # overridden here for simpler flavor
+ success_message: "You smelt a %n." # default is "You remove a white hot %n!"
+ # overridden here for simpler flavor
```
```yaml
@@ -154,7 +154,7 @@ craft:
ingredients:
- items: [bronze_bar]
quantity: 1
- # message omitted — uses default "You smith a %n."
+ # success_message omitted — uses default "You smith a %n."
```
For stackable outputs, use `output_qty`:
@@ -171,7 +171,7 @@ craft:
- items: [bronze_bar]
quantity: 1
output_qty: 15
- # message omitted — uses default "You smith a %n."
+ # success_message omitted — uses default "You smith a %n."
```
### Tool-Based Production
@@ -206,7 +206,7 @@ craft:
byproducts: [empty_vial, empty_jug]
- items: [empty_bucket]
quantity: 1
- message: "You pour the %i1 into the %i2." # %i1 = water source, %i2 = bucket
+ success_message: "You pour the %i1 into the %i2." # %i1 = water source, %i2 = bucket
```
### Multi-Piece Assembly (3+ items → 1)
@@ -227,7 +227,7 @@ craft:
message: "You try to puzzle how the pieces fit together."
- tick: 3
message: "Aha, this edge lines up here!"
- message: "You assemble the %n."
+ success_message: "You assemble the %n."
```
### Clean Recipes
@@ -245,7 +245,7 @@ craft:
ingredients:
- items: [grimy_guam]
quantity: 1
- # message omitted — uses default "You clean the %i1."
+ # success_message omitted — uses default "You clean the %i1."
# %i1 expands to the grimy herb name with its color
```
diff --git a/building_guide/rooms.md b/building_guide/rooms.md
index 0211c33..d3412de 100644
--- a/building_guide/rooms.md
+++ b/building_guide/rooms.md
@@ -42,6 +42,8 @@ Simple exit — always passable:
```yaml
exits:
north: 2
+ ne: 3 # ne/nw/se/sw also work
+ southeast: 4
```
Conditional exit — blocked until a world flag is set:
@@ -90,10 +92,11 @@ exits:
### Map grid & one-way exits
-Horizontal exits (north/south/east/west) must form a consistent 2D grid: from any
-starting room, walking N/S/E/W should never land two different rooms on the same
-spot, and a given room must always resolve to the same spot. Up/down don't move on
-the grid — they lead to a separate plane (floor) that is laid out on its own.
+All exits (north/south/east/west/ne/nw/se/sw) must form a consistent 3D grid: from
+any starting room, walking horizontal directions should never land two different
+rooms on the same 3D coordinate, and a given room must always resolve to the same
+spot. Up/down move on the z-axis within this same grid (different floors share the
+same x/y coordinate space).
Startup validation enforces this, starting from `startup_validation.root_rooms`
(see config.yaml). It reports an **overlap** (two rooms on one grid cell) or a
@@ -101,9 +104,10 @@ Startup validation enforces this, starting from `startup_validation.root_rooms`
Exits don't have to be reciprocal. A one-way link (e.g. room 2001 has `east: 2006`
but 2006 has no `west` back) renders on the map as a directional arrow
-(`← ↑ → ↓`, or `< ^ > v` in ASCII) instead of a two-way bar. If a link is open in
-one direction but blocked (failing condition) in the other, the open direction's
-arrow is shown; only a link with no traversable direction shows the blocked `X`.
+(`← ↑ → ↓`, or `< ^ > v` in ASCII, with `↗↖↘↙` for diagonal) instead of a
+two-way bar. If a link is open in one direction but blocked (failing condition) in
+the other, the open direction's arrow is shown; only a link with no traversable
+direction shows the blocked `X`. Blocked up/down exits also show the blocked `X`.
### Item Spawns — ground items that respawn
diff --git a/data/.admin_history.json b/data/.admin_history.json
index 8519fc5..4bdaf10 100644
--- a/data/.admin_history.json
+++ b/data/.admin_history.json
@@ -1 +1 @@
-{"history":[{"time":"2026-06-30T14:29:37-04:00","description":"delete room 1014 (cleaned 0 exits)","file_path":"data/rooms/intro/1014.yaml","old_content":"id: 1014\nname: 'Room #1014'\ncolor: D5\ndescription: []\nexits:\n southwest:\n room: 1013\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:29:37-04:00","description":"delete room 1016 (cleaned 0 exits)","file_path":"data/rooms/intro/1016.yaml","old_content":"id: 1016\nname: New Room\ncolor: \"97\"\ndescription:\n - text: A featureless room.\nexits:\n down:\n room: 1017\n south:\n room: 1009\n southwest:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:29:38-04:00","description":"delete room 1015 (cleaned 0 exits)","file_path":"data/rooms/intro/1015.yaml","old_content":"id: 1015\nname: 'Room #1015'\ncolor: 0C\ndescription: []\nexits:\n northwest:\n room: 1018\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:29:39-04:00","description":"delete room 1017 (cleaned 0 exits)","file_path":"data/rooms/intro/1017.yaml","old_content":"id: 1017\nname: New Room\ncolor: \"\"\ndescription:\n - text: A featureless room.\nexits: {}\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:29:40-04:00","description":"delete room 1018 (cleaned 0 exits)","file_path":"data/rooms/intro/1018.yaml","old_content":"id: 1018\nname: 'Room #1018'\ncolor: D0\ndescription: []\nexits: {}\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:29:40-04:00","description":"delete room 1019 (cleaned 0 exits)","file_path":"data/rooms/intro/1019.yaml","old_content":"id: 1019\nname: 'Room #1019'\ncolor: \"\"\ndescription: []\nexits: {}\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:29:41-04:00","description":"delete room 1020 (cleaned 0 exits)","file_path":"data/rooms/intro/1020.yaml","old_content":"id: 1020\nname: 'Room #1020'\ncolor: \"\"\ndescription: []\nexits: {}\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:30:45-04:00","description":"delete room 1013 (cleaned 1 exits)","file_path":"data/rooms/intro/1013.yaml","old_content":"id: 1013\nname: 'Room #1013'\ncolor: \"72\"\ndescription: []\nexits:\n west:\n room: 1007\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:30:49-04:00","description":"delete room 1009 (cleaned 0 exits)","file_path":"data/rooms/intro/1009.yaml","old_content":"id: 1009\nname: New Room\ncolor: \"27\"\ndescription:\n - text: A featureless room.\nexits:\n northwest:\n room: 8\n southwest:\n room: 1006\n west:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs:\n - id: man\n - id: man\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:30:51-04:00","description":"delete room 1007 (cleaned 1 exits)","file_path":"data/rooms/intro/1007.yaml","old_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n west:\n room: 2004\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:30:53-04:00","description":"delete room 1006 (cleaned 4 exits)","file_path":"data/rooms/intro/1006.yaml","old_content":"id: 1006\nname: New Room\ncolor: E2\ndescription:\n - text: A featureless room.\nexits:\n north:\n room: 1005\n northwest:\n room: 1008\n up:\n room: 1010\n west:\n room: 2003\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:30:59-04:00","description":"delete room 1010 (cleaned 1 exits)","file_path":"data/rooms/intro/1010.yaml","old_content":"id: 1010\nname: New Room\ncolor: \"\"\ndescription:\n - text: A featureless room.\nexits:\n north:\n room: 1011\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:31:00-04:00","description":"delete room 1011 (cleaned 0 exits)","file_path":"data/rooms/intro/1011.yaml","old_content":"id: 1011\nname: New Room\ncolor: \"\"\ndescription:\n - text: A featureless room.\nexits:\n east:\n room: 1012\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:31:01-04:00","description":"delete room 1012 (cleaned 0 exits)","file_path":"data/rooms/intro/1012.yaml","old_content":"id: 1012\nname: New Room\ncolor: \"\"\ndescription:\n - text: A featureless room.\nexits: {}\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:31:02-04:00","description":"delete room 1005 (cleaned 2 exits)","file_path":"data/rooms/intro/1005.yaml","old_content":"id: 1005\nname: New Room\ncolor: CC\ndescription:\n - text: A featureless room.\nexits:\n southwest:\n room: 2003\n west:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:31:03-04:00","description":"delete room 1008 (cleaned 1 exits)","file_path":"data/rooms/intro/1008.yaml","old_content":"id: 1008\nname: New Room\ncolor: A9\ndescription:\n - text: A featureless room.\nexits:\n north:\n room: 7\n south:\n room: 2003\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:29-04:00","description":"update room 2003 (add exit north to 1005)","file_path":"data/rooms/intro/2003.yaml","old_content":"id: 2003\nname: Test Room\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n south:\n room: 2004\n southwest:\n room: 2005\n west:\n room: 2002\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"id: 2003\nname: Test Room\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n north:\n room: 1005\n south:\n room: 2004\n southwest:\n room: 2005\n west:\n room: 2002\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:29-04:00","description":"create room 1005","file_path":"data/rooms/intro/1005.yaml","old_content":"","new_content":"exits:\n south: 2003\nid: 1005\nname: 'Room #1005'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:30-04:00","description":"update room 1005 (add exit east to 1006)","file_path":"data/rooms/intro/1005.yaml","old_content":"exits:\n south: 2003\nid: 1005\nname: 'Room #1005'\n","new_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1006\n south:\n room: 2003\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:30-04:00","description":"create room 1006","file_path":"data/rooms/intro/1006.yaml","old_content":"","new_content":"exits:\n west: 1005\nid: 1006\nname: 'Room #1006'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:31-04:00","description":"update room 1006 (add exit south to 1007)","file_path":"data/rooms/intro/1006.yaml","old_content":"exits:\n west: 1005\nid: 1006\nname: 'Room #1006'\n","new_content":"id: 1006\nname: 'Room #1006'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1007\n west:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:31-04:00","description":"create room 1007","file_path":"data/rooms/intro/1007.yaml","old_content":"","new_content":"exits:\n north: 1006\nid: 1007\nname: 'Room #1007'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:32-04:00","description":"update room 1007 (add exit east to 1008)","file_path":"data/rooms/intro/1007.yaml","old_content":"exits:\n north: 1006\nid: 1007\nname: 'Room #1007'\n","new_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1008\n north:\n room: 1006\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:32-04:00","description":"create room 1008","file_path":"data/rooms/intro/1008.yaml","old_content":"","new_content":"exits:\n west: 1007\nid: 1008\nname: 'Room #1008'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:32-04:00","description":"update room 1008 (add exit north to 1009)","file_path":"data/rooms/intro/1008.yaml","old_content":"exits:\n west: 1007\nid: 1008\nname: 'Room #1008'\n","new_content":"id: 1008\nname: 'Room #1008'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1009\n west:\n room: 1007\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:32-04:00","description":"create room 1009","file_path":"data/rooms/intro/1009.yaml","old_content":"","new_content":"exits:\n south: 1008\nid: 1009\nname: 'Room #1009'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:33-04:00","description":"update room 1009 (add exit north to 1010)","file_path":"data/rooms/intro/1009.yaml","old_content":"exits:\n south: 1008\nid: 1009\nname: 'Room #1009'\n","new_content":"id: 1009\nname: 'Room #1009'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1010\n south:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:33-04:00","description":"create room 1010","file_path":"data/rooms/intro/1010.yaml","old_content":"","new_content":"exits:\n south: 1009\nid: 1010\nname: 'Room #1010'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:33-04:00","description":"update room 1010 (add exit east to 1011)","file_path":"data/rooms/intro/1010.yaml","old_content":"exits:\n south: 1009\nid: 1010\nname: 'Room #1010'\n","new_content":"id: 1010\nname: 'Room #1010'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1011\n south:\n room: 1009\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:33-04:00","description":"create room 1011","file_path":"data/rooms/intro/1011.yaml","old_content":"","new_content":"exits:\n west: 1010\nid: 1011\nname: 'Room #1011'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:34-04:00","description":"update room 1011 (add exit south to 1012)","file_path":"data/rooms/intro/1011.yaml","old_content":"exits:\n west: 1010\nid: 1011\nname: 'Room #1011'\n","new_content":"id: 1011\nname: 'Room #1011'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1012\n west:\n room: 1010\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:34-04:00","description":"create room 1012","file_path":"data/rooms/intro/1012.yaml","old_content":"","new_content":"exits:\n north: 1011\nid: 1012\nname: 'Room #1012'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:34-04:00","description":"update room 1012 (add exit south to 1013)","file_path":"data/rooms/intro/1012.yaml","old_content":"exits:\n north: 1011\nid: 1012\nname: 'Room #1012'\n","new_content":"id: 1012\nname: 'Room #1012'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1011\n south:\n room: 1013\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:34-04:00","description":"create room 1013","file_path":"data/rooms/intro/1013.yaml","old_content":"","new_content":"exits:\n north: 1012\nid: 1013\nname: 'Room #1013'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:35-04:00","description":"delete room 1013 (cleaned 1 exits)","file_path":"data/rooms/intro/1013.yaml","old_content":"exits:\n north: 1012\nid: 1013\nname: 'Room #1013'\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:36-04:00","description":"delete room 1012 (cleaned 1 exits)","file_path":"data/rooms/intro/1012.yaml","old_content":"id: 1012\nname: 'Room #1012'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1011\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:37-04:00","description":"delete room 1011 (cleaned 1 exits)","file_path":"data/rooms/intro/1011.yaml","old_content":"id: 1011\nname: 'Room #1011'\ncolor: \"\"\ndescription: []\nexits:\n west:\n room: 1010\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:37-04:00","description":"delete room 1010 (cleaned 1 exits)","file_path":"data/rooms/intro/1010.yaml","old_content":"id: 1010\nname: 'Room #1010'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1009\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:38-04:00","description":"delete room 1009 (cleaned 1 exits)","file_path":"data/rooms/intro/1009.yaml","old_content":"id: 1009\nname: 'Room #1009'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:39-04:00","description":"delete room 1008 (cleaned 1 exits)","file_path":"data/rooms/intro/1008.yaml","old_content":"id: 1008\nname: 'Room #1008'\ncolor: \"\"\ndescription: []\nexits:\n west:\n room: 1007\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:40-04:00","description":"delete room 1007 (cleaned 1 exits)","file_path":"data/rooms/intro/1007.yaml","old_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1006\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:40-04:00","description":"delete room 1006 (cleaned 1 exits)","file_path":"data/rooms/intro/1006.yaml","old_content":"id: 1006\nname: 'Room #1006'\ncolor: \"\"\ndescription: []\nexits:\n west:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:41-04:00","description":"delete room 1005 (cleaned 1 exits)","file_path":"data/rooms/intro/1005.yaml","old_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 2003\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T15:05:42-04:00","description":"update room 1001","file_path":"data/rooms/intro/1001.yaml","old_content":"id: 1001\nname: Inside Transport Shuttle\ncolor: \"\"\ndescription:\n - text: |-\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large framed sign hangs next to the closed cockpit door. The piston-powered staircase is firmly closed at the rear of the craft.\n\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large {11}framed sign{/} hangs next to the closed cockpit door. A piston-powered staircase leads down out the back of the small craft with a smiling attendant ready to help disembark.\nexits:\n down:\n room: 1002\n condition:\n flag: \"\"\n value: null\n not: false\n player_flag: 1001_touchdown\n has_item: \"\"\n min_credits: 0\n all_of: []\n any_of: []\n blocked_message: The piston-powered staircase is firmly closed.\n east:\n room: 2001\nobjects:\n - name: framed sign\n hidden: true\n description:\n - text: \"We hope you enjoy your trip on our shuttle! Your pilot is [TYLER].\\n\\nWhen you arrive at your final destination, please be aware of the following basic commands:\\n\\n{0B bold}Movement:{/} {0A}north{/}, {0A}south{/}, {0A}east{/}, {0A}west{/}, {0A}up{/}, {0A}down{/}\\n{0B bold}Look:{/} {0A}look{/}, {0A}look \u003citem/object/mob\u003e{/}, and {0A}map{/}\\n{0B bold}Items:{/} {0A}inv{/} to see your 28-slot inventory\\n {0A}get \u003citem\u003e{/}, {0A}drop \u003citem\u003e{/}, {0A}wear \u003citem\u003e{/}, {0A}remove \u003citem\u003e{/}\\n {0A}use \u003citem\u003e on \u003citem/object\u003e{/}\\n {0A}eq{/} for equiped gear\\n{0B bold}Comms:{/} {0A}talk \u003cmob\u003e{/} for NPC chat\\n {0A}say \u003cmsg\u003e{/} and {0A}global \u003cmsg\u003e{/} for human chat\\n{0B bold}Character:{/} {0A}score{/} and {0A}skills{/} for stats. \\n{0B bold}Combat{/} {0A}attack \u003cmob\u003e{/}\\n{0B bold}Skills{/} Many skills have shortcuts like {0A}mine{/}, {0A}fish{/}, or {0A}chop{/}\\n{0B bold}Options{/} {0A}options{/} for a list of account options\\n {0A}options \u003copt\u003e \u003cvalue\u003e{/} to change an option\\n\\nType {0C bold}what{/} for a list of possible common actions\\n\\nMake sure to check the {0A}help{/} pages, especially {0A}help newplayer{/}!\"\n on_look:\n set_flags: {}\n set_player_flags:\n 1001_look_sign: true\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\n cost: 0\n shop: null\n assign_task: false\n skip_task: false\n extend_task: false\n reputation_cost: 0\n sawmill: false\n aps_node: false\n - name: door\n hidden: true\n description:\n - text: A sturdy, locked steel door that keeps passengers out of the cockpit. There is a tiny, thin window at eye level.\n - name: window\n hidden: true\n description:\n - text: A thick trim with rounded bolt heads frames the tiny window. It looks like it was cut into the door at some point, potentially as a simple replacement to computerized security systems. Inside the cockpit there's a pilot finishing up landing procedures on a large panel of analog instruments.\n - name: instrument panel\n aliases:\n - cockpit\n hidden: true\n description:\n - text: The cramped cockpit is surrounded by buttons, swiches, dials, and analog gauges. The parts are a mix of technologies from the last century. Part of the panel is obscured by a draped cloth as if to hide it from view. Odd.\n - name: pilot\n hidden: true\n description:\n - text: That's the guy who took you from the Passenger Barge Denali down here to Vesta. A middle-aged man in a neat white jumpsuit with a matching white sort of conductor's hat. He's following a written procedure checklist and seems to take his job seriously.\nitem_spawns: []\nmobs:\n - id: flight_attendant\non_enter:\n - message: '{0B bold}Welcome to The House of Icarus{/}'\n condition:\n flag: \"\"\n value: null\n not: true\n player_flag: 1001_welcome\n has_item: \"\"\n min_credits: 0\n all_of: []\n any_of: []\n delay: 5\n set_flags: {}\n set_player_flags: {}\n broadcast: \"\"\n broadcast_global: \"\"\n spawn_mob: null\n despawn_mob: \"\"\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\n - message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'\n condition:\n flag: \"\"\n value: null\n not: true\n player_flag: 1001_welcome\n has_item: \"\"\n min_credits: 0\n all_of: []\n any_of: []\n delay: 7\n set_flags: {}\n set_player_flags:\n 1001_welcome: true\n broadcast: \"\"\n broadcast_global: \"\"\n spawn_mob: null\n despawn_mob: \"\"\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\n - message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'\n condition:\n flag: \"\"\n value: null\n not: true\n player_flag: 1001_welcome\n has_item: \"\"\n min_credits: 0\n all_of: []\n any_of: []\n delay: 7\n set_flags: {}\n set_player_flags: {}\n broadcast: \"\"\n broadcast_global: \"\"\n spawn_mob: null\n despawn_mob: \"\"\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\nhazard: \"\"\nblock_transport: true\ntriggers:\n - id: \"\"\n on_player_flag: 1001_look_sign\n on_flag: \"\"\n value: null\n room: 0\n steps:\n - delay: 5\n message: The cabin shakes as the small craft touches down\n broadcast: \"\"\n broadcast_global: \"\"\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\n despawn_mob: \"\"\n - delay: 5\n message: The pistons hiss as the rear staircase opens\n broadcast: \"\"\n broadcast_global: \"\"\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\n despawn_mob: \"\"\n - delay: 0\n message: \"\"\n broadcast: \"\"\n broadcast_global: \"\"\n set_flags: {}\n set_player_flags:\n 1001_touchdown: true\n spawn_mob: null\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\n despawn_mob: \"\"\n","new_content":"block_transport: true\ncolor: \"\"\ndescription:\n - text: |-\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large framed sign hangs next to the closed cockpit door. The piston-powered staircase is firmly closed at the rear of the craft.\n\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large {11}framed sign{/} hangs next to the closed cockpit door. A piston-powered staircase leads down out the back of the small craft with a smiling attendant ready to help disembark.\nexits:\n down:\n blocked_message: The piston-powered staircase is firmly closed.\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: false\n player_flag: 1001_touchdown\n value: null\n room: 1002\n east:\n room: 2001\nhazard: \"\"\nid: 1001\nitem_spawns: []\nmobs:\n - id: flight_attendant\nname: Inside Transport Shuttle\nobjects:\n - description:\n - text: \"We hope you enjoy your trip on our shuttle! Your pilot is [TYLER].\\n\\nWhen you arrive at your final destination, please be aware of the following basic commands:\\n\\n{0B bold}Movement:{/} {0A}north{/}, {0A}south{/}, {0A}east{/}, {0A}west{/}, {0A}up{/}, {0A}down{/}\\n{0B bold}Look:{/} {0A}look{/}, {0A}look \u003citem/object/mob\u003e{/}, and {0A}map{/}\\n{0B bold}Items:{/} {0A}inv{/} to see your 28-slot inventory\\n {0A}get \u003citem\u003e{/}, {0A}drop \u003citem\u003e{/}, {0A}wear \u003citem\u003e{/}, {0A}remove \u003citem\u003e{/}\\n {0A}use \u003citem\u003e on \u003citem/object\u003e{/}\\n {0A}eq{/} for equiped gear\\n{0B bold}Comms:{/} {0A}talk \u003cmob\u003e{/} for NPC chat\\n {0A}say \u003cmsg\u003e{/} and {0A}global \u003cmsg\u003e{/} for human chat\\n{0B bold}Character:{/} {0A}score{/} and {0A}skills{/} for stats. \\n{0B bold}Combat{/} {0A}attack \u003cmob\u003e{/}\\n{0B bold}Skills{/} Many skills have shortcuts like {0A}mine{/}, {0A}fish{/}, or {0A}chop{/}\\n{0B bold}Options{/} {0A}options{/} for a list of account options\\n {0A}options \u003copt\u003e \u003cvalue\u003e{/} to change an option\\n\\nType {0C bold}what{/} for a list of possible common actions\\n\\nMake sure to check the {0A}help{/} pages, especially {0A}help newplayer{/}!\"\n hidden: true\n name: framed sign\n on_look:\n aps_node: false\n assign_task: false\n cost: 0\n extend_task: false\n give_item: \"\"\n heal: 0\n reputation_cost: 0\n sawmill: false\n set_flags: {}\n set_player_flags:\n 1001_look_sign: true\n shop: null\n skip_task: false\n take_item: \"\"\n teleport: 0\n - description:\n - text: A sturdy, locked steel door that keeps passengers out of the cockpit. There is a tiny, thin window at eye level.\n hidden: true\n name: door\n - description:\n - text: A thick trim with rounded bolt heads frames the tiny window. It looks like it was cut into the door at some point, potentially as a simple replacement to computerized security systems. Inside the cockpit there's a pilot finishing up landing procedures on a large panel of analog instruments.\n hidden: true\n name: window\n - aliases:\n - cockpit\n description:\n - text: The cramped cockpit is surrounded by buttons, swiches, dials, and analog gauges. The parts are a mix of technologies from the last century. Part of the panel is obscured by a draped cloth as if to hide it from view. Odd.\n hidden: true\n name: instrument panel\n - aliases: []\n description:\n - text: That's the guy who took you from the Passenger Barge Denali down here to Vesta. A middle-aged man in a neat white jumpsuit with a matching white sort of conductor's hat. He's following a written procedure checklist and seems to take his job seriously.\n hidden: false\n name: pilot\non_enter:\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B bold}Welcome to The House of Icarus{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'\n set_flags: {}\n set_player_flags:\n 1001_welcome: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\ntriggers:\n - id: \"\"\n on_flag: \"\"\n on_player_flag: 1001_look_sign\n room: 0\n steps:\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The cabin shakes as the small craft touches down\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The pistons hiss as the rear staircase opens\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 0\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: \"\"\n set_flags: {}\n set_player_flags:\n 1001_touchdown: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n value: null\n","is_delete":false,"is_create":false},{"time":"2026-06-30T15:05:43-04:00","description":"update room 1001","file_path":"data/rooms/intro/1001.yaml","old_content":"block_transport: true\ncolor: \"\"\ndescription:\n - text: |-\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large framed sign hangs next to the closed cockpit door. The piston-powered staircase is firmly closed at the rear of the craft.\n\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large {11}framed sign{/} hangs next to the closed cockpit door. A piston-powered staircase leads down out the back of the small craft with a smiling attendant ready to help disembark.\nexits:\n down:\n blocked_message: The piston-powered staircase is firmly closed.\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: false\n player_flag: 1001_touchdown\n value: null\n room: 1002\n east:\n room: 2001\nhazard: \"\"\nid: 1001\nitem_spawns: []\nmobs:\n - id: flight_attendant\nname: Inside Transport Shuttle\nobjects:\n - description:\n - text: \"We hope you enjoy your trip on our shuttle! Your pilot is [TYLER].\\n\\nWhen you arrive at your final destination, please be aware of the following basic commands:\\n\\n{0B bold}Movement:{/} {0A}north{/}, {0A}south{/}, {0A}east{/}, {0A}west{/}, {0A}up{/}, {0A}down{/}\\n{0B bold}Look:{/} {0A}look{/}, {0A}look \u003citem/object/mob\u003e{/}, and {0A}map{/}\\n{0B bold}Items:{/} {0A}inv{/} to see your 28-slot inventory\\n {0A}get \u003citem\u003e{/}, {0A}drop \u003citem\u003e{/}, {0A}wear \u003citem\u003e{/}, {0A}remove \u003citem\u003e{/}\\n {0A}use \u003citem\u003e on \u003citem/object\u003e{/}\\n {0A}eq{/} for equiped gear\\n{0B bold}Comms:{/} {0A}talk \u003cmob\u003e{/} for NPC chat\\n {0A}say \u003cmsg\u003e{/} and {0A}global \u003cmsg\u003e{/} for human chat\\n{0B bold}Character:{/} {0A}score{/} and {0A}skills{/} for stats. \\n{0B bold}Combat{/} {0A}attack \u003cmob\u003e{/}\\n{0B bold}Skills{/} Many skills have shortcuts like {0A}mine{/}, {0A}fish{/}, or {0A}chop{/}\\n{0B bold}Options{/} {0A}options{/} for a list of account options\\n {0A}options \u003copt\u003e \u003cvalue\u003e{/} to change an option\\n\\nType {0C bold}what{/} for a list of possible common actions\\n\\nMake sure to check the {0A}help{/} pages, especially {0A}help newplayer{/}!\"\n hidden: true\n name: framed sign\n on_look:\n aps_node: false\n assign_task: false\n cost: 0\n extend_task: false\n give_item: \"\"\n heal: 0\n reputation_cost: 0\n sawmill: false\n set_flags: {}\n set_player_flags:\n 1001_look_sign: true\n shop: null\n skip_task: false\n take_item: \"\"\n teleport: 0\n - description:\n - text: A sturdy, locked steel door that keeps passengers out of the cockpit. There is a tiny, thin window at eye level.\n hidden: true\n name: door\n - description:\n - text: A thick trim with rounded bolt heads frames the tiny window. It looks like it was cut into the door at some point, potentially as a simple replacement to computerized security systems. Inside the cockpit there's a pilot finishing up landing procedures on a large panel of analog instruments.\n hidden: true\n name: window\n - aliases:\n - cockpit\n description:\n - text: The cramped cockpit is surrounded by buttons, swiches, dials, and analog gauges. The parts are a mix of technologies from the last century. Part of the panel is obscured by a draped cloth as if to hide it from view. Odd.\n hidden: true\n name: instrument panel\n - aliases: []\n description:\n - text: That's the guy who took you from the Passenger Barge Denali down here to Vesta. A middle-aged man in a neat white jumpsuit with a matching white sort of conductor's hat. He's following a written procedure checklist and seems to take his job seriously.\n hidden: false\n name: pilot\non_enter:\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B bold}Welcome to The House of Icarus{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'\n set_flags: {}\n set_player_flags:\n 1001_welcome: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\ntriggers:\n - id: \"\"\n on_flag: \"\"\n on_player_flag: 1001_look_sign\n room: 0\n steps:\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The cabin shakes as the small craft touches down\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The pistons hiss as the rear staircase opens\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 0\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: \"\"\n set_flags: {}\n set_player_flags:\n 1001_touchdown: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n value: null\n","new_content":"block_transport: true\ncolor: \"\"\ndescription:\n - text: |-\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large framed sign hangs next to the closed cockpit door. The piston-powered staircase is firmly closed at the rear of the craft.\n\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large {11}framed sign{/} hangs next to the closed cockpit door. A piston-powered staircase leads down out the back of the small craft with a smiling attendant ready to help disembark.\nexits:\n down:\n blocked_message: The piston-powered staircase is firmly closed.\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: false\n player_flag: 1001_touchdown\n value: null\n room: 1002\n east:\n room: 2001\nhazard: \"\"\nid: 1001\nitem_spawns: []\nmobs:\n - id: flight_attendant\nname: Inside Transport Shuttle\nobjects:\n - description:\n - text: \"We hope you enjoy your trip on our shuttle! Your pilot is [TYLER].\\n\\nWhen you arrive at your final destination, please be aware of the following basic commands:\\n\\n{0B bold}Movement:{/} {0A}north{/}, {0A}south{/}, {0A}east{/}, {0A}west{/}, {0A}up{/}, {0A}down{/}\\n{0B bold}Look:{/} {0A}look{/}, {0A}look \u003citem/object/mob\u003e{/}, and {0A}map{/}\\n{0B bold}Items:{/} {0A}inv{/} to see your 28-slot inventory\\n {0A}get \u003citem\u003e{/}, {0A}drop \u003citem\u003e{/}, {0A}wear \u003citem\u003e{/}, {0A}remove \u003citem\u003e{/}\\n {0A}use \u003citem\u003e on \u003citem/object\u003e{/}\\n {0A}eq{/} for equiped gear\\n{0B bold}Comms:{/} {0A}talk \u003cmob\u003e{/} for NPC chat\\n {0A}say \u003cmsg\u003e{/} and {0A}global \u003cmsg\u003e{/} for human chat\\n{0B bold}Character:{/} {0A}score{/} and {0A}skills{/} for stats. \\n{0B bold}Combat{/} {0A}attack \u003cmob\u003e{/}\\n{0B bold}Skills{/} Many skills have shortcuts like {0A}mine{/}, {0A}fish{/}, or {0A}chop{/}\\n{0B bold}Options{/} {0A}options{/} for a list of account options\\n {0A}options \u003copt\u003e \u003cvalue\u003e{/} to change an option\\n\\nType {0C bold}what{/} for a list of possible common actions\\n\\nMake sure to check the {0A}help{/} pages, especially {0A}help newplayer{/}!\"\n hidden: true\n name: framed sign\n on_look:\n aps_node: false\n assign_task: false\n cost: 0\n extend_task: false\n give_item: \"\"\n heal: 0\n reputation_cost: 0\n sawmill: false\n set_flags: {}\n set_player_flags:\n 1001_look_sign: true\n shop: null\n skip_task: false\n take_item: \"\"\n teleport: 0\n - description:\n - text: A sturdy, locked steel door that keeps passengers out of the cockpit. There is a tiny, thin window at eye level.\n hidden: true\n name: door\n - description:\n - text: A thick trim with rounded bolt heads frames the tiny window. It looks like it was cut into the door at some point, potentially as a simple replacement to computerized security systems. Inside the cockpit there's a pilot finishing up landing procedures on a large panel of analog instruments.\n hidden: true\n name: window\n - aliases:\n - cockpit\n description:\n - text: The cramped cockpit is surrounded by buttons, swiches, dials, and analog gauges. The parts are a mix of technologies from the last century. Part of the panel is obscured by a draped cloth as if to hide it from view. Odd.\n hidden: true\n name: instrument panel\n - aliases: []\n description:\n - text: That's the guy who took you from the Passenger Barge Denali down here to Vesta. A middle-aged man in a neat white jumpsuit with a matching white sort of conductor's hat. He's following a written procedure checklist and seems to take his job seriously.\n hidden: true\n name: pilot\non_enter:\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B bold}Welcome to The House of Icarus{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'\n set_flags: {}\n set_player_flags:\n 1001_welcome: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\ntriggers:\n - id: \"\"\n on_flag: \"\"\n on_player_flag: 1001_look_sign\n room: 0\n steps:\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The cabin shakes as the small craft touches down\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The pistons hiss as the rear staircase opens\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 0\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: \"\"\n set_flags: {}\n set_player_flags:\n 1001_touchdown: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n value: null\n","is_delete":false,"is_create":false},{"time":"2026-06-30T16:34:21-04:00","description":"update room 2001","file_path":"data/rooms/intro/2001.yaml","old_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns: []\nmobs: []\nname: Test Room\nobjects: []\non_enter: []\ntriggers: []\n","new_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns: []\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T16:35:02-04:00","description":"update room 2001","file_path":"data/rooms/intro/2001.yaml","old_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns: []\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","new_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T16:35:15-04:00","description":"update room 2001","file_path":"data/rooms/intro/2001.yaml","old_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","new_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T17:43:28-04:00","description":"Update object copper_rock","file_path":"data/objects/copper_rock.yaml","old_content":"color: \"B2\"\ngather:\n depleted_message: You don't see any ore in this rock.\n drops:\n - depletes: true\n item_id: copper_ore\n level: 1\n message: You manage to mine some {B2}copper ore{/}.\n weight: 90\n xp: 17\n - depletes: false\n message: You spot a glint of something valuable!\n table: gem_table\n weight: 10\n fail_message: You chip away but get nothing useful.\n gather_message: You swing your pickaxe at the rock...\n respawn_broadcast: A glint of copper catches your eye from some {name}.\n respawn_timer: 50\n skill: mining\n success:\n base: 0.5\n cap: 0.95\n per_level: 0.01\n tools:\n - pickaxe\nname: copper rock\n","new_content":"aliases: []\ncolor: B2\ndescription: \"\"\ngather:\n bait: \"\"\n deplete_timer: 0\n depleted_message: You don't see any ore in this rock.\n drops:\n - depletes: true\n item_id: copper_ore\n level: 1\n message: You manage to mine some {B2}copper ore{/}.\n quantity: 0\n table: \"\"\n weight: 90\n xp: 17\n - depletes: false\n item_id: \"\"\n level: 0\n message: You spot a glint of something valuable!\n quantity: 0\n table: gem_table\n weight: 10\n xp: 0\n exhausted_message: \"\"\n fail_message: You chip away but get nothing useful.\n gather_message: You swing your pickaxe at the rock...\n nest_chance: 0\n respawn_broadcast: A glint of copper catches your eye from some {name}.\n respawn_timer: 50\n skill: mining\n success:\n base: 0.5\n cap: 0.95\n per_level: 0.01\n tools:\n - pickaxe\nhidden: false\nid: copper_rock\ninroom_description: \"\"\nname: copper rock\nremoval_item: \"\"\n","is_delete":false,"is_create":false},{"time":"2026-06-30T23:48:18-04:00","description":"update room 2001","file_path":"data/rooms/intro/2001.yaml","old_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","new_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\n - id: pot_of_flour\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T23:48:23-04:00","description":"update room 2001","file_path":"data/rooms/intro/2001.yaml","old_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\n - id: pot_of_flour\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","new_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\n - id: pot_of_flour\n quantity: 1\n respawn_ticks: 0\n - id: bucket_of_water\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T23:53:08-04:00","description":"Update item bread_dough","file_path":"data/items/consumables/bread_dough.yaml","old_content":"color: \"DE\"\ncraft:\n type: combine\n consume:\n - byproducts:\n - empty_pot\n items:\n - pot_of_flour\n quantity: 1\n - byproducts:\n - empty_bucket\n - empty_jug\n items:\n - bucket_of_water\n - jug_of_water\n quantity: 1\n wait: 2\ndescription: A ball of uncooked bread dough. It needs to be baked.\nname: bread dough\nstackable: false\nvalue: 3\n","new_content":"color: DE\ncraft:\n - consume:\n - byproducts:\n - empty_pot\n items:\n - pot_of_flour\n quantity: 1\n - byproducts:\n - empty_bucket\n - empty_jug\n items:\n - bucket_of_water\n - jug_of_water\n quantity: 1\n end_message: testend\n fail: \"\"\n level: 0\n message: testmsg\n output_qty: 0\n start_message: teststart\n station: []\n subtype: \"\"\n tool: \"\"\n type: combine\n wait: 2\n xp: 0\ndescription: A ball of uncooked bread dough. It needs to be baked.\nid: bread_dough\nmax_quality: 0\nname: bread dough\nquality: 0\nstackable: false\nvalue: 3\n","is_delete":false,"is_create":false},{"time":"2026-07-01T00:23:04-04:00","description":"Update item bread_dough","file_path":"data/items/consumables/bread_dough.yaml","old_content":"color: DE\ncraft:\n - consume:\n - byproducts:\n - empty_pot\n items:\n - pot_of_flour\n quantity: 1\n - byproducts:\n - empty_bucket\n - empty_jug\n items:\n - bucket_of_water\n - jug_of_water\n quantity: 1\n end_message: testend\n fail: \"\"\n level: 0\n message: testmsg\n output_qty: 0\n start_message: teststart\n station: []\n subtype: \"\"\n tool: \"\"\n type: combine\n wait: 2\n xp: 0\ndescription: A ball of uncooked bread dough. It needs to be baked.\nid: bread_dough\nmax_quality: 0\nname: bread dough\nquality: 0\nstackable: false\nvalue: 3\n","new_content":"color: DE\ncraft:\n - consume:\n - byproducts:\n - empty_pot\n items:\n - pot_of_flour\n quantity: 1\n - byproducts:\n - empty_bucket\n - empty_jug\n items:\n - bucket_of_water\n - jug_of_water\n quantity: 1\n end_message: testend\n fail: \"\"\n level: 0\n message: testmsg\n output_qty: 0\n start_message: teststart\n station: []\n subtype: \"\"\n tool: \"\"\n type: combine\n wait: 2\n xp: 0\ndescription: A ball of uncooked bread dough. It needs to be baked.\nid: bread_dough\nmax_quality: 0\nname: bread dough\nquality: 0\nstackable: false\nvalue: 3\n","is_delete":false,"is_create":false},{"time":"2026-07-01T01:22:55-04:00","description":"update room 1001","file_path":"data/rooms/intro/1001.yaml","old_content":"block_transport: true\ncolor: \"\"\ndescription:\n - text: |-\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large framed sign hangs next to the closed cockpit door. The piston-powered staircase is firmly closed at the rear of the craft.\n\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large {11}framed sign{/} hangs next to the closed cockpit door. A piston-powered staircase leads down out the back of the small craft with a smiling attendant ready to help disembark.\nexits:\n down:\n blocked_message: The piston-powered staircase is firmly closed.\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: false\n player_flag: 1001_touchdown\n value: null\n room: 1002\n east:\n room: 2001\nhazard: \"\"\nid: 1001\nitem_spawns: []\nmobs:\n - id: flight_attendant\nname: Inside Transport Shuttle\nobjects:\n - description:\n - text: \"We hope you enjoy your trip on our shuttle! Your pilot is [TYLER].\\n\\nWhen you arrive at your final destination, please be aware of the following basic commands:\\n\\n{0B bold}Movement:{/} {0A}north{/}, {0A}south{/}, {0A}east{/}, {0A}west{/}, {0A}up{/}, {0A}down{/}\\n{0B bold}Look:{/} {0A}look{/}, {0A}look \u003citem/object/mob\u003e{/}, and {0A}map{/}\\n{0B bold}Items:{/} {0A}inv{/} to see your 28-slot inventory\\n {0A}get \u003citem\u003e{/}, {0A}drop \u003citem\u003e{/}, {0A}wear \u003citem\u003e{/}, {0A}remove \u003citem\u003e{/}\\n {0A}use \u003citem\u003e on \u003citem/object\u003e{/}\\n {0A}eq{/} for equiped gear\\n{0B bold}Comms:{/} {0A}talk \u003cmob\u003e{/} for NPC chat\\n {0A}say \u003cmsg\u003e{/} and {0A}global \u003cmsg\u003e{/} for human chat\\n{0B bold}Character:{/} {0A}score{/} and {0A}skills{/} for stats. \\n{0B bold}Combat{/} {0A}attack \u003cmob\u003e{/}\\n{0B bold}Skills{/} Many skills have shortcuts like {0A}mine{/}, {0A}fish{/}, or {0A}chop{/}\\n{0B bold}Options{/} {0A}options{/} for a list of account options\\n {0A}options \u003copt\u003e \u003cvalue\u003e{/} to change an option\\n\\nType {0C bold}what{/} for a list of possible common actions\\n\\nMake sure to check the {0A}help{/} pages, especially {0A}help newplayer{/}!\"\n hidden: true\n name: framed sign\n on_look:\n aps_node: false\n assign_task: false\n cost: 0\n extend_task: false\n give_item: \"\"\n heal: 0\n reputation_cost: 0\n sawmill: false\n set_flags: {}\n set_player_flags:\n 1001_look_sign: true\n shop: null\n skip_task: false\n take_item: \"\"\n teleport: 0\n - description:\n - text: A sturdy, locked steel door that keeps passengers out of the cockpit. There is a tiny, thin window at eye level.\n hidden: true\n name: door\n - description:\n - text: A thick trim with rounded bolt heads frames the tiny window. It looks like it was cut into the door at some point, potentially as a simple replacement to computerized security systems. Inside the cockpit there's a pilot finishing up landing procedures on a large panel of analog instruments.\n hidden: true\n name: window\n - aliases:\n - cockpit\n description:\n - text: The cramped cockpit is surrounded by buttons, swiches, dials, and analog gauges. The parts are a mix of technologies from the last century. Part of the panel is obscured by a draped cloth as if to hide it from view. Odd.\n hidden: true\n name: instrument panel\n - aliases: []\n description:\n - text: That's the guy who took you from the Passenger Barge Denali down here to Vesta. A middle-aged man in a neat white jumpsuit with a matching white sort of conductor's hat. He's following a written procedure checklist and seems to take his job seriously.\n hidden: true\n name: pilot\non_enter:\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B bold}Welcome to The House of Icarus{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'\n set_flags: {}\n set_player_flags:\n 1001_welcome: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\ntriggers:\n - id: \"\"\n on_flag: \"\"\n on_player_flag: 1001_look_sign\n room: 0\n steps:\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The cabin shakes as the small craft touches down\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The pistons hiss as the rear staircase opens\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 0\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: \"\"\n set_flags: {}\n set_player_flags:\n 1001_touchdown: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n value: null\n","new_content":"block_transport: true\ncolor: \"\"\ndescription:\n - text: |-\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large framed sign hangs next to the closed cockpit door. The piston-powered staircase is firmly closed at the rear of the craft.\n\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large {11}framed sign{/} hangs next to the closed cockpit door. A piston-powered staircase leads down out the back of the small craft with a smiling attendant ready to help disembark.\nexits:\n down:\n blocked_message: The piston-powered staircase is firmly closed.\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: false\n player_flag: 1001_touchdown\n value: null\n room: 1002\n east:\n room: 2001\nhazard: \"\"\nid: 1001\nitem_spawns: []\nmobs:\n - id: flight_attendant\nname: Inside Transport Shuttle\nobjects:\n - description:\n - text: \"We hope you enjoy your trip on our shuttle! Your pilot is [TYLER].\\n\\nWhen you arrive at your final destination, please be aware of the following basic commands:\\n\\n{0B bold}Movement:{/} {0A}north{/}, {0A}south{/}, {0A}east{/}, {0A}west{/}, {0A}up{/}, {0A}down{/}\\n{0B bold}Look:{/} {0A}look{/}, {0A}look \u003citem/object/mob\u003e{/}, and {0A}map{/}\\n{0B bold}Items:{/} {0A}inv{/} to see your 28-slot inventory\\n {0A}get \u003citem\u003e{/}, {0A}drop \u003citem\u003e{/}, {0A}wear \u003citem\u003e{/}, {0A}remove \u003citem\u003e{/}\\n {0A}use \u003citem\u003e on \u003citem/object\u003e{/}\\n {0A}eq{/} for equiped gear\\n{0B bold}Comms:{/} {0A}talk \u003cmob\u003e{/} for NPC chat\\n {0A}say \u003cmsg\u003e{/} and {0A}global \u003cmsg\u003e{/} for human chat\\n{0B bold}Character:{/} {0A}score{/} and {0A}skills{/} for stats. \\n{0B bold}Combat{/} {0A}attack \u003cmob\u003e{/}\\n{0B bold}Skills{/} Many skills have shortcuts like {0A}mine{/}, {0A}fish{/}, or {0A}chop{/}\\n{0B bold}Options{/} {0A}options{/} for a list of account options\\n {0A}options \u003copt\u003e \u003cvalue\u003e{/} to change an option\\n\\nType {0C bold}what{/} for a list of possible common actions\\n\\nMake sure to check the {0A}help{/} pages, especially {0A}help newplayer{/}!\"\n hidden: true\n name: framed sign\n on_look:\n aps_node: false\n assign_task: false\n cost: 0\n extend_task: false\n give_item: \"\"\n heal: 0\n reputation_cost: 0\n sawmill: false\n set_flags: {}\n set_player_flags:\n 1001_look_sign: true\n shop: null\n skip_task: false\n take_item: \"\"\n teleport: 0\n - description:\n - text: A sturdy, locked steel door that keeps passengers out of the cockpit. There is a tiny, thin window at eye level.\n hidden: true\n name: door\n - description:\n - text: A thick trim with rounded bolt heads frames the tiny window. It looks like it was cut into the door at some point, potentially as a simple replacement to computerized security systems. Inside the cockpit there's a pilot finishing up landing procedures on a large panel of analog instruments.\n hidden: true\n name: window\n - aliases:\n - cockpit\n description:\n - text: The cramped cockpit is surrounded by buttons, swiches, dials, and analog gauges. The parts are a mix of technologies from the last century. Part of the panel is obscured by a draped cloth as if to hide it from view. Odd.\n hidden: true\n name: instrument panel\n - aliases: []\n description:\n - text: That's the guy who took you from the Passenger Barge Denali down here to Vesta. A middle-aged man in a neat white jumpsuit with a matching white sort of conductor's hat. He's following a written procedure checklist and seems to take his job seriously.\n hidden: true\n name: pilot\non_enter:\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B bold}Welcome to The House of Icarus{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'\n set_flags: {}\n set_player_flags:\n 1001_welcome: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\ntriggers:\n - id: \"\"\n on_flag: \"\"\n on_player_flag: 1001_look_sign\n room: 0\n steps:\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The cabin shakes as the small craft touches down\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The pistons hiss as the rear staircase opens\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 0\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: \"\"\n set_flags: {}\n set_player_flags:\n 1001_touchdown: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n value: null\n","is_delete":false,"is_create":false}],"redo":null} \ No newline at end of file
+{"history":[{"time":"2026-06-30T14:29:37-04:00","description":"delete room 1014 (cleaned 0 exits)","file_path":"data/rooms/intro/1014.yaml","old_content":"id: 1014\nname: 'Room #1014'\ncolor: D5\ndescription: []\nexits:\n southwest:\n room: 1013\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:29:37-04:00","description":"delete room 1016 (cleaned 0 exits)","file_path":"data/rooms/intro/1016.yaml","old_content":"id: 1016\nname: New Room\ncolor: \"97\"\ndescription:\n - text: A featureless room.\nexits:\n down:\n room: 1017\n south:\n room: 1009\n southwest:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:29:38-04:00","description":"delete room 1015 (cleaned 0 exits)","file_path":"data/rooms/intro/1015.yaml","old_content":"id: 1015\nname: 'Room #1015'\ncolor: 0C\ndescription: []\nexits:\n northwest:\n room: 1018\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:29:39-04:00","description":"delete room 1017 (cleaned 0 exits)","file_path":"data/rooms/intro/1017.yaml","old_content":"id: 1017\nname: New Room\ncolor: \"\"\ndescription:\n - text: A featureless room.\nexits: {}\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:29:40-04:00","description":"delete room 1018 (cleaned 0 exits)","file_path":"data/rooms/intro/1018.yaml","old_content":"id: 1018\nname: 'Room #1018'\ncolor: D0\ndescription: []\nexits: {}\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:29:40-04:00","description":"delete room 1019 (cleaned 0 exits)","file_path":"data/rooms/intro/1019.yaml","old_content":"id: 1019\nname: 'Room #1019'\ncolor: \"\"\ndescription: []\nexits: {}\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:29:41-04:00","description":"delete room 1020 (cleaned 0 exits)","file_path":"data/rooms/intro/1020.yaml","old_content":"id: 1020\nname: 'Room #1020'\ncolor: \"\"\ndescription: []\nexits: {}\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:30:45-04:00","description":"delete room 1013 (cleaned 1 exits)","file_path":"data/rooms/intro/1013.yaml","old_content":"id: 1013\nname: 'Room #1013'\ncolor: \"72\"\ndescription: []\nexits:\n west:\n room: 1007\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:30:49-04:00","description":"delete room 1009 (cleaned 0 exits)","file_path":"data/rooms/intro/1009.yaml","old_content":"id: 1009\nname: New Room\ncolor: \"27\"\ndescription:\n - text: A featureless room.\nexits:\n northwest:\n room: 8\n southwest:\n room: 1006\n west:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs:\n - id: man\n - id: man\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:30:51-04:00","description":"delete room 1007 (cleaned 1 exits)","file_path":"data/rooms/intro/1007.yaml","old_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n west:\n room: 2004\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:30:53-04:00","description":"delete room 1006 (cleaned 4 exits)","file_path":"data/rooms/intro/1006.yaml","old_content":"id: 1006\nname: New Room\ncolor: E2\ndescription:\n - text: A featureless room.\nexits:\n north:\n room: 1005\n northwest:\n room: 1008\n up:\n room: 1010\n west:\n room: 2003\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:30:59-04:00","description":"delete room 1010 (cleaned 1 exits)","file_path":"data/rooms/intro/1010.yaml","old_content":"id: 1010\nname: New Room\ncolor: \"\"\ndescription:\n - text: A featureless room.\nexits:\n north:\n room: 1011\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:31:00-04:00","description":"delete room 1011 (cleaned 0 exits)","file_path":"data/rooms/intro/1011.yaml","old_content":"id: 1011\nname: New Room\ncolor: \"\"\ndescription:\n - text: A featureless room.\nexits:\n east:\n room: 1012\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:31:01-04:00","description":"delete room 1012 (cleaned 0 exits)","file_path":"data/rooms/intro/1012.yaml","old_content":"id: 1012\nname: New Room\ncolor: \"\"\ndescription:\n - text: A featureless room.\nexits: {}\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:31:02-04:00","description":"delete room 1005 (cleaned 2 exits)","file_path":"data/rooms/intro/1005.yaml","old_content":"id: 1005\nname: New Room\ncolor: CC\ndescription:\n - text: A featureless room.\nexits:\n southwest:\n room: 2003\n west:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:31:03-04:00","description":"delete room 1008 (cleaned 1 exits)","file_path":"data/rooms/intro/1008.yaml","old_content":"id: 1008\nname: New Room\ncolor: A9\ndescription:\n - text: A featureless room.\nexits:\n north:\n room: 7\n south:\n room: 2003\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:29-04:00","description":"update room 2003 (add exit north to 1005)","file_path":"data/rooms/intro/2003.yaml","old_content":"id: 2003\nname: Test Room\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n south:\n room: 2004\n southwest:\n room: 2005\n west:\n room: 2002\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"id: 2003\nname: Test Room\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n north:\n room: 1005\n south:\n room: 2004\n southwest:\n room: 2005\n west:\n room: 2002\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:29-04:00","description":"create room 1005","file_path":"data/rooms/intro/1005.yaml","old_content":"","new_content":"exits:\n south: 2003\nid: 1005\nname: 'Room #1005'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:30-04:00","description":"update room 1005 (add exit east to 1006)","file_path":"data/rooms/intro/1005.yaml","old_content":"exits:\n south: 2003\nid: 1005\nname: 'Room #1005'\n","new_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1006\n south:\n room: 2003\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:30-04:00","description":"create room 1006","file_path":"data/rooms/intro/1006.yaml","old_content":"","new_content":"exits:\n west: 1005\nid: 1006\nname: 'Room #1006'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:31-04:00","description":"update room 1006 (add exit south to 1007)","file_path":"data/rooms/intro/1006.yaml","old_content":"exits:\n west: 1005\nid: 1006\nname: 'Room #1006'\n","new_content":"id: 1006\nname: 'Room #1006'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1007\n west:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:31-04:00","description":"create room 1007","file_path":"data/rooms/intro/1007.yaml","old_content":"","new_content":"exits:\n north: 1006\nid: 1007\nname: 'Room #1007'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:32-04:00","description":"update room 1007 (add exit east to 1008)","file_path":"data/rooms/intro/1007.yaml","old_content":"exits:\n north: 1006\nid: 1007\nname: 'Room #1007'\n","new_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1008\n north:\n room: 1006\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:32-04:00","description":"create room 1008","file_path":"data/rooms/intro/1008.yaml","old_content":"","new_content":"exits:\n west: 1007\nid: 1008\nname: 'Room #1008'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:32-04:00","description":"update room 1008 (add exit north to 1009)","file_path":"data/rooms/intro/1008.yaml","old_content":"exits:\n west: 1007\nid: 1008\nname: 'Room #1008'\n","new_content":"id: 1008\nname: 'Room #1008'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1009\n west:\n room: 1007\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:32-04:00","description":"create room 1009","file_path":"data/rooms/intro/1009.yaml","old_content":"","new_content":"exits:\n south: 1008\nid: 1009\nname: 'Room #1009'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:33-04:00","description":"update room 1009 (add exit north to 1010)","file_path":"data/rooms/intro/1009.yaml","old_content":"exits:\n south: 1008\nid: 1009\nname: 'Room #1009'\n","new_content":"id: 1009\nname: 'Room #1009'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1010\n south:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:33-04:00","description":"create room 1010","file_path":"data/rooms/intro/1010.yaml","old_content":"","new_content":"exits:\n south: 1009\nid: 1010\nname: 'Room #1010'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:33-04:00","description":"update room 1010 (add exit east to 1011)","file_path":"data/rooms/intro/1010.yaml","old_content":"exits:\n south: 1009\nid: 1010\nname: 'Room #1010'\n","new_content":"id: 1010\nname: 'Room #1010'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1011\n south:\n room: 1009\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:33-04:00","description":"create room 1011","file_path":"data/rooms/intro/1011.yaml","old_content":"","new_content":"exits:\n west: 1010\nid: 1011\nname: 'Room #1011'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:34-04:00","description":"update room 1011 (add exit south to 1012)","file_path":"data/rooms/intro/1011.yaml","old_content":"exits:\n west: 1010\nid: 1011\nname: 'Room #1011'\n","new_content":"id: 1011\nname: 'Room #1011'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1012\n west:\n room: 1010\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:34-04:00","description":"create room 1012","file_path":"data/rooms/intro/1012.yaml","old_content":"","new_content":"exits:\n north: 1011\nid: 1012\nname: 'Room #1012'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:34-04:00","description":"update room 1012 (add exit south to 1013)","file_path":"data/rooms/intro/1012.yaml","old_content":"exits:\n north: 1011\nid: 1012\nname: 'Room #1012'\n","new_content":"id: 1012\nname: 'Room #1012'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1011\n south:\n room: 1013\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T14:32:34-04:00","description":"create room 1013","file_path":"data/rooms/intro/1013.yaml","old_content":"","new_content":"exits:\n north: 1012\nid: 1013\nname: 'Room #1013'\n","is_delete":false,"is_create":true},{"time":"2026-06-30T14:32:35-04:00","description":"delete room 1013 (cleaned 1 exits)","file_path":"data/rooms/intro/1013.yaml","old_content":"exits:\n north: 1012\nid: 1013\nname: 'Room #1013'\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:36-04:00","description":"delete room 1012 (cleaned 1 exits)","file_path":"data/rooms/intro/1012.yaml","old_content":"id: 1012\nname: 'Room #1012'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1011\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:37-04:00","description":"delete room 1011 (cleaned 1 exits)","file_path":"data/rooms/intro/1011.yaml","old_content":"id: 1011\nname: 'Room #1011'\ncolor: \"\"\ndescription: []\nexits:\n west:\n room: 1010\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:37-04:00","description":"delete room 1010 (cleaned 1 exits)","file_path":"data/rooms/intro/1010.yaml","old_content":"id: 1010\nname: 'Room #1010'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1009\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:38-04:00","description":"delete room 1009 (cleaned 1 exits)","file_path":"data/rooms/intro/1009.yaml","old_content":"id: 1009\nname: 'Room #1009'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:39-04:00","description":"delete room 1008 (cleaned 1 exits)","file_path":"data/rooms/intro/1008.yaml","old_content":"id: 1008\nname: 'Room #1008'\ncolor: \"\"\ndescription: []\nexits:\n west:\n room: 1007\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:40-04:00","description":"delete room 1007 (cleaned 1 exits)","file_path":"data/rooms/intro/1007.yaml","old_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1006\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:40-04:00","description":"delete room 1006 (cleaned 1 exits)","file_path":"data/rooms/intro/1006.yaml","old_content":"id: 1006\nname: 'Room #1006'\ncolor: \"\"\ndescription: []\nexits:\n west:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T14:32:41-04:00","description":"delete room 1005 (cleaned 1 exits)","file_path":"data/rooms/intro/1005.yaml","old_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 2003\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false},{"time":"2026-06-30T15:05:42-04:00","description":"update room 1001","file_path":"data/rooms/intro/1001.yaml","old_content":"id: 1001\nname: Inside Transport Shuttle\ncolor: \"\"\ndescription:\n - text: |-\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large framed sign hangs next to the closed cockpit door. The piston-powered staircase is firmly closed at the rear of the craft.\n\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large {11}framed sign{/} hangs next to the closed cockpit door. A piston-powered staircase leads down out the back of the small craft with a smiling attendant ready to help disembark.\nexits:\n down:\n room: 1002\n condition:\n flag: \"\"\n value: null\n not: false\n player_flag: 1001_touchdown\n has_item: \"\"\n min_credits: 0\n all_of: []\n any_of: []\n blocked_message: The piston-powered staircase is firmly closed.\n east:\n room: 2001\nobjects:\n - name: framed sign\n hidden: true\n description:\n - text: \"We hope you enjoy your trip on our shuttle! Your pilot is [TYLER].\\n\\nWhen you arrive at your final destination, please be aware of the following basic commands:\\n\\n{0B bold}Movement:{/} {0A}north{/}, {0A}south{/}, {0A}east{/}, {0A}west{/}, {0A}up{/}, {0A}down{/}\\n{0B bold}Look:{/} {0A}look{/}, {0A}look \u003citem/object/mob\u003e{/}, and {0A}map{/}\\n{0B bold}Items:{/} {0A}inv{/} to see your 28-slot inventory\\n {0A}get \u003citem\u003e{/}, {0A}drop \u003citem\u003e{/}, {0A}wear \u003citem\u003e{/}, {0A}remove \u003citem\u003e{/}\\n {0A}use \u003citem\u003e on \u003citem/object\u003e{/}\\n {0A}eq{/} for equiped gear\\n{0B bold}Comms:{/} {0A}talk \u003cmob\u003e{/} for NPC chat\\n {0A}say \u003cmsg\u003e{/} and {0A}global \u003cmsg\u003e{/} for human chat\\n{0B bold}Character:{/} {0A}score{/} and {0A}skills{/} for stats. \\n{0B bold}Combat{/} {0A}attack \u003cmob\u003e{/}\\n{0B bold}Skills{/} Many skills have shortcuts like {0A}mine{/}, {0A}fish{/}, or {0A}chop{/}\\n{0B bold}Options{/} {0A}options{/} for a list of account options\\n {0A}options \u003copt\u003e \u003cvalue\u003e{/} to change an option\\n\\nType {0C bold}what{/} for a list of possible common actions\\n\\nMake sure to check the {0A}help{/} pages, especially {0A}help newplayer{/}!\"\n on_look:\n set_flags: {}\n set_player_flags:\n 1001_look_sign: true\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\n cost: 0\n shop: null\n assign_task: false\n skip_task: false\n extend_task: false\n reputation_cost: 0\n sawmill: false\n aps_node: false\n - name: door\n hidden: true\n description:\n - text: A sturdy, locked steel door that keeps passengers out of the cockpit. There is a tiny, thin window at eye level.\n - name: window\n hidden: true\n description:\n - text: A thick trim with rounded bolt heads frames the tiny window. It looks like it was cut into the door at some point, potentially as a simple replacement to computerized security systems. Inside the cockpit there's a pilot finishing up landing procedures on a large panel of analog instruments.\n - name: instrument panel\n aliases:\n - cockpit\n hidden: true\n description:\n - text: The cramped cockpit is surrounded by buttons, swiches, dials, and analog gauges. The parts are a mix of technologies from the last century. Part of the panel is obscured by a draped cloth as if to hide it from view. Odd.\n - name: pilot\n hidden: true\n description:\n - text: That's the guy who took you from the Passenger Barge Denali down here to Vesta. A middle-aged man in a neat white jumpsuit with a matching white sort of conductor's hat. He's following a written procedure checklist and seems to take his job seriously.\nitem_spawns: []\nmobs:\n - id: flight_attendant\non_enter:\n - message: '{0B bold}Welcome to The House of Icarus{/}'\n condition:\n flag: \"\"\n value: null\n not: true\n player_flag: 1001_welcome\n has_item: \"\"\n min_credits: 0\n all_of: []\n any_of: []\n delay: 5\n set_flags: {}\n set_player_flags: {}\n broadcast: \"\"\n broadcast_global: \"\"\n spawn_mob: null\n despawn_mob: \"\"\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\n - message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'\n condition:\n flag: \"\"\n value: null\n not: true\n player_flag: 1001_welcome\n has_item: \"\"\n min_credits: 0\n all_of: []\n any_of: []\n delay: 7\n set_flags: {}\n set_player_flags:\n 1001_welcome: true\n broadcast: \"\"\n broadcast_global: \"\"\n spawn_mob: null\n despawn_mob: \"\"\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\n - message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'\n condition:\n flag: \"\"\n value: null\n not: true\n player_flag: 1001_welcome\n has_item: \"\"\n min_credits: 0\n all_of: []\n any_of: []\n delay: 7\n set_flags: {}\n set_player_flags: {}\n broadcast: \"\"\n broadcast_global: \"\"\n spawn_mob: null\n despawn_mob: \"\"\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\nhazard: \"\"\nblock_transport: true\ntriggers:\n - id: \"\"\n on_player_flag: 1001_look_sign\n on_flag: \"\"\n value: null\n room: 0\n steps:\n - delay: 5\n message: The cabin shakes as the small craft touches down\n broadcast: \"\"\n broadcast_global: \"\"\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\n despawn_mob: \"\"\n - delay: 5\n message: The pistons hiss as the rear staircase opens\n broadcast: \"\"\n broadcast_global: \"\"\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\n despawn_mob: \"\"\n - delay: 0\n message: \"\"\n broadcast: \"\"\n broadcast_global: \"\"\n set_flags: {}\n set_player_flags:\n 1001_touchdown: true\n spawn_mob: null\n give_item: \"\"\n take_item: \"\"\n teleport: 0\n heal: 0\n despawn_mob: \"\"\n","new_content":"block_transport: true\ncolor: \"\"\ndescription:\n - text: |-\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large framed sign hangs next to the closed cockpit door. The piston-powered staircase is firmly closed at the rear of the craft.\n\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large {11}framed sign{/} hangs next to the closed cockpit door. A piston-powered staircase leads down out the back of the small craft with a smiling attendant ready to help disembark.\nexits:\n down:\n blocked_message: The piston-powered staircase is firmly closed.\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: false\n player_flag: 1001_touchdown\n value: null\n room: 1002\n east:\n room: 2001\nhazard: \"\"\nid: 1001\nitem_spawns: []\nmobs:\n - id: flight_attendant\nname: Inside Transport Shuttle\nobjects:\n - description:\n - text: \"We hope you enjoy your trip on our shuttle! Your pilot is [TYLER].\\n\\nWhen you arrive at your final destination, please be aware of the following basic commands:\\n\\n{0B bold}Movement:{/} {0A}north{/}, {0A}south{/}, {0A}east{/}, {0A}west{/}, {0A}up{/}, {0A}down{/}\\n{0B bold}Look:{/} {0A}look{/}, {0A}look \u003citem/object/mob\u003e{/}, and {0A}map{/}\\n{0B bold}Items:{/} {0A}inv{/} to see your 28-slot inventory\\n {0A}get \u003citem\u003e{/}, {0A}drop \u003citem\u003e{/}, {0A}wear \u003citem\u003e{/}, {0A}remove \u003citem\u003e{/}\\n {0A}use \u003citem\u003e on \u003citem/object\u003e{/}\\n {0A}eq{/} for equiped gear\\n{0B bold}Comms:{/} {0A}talk \u003cmob\u003e{/} for NPC chat\\n {0A}say \u003cmsg\u003e{/} and {0A}global \u003cmsg\u003e{/} for human chat\\n{0B bold}Character:{/} {0A}score{/} and {0A}skills{/} for stats. \\n{0B bold}Combat{/} {0A}attack \u003cmob\u003e{/}\\n{0B bold}Skills{/} Many skills have shortcuts like {0A}mine{/}, {0A}fish{/}, or {0A}chop{/}\\n{0B bold}Options{/} {0A}options{/} for a list of account options\\n {0A}options \u003copt\u003e \u003cvalue\u003e{/} to change an option\\n\\nType {0C bold}what{/} for a list of possible common actions\\n\\nMake sure to check the {0A}help{/} pages, especially {0A}help newplayer{/}!\"\n hidden: true\n name: framed sign\n on_look:\n aps_node: false\n assign_task: false\n cost: 0\n extend_task: false\n give_item: \"\"\n heal: 0\n reputation_cost: 0\n sawmill: false\n set_flags: {}\n set_player_flags:\n 1001_look_sign: true\n shop: null\n skip_task: false\n take_item: \"\"\n teleport: 0\n - description:\n - text: A sturdy, locked steel door that keeps passengers out of the cockpit. There is a tiny, thin window at eye level.\n hidden: true\n name: door\n - description:\n - text: A thick trim with rounded bolt heads frames the tiny window. It looks like it was cut into the door at some point, potentially as a simple replacement to computerized security systems. Inside the cockpit there's a pilot finishing up landing procedures on a large panel of analog instruments.\n hidden: true\n name: window\n - aliases:\n - cockpit\n description:\n - text: The cramped cockpit is surrounded by buttons, swiches, dials, and analog gauges. The parts are a mix of technologies from the last century. Part of the panel is obscured by a draped cloth as if to hide it from view. Odd.\n hidden: true\n name: instrument panel\n - aliases: []\n description:\n - text: That's the guy who took you from the Passenger Barge Denali down here to Vesta. A middle-aged man in a neat white jumpsuit with a matching white sort of conductor's hat. He's following a written procedure checklist and seems to take his job seriously.\n hidden: false\n name: pilot\non_enter:\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B bold}Welcome to The House of Icarus{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'\n set_flags: {}\n set_player_flags:\n 1001_welcome: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\ntriggers:\n - id: \"\"\n on_flag: \"\"\n on_player_flag: 1001_look_sign\n room: 0\n steps:\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The cabin shakes as the small craft touches down\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The pistons hiss as the rear staircase opens\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 0\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: \"\"\n set_flags: {}\n set_player_flags:\n 1001_touchdown: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n value: null\n","is_delete":false,"is_create":false},{"time":"2026-06-30T15:05:43-04:00","description":"update room 1001","file_path":"data/rooms/intro/1001.yaml","old_content":"block_transport: true\ncolor: \"\"\ndescription:\n - text: |-\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large framed sign hangs next to the closed cockpit door. The piston-powered staircase is firmly closed at the rear of the craft.\n\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large {11}framed sign{/} hangs next to the closed cockpit door. A piston-powered staircase leads down out the back of the small craft with a smiling attendant ready to help disembark.\nexits:\n down:\n blocked_message: The piston-powered staircase is firmly closed.\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: false\n player_flag: 1001_touchdown\n value: null\n room: 1002\n east:\n room: 2001\nhazard: \"\"\nid: 1001\nitem_spawns: []\nmobs:\n - id: flight_attendant\nname: Inside Transport Shuttle\nobjects:\n - description:\n - text: \"We hope you enjoy your trip on our shuttle! Your pilot is [TYLER].\\n\\nWhen you arrive at your final destination, please be aware of the following basic commands:\\n\\n{0B bold}Movement:{/} {0A}north{/}, {0A}south{/}, {0A}east{/}, {0A}west{/}, {0A}up{/}, {0A}down{/}\\n{0B bold}Look:{/} {0A}look{/}, {0A}look \u003citem/object/mob\u003e{/}, and {0A}map{/}\\n{0B bold}Items:{/} {0A}inv{/} to see your 28-slot inventory\\n {0A}get \u003citem\u003e{/}, {0A}drop \u003citem\u003e{/}, {0A}wear \u003citem\u003e{/}, {0A}remove \u003citem\u003e{/}\\n {0A}use \u003citem\u003e on \u003citem/object\u003e{/}\\n {0A}eq{/} for equiped gear\\n{0B bold}Comms:{/} {0A}talk \u003cmob\u003e{/} for NPC chat\\n {0A}say \u003cmsg\u003e{/} and {0A}global \u003cmsg\u003e{/} for human chat\\n{0B bold}Character:{/} {0A}score{/} and {0A}skills{/} for stats. \\n{0B bold}Combat{/} {0A}attack \u003cmob\u003e{/}\\n{0B bold}Skills{/} Many skills have shortcuts like {0A}mine{/}, {0A}fish{/}, or {0A}chop{/}\\n{0B bold}Options{/} {0A}options{/} for a list of account options\\n {0A}options \u003copt\u003e \u003cvalue\u003e{/} to change an option\\n\\nType {0C bold}what{/} for a list of possible common actions\\n\\nMake sure to check the {0A}help{/} pages, especially {0A}help newplayer{/}!\"\n hidden: true\n name: framed sign\n on_look:\n aps_node: false\n assign_task: false\n cost: 0\n extend_task: false\n give_item: \"\"\n heal: 0\n reputation_cost: 0\n sawmill: false\n set_flags: {}\n set_player_flags:\n 1001_look_sign: true\n shop: null\n skip_task: false\n take_item: \"\"\n teleport: 0\n - description:\n - text: A sturdy, locked steel door that keeps passengers out of the cockpit. There is a tiny, thin window at eye level.\n hidden: true\n name: door\n - description:\n - text: A thick trim with rounded bolt heads frames the tiny window. It looks like it was cut into the door at some point, potentially as a simple replacement to computerized security systems. Inside the cockpit there's a pilot finishing up landing procedures on a large panel of analog instruments.\n hidden: true\n name: window\n - aliases:\n - cockpit\n description:\n - text: The cramped cockpit is surrounded by buttons, swiches, dials, and analog gauges. The parts are a mix of technologies from the last century. Part of the panel is obscured by a draped cloth as if to hide it from view. Odd.\n hidden: true\n name: instrument panel\n - aliases: []\n description:\n - text: That's the guy who took you from the Passenger Barge Denali down here to Vesta. A middle-aged man in a neat white jumpsuit with a matching white sort of conductor's hat. He's following a written procedure checklist and seems to take his job seriously.\n hidden: false\n name: pilot\non_enter:\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B bold}Welcome to The House of Icarus{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'\n set_flags: {}\n set_player_flags:\n 1001_welcome: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\ntriggers:\n - id: \"\"\n on_flag: \"\"\n on_player_flag: 1001_look_sign\n room: 0\n steps:\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The cabin shakes as the small craft touches down\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The pistons hiss as the rear staircase opens\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 0\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: \"\"\n set_flags: {}\n set_player_flags:\n 1001_touchdown: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n value: null\n","new_content":"block_transport: true\ncolor: \"\"\ndescription:\n - text: |-\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large framed sign hangs next to the closed cockpit door. The piston-powered staircase is firmly closed at the rear of the craft.\n\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large {11}framed sign{/} hangs next to the closed cockpit door. A piston-powered staircase leads down out the back of the small craft with a smiling attendant ready to help disembark.\nexits:\n down:\n blocked_message: The piston-powered staircase is firmly closed.\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: false\n player_flag: 1001_touchdown\n value: null\n room: 1002\n east:\n room: 2001\nhazard: \"\"\nid: 1001\nitem_spawns: []\nmobs:\n - id: flight_attendant\nname: Inside Transport Shuttle\nobjects:\n - description:\n - text: \"We hope you enjoy your trip on our shuttle! Your pilot is [TYLER].\\n\\nWhen you arrive at your final destination, please be aware of the following basic commands:\\n\\n{0B bold}Movement:{/} {0A}north{/}, {0A}south{/}, {0A}east{/}, {0A}west{/}, {0A}up{/}, {0A}down{/}\\n{0B bold}Look:{/} {0A}look{/}, {0A}look \u003citem/object/mob\u003e{/}, and {0A}map{/}\\n{0B bold}Items:{/} {0A}inv{/} to see your 28-slot inventory\\n {0A}get \u003citem\u003e{/}, {0A}drop \u003citem\u003e{/}, {0A}wear \u003citem\u003e{/}, {0A}remove \u003citem\u003e{/}\\n {0A}use \u003citem\u003e on \u003citem/object\u003e{/}\\n {0A}eq{/} for equiped gear\\n{0B bold}Comms:{/} {0A}talk \u003cmob\u003e{/} for NPC chat\\n {0A}say \u003cmsg\u003e{/} and {0A}global \u003cmsg\u003e{/} for human chat\\n{0B bold}Character:{/} {0A}score{/} and {0A}skills{/} for stats. \\n{0B bold}Combat{/} {0A}attack \u003cmob\u003e{/}\\n{0B bold}Skills{/} Many skills have shortcuts like {0A}mine{/}, {0A}fish{/}, or {0A}chop{/}\\n{0B bold}Options{/} {0A}options{/} for a list of account options\\n {0A}options \u003copt\u003e \u003cvalue\u003e{/} to change an option\\n\\nType {0C bold}what{/} for a list of possible common actions\\n\\nMake sure to check the {0A}help{/} pages, especially {0A}help newplayer{/}!\"\n hidden: true\n name: framed sign\n on_look:\n aps_node: false\n assign_task: false\n cost: 0\n extend_task: false\n give_item: \"\"\n heal: 0\n reputation_cost: 0\n sawmill: false\n set_flags: {}\n set_player_flags:\n 1001_look_sign: true\n shop: null\n skip_task: false\n take_item: \"\"\n teleport: 0\n - description:\n - text: A sturdy, locked steel door that keeps passengers out of the cockpit. There is a tiny, thin window at eye level.\n hidden: true\n name: door\n - description:\n - text: A thick trim with rounded bolt heads frames the tiny window. It looks like it was cut into the door at some point, potentially as a simple replacement to computerized security systems. Inside the cockpit there's a pilot finishing up landing procedures on a large panel of analog instruments.\n hidden: true\n name: window\n - aliases:\n - cockpit\n description:\n - text: The cramped cockpit is surrounded by buttons, swiches, dials, and analog gauges. The parts are a mix of technologies from the last century. Part of the panel is obscured by a draped cloth as if to hide it from view. Odd.\n hidden: true\n name: instrument panel\n - aliases: []\n description:\n - text: That's the guy who took you from the Passenger Barge Denali down here to Vesta. A middle-aged man in a neat white jumpsuit with a matching white sort of conductor's hat. He's following a written procedure checklist and seems to take his job seriously.\n hidden: true\n name: pilot\non_enter:\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B bold}Welcome to The House of Icarus{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'\n set_flags: {}\n set_player_flags:\n 1001_welcome: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\ntriggers:\n - id: \"\"\n on_flag: \"\"\n on_player_flag: 1001_look_sign\n room: 0\n steps:\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The cabin shakes as the small craft touches down\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The pistons hiss as the rear staircase opens\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 0\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: \"\"\n set_flags: {}\n set_player_flags:\n 1001_touchdown: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n value: null\n","is_delete":false,"is_create":false},{"time":"2026-06-30T16:34:21-04:00","description":"update room 2001","file_path":"data/rooms/intro/2001.yaml","old_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns: []\nmobs: []\nname: Test Room\nobjects: []\non_enter: []\ntriggers: []\n","new_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns: []\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T16:35:02-04:00","description":"update room 2001","file_path":"data/rooms/intro/2001.yaml","old_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns: []\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","new_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T16:35:15-04:00","description":"update room 2001","file_path":"data/rooms/intro/2001.yaml","old_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","new_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T17:43:28-04:00","description":"Update object copper_rock","file_path":"data/objects/copper_rock.yaml","old_content":"color: \"B2\"\ngather:\n depleted_message: You don't see any ore in this rock.\n drops:\n - depletes: true\n item_id: copper_ore\n level: 1\n message: You manage to mine some {B2}copper ore{/}.\n weight: 90\n xp: 17\n - depletes: false\n message: You spot a glint of something valuable!\n table: gem_table\n weight: 10\n fail_message: You chip away but get nothing useful.\n gather_message: You swing your pickaxe at the rock...\n respawn_broadcast: A glint of copper catches your eye from some {name}.\n respawn_timer: 50\n skill: mining\n success:\n base: 0.5\n cap: 0.95\n per_level: 0.01\n tools:\n - pickaxe\nname: copper rock\n","new_content":"aliases: []\ncolor: B2\ndescription: \"\"\ngather:\n bait: \"\"\n deplete_timer: 0\n depleted_message: You don't see any ore in this rock.\n drops:\n - depletes: true\n item_id: copper_ore\n level: 1\n message: You manage to mine some {B2}copper ore{/}.\n quantity: 0\n table: \"\"\n weight: 90\n xp: 17\n - depletes: false\n item_id: \"\"\n level: 0\n message: You spot a glint of something valuable!\n quantity: 0\n table: gem_table\n weight: 10\n xp: 0\n exhausted_message: \"\"\n fail_message: You chip away but get nothing useful.\n gather_message: You swing your pickaxe at the rock...\n nest_chance: 0\n respawn_broadcast: A glint of copper catches your eye from some {name}.\n respawn_timer: 50\n skill: mining\n success:\n base: 0.5\n cap: 0.95\n per_level: 0.01\n tools:\n - pickaxe\nhidden: false\nid: copper_rock\ninroom_description: \"\"\nname: copper rock\nremoval_item: \"\"\n","is_delete":false,"is_create":false},{"time":"2026-06-30T23:48:18-04:00","description":"update room 2001","file_path":"data/rooms/intro/2001.yaml","old_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","new_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\n - id: pot_of_flour\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T23:48:23-04:00","description":"update room 2001","file_path":"data/rooms/intro/2001.yaml","old_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\n - id: pot_of_flour\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","new_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\n - id: pot_of_flour\n quantity: 1\n respawn_ticks: 0\n - id: bucket_of_water\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-06-30T23:53:08-04:00","description":"Update item bread_dough","file_path":"data/items/consumables/bread_dough.yaml","old_content":"color: \"DE\"\ncraft:\n type: combine\n consume:\n - byproducts:\n - empty_pot\n items:\n - pot_of_flour\n quantity: 1\n - byproducts:\n - empty_bucket\n - empty_jug\n items:\n - bucket_of_water\n - jug_of_water\n quantity: 1\n wait: 2\ndescription: A ball of uncooked bread dough. It needs to be baked.\nname: bread dough\nstackable: false\nvalue: 3\n","new_content":"color: DE\ncraft:\n - consume:\n - byproducts:\n - empty_pot\n items:\n - pot_of_flour\n quantity: 1\n - byproducts:\n - empty_bucket\n - empty_jug\n items:\n - bucket_of_water\n - jug_of_water\n quantity: 1\n end_message: testend\n fail: \"\"\n level: 0\n message: testmsg\n output_qty: 0\n start_message: teststart\n station: []\n subtype: \"\"\n tool: \"\"\n type: combine\n wait: 2\n xp: 0\ndescription: A ball of uncooked bread dough. It needs to be baked.\nid: bread_dough\nmax_quality: 0\nname: bread dough\nquality: 0\nstackable: false\nvalue: 3\n","is_delete":false,"is_create":false},{"time":"2026-07-01T00:23:04-04:00","description":"Update item bread_dough","file_path":"data/items/consumables/bread_dough.yaml","old_content":"color: DE\ncraft:\n - consume:\n - byproducts:\n - empty_pot\n items:\n - pot_of_flour\n quantity: 1\n - byproducts:\n - empty_bucket\n - empty_jug\n items:\n - bucket_of_water\n - jug_of_water\n quantity: 1\n end_message: testend\n fail: \"\"\n level: 0\n message: testmsg\n output_qty: 0\n start_message: teststart\n station: []\n subtype: \"\"\n tool: \"\"\n type: combine\n wait: 2\n xp: 0\ndescription: A ball of uncooked bread dough. It needs to be baked.\nid: bread_dough\nmax_quality: 0\nname: bread dough\nquality: 0\nstackable: false\nvalue: 3\n","new_content":"color: DE\ncraft:\n - consume:\n - byproducts:\n - empty_pot\n items:\n - pot_of_flour\n quantity: 1\n - byproducts:\n - empty_bucket\n - empty_jug\n items:\n - bucket_of_water\n - jug_of_water\n quantity: 1\n end_message: testend\n fail: \"\"\n level: 0\n message: testmsg\n output_qty: 0\n start_message: teststart\n station: []\n subtype: \"\"\n tool: \"\"\n type: combine\n wait: 2\n xp: 0\ndescription: A ball of uncooked bread dough. It needs to be baked.\nid: bread_dough\nmax_quality: 0\nname: bread dough\nquality: 0\nstackable: false\nvalue: 3\n","is_delete":false,"is_create":false},{"time":"2026-07-01T01:22:55-04:00","description":"update room 1001","file_path":"data/rooms/intro/1001.yaml","old_content":"block_transport: true\ncolor: \"\"\ndescription:\n - text: |-\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large framed sign hangs next to the closed cockpit door. The piston-powered staircase is firmly closed at the rear of the craft.\n\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large {11}framed sign{/} hangs next to the closed cockpit door. A piston-powered staircase leads down out the back of the small craft with a smiling attendant ready to help disembark.\nexits:\n down:\n blocked_message: The piston-powered staircase is firmly closed.\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: false\n player_flag: 1001_touchdown\n value: null\n room: 1002\n east:\n room: 2001\nhazard: \"\"\nid: 1001\nitem_spawns: []\nmobs:\n - id: flight_attendant\nname: Inside Transport Shuttle\nobjects:\n - description:\n - text: \"We hope you enjoy your trip on our shuttle! Your pilot is [TYLER].\\n\\nWhen you arrive at your final destination, please be aware of the following basic commands:\\n\\n{0B bold}Movement:{/} {0A}north{/}, {0A}south{/}, {0A}east{/}, {0A}west{/}, {0A}up{/}, {0A}down{/}\\n{0B bold}Look:{/} {0A}look{/}, {0A}look \u003citem/object/mob\u003e{/}, and {0A}map{/}\\n{0B bold}Items:{/} {0A}inv{/} to see your 28-slot inventory\\n {0A}get \u003citem\u003e{/}, {0A}drop \u003citem\u003e{/}, {0A}wear \u003citem\u003e{/}, {0A}remove \u003citem\u003e{/}\\n {0A}use \u003citem\u003e on \u003citem/object\u003e{/}\\n {0A}eq{/} for equiped gear\\n{0B bold}Comms:{/} {0A}talk \u003cmob\u003e{/} for NPC chat\\n {0A}say \u003cmsg\u003e{/} and {0A}global \u003cmsg\u003e{/} for human chat\\n{0B bold}Character:{/} {0A}score{/} and {0A}skills{/} for stats. \\n{0B bold}Combat{/} {0A}attack \u003cmob\u003e{/}\\n{0B bold}Skills{/} Many skills have shortcuts like {0A}mine{/}, {0A}fish{/}, or {0A}chop{/}\\n{0B bold}Options{/} {0A}options{/} for a list of account options\\n {0A}options \u003copt\u003e \u003cvalue\u003e{/} to change an option\\n\\nType {0C bold}what{/} for a list of possible common actions\\n\\nMake sure to check the {0A}help{/} pages, especially {0A}help newplayer{/}!\"\n hidden: true\n name: framed sign\n on_look:\n aps_node: false\n assign_task: false\n cost: 0\n extend_task: false\n give_item: \"\"\n heal: 0\n reputation_cost: 0\n sawmill: false\n set_flags: {}\n set_player_flags:\n 1001_look_sign: true\n shop: null\n skip_task: false\n take_item: \"\"\n teleport: 0\n - description:\n - text: A sturdy, locked steel door that keeps passengers out of the cockpit. There is a tiny, thin window at eye level.\n hidden: true\n name: door\n - description:\n - text: A thick trim with rounded bolt heads frames the tiny window. It looks like it was cut into the door at some point, potentially as a simple replacement to computerized security systems. Inside the cockpit there's a pilot finishing up landing procedures on a large panel of analog instruments.\n hidden: true\n name: window\n - aliases:\n - cockpit\n description:\n - text: The cramped cockpit is surrounded by buttons, swiches, dials, and analog gauges. The parts are a mix of technologies from the last century. Part of the panel is obscured by a draped cloth as if to hide it from view. Odd.\n hidden: true\n name: instrument panel\n - aliases: []\n description:\n - text: That's the guy who took you from the Passenger Barge Denali down here to Vesta. A middle-aged man in a neat white jumpsuit with a matching white sort of conductor's hat. He's following a written procedure checklist and seems to take his job seriously.\n hidden: true\n name: pilot\non_enter:\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B bold}Welcome to The House of Icarus{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'\n set_flags: {}\n set_player_flags:\n 1001_welcome: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\ntriggers:\n - id: \"\"\n on_flag: \"\"\n on_player_flag: 1001_look_sign\n room: 0\n steps:\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The cabin shakes as the small craft touches down\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The pistons hiss as the rear staircase opens\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 0\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: \"\"\n set_flags: {}\n set_player_flags:\n 1001_touchdown: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n value: null\n","new_content":"block_transport: true\ncolor: \"\"\ndescription:\n - text: |-\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large framed sign hangs next to the closed cockpit door. The piston-powered staircase is firmly closed at the rear of the craft.\n\n A dim, windowless shuttle interior with a few uncomfortable rows of steel seats facing forward. A large {11}framed sign{/} hangs next to the closed cockpit door. A piston-powered staircase leads down out the back of the small craft with a smiling attendant ready to help disembark.\nexits:\n down:\n blocked_message: The piston-powered staircase is firmly closed.\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: false\n player_flag: 1001_touchdown\n value: null\n room: 1002\n east:\n room: 2001\nhazard: \"\"\nid: 1001\nitem_spawns: []\nmobs:\n - id: flight_attendant\nname: Inside Transport Shuttle\nobjects:\n - description:\n - text: \"We hope you enjoy your trip on our shuttle! Your pilot is [TYLER].\\n\\nWhen you arrive at your final destination, please be aware of the following basic commands:\\n\\n{0B bold}Movement:{/} {0A}north{/}, {0A}south{/}, {0A}east{/}, {0A}west{/}, {0A}up{/}, {0A}down{/}\\n{0B bold}Look:{/} {0A}look{/}, {0A}look \u003citem/object/mob\u003e{/}, and {0A}map{/}\\n{0B bold}Items:{/} {0A}inv{/} to see your 28-slot inventory\\n {0A}get \u003citem\u003e{/}, {0A}drop \u003citem\u003e{/}, {0A}wear \u003citem\u003e{/}, {0A}remove \u003citem\u003e{/}\\n {0A}use \u003citem\u003e on \u003citem/object\u003e{/}\\n {0A}eq{/} for equiped gear\\n{0B bold}Comms:{/} {0A}talk \u003cmob\u003e{/} for NPC chat\\n {0A}say \u003cmsg\u003e{/} and {0A}global \u003cmsg\u003e{/} for human chat\\n{0B bold}Character:{/} {0A}score{/} and {0A}skills{/} for stats. \\n{0B bold}Combat{/} {0A}attack \u003cmob\u003e{/}\\n{0B bold}Skills{/} Many skills have shortcuts like {0A}mine{/}, {0A}fish{/}, or {0A}chop{/}\\n{0B bold}Options{/} {0A}options{/} for a list of account options\\n {0A}options \u003copt\u003e \u003cvalue\u003e{/} to change an option\\n\\nType {0C bold}what{/} for a list of possible common actions\\n\\nMake sure to check the {0A}help{/} pages, especially {0A}help newplayer{/}!\"\n hidden: true\n name: framed sign\n on_look:\n aps_node: false\n assign_task: false\n cost: 0\n extend_task: false\n give_item: \"\"\n heal: 0\n reputation_cost: 0\n sawmill: false\n set_flags: {}\n set_player_flags:\n 1001_look_sign: true\n shop: null\n skip_task: false\n take_item: \"\"\n teleport: 0\n - description:\n - text: A sturdy, locked steel door that keeps passengers out of the cockpit. There is a tiny, thin window at eye level.\n hidden: true\n name: door\n - description:\n - text: A thick trim with rounded bolt heads frames the tiny window. It looks like it was cut into the door at some point, potentially as a simple replacement to computerized security systems. Inside the cockpit there's a pilot finishing up landing procedures on a large panel of analog instruments.\n hidden: true\n name: window\n - aliases:\n - cockpit\n description:\n - text: The cramped cockpit is surrounded by buttons, swiches, dials, and analog gauges. The parts are a mix of technologies from the last century. Part of the panel is obscured by a draped cloth as if to hide it from view. Odd.\n hidden: true\n name: instrument panel\n - aliases: []\n description:\n - text: That's the guy who took you from the Passenger Barge Denali down here to Vesta. A middle-aged man in a neat white jumpsuit with a matching white sort of conductor's hat. He's following a written procedure checklist and seems to take his job seriously.\n hidden: true\n name: pilot\non_enter:\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B bold}Welcome to The House of Icarus{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'\n set_flags: {}\n set_player_flags:\n 1001_welcome: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n condition:\n all_of: []\n any_of: []\n flag: \"\"\n has_item: \"\"\n min_credits: 0\n not: true\n player_flag: 1001_welcome\n value: null\n delay: 7\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\ntriggers:\n - id: \"\"\n on_flag: \"\"\n on_player_flag: 1001_look_sign\n room: 0\n steps:\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The cabin shakes as the small craft touches down\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 5\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: The pistons hiss as the rear staircase opens\n set_flags: {}\n set_player_flags: {}\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n - broadcast: \"\"\n broadcast_global: \"\"\n delay: 0\n despawn_mob: \"\"\n give_item: \"\"\n heal: 0\n message: \"\"\n set_flags: {}\n set_player_flags:\n 1001_touchdown: true\n spawn_mob: null\n take_item: \"\"\n teleport: 0\n value: null\n","is_delete":false,"is_create":false},{"time":"2026-07-01T03:00:10-04:00","description":"update room 2001","file_path":"data/rooms/intro/2001.yaml","old_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\n - id: pot_of_flour\n quantity: 1\n respawn_ticks: 0\n - id: bucket_of_water\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","new_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\n - id: pot_of_flour\n quantity: 1\n respawn_ticks: 1\n - id: bucket_of_water\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-07-01T03:00:11-04:00","description":"update room 2001","file_path":"data/rooms/intro/2001.yaml","old_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\n - id: pot_of_flour\n quantity: 1\n respawn_ticks: 1\n - id: bucket_of_water\n quantity: 1\n respawn_ticks: 0\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","new_content":"block_transport: false\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2002\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nhazard: \"\"\nid: 2001\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\n - id: pot_of_flour\n quantity: 1\n respawn_ticks: 1\n - id: bucket_of_water\n quantity: 1\n respawn_ticks: 1\nmobs: []\nname: Test Room\nobjects:\n - id: tree\non_enter: []\ntriggers: []\n","is_delete":false,"is_create":false}],"redo":null} \ No newline at end of file
diff --git a/data/items/ammo/headless_arrow.yaml b/data/items/ammo/headless_arrow.yaml
index 61ad40f..ca62b1d 100644
--- a/data/items/ammo/headless_arrow.yaml
+++ b/data/items/ammo/headless_arrow.yaml
@@ -8,7 +8,7 @@ craft:
- feather
quantity: 15
level: 1
- message: "You attach feathers to the %i1."
+ success_message: "You attach feathers to the %i1."
output_qty: 15
type: fletching
wait: 2
diff --git a/data/items/consumables/bread.yaml b/data/items/consumables/bread.yaml
index 1f8a996..6ded8b0 100644
--- a/data/items/consumables/bread.yaml
+++ b/data/items/consumables/bread.yaml
@@ -7,7 +7,7 @@ craft:
fail: burnt_meat
fail_message: You burn the bread to a crisp.
level: 1
- message: "You bake the dough into a %n."
+ success_message: "You bake the dough into a %n."
station:
- cooking_range
type: cooking
diff --git a/data/items/consumables/bread_dough.yaml b/data/items/consumables/bread_dough.yaml
index 48fa986..cce972d 100644
--- a/data/items/consumables/bread_dough.yaml
+++ b/data/items/consumables/bread_dough.yaml
@@ -16,7 +16,7 @@ craft:
end_message: testend
fail: ""
level: 0
- message: testmsg
+ success_message: testmsg
output_qty: 0
start_message: teststart
station: []
diff --git a/data/items/consumables/guam_potion_unf.yaml b/data/items/consumables/guam_potion_unf.yaml
index dc963ed..b758ac1 100644
--- a/data/items/consumables/guam_potion_unf.yaml
+++ b/data/items/consumables/guam_potion_unf.yaml
@@ -9,7 +9,7 @@ craft:
- vial_of_water
quantity: 1
wait: 0
- message: "You mix %i1 and %i2 into %n!!!"
+ success_message: "You mix %i1 and %i2 into %n!!!"
description: An unfinished potion with guam floating in water.
name: guam potion (unf)
stackable: false
diff --git a/data/items/consumables/pie_dish.yaml b/data/items/consumables/pie_dish.yaml
index 89d82e4..959cb65 100644
--- a/data/items/consumables/pie_dish.yaml
+++ b/data/items/consumables/pie_dish.yaml
@@ -6,7 +6,7 @@ craft:
quantity: 1
fail_message: The pie dish cracks in the oven and is ruined.
level: 7
- message: "You fire the %i1 in the oven."
+ success_message: "You fire the %i1 in the oven."
station:
- pottery_oven
success:
diff --git a/data/items/materials/ball_of_wool.yaml b/data/items/materials/ball_of_wool.yaml
index d074203..5dc1a2d 100644
--- a/data/items/materials/ball_of_wool.yaml
+++ b/data/items/materials/ball_of_wool.yaml
@@ -5,7 +5,7 @@ craft:
- wool
quantity: 1
level: 1
- message: "You spin the wool into a %n."
+ success_message: "You spin the wool into a %n."
station:
- spinning_wheel
type: crafting
diff --git a/data/items/materials/bowstring.yaml b/data/items/materials/bowstring.yaml
index fab0510..e0f63b3 100644
--- a/data/items/materials/bowstring.yaml
+++ b/data/items/materials/bowstring.yaml
@@ -5,7 +5,7 @@ craft:
- flax
quantity: 1
level: 1
- message: "You spin the flax into a %n."
+ success_message: "You spin the flax into a %n."
station:
- spinning_wheel
type: crafting
diff --git a/data/items/materials/diamond.yaml b/data/items/materials/diamond.yaml
index 4bf610a..0e8fe92 100644
--- a/data/items/materials/diamond.yaml
+++ b/data/items/materials/diamond.yaml
@@ -5,7 +5,7 @@ craft:
- uncut_diamond
quantity: 1
level: 43
- message: "You cut the %i1 into a beautiful %n."
+ success_message: "You cut the %i1 into a beautiful %n."
tool: chisel
type: crafting
wait: 3
diff --git a/data/items/materials/emerald.yaml b/data/items/materials/emerald.yaml
index 5d729cd..a39257c 100644
--- a/data/items/materials/emerald.yaml
+++ b/data/items/materials/emerald.yaml
@@ -5,7 +5,7 @@ craft:
- uncut_emerald
quantity: 1
level: 27
- message: "You cut the %i1 into a beautiful %n."
+ success_message: "You cut the %i1 into a beautiful %n."
tool: chisel
type: crafting
wait: 3
diff --git a/data/items/materials/mahogany_planks.yaml b/data/items/materials/mahogany_planks.yaml
index a3f3e0b..e75ec1c 100644
--- a/data/items/materials/mahogany_planks.yaml
+++ b/data/items/materials/mahogany_planks.yaml
@@ -5,7 +5,7 @@ craft:
- mahogany_logs
quantity: 1
level: 50
- message: "You saw the mahogany logs into %n."
+ success_message: "You saw the mahogany logs into %n."
station:
- workbench
tool: saw
diff --git a/data/items/materials/oak_planks.yaml b/data/items/materials/oak_planks.yaml
index 82e5504..6b2571c 100644
--- a/data/items/materials/oak_planks.yaml
+++ b/data/items/materials/oak_planks.yaml
@@ -5,7 +5,7 @@ craft:
- oak_logs
quantity: 1
level: 15
- message: "You saw the oak logs into %n."
+ success_message: "You saw the oak logs into %n."
station:
- workbench
tool: saw
diff --git a/data/items/materials/planks.yaml b/data/items/materials/planks.yaml
index d2ad6c2..b616120 100644
--- a/data/items/materials/planks.yaml
+++ b/data/items/materials/planks.yaml
@@ -5,7 +5,7 @@ craft:
- logs
quantity: 1
level: 1
- message: "You saw the logs into %n."
+ success_message: "You saw the logs into %n."
station:
- workbench
tool: saw
diff --git a/data/items/materials/ruby.yaml b/data/items/materials/ruby.yaml
index eb7968b..6a2b18f 100644
--- a/data/items/materials/ruby.yaml
+++ b/data/items/materials/ruby.yaml
@@ -5,7 +5,7 @@ craft:
- uncut_ruby
quantity: 1
level: 63
- message: "You cut the %i1 into a beautiful %n."
+ success_message: "You cut the %i1 into a beautiful %n."
tool: chisel
type: crafting
wait: 3
diff --git a/data/items/materials/sapphire.yaml b/data/items/materials/sapphire.yaml
index bc73cd9..9a5a7d6 100644
--- a/data/items/materials/sapphire.yaml
+++ b/data/items/materials/sapphire.yaml
@@ -5,7 +5,7 @@ craft:
- uncut_sapphire
quantity: 1
level: 20
- message: "You cut the %i1 into a beautiful %n."
+ success_message: "You cut the %i1 into a beautiful %n."
tool: chisel
type: crafting
wait: 3
diff --git a/data/items/materials/teak_planks.yaml b/data/items/materials/teak_planks.yaml
index 400c9e5..145b731 100644
--- a/data/items/materials/teak_planks.yaml
+++ b/data/items/materials/teak_planks.yaml
@@ -5,7 +5,7 @@ craft:
- teak_logs
quantity: 1
level: 35
- message: "You saw the teak logs into %n."
+ success_message: "You saw the teak logs into %n."
station:
- workbench
tool: saw
diff --git a/data/items/tools/empty_pot.yaml b/data/items/tools/empty_pot.yaml
index 371a783..0386b37 100644
--- a/data/items/tools/empty_pot.yaml
+++ b/data/items/tools/empty_pot.yaml
@@ -6,7 +6,7 @@ craft:
quantity: 1
fail_message: The pot cracks in the oven and is ruined.
level: 1
- message: "You fire the %i1 in the oven."
+ success_message: "You fire the %i1 in the oven."
station:
- pottery_oven
success:
diff --git a/data/items/tools/plant_pot.yaml b/data/items/tools/plant_pot.yaml
index e758167..b748877 100644
--- a/data/items/tools/plant_pot.yaml
+++ b/data/items/tools/plant_pot.yaml
@@ -6,7 +6,7 @@ craft:
quantity: 1
fail_message: The plant pot cracks in the oven and is ruined.
level: 19
- message: "You fire the %i1 in the oven."
+ success_message: "You fire the %i1 in the oven."
station:
- pottery_oven
success:
diff --git a/data/items/tools/unfired_pie_dish.yaml b/data/items/tools/unfired_pie_dish.yaml
index bc0c5a5..455921d 100644
--- a/data/items/tools/unfired_pie_dish.yaml
+++ b/data/items/tools/unfired_pie_dish.yaml
@@ -5,7 +5,7 @@ craft:
- soft_clay
quantity: 1
level: 7
- message: "You shape the clay into a %n."
+ success_message: "You shape the clay into a %n."
station:
- pottery_wheel
type: crafting
diff --git a/data/items/tools/unfired_plant_pot.yaml b/data/items/tools/unfired_plant_pot.yaml
index 30ccd83..26dfcca 100644
--- a/data/items/tools/unfired_plant_pot.yaml
+++ b/data/items/tools/unfired_plant_pot.yaml
@@ -5,7 +5,7 @@ craft:
- soft_clay
quantity: 1
level: 19
- message: "You shape the clay into a %n."
+ success_message: "You shape the clay into a %n."
station:
- pottery_wheel
type: crafting
diff --git a/data/items/tools/unfired_pot.yaml b/data/items/tools/unfired_pot.yaml
index 9dc2406..6ba9551 100644
--- a/data/items/tools/unfired_pot.yaml
+++ b/data/items/tools/unfired_pot.yaml
@@ -5,7 +5,7 @@ craft:
- soft_clay
quantity: 1
level: 1
- message: "You shape the clay into a %n."
+ success_message: "You shape the clay into a %n."
station:
- pottery_wheel
type: crafting
diff --git a/data/objects/adamantite_rock.yaml b/data/objects/adamantite_rock.yaml
index 6fa88b8..06ce7bd 100644
--- a/data/objects/adamantite_rock.yaml
+++ b/data/objects/adamantite_rock.yaml
@@ -4,7 +4,7 @@ gather:
- depletes: true
item_id: adamantite_ore
level: 70
- message: You manage to mine some {78}adamantite ore{/}.
+ success_message: You manage to mine some {78}adamantite ore{/}.
weight: 100
xp: 95
fail_message: You chip away but get nothing useful.
diff --git a/data/objects/anglerfish_spot.yaml b/data/objects/anglerfish_spot.yaml
index def90ea..0c670a2 100644
--- a/data/objects/anglerfish_spot.yaml
+++ b/data/objects/anglerfish_spot.yaml
@@ -5,7 +5,7 @@ gather:
drops:
- item_id: raw_anglerfish
level: 82
- message: You catch a {50}raw anglerfish{/}.
+ success_message: You catch a {50}raw anglerfish{/}.
weight: 100
xp: 120
fail_message: Nothing seems to bite.
diff --git a/data/objects/bait_fishing_spot.yaml b/data/objects/bait_fishing_spot.yaml
index f687016..b870a7f 100644
--- a/data/objects/bait_fishing_spot.yaml
+++ b/data/objects/bait_fishing_spot.yaml
@@ -5,17 +5,17 @@ gather:
drops:
- item_id: raw_sardine
level: 5
- message: You catch a {4B}raw sardine{/}.
+ success_message: You catch a {4B}raw sardine{/}.
weight: 60
xp: 20
- item_id: raw_herring
level: 10
- message: You catch a {4B}raw herring{/}.
+ success_message: You catch a {4B}raw herring{/}.
weight: 40
xp: 30
- item_id: raw_pike
level: 25
- message: You catch a {45}raw pike{/}.
+ success_message: You catch a {45}raw pike{/}.
weight: 30
xp: 60
fail_message: Nothing seems to bite.
diff --git a/data/objects/cage_fishing_spot.yaml b/data/objects/cage_fishing_spot.yaml
index 051f1aa..977d4fb 100644
--- a/data/objects/cage_fishing_spot.yaml
+++ b/data/objects/cage_fishing_spot.yaml
@@ -4,7 +4,7 @@ gather:
drops:
- item_id: raw_lobster
level: 40
- message: You catch a {21}raw lobster{/}.
+ success_message: You catch a {21}raw lobster{/}.
weight: 100
xp: 90
fail_message: The pot comes up empty.
diff --git a/data/objects/clay_rock.yaml b/data/objects/clay_rock.yaml
index 2bf4169..8f9275c 100644
--- a/data/objects/clay_rock.yaml
+++ b/data/objects/clay_rock.yaml
@@ -5,7 +5,7 @@ gather:
- depletes: true
item_id: clay
level: 1
- message: You manage to mine some clay.
+ success_message: You manage to mine some clay.
weight: 100
xp: 5
fail_message: You chip away but get nothing useful.
diff --git a/data/objects/coal_rock.yaml b/data/objects/coal_rock.yaml
index 40bf4d6..9d09672 100644
--- a/data/objects/coal_rock.yaml
+++ b/data/objects/coal_rock.yaml
@@ -4,7 +4,7 @@ gather:
- depletes: true
item_id: coal
level: 30
- message: You manage to mine some {F0}coal{/}.
+ success_message: You manage to mine some {F0}coal{/}.
weight: 100
xp: 50
fail_message: You chip away but get nothing useful.
diff --git a/data/objects/copper_rock.yaml b/data/objects/copper_rock.yaml
index 9694d1f..dc4e166 100644
--- a/data/objects/copper_rock.yaml
+++ b/data/objects/copper_rock.yaml
@@ -9,7 +9,7 @@ gather:
- depletes: true
item_id: copper_ore
level: 1
- message: You manage to mine some {B2}copper ore{/}.
+ success_message: You manage to mine some {B2}copper ore{/}.
quantity: 0
table: ""
weight: 90
@@ -17,7 +17,7 @@ gather:
- depletes: false
item_id: ""
level: 0
- message: You spot a glint of something valuable!
+ success_message: You spot a glint of something valuable!
quantity: 0
table: gem_table
weight: 10
diff --git a/data/objects/fly_fishing_spot.yaml b/data/objects/fly_fishing_spot.yaml
index e3bf342..8c32d38 100644
--- a/data/objects/fly_fishing_spot.yaml
+++ b/data/objects/fly_fishing_spot.yaml
@@ -5,17 +5,17 @@ gather:
drops:
- item_id: raw_trout
level: 20
- message: You catch a {45}raw trout{/}.
+ success_message: You catch a {45}raw trout{/}.
weight: 50
xp: 50
- item_id: raw_salmon
level: 30
- message: You catch a {45}raw salmon{/}.
+ success_message: You catch a {45}raw salmon{/}.
weight: 50
xp: 70
- item_id: raw_rainbow_fish
level: 38
- message: You catch a {B1}raw rainbow fish{/}.
+ success_message: You catch a {B1}raw rainbow fish{/}.
weight: 30
xp: 80
fail_message: Nothing seems to bite.
diff --git a/data/objects/gold_rock.yaml b/data/objects/gold_rock.yaml
index a6228a4..d1326c2 100644
--- a/data/objects/gold_rock.yaml
+++ b/data/objects/gold_rock.yaml
@@ -4,7 +4,7 @@ gather:
- depletes: true
item_id: gold_ore
level: 40
- message: You manage to mine some {DC}gold ore{/}.
+ success_message: You manage to mine some {DC}gold ore{/}.
weight: 100
xp: 65
fail_message: You chip away but get nothing useful.
diff --git a/data/objects/harpoon_fishing_spot.yaml b/data/objects/harpoon_fishing_spot.yaml
index ca377b2..2a78bc5 100644
--- a/data/objects/harpoon_fishing_spot.yaml
+++ b/data/objects/harpoon_fishing_spot.yaml
@@ -4,17 +4,17 @@ gather:
drops:
- item_id: raw_tuna
level: 35
- message: You harpoon a {21}raw tuna{/}.
+ success_message: You harpoon a {21}raw tuna{/}.
weight: 60
xp: 80
- item_id: raw_swordfish
level: 50
- message: You harpoon a {21}raw swordfish{/}.
+ success_message: You harpoon a {21}raw swordfish{/}.
weight: 50
xp: 100
- item_id: raw_shark
level: 76
- message: You harpoon a {1B}raw shark{/}.
+ success_message: You harpoon a {1B}raw shark{/}.
weight: 30
xp: 110
fail_message: You fail to spear anything.
diff --git a/data/objects/iron_rock.yaml b/data/objects/iron_rock.yaml
index 70eafe6..3e94dc8 100644
--- a/data/objects/iron_rock.yaml
+++ b/data/objects/iron_rock.yaml
@@ -4,7 +4,7 @@ gather:
- depletes: true
item_id: iron_ore
level: 15
- message: You manage to mine some {FA}iron ore{/}.
+ success_message: You manage to mine some {FA}iron ore{/}.
weight: 100
xp: 35
fail_message: You chip away but get nothing useful.
diff --git a/data/objects/magic_tree.yaml b/data/objects/magic_tree.yaml
index 9016853..39ee11d 100644
--- a/data/objects/magic_tree.yaml
+++ b/data/objects/magic_tree.yaml
@@ -5,7 +5,7 @@ gather:
- depletes: false
item_id: magic_logs
level: 75
- message: You get some {87}magic logs{/}.
+ success_message: You get some {87}magic logs{/}.
weight: 100
xp: 250
fail_message: You swing but get no logs.
diff --git a/data/objects/mahogany_tree.yaml b/data/objects/mahogany_tree.yaml
index bb5077b..25b344e 100644
--- a/data/objects/mahogany_tree.yaml
+++ b/data/objects/mahogany_tree.yaml
@@ -5,7 +5,7 @@ gather:
- depletes: false
item_id: mahogany_logs
level: 50
- message: You get some {83}mahogany logs{/}.
+ success_message: You get some {83}mahogany logs{/}.
weight: 100
xp: 125
fail_message: You swing but get no logs.
diff --git a/data/objects/maple_tree.yaml b/data/objects/maple_tree.yaml
index 3bf4005..fe80732 100644
--- a/data/objects/maple_tree.yaml
+++ b/data/objects/maple_tree.yaml
@@ -5,7 +5,7 @@ gather:
- depletes: false
item_id: maple_logs
level: 45
- message: You get some {AD}maple logs{/}.
+ success_message: You get some {AD}maple logs{/}.
weight: 100
xp: 100
fail_message: You swing but get no logs.
diff --git a/data/objects/mithril_rock.yaml b/data/objects/mithril_rock.yaml
index b561c97..e5da31e 100644
--- a/data/objects/mithril_rock.yaml
+++ b/data/objects/mithril_rock.yaml
@@ -4,7 +4,7 @@ gather:
- depletes: true
item_id: mithril_ore
level: 55
- message: You manage to mine some {4B}mithril ore{/}.
+ success_message: You manage to mine some {4B}mithril ore{/}.
weight: 100
xp: 80
fail_message: You chip away but get nothing useful.
diff --git a/data/objects/net_fishing_spot.yaml b/data/objects/net_fishing_spot.yaml
index 7acfcce..4c97ea8 100644
--- a/data/objects/net_fishing_spot.yaml
+++ b/data/objects/net_fishing_spot.yaml
@@ -5,27 +5,27 @@ gather:
drops:
- item_id: raw_shrimps
level: 1
- message: You catch some {4B}raw shrimps{/}.
+ success_message: You catch some {4B}raw shrimps{/}.
weight: 50
xp: 10
- item_id: raw_anchovies
level: 1
- message: You catch some {4B}raw anchovies{/}.
+ success_message: You catch some {4B}raw anchovies{/}.
weight: 50
xp: 10
- item_id: raw_mackerel
level: 16
- message: You catch a {45}raw mackerel{/}.
+ success_message: You catch a {45}raw mackerel{/}.
weight: 50
xp: 20
- item_id: raw_cod
level: 23
- message: You catch a {45}raw cod{/}.
+ success_message: You catch a {45}raw cod{/}.
weight: 50
xp: 30
- item_id: raw_bass
level: 46
- message: You catch a {45}raw bass{/}.
+ success_message: You catch a {45}raw bass{/}.
weight: 50
xp: 50
fail_message: You catch nothing.
diff --git a/data/objects/oak_tree.yaml b/data/objects/oak_tree.yaml
index 98c35b8..47a4354 100644
--- a/data/objects/oak_tree.yaml
+++ b/data/objects/oak_tree.yaml
@@ -5,7 +5,7 @@ gather:
- depletes: false
item_id: oak_logs
level: 15
- message: You get some {71}oak logs{/}.
+ success_message: You get some {71}oak logs{/}.
weight: 100
xp: 37
fail_message: You swing but get no logs.
diff --git a/data/objects/redwood_tree.yaml b/data/objects/redwood_tree.yaml
index 9eee250..f599d18 100644
--- a/data/objects/redwood_tree.yaml
+++ b/data/objects/redwood_tree.yaml
@@ -5,7 +5,7 @@ gather:
- depletes: false
item_id: redwood_logs
level: 90
- message: You get some {83}redwood logs{/}.
+ success_message: You get some {83}redwood logs{/}.
weight: 100
xp: 380
fail_message: You swing but get no logs.
diff --git a/data/objects/runite_rock.yaml b/data/objects/runite_rock.yaml
index 7bc3681..53f9cf2 100644
--- a/data/objects/runite_rock.yaml
+++ b/data/objects/runite_rock.yaml
@@ -4,7 +4,7 @@ gather:
- depletes: true
item_id: runite_ore
level: 85
- message: You manage to mine some {57}runite ore{/}.
+ success_message: You manage to mine some {57}runite ore{/}.
weight: 100
xp: 125
fail_message: You chip away but get nothing useful.
diff --git a/data/objects/sacred_eel_spot.yaml b/data/objects/sacred_eel_spot.yaml
index 3c05576..1a712cb 100644
--- a/data/objects/sacred_eel_spot.yaml
+++ b/data/objects/sacred_eel_spot.yaml
@@ -5,7 +5,7 @@ gather:
drops:
- item_id: sacred_eel
level: 87
- message: You catch a {B1}sacred eel{/}.
+ success_message: You catch a {B1}sacred eel{/}.
weight: 100
xp: 125
fail_message: Nothing seems to bite.
diff --git a/data/objects/scrap_pile.yaml b/data/objects/scrap_pile.yaml
index f85815b..f05a234 100644
--- a/data/objects/scrap_pile.yaml
+++ b/data/objects/scrap_pile.yaml
@@ -4,7 +4,7 @@ gather:
- depletes: false
item_id: scrap
level: 1
- message: You salvage some {F3}scrap{/}.
+ success_message: You salvage some {F3}scrap{/}.
weight: 100
xp: 5
gather_message: You swing your pickaxe at the scrap pile...
diff --git a/data/objects/sheep.yaml b/data/objects/sheep.yaml
index 234714b..e482198 100644
--- a/data/objects/sheep.yaml
+++ b/data/objects/sheep.yaml
@@ -5,7 +5,7 @@ gather:
- depletes: true
item_id: wool
level: 1
- message: You shear some wool from the sheep.
+ success_message: You shear some wool from the sheep.
weight: 100
xp: 0
fail_message: The sheep wriggles free!
diff --git a/data/objects/silver_rock.yaml b/data/objects/silver_rock.yaml
index c9b436f..b9f7777 100644
--- a/data/objects/silver_rock.yaml
+++ b/data/objects/silver_rock.yaml
@@ -4,7 +4,7 @@ gather:
- depletes: true
item_id: silver_ore
level: 20
- message: You manage to mine some {FD}silver ore{/}.
+ success_message: You manage to mine some {FD}silver ore{/}.
weight: 100
xp: 40
fail_message: You chip away but get nothing useful.
diff --git a/data/objects/swamp_fishing_spot.yaml b/data/objects/swamp_fishing_spot.yaml
index 8bca2bd..5c2cafc 100644
--- a/data/objects/swamp_fishing_spot.yaml
+++ b/data/objects/swamp_fishing_spot.yaml
@@ -5,12 +5,12 @@ gather:
drops:
- item_id: raw_slimy_eel
level: 28
- message: You catch a {6C}raw slimy eel{/}.
+ success_message: You catch a {6C}raw slimy eel{/}.
weight: 60
xp: 80
- item_id: raw_cave_eel
level: 38
- message: You catch a {50}raw cave eel{/}.
+ success_message: You catch a {50}raw cave eel{/}.
weight: 40
xp: 80
fail_message: Nothing seems to bite.
diff --git a/data/objects/teak_tree.yaml b/data/objects/teak_tree.yaml
index 5e47900..0419838 100644
--- a/data/objects/teak_tree.yaml
+++ b/data/objects/teak_tree.yaml
@@ -5,7 +5,7 @@ gather:
- depletes: false
item_id: teak_logs
level: 35
- message: You get some {B3}teak logs{/}.
+ success_message: You get some {B3}teak logs{/}.
weight: 100
xp: 85
fail_message: You swing but get no logs.
diff --git a/data/objects/tin_rock.yaml b/data/objects/tin_rock.yaml
index 20b2f21..9ec1011 100644
--- a/data/objects/tin_rock.yaml
+++ b/data/objects/tin_rock.yaml
@@ -4,7 +4,7 @@ gather:
- depletes: true
item_id: tin_ore
level: 1
- message: You manage to mine some {FD}tin ore{/}.
+ success_message: You manage to mine some {FD}tin ore{/}.
weight: 100
xp: 17
fail_message: You chip away but get nothing useful.
diff --git a/data/objects/tree.yaml b/data/objects/tree.yaml
index 4bd4ab0..83aba3a 100644
--- a/data/objects/tree.yaml
+++ b/data/objects/tree.yaml
@@ -5,7 +5,7 @@ gather:
- depletes: true
item_id: logs
level: 1
- message: You get some {6B}logs{/}.
+ success_message: You get some {6B}logs{/}.
weight: 100
xp: 25
exhausted_message: The tree comes crashing down!
diff --git a/data/objects/willow_tree.yaml b/data/objects/willow_tree.yaml
index 1b7645e..a0e697a 100644
--- a/data/objects/willow_tree.yaml
+++ b/data/objects/willow_tree.yaml
@@ -5,7 +5,7 @@ gather:
- depletes: false
item_id: willow_logs
level: 30
- message: You get some {72}willow logs{/}.
+ success_message: You get some {72}willow logs{/}.
weight: 100
xp: 67
fail_message: You swing but get no logs.
diff --git a/data/objects/yew_tree.yaml b/data/objects/yew_tree.yaml
index a9313ea..61aa71e 100644
--- a/data/objects/yew_tree.yaml
+++ b/data/objects/yew_tree.yaml
@@ -5,7 +5,7 @@ gather:
- depletes: false
item_id: yew_logs
level: 60
- message: You get some {6C}yew logs{/}.
+ success_message: You get some {6C}yew logs{/}.
weight: 100
xp: 175
fail_message: You swing but get no logs.
diff --git a/data/rooms/intro/2001.yaml b/data/rooms/intro/2001.yaml
index 4b21ab8..ad4667a 100644
--- a/data/rooms/intro/2001.yaml
+++ b/data/rooms/intro/2001.yaml
@@ -19,10 +19,10 @@ item_spawns:
respawn_ticks: 1
- id: pot_of_flour
quantity: 1
- respawn_ticks: 0
+ respawn_ticks: 1
- id: bucket_of_water
quantity: 1
- respawn_ticks: 0
+ respawn_ticks: 1
mobs: []
name: Test Room
objects:
diff --git a/internal/admin/static/itemeditor.js b/internal/admin/static/itemeditor.js
index d02e7ab..d02945a 100644
--- a/internal/admin/static/itemeditor.js
+++ b/internal/admin/static/itemeditor.js
@@ -59,24 +59,24 @@ var SECTIONS = [
['success_base', 'Base', 'number', '', 'narrow'],
['success_per_level', 'Per Lvl', 'number', '', 'narrow'],
['success_cap', 'Cap', 'number', '', 'narrow'],
- ['start_message', 'Start Message', 'text', '', 'wide'],
- ['message', 'Message', 'text', '', 'wide'],
- ['fail_message', 'Fail Message', 'text', '', 'wide', function(item) { return item.type === 'cooking' || item.subtype === 'smelt'; }],
- ['end_message', 'End Message', 'text', '', 'wide'],
+ ['start_message', 'Start Message', 'text', 'e.g. You begin smithing %i1.', 'wide'],
+ ['success_message', 'Success Message', 'text', 'e.g. You smith a %n.', 'wide'],
+ ['fail_message', 'Fail Message', 'text', 'e.g. You accidentally burn the %i1.', 'wide', function(item) { return item.type === 'cooking' || item.subtype === 'smelt'; }],
+ ['end_message', 'End Message', 'text', 'e.g. You\'ve finished smithing.', 'wide'],
],
fieldGroups: [
['type', 'subtype', 'level', 'xp', 'wait', 'output_qty'],
{fields: ['station', 'tool'], right: true},
{label: 'Success', fields: ['success_base', 'success_per_level', 'success_cap', 'fail']},
- ['start_message', 'message', 'fail_message', 'end_message'],
+ ['start_message', 'success_message', 'fail_message', 'end_message'],
],
subSubtables: [
{label:'Ingredients', key:'ingredients', fields:[
['quantity', 'Qty', 'number', '1', 'narrow'],
]},
{label:'Steps', key:'steps', fields:[
- ['tick', 'Ticks', 'number', '', 'narrow'],
- ['message', 'Message', 'text', '', 'wide'],
+ ['delay', 'Delay (ticks)', 'number', '', 'narrow'],
+ ['message', 'Message', 'text', '', 'wide'],
]},
],
}
@@ -556,6 +556,8 @@ function renderSubFieldHTML(sec, stKey, idx, f, val) {
if (f[4] === 'narrow') extraCls = ' obj-narrow';
else if (f[4] === 'grow') extraCls = ' obj-grow';
if (f[4] === 'wide') extraStyle = ' style="flex:1 1 100%"';
+ var defaultPh = '';
+ if (f[3]) defaultPh = ' placeholder="' + escAttr(f[3]) + '"';
if (f[2] === 'search') {
var entity = f[6] || 'items';
var ph = f[3] ? ' placeholder="' + escAttr(f[3]) + '"' : ' placeholder="Search ' + entity + '..."';
@@ -573,7 +575,7 @@ function renderSubFieldHTML(sec, stKey, idx, f, val) {
sel += '</select></label>';
return sel;
}
- return '<label class="obj-field' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"></label>';
+ return '<label class="obj-field' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + defaultPh + '></label>';
}
function renderSubtable(sec, st, data) {
@@ -653,7 +655,7 @@ function renderSubtable(sec, st, data) {
if (sst.key === 'steps') {
var stepsArr = item[sst.key];
if (!stepsArr || !Array.isArray(stepsArr) || stepsArr.length === 0) {
- h += '<button class="btn btn-sm obj-sub-add" style="margin-top:4px" onclick="addSubSubRow(\'' + sec.id + '\',\'' + st.key + '\',' + idx + ',\'' + sst.key + '\')">+ Add Custom Delay/Message</button>';
+ h += '<button class="btn btn-sm obj-sub-add" data-section="steps-add-' + idx + '" style="margin-top:4px" onclick="addSubSubRow(\'' + sec.id + '\',\'' + st.key + '\',' + idx + ',\'' + sst.key + '\')">+ Add Custom Delay/Message</button>';
return;
}
}
@@ -721,7 +723,9 @@ function removeConsumePair(secID, subKey, rowIdx, subSubKey, subIdx, pairIdx) {
function renderSubSubtable(sec, st, rowIdx, sst, parentItem) {
var arr = parentItem[sst.key] || [];
var isIngredients = sst.key === 'ingredients';
- var h = '<div class="obj-subtable" style="margin:6px 0 6px 12px;border-left:2px solid var(--accent)">';
+ var isSteps = sst.key === 'steps';
+ var stepsAttr = isSteps ? ' data-section="steps-section"' : '';
+ var h = '<div class="obj-subtable"' + stepsAttr + ' style="margin:6px 0 6px 12px;border-left:2px solid var(--accent)">';
if (!isIngredients) {
h += '<div class="obj-subtable-header">' + esc(sst.label) + '</div>';
}
@@ -1035,6 +1039,11 @@ function setupCraftTypeChange() {
var xpField = document.getElementById(id.replace(/_type$/, '_xp'));
var stationField = document.getElementById(id.replace(/_type$/, '_station'));
+ var combineHideFields = ['success_message', 'fail_message', 'tool', 'wait',
+ 'success_base', 'success_per_level', 'success_cap', 'fail'];
+
+ var recipeRow = typeSelect.closest('.obj-sub-row');
+
function update() {
var t = typeSelect.value;
@@ -1049,6 +1058,25 @@ function setupCraftTypeChange() {
if (sLbl) sLbl.style.display = (t === 'fletching' || t === 'combine' || t === 'pharmacy') ? 'none' : '';
}
+ combineHideFields.forEach(function(fKey) {
+ var el = document.getElementById(id.replace(/_type$/, '_' + fKey));
+ if (!el) return;
+ var lbl = el.closest('.obj-field');
+ if (lbl) lbl.style.display = t === 'combine' ? 'none' : '';
+ });
+
+ var successGroup = document.querySelector('.success-group');
+ if (successGroup) successGroup.style.display = t === 'combine' ? 'none' : '';
+ var successBtn = document.getElementById(successGroup ? successGroup.id + '_btn' : '');
+ if (successBtn) successBtn.style.display = t === 'combine' ? 'none' : '';
+
+ var stepsSection = recipeRow.querySelector('[data-section="steps-section"]');
+ var stepsAddBtn = recipeRow.querySelector('[data-section^="steps-add-"]');
+
+ var showSteps = t === 'combine';
+ if (stepsSection) stepsSection.style.display = showSteps ? '' : 'none';
+ if (stepsAddBtn) stepsAddBtn.style.display = showSteps ? '' : 'none';
+
if (subtypeSel) {
var sLbl = subtypeSel.closest('.obj-field');
if (t === 'smithing') {
diff --git a/internal/behavior/behavior.go b/internal/behavior/behavior.go
index 8333a05..c0a714c 100644
--- a/internal/behavior/behavior.go
+++ b/internal/behavior/behavior.go
@@ -5,15 +5,16 @@ type GatherConfig struct {
Tools []string `yaml:"tools"`
Bait string `yaml:"bait"`
Success SuccessFormula `yaml:"success"`
- GatherMsg string `yaml:"gather_message"`
+ GatherMessage string `yaml:"gather_message"`
DepletedMessage string `yaml:"depleted_message"`
ExhaustedMessage string `yaml:"exhausted_message"`
- FailMsg string `yaml:"fail_message"`
+ FailMessage string `yaml:"fail_message"`
Drops []DropEntry `yaml:"drops"`
RespawnTimer float64 `yaml:"respawn_timer"`
RespawnBroadcast string `yaml:"respawn_broadcast"`
DepleteTimer float64 `yaml:"deplete_timer"`
NestChance int `yaml:"nest_chance"`
+ BroadcastMessage string `yaml:"broadcast_message"`
}
type SuccessFormula struct {
@@ -82,13 +83,16 @@ type Condition struct {
}
type UseConfig struct {
- Message string `yaml:"message"`
- Wait float64 `yaml:"wait"`
- Consume map[string]int `yaml:"consume"`
- Reward DropEntry `yaml:"reward"`
- FailMsg string `yaml:"fail_message"`
- Success *SuccessFormula `yaml:"success"`
- Skill string `yaml:"skill"`
- Level int `yaml:"level"`
- XP int `yaml:"xp"`
+ StartMessage string `yaml:"start_message"`
+ Wait float64 `yaml:"wait"`
+ Consume map[string]int `yaml:"consume"`
+ Reward DropEntry `yaml:"reward"`
+ FailMessage string `yaml:"fail_message"`
+ Success *SuccessFormula `yaml:"success"`
+ Skill string `yaml:"skill"`
+ Level int `yaml:"level"`
+ XP int `yaml:"xp"`
+ SuccessMessage string `yaml:"success_message"`
+ EndMessage string `yaml:"end_message"`
+ BroadcastMessage string `yaml:"broadcast_message"`
}
diff --git a/internal/behavior/store.go b/internal/behavior/store.go
index 710506e..38cc72b 100644
--- a/internal/behavior/store.go
+++ b/internal/behavior/store.go
@@ -154,10 +154,10 @@ func ResolveDrop(dataDir string, drops []DropEntry) *DropEntry {
qty = 1
}
return &DropEntry{
- ItemID: resolved.ItemID,
- Quantity: qty,
- Depletes: drops[i].Depletes,
- Message: drops[i].Message,
+ ItemID: resolved.ItemID,
+ Quantity: qty,
+ Depletes: drops[i].Depletes,
+ SuccessMessage: drops[i].SuccessMessage,
}
}
}
diff --git a/internal/behavior/types.go b/internal/behavior/types.go
index c061742..571f2c4 100644
--- a/internal/behavior/types.go
+++ b/internal/behavior/types.go
@@ -55,6 +55,7 @@ const (
TypeMix ActionType = "mix"
TypeConstruct ActionType = "construct"
TypeTriggerModule ActionType = "trigger_module"
+ TypeUseInteraction ActionType = "use_interaction"
)
type Action struct {
@@ -85,14 +86,12 @@ type GatherData struct {
}
type ProductionData struct {
- ItemID string
- Phase int
- Wait float64
- StartMsg string
- EndMsg string
- Remaining int
- NextStepIndex int
- StepsAccumulatedTicks float64
+ ItemID string
+ Phase int
+ Wait float64
+ StartMessage string
+ EndMessage string
+ Remaining int
}
type StealData struct {
@@ -126,6 +125,12 @@ type UseData struct {
Step int
}
+type UseInteractionData struct {
+ Message string
+ Action *NodeAction
+ ObjName string
+}
+
type BurnData struct {
ItemID string
ToolSpeed float64
@@ -172,6 +177,16 @@ type SearchData struct {
Started bool
}
+type CombineData struct {
+ ItemID string
+ Phase int
+ StepIndex int
+ Remaining int
+ StartMessage string
+ EndMessage string
+ Wait float64
+}
+
type FletchData struct {
ItemID string
Phase int
@@ -196,14 +211,14 @@ type TriggerModuleData struct {
}
type DropEntry struct {
- ItemID string `yaml:"item_id"`
- Table string `yaml:"table"`
- Weight int `yaml:"weight"`
- Quantity int `yaml:"quantity"`
- Depletes bool `yaml:"depletes"`
- Message string `yaml:"message"`
- Level int `yaml:"level"`
- XP int `yaml:"xp"`
+ ItemID string `yaml:"item_id"`
+ Table string `yaml:"table"`
+ Weight int `yaml:"weight"`
+ Quantity int `yaml:"quantity"`
+ Depletes bool `yaml:"depletes"`
+ SuccessMessage string `yaml:"success_message"`
+ Level int `yaml:"level"`
+ XP int `yaml:"xp"`
}
type DropTableDef struct {
diff --git a/internal/game/act.go b/internal/game/act.go
index aa25625..c8101ab 100644
--- a/internal/game/act.go
+++ b/internal/game/act.go
@@ -236,9 +236,13 @@ func (g *Game) AdvanceActions() {
g.advanceObstacle(sess, p)
case behavior.TypeTalk:
g.advanceTalk(sess, p)
- case behavior.TypeTriggerModule:
- g.advanceTriggerModule(sess, p)
- default:
+ case behavior.TypeTriggerModule:
+ g.advanceTriggerModule(sess, p)
+ case behavior.TypeUseInteraction:
+ g.advanceUseInteraction(sess, p)
+ case behavior.TypeCombine:
+ g.advanceCombine(sess, p)
+ default:
if productionActionTypes[string(p.Action.Type)] {
g.advanceProduction(sess, p)
}
diff --git a/internal/game/act_clean.go b/internal/game/act_clean.go
index 021a07c..7647f4f 100644
--- a/internal/game/act_clean.go
+++ b/internal/game/act_clean.go
@@ -71,7 +71,7 @@ func (g *Game) advanceClean(sess *net.Session, p *player.Player) {
}
g.AccountStore.SaveCharacter(p)
- msg := g.resolveCraftMessage(sess, craft.Message, "You clean the %i1.", craft, matchItem.ID, nil, p.HasItem)
+ msg := g.resolveCraftMessage(sess, craft.SuccessMessage, "You clean the %i1.", craft, matchItem.ID, nil, p.HasItem)
if p.OptionBool("xp_drops") && craft.XP > 0 {
msg += g.formatXpDropSingle(sess, p, player.Pharmacy, craft.XP)
}
diff --git a/internal/game/act_combine.go b/internal/game/act_combine.go
new file mode 100644
index 0000000..8937d38
--- /dev/null
+++ b/internal/game/act_combine.go
@@ -0,0 +1,157 @@
+package game
+
+import (
+ "thehouseoficarus/internal/behavior"
+ "thehouseoficarus/internal/engine"
+ "thehouseoficarus/internal/item"
+ "thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/player"
+)
+
+func (g *Game) startCombine(sess *net.Session, p *player.Player, item *item.ItemDef, craft *item.CraftDef, count int) {
+ g.cancelAction(p)
+ g.cancelBgAction(p)
+
+ if !craftHasAllItemsQty(craft, p.CountItem) {
+ sess.WriteLine("You don't have the required materials.")
+ g.reprompt(sess)
+ return
+ }
+
+ outDef, _ := g.ItemStore.Load(item.ID)
+ outputName := item.ID
+ if outDef != nil {
+ outputName = outDef.Name
+ }
+
+ startMsg := g.resolveCraftMessage(sess, craft.StartMessage, "You start combining %i1.", craft, item.ID, nil, p.HasItem)
+ endMsg := g.resolveCraftMessage(sess, craft.EndMessage, "You've finished combining.", craft, item.ID, nil, p.HasItem)
+
+ p.Action = &behavior.Action{
+ Type: behavior.TypeCombine,
+ TargetID: item.ID,
+ TargetName: outputName,
+ Data: &behavior.CombineData{
+ ItemID: item.ID,
+ Phase: 0,
+ StepIndex: 0,
+ Remaining: count,
+ StartMessage: startMsg,
+ EndMessage: endMsg,
+ Wait: craft.Wait,
+ },
+ WaitLeft: engine.ToTicks(1),
+ }
+
+ g.broadcastAction(sess, "%s starts combining.", p.Name)
+}
+
+func (g *Game) advanceCombine(sess *net.Session, p *player.Player) {
+ d, ok := p.Action.Data.(*behavior.CombineData)
+ if !ok {
+ g.cancelAction(p)
+ return
+ }
+
+ item := g.loadCraftItem(d.ItemID)
+ if item == nil {
+ g.cancelAction(p)
+ return
+ }
+ craft := item.FirstCraft()
+
+ switch d.Phase {
+ case 0:
+ if d.StartMessage != "" {
+ sess.WriteLine(d.StartMessage)
+ }
+ d.Phase = 1
+ if len(craft.Steps) > 0 {
+ p.Action.WaitLeft = engine.ToTicks(craft.Steps[0].Delay)
+ } else {
+ firstWait := craft.Wait
+ if firstWait <= 0 {
+ firstWait = 4
+ }
+ p.Action.WaitLeft = engine.ToTicks(firstWait)
+ }
+ return
+
+ case 1:
+ if len(craft.Steps) > 0 && d.StepIndex < len(craft.Steps) {
+ step := craft.Steps[d.StepIndex]
+ sess.WriteLine(g.resolveCraftVar(sess, step.Message, craft, item.ID, nil, p.HasItem))
+ d.StepIndex++
+ if d.StepIndex < len(craft.Steps) {
+ p.Action.WaitLeft = engine.ToTicks(craft.Steps[d.StepIndex].Delay)
+ return
+ }
+ }
+ d.Phase = 2
+ p.Action.WaitLeft = engine.ToTicks(1)
+ return
+
+ case 2:
+ if !craftHasAllItemsQty(craft, p.CountItem) {
+ if d.EndMessage != "" {
+ sess.WriteLine(d.EndMessage)
+ }
+ g.cancelAction(p)
+ return
+ }
+
+ consumeIngredients(craft, p.HasItem, p.RemoveItem)
+
+ outputQty := craft.OutputQty
+ if outputQty <= 0 {
+ outputQty = 1
+ }
+
+ outDef, _ := g.ItemStore.Load(item.ID)
+ placed := false
+ if outDef != nil && outDef.Stackable {
+ for i := 0; i < 28; i++ {
+ slot := p.InvSlot(i)
+ if slot != nil && slot.ItemID == item.ID {
+ slot.Quantity += outputQty
+ placed = true
+ break
+ }
+ }
+ }
+ if !placed {
+ freeSlot := p.FirstFreeSlot()
+ if freeSlot == -1 {
+ sess.WriteLine("Your inventory is too full!")
+ g.cancelAction(p)
+ return
+ }
+ p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: item.ID, Quantity: outputQty})
+ }
+
+ g.AccountStore.SaveCharacter(p)
+
+ if d.Remaining > 0 {
+ d.Remaining--
+ if d.Remaining <= 0 {
+ if d.EndMessage != "" {
+ sess.WriteLine(d.EndMessage)
+ }
+ g.cancelAction(p)
+ return
+ }
+ }
+
+ if !craftHasAllItemsQty(craft, p.CountItem) {
+ if d.EndMessage != "" {
+ sess.WriteLine(d.EndMessage)
+ }
+ g.cancelAction(p)
+ return
+ }
+
+ d.Phase = 0
+ d.StepIndex = 0
+ p.Action.WaitLeft = engine.ToTicks(1)
+ }
+}
diff --git a/internal/game/act_fletch.go b/internal/game/act_fletch.go
index 800bc8e..ce3a525 100644
--- a/internal/game/act_fletch.go
+++ b/internal/game/act_fletch.go
@@ -188,7 +188,7 @@ func (g *Game) advanceFletch(sess *net.Session, p *player.Player) {
}
g.AccountStore.SaveCharacter(p)
- msg := g.resolveCraftMessage(sess, c.Message, "You fletch a %n.", c, def.ID, nil, p.HasItem)
+ msg := g.resolveCraftMessage(sess, c.SuccessMessage, "You fletch a %n.", c, def.ID, nil, p.HasItem)
if p.OptionBool("xp_drops") && c.XP > 0 {
msg += g.formatXpDropSingle(sess, p, skill, c.XP)
}
diff --git a/internal/game/act_gather.go b/internal/game/act_gather.go
index ab59de0..c34c6ae 100644
--- a/internal/game/act_gather.go
+++ b/internal/game/act_gather.go
@@ -106,7 +106,12 @@ func (g *Game) startGather(sess *net.Session, p *player.Player, obj *object.Obje
verb = "chopping"
}
- g.broadcastAction(sess, "%s starts %s the %s.", p.Name, verb, obj.Name)
+ if cfg.BroadcastMessage != "" {
+ bcMsg := strings.ReplaceAll(cfg.BroadcastMessage, "%o", obj.Name)
+ g.broadcastAction(sess, bcMsg, p.Name)
+ } else {
+ g.broadcastAction(sess, "%s starts %s the %s.", p.Name, verb, obj.Name)
+ }
d := &behavior.GatherData{
ObjDefID: obj.ID,
@@ -158,7 +163,7 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) {
return
}
}
- sess.WriteLine(fmt.Sprintf("%s", cfg.GatherMsg))
+ sess.WriteLine(fmt.Sprintf("%s", cfg.GatherMessage))
d.Step = 1
p.Action.WaitLeft = engine.ToTicks(wait)
return
@@ -220,12 +225,14 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) {
}
g.AccountStore.SaveCharacter(p)
- msg := drop.Message
- if msg == "" {
- msg = fmt.Sprintf("You manage to get some %s.", g.itemColorize(sess, itemDef, itemName))
- } else {
- msg = color.ExpandTags(g.colorMode(sess), msg)
- }
+ msg := drop.SuccessMessage
+ coloredName := g.itemColorize(sess, itemDef, itemName)
+ if msg == "" {
+ msg = fmt.Sprintf("You manage to get some %s.", coloredName)
+ } else {
+ msg = strings.ReplaceAll(msg, "%n", coloredName)
+ msg = color.ExpandTags(g.colorMode(sess), msg)
+ }
if xp > 0 && p.OptionBool("xp_drops") {
msg += g.formatXpDropSingle(sess, p, player.SkillName(cfg.Skill), xp)
}
@@ -294,7 +301,7 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) {
}
}
- sess.WriteLine(cfg.FailMsg)
+ sess.WriteLine(cfg.FailMessage)
if p.FirstFreeSlot() == -1 {
sess.WriteLine(g.colorize(sess, "error", "Your inventory is too full!"))
g.cancelAction(p)
diff --git a/internal/game/act_state.go b/internal/game/act_state.go
index 051390f..c4ccaba 100644
--- a/internal/game/act_state.go
+++ b/internal/game/act_state.go
@@ -81,9 +81,11 @@ func (g *Game) playerActionDisplay(p *player.Player) string {
return "mixing some " + a.TargetName
case behavior.TypeConstruct:
return "constructing some " + a.TargetName
- case behavior.TypeTriggerModule:
- return "triggering " + a.TargetName
- }
+ case behavior.TypeTriggerModule:
+ return "triggering " + a.TargetName
+ case behavior.TypeUseInteraction:
+ return "using " + a.TargetName
+ }
}
if a := p.BackgroundAction; a != nil {
diff --git a/internal/game/act_use.go b/internal/game/act_use.go
index 0d8582d..1e9d8e9 100644
--- a/internal/game/act_use.go
+++ b/internal/game/act_use.go
@@ -3,6 +3,7 @@ package game
import (
"fmt"
"math/rand"
+ "strings"
"thehouseoficarus/internal/behavior"
"thehouseoficarus/internal/engine"
@@ -34,7 +35,11 @@ func (g *Game) startUse(sess *net.Session, p *player.Player, obj *object.ObjectD
return
}
- g.broadcastAction(sess, "%s uses the %s.", p.Name, obj.Name)
+ if cfg.BroadcastMessage != "" {
+ g.broadcastAction(sess, strings.ReplaceAll(cfg.BroadcastMessage, "%o", obj.Name), p.Name)
+ } else {
+ g.broadcastAction(sess, "%s uses the %s.", p.Name, obj.Name)
+ }
p.Action = &behavior.Action{
Type: "use",
@@ -44,7 +49,7 @@ func (g *Game) startUse(sess *net.Session, p *player.Player, obj *object.ObjectD
Data: &behavior.UseData{Cfg: cfg, Step: 0},
}
- sess.WriteLine(fmt.Sprintf("%s", cfg.Message))
+ sess.WriteLine(fmt.Sprintf("%s", cfg.StartMessage))
}
func (g *Game) advanceUse(sess *net.Session, p *player.Player) {
@@ -76,8 +81,8 @@ func (g *Game) advanceUse(sess *net.Session, p *player.Player) {
skillLevel := p.Level(player.SkillName(cfg.Skill))
chance := behavior.SuccessChance(*cfg.Success, skillLevel, cfg.Level)
if rand.Float64() >= chance {
- if cfg.FailMsg != "" {
- sess.WriteLine(cfg.FailMsg)
+ if cfg.FailMessage != "" {
+ sess.WriteLine(cfg.FailMessage)
}
p.Action.WaitLeft = engine.ToTicks(cfg.Wait)
return
@@ -107,17 +112,30 @@ func (g *Game) advanceUse(sess *net.Session, p *player.Player) {
if rewardDef != nil {
itemName = rewardDef.Name
}
- line := fmt.Sprintf("You make a %s.", g.itemColorize(sess, rewardDef, itemName))
+ coloredName := g.itemColorize(sess, rewardDef, itemName)
+
+ var line string
+ if cfg.SuccessMessage != "" {
+ line = strings.ReplaceAll(cfg.SuccessMessage, "%n", coloredName)
+ } else {
+ line = fmt.Sprintf("You make a %s.", coloredName)
+ }
if cfg.XP > 0 && p.OptionBool("xp_drops") {
line += g.formatXpDropSingle(sess, p, player.SkillName(cfg.Skill), cfg.XP)
}
sess.WriteLine(line)
if p.FirstFreeSlot() == -1 {
+ if cfg.EndMessage != "" {
+ sess.WriteLine(cfg.EndMessage)
+ }
sess.WriteLine("Your inventory is full.")
g.cancelAction(p)
return
}
+ if cfg.EndMessage != "" {
+ sess.WriteLine(cfg.EndMessage)
+ }
p.Action.WaitLeft = engine.ToTicks(cfg.Wait)
}
diff --git a/internal/game/act_use_interaction.go b/internal/game/act_use_interaction.go
new file mode 100644
index 0000000..b96aaa3
--- /dev/null
+++ b/internal/game/act_use_interaction.go
@@ -0,0 +1,23 @@
+package game
+
+import (
+ "thehouseoficarus/internal/behavior"
+ "thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/player"
+)
+
+func (g *Game) advanceUseInteraction(sess *net.Session, p *player.Player) {
+ d, ok := p.Action.Data.(*behavior.UseInteractionData)
+ if !ok {
+ g.cancelAction(p)
+ return
+ }
+ if d.Message != "" {
+ sess.WriteLine(d.Message)
+ }
+ if d.Action != nil {
+ g.applyNodeAction(sess, d.Action)
+ }
+ g.broadcastAction(sess, "%s uses the %s.", p.Name, d.ObjName)
+ g.cancelAction(p)
+}
diff --git a/internal/game/cmd_use.go b/internal/game/cmd_use.go
index 033fd62..1989a3e 100644
--- a/internal/game/cmd_use.go
+++ b/internal/game/cmd_use.go
@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
+ "thehouseoficarus/internal/behavior"
"thehouseoficarus/internal/item"
"thehouseoficarus/internal/net"
"thehouseoficarus/internal/player"
@@ -97,13 +98,17 @@ func (g *Game) useRoomObject(sess *net.Session, p *player.Player, input string)
if ui.Condition != nil && !g.checkCondition(sess, ui.Condition) {
continue
}
- if ui.Message != "" {
- sess.WriteLine(ui.Message)
+ g.cancelAction(p)
+ p.Action = &behavior.Action{
+ Type: behavior.TypeUseInteraction,
+ TargetName: def.Name,
+ WaitLeft: 1,
+ Data: &behavior.UseInteractionData{
+ Message: ui.Message,
+ Action: ui.Action,
+ ObjName: def.Name,
+ },
}
- if ui.Action != nil {
- g.applyNodeAction(sess, ui.Action)
- }
- g.broadcastAction(sess, "%s uses the %s.", p.Name, def.Name)
return true
}
if len(def.UseInteractions) > 0 {
@@ -286,11 +291,16 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName,
if ui.Condition != nil && !g.checkCondition(sess, ui.Condition) {
continue
}
- if ui.Message != "" {
- sess.WriteLine(ui.Message)
- }
- if ui.Action != nil {
- g.applyNodeAction(sess, ui.Action)
+ g.cancelAction(p)
+ p.Action = &behavior.Action{
+ Type: behavior.TypeUseInteraction,
+ TargetName: def.Name,
+ WaitLeft: 1,
+ Data: &behavior.UseInteractionData{
+ Message: ui.Message,
+ Action: ui.Action,
+ ObjName: def.Name,
+ },
}
return
}
diff --git a/internal/game/core_production.go b/internal/game/core_production.go
index da9466f..e6a62d4 100644
--- a/internal/game/core_production.go
+++ b/internal/game/core_production.go
@@ -80,13 +80,6 @@ var productionTypes = map[string]productionTypeInfo{
SuccessMessage: "You craft a %n.",
EndMessage: "You've finished crafting.",
},
- "combine": {
- ActionType: behavior.TypeCombine,
- DisplayVerb: "combining",
- StartMessage: "You start combining %i1.",
- SuccessMessage: "You produce %n.",
- EndMessage: "You've finished combining.",
- },
"fletching": {
ActionType: behavior.TypeFletch,
DisplayVerb: "fletching",
diff --git a/internal/game/production_engine.go b/internal/game/production_engine.go
index 048e152..1d54714 100644
--- a/internal/game/production_engine.go
+++ b/internal/game/production_engine.go
@@ -113,8 +113,8 @@ func (g *Game) startProduction(sess *net.Session, p *player.Player, item *item.I
ItemID: item.ID,
Phase: 0,
Wait: wait,
- StartMsg: startMsg,
- EndMsg: endMsg,
+ StartMessage: startMsg,
+ EndMessage: endMsg,
Remaining: count,
},
WaitLeft: engine.ToTicks(1),
@@ -129,6 +129,11 @@ func (g *Game) startProductionFromItem(sess *net.Session, p *player.Player, item
return
}
+ if craft.Type == "combine" {
+ g.startCombine(sess, p, item, craft, count)
+ return
+ }
+
_, stationName := g.findStation(p.RoomID, craft.Station)
if stationName == "" {
stationName = "inventory"
@@ -177,8 +182,8 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool {
itemID := d.ItemID
phase := d.Phase
wait := d.Wait
- startMsg := d.StartMsg
- endMsg := d.EndMsg
+ startMsg := d.StartMessage
+ endMsg := d.EndMessage
remaining := d.Remaining
item := g.loadCraftItem(itemID)
@@ -192,27 +197,11 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool {
if startMsg != "" {
sess.WriteLine(startMsg)
}
- if len(craft.Steps) > 0 {
- d.NextStepIndex = 0
- d.StepsAccumulatedTicks = 0
- }
d.Phase = 1
p.Action.WaitLeft = engine.ToTicks(wait)
return true
}
- if stepsIdx := d.NextStepIndex; stepsIdx < len(craft.Steps) {
- accTicks := d.StepsAccumulatedTicks
- accTicks += wait
- d.StepsAccumulatedTicks = accTicks
- for i := stepsIdx; i < len(craft.Steps); i++ {
- if accTicks >= craft.Steps[i].Tick {
- sess.WriteLine(g.resolveCraftVar(sess, craft.Steps[i].Message, craft, item.ID, nil, p.HasItem))
- d.NextStepIndex = i + 1
- }
- }
- }
-
skill := craft.EffectiveSkill()
skillLevel := p.Level(player.SkillName(skill))
chance := 1.0
@@ -278,7 +267,7 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool {
} else {
defaultSuccess = "You produce %n."
}
- msg := g.resolveCraftMessage(sess, craft.Message, defaultSuccess, craft, item.ID, byproducts, p.HasItem)
+ msg := g.resolveCraftMessage(sess, craft.SuccessMessage, defaultSuccess, craft, item.ID, byproducts, p.HasItem)
if p.OptionBool("xp_drops") && craft.XP > 0 {
msg += g.formatXpDropSingle(sess, p, player.SkillName(skill), craft.XP)
}
diff --git a/internal/item/item.go b/internal/item/item.go
index 0e63156..9c753b1 100644
--- a/internal/item/item.go
+++ b/internal/item/item.go
@@ -265,7 +265,7 @@ type IngredientEntry struct {
}
type CraftStep struct {
- Tick float64 `yaml:"tick"`
+ Delay float64 `yaml:"delay"`
Message string `yaml:"message"`
}
@@ -286,7 +286,7 @@ type CraftDef struct {
Ingredients []IngredientEntry `yaml:"ingredients"`
OutputQty int `yaml:"output_qty"`
Fail string `yaml:"fail"`
- Message string `yaml:"message"`
+ SuccessMessage string `yaml:"success_message"`
FailMessage string `yaml:"fail_message"`
StartMessage string `yaml:"start_message"`
EndMessage string `yaml:"end_message"`