aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-07-01 02:21:42 -0400
committerhistoria <[not public]>2026-07-01 02:21:42 -0400
commit5f37dc9b6f6b79da04da70ae0c33ec8d02998587 (patch)
treef7c309e36267a4c456fcf5bca800e614507a80af
parentfee376102192dc6f24297a7b11324636ace3a07d (diff)
downloadthehouseoficarus-5f37dc9b6f6b79da04da70ae0c33ec8d02998587.tar.gz
feat: qol improvements to items crud in admin gui
-rw-r--r--building_guide/items.md2
-rw-r--r--building_guide/recipes.md65
-rw-r--r--data/.admin_history.json2
-rw-r--r--data/drops/christmas_cracker_hat.yaml8
-rw-r--r--data/drops/christmas_cracker_misc.yaml9
-rw-r--r--data/items/ammo/adamant_arrow.yaml35
-rw-r--r--data/items/ammo/adamant_arrowtips.yaml3
-rw-r--r--data/items/ammo/adamant_bolts.yaml33
-rw-r--r--data/items/ammo/adamant_bolts_unf.yaml3
-rw-r--r--data/items/ammo/arrow_shaft.yaml18
-rw-r--r--data/items/ammo/bronze_arrow.yaml37
-rw-r--r--data/items/ammo/bronze_arrowtips.yaml3
-rw-r--r--data/items/ammo/bronze_bolts.yaml35
-rw-r--r--data/items/ammo/bronze_bolts_unf.yaml3
-rw-r--r--data/items/ammo/diamond_bolt_tips.yaml3
-rw-r--r--data/items/ammo/diamond_bolts.yaml35
-rw-r--r--data/items/ammo/diamond_bolts_e.yaml14
-rw-r--r--data/items/ammo/emerald_bolt_tips.yaml3
-rw-r--r--data/items/ammo/emerald_bolts.yaml33
-rw-r--r--data/items/ammo/emerald_bolts_e.yaml14
-rw-r--r--data/items/ammo/headless_arrow.yaml3
-rw-r--r--data/items/ammo/iron_arrow.yaml37
-rw-r--r--data/items/ammo/iron_arrowtips.yaml3
-rw-r--r--data/items/ammo/iron_bolts.yaml35
-rw-r--r--data/items/ammo/iron_bolts_unf.yaml3
-rw-r--r--data/items/ammo/mithril_arrow.yaml37
-rw-r--r--data/items/ammo/mithril_arrowtips.yaml3
-rw-r--r--data/items/ammo/mithril_bolts.yaml35
-rw-r--r--data/items/ammo/mithril_bolts_unf.yaml3
-rw-r--r--data/items/ammo/ruby_bolt_tips.yaml3
-rw-r--r--data/items/ammo/ruby_bolts.yaml35
-rw-r--r--data/items/ammo/ruby_bolts_e.yaml14
-rw-r--r--data/items/ammo/rune_arrow.yaml35
-rw-r--r--data/items/ammo/rune_arrowtips.yaml3
-rw-r--r--data/items/ammo/rune_bolts.yaml33
-rw-r--r--data/items/ammo/rune_bolts_unf.yaml3
-rw-r--r--data/items/ammo/sapphire_bolt_tips.yaml3
-rw-r--r--data/items/ammo/sapphire_bolts.yaml33
-rw-r--r--data/items/ammo/sapphire_bolts_e.yaml14
-rw-r--r--data/items/ammo/steel_arrow.yaml37
-rw-r--r--data/items/ammo/steel_arrowtips.yaml3
-rw-r--r--data/items/ammo/steel_bolts.yaml35
-rw-r--r--data/items/ammo/steel_bolts_unf.yaml3
-rw-r--r--data/items/consumables/amp_potion.yaml3
-rw-r--r--data/items/consumables/anchovies.yaml3
-rw-r--r--data/items/consumables/avantoe_potion_unf.yaml2
-rw-r--r--data/items/consumables/bio_serum.yaml3
-rw-r--r--data/items/consumables/bread.yaml3
-rw-r--r--data/items/consumables/bread_dough.yaml41
-rw-r--r--data/items/consumables/cadantine_potion_unf.yaml2
-rw-r--r--data/items/consumables/dwarf_weed_potion_unf.yaml2
-rw-r--r--data/items/consumables/full_restore.yaml3
-rw-r--r--data/items/consumables/guam_potion_unf.yaml2
-rw-r--r--data/items/consumables/harralander_potion_unf.yaml2
-rw-r--r--data/items/consumables/herring.yaml3
-rw-r--r--data/items/consumables/irit_potion_unf.yaml2
-rw-r--r--data/items/consumables/kwuarm_potion_unf.yaml2
-rw-r--r--data/items/consumables/lantadyme_potion_unf.yaml2
-rw-r--r--data/items/consumables/marrentill_potion_unf.yaml2
-rw-r--r--data/items/consumables/nano_restore.yaml3
-rw-r--r--data/items/consumables/pastry_dough.yaml2
-rw-r--r--data/items/consumables/pie_dish.yaml3
-rw-r--r--data/items/consumables/pizza_dough.yaml2
-rw-r--r--data/items/consumables/ranarr_potion_unf.yaml2
-rw-r--r--data/items/consumables/restoration_compound.yaml3
-rw-r--r--data/items/consumables/salmon.yaml3
-rw-r--r--data/items/consumables/sardine.yaml3
-rw-r--r--data/items/consumables/science_serum.yaml3
-rw-r--r--data/items/consumables/shield_potion.yaml3
-rw-r--r--data/items/consumables/shrimps.yaml3
-rw-r--r--data/items/consumables/snapdragon_potion_unf.yaml2
-rw-r--r--data/items/consumables/stim_potion.yaml3
-rw-r--r--data/items/consumables/super_amp.yaml3
-rw-r--r--data/items/consumables/super_bio_serum.yaml3
-rw-r--r--data/items/consumables/super_shield.yaml3
-rw-r--r--data/items/consumables/super_stim.yaml3
-rw-r--r--data/items/consumables/super_stim_cell.yaml3
-rw-r--r--data/items/consumables/targeting_serum.yaml3
-rw-r--r--data/items/consumables/tarromin_potion_unf.yaml2
-rw-r--r--data/items/consumables/tech_serum.yaml3
-rw-r--r--data/items/consumables/toadflax_potion_unf.yaml2
-rw-r--r--data/items/consumables/torstol_potion_unf.yaml2
-rw-r--r--data/items/consumables/trout.yaml3
-rw-r--r--data/items/decks/advanced_bio_deck.yaml17
-rw-r--r--data/items/decks/advanced_eco_deck.yaml17
-rw-r--r--data/items/decks/advanced_hydro_deck.yaml17
-rw-r--r--data/items/decks/advanced_solar_deck.yaml17
-rw-r--r--data/items/decks/basic_deck.yaml15
-rw-r--r--data/items/decks/bio_deck.yaml17
-rw-r--r--data/items/decks/eco_deck.yaml17
-rw-r--r--data/items/decks/hydro_deck.yaml17
-rw-r--r--data/items/decks/solar_deck.yaml17
-rw-r--r--data/items/equipment/abyssal_bracelet.yaml8
-rw-r--r--data/items/equipment/adamant_dagger.yaml43
-rw-r--r--data/items/equipment/adamant_full_helm.yaml39
-rw-r--r--data/items/equipment/adamant_med_helm.yaml39
-rw-r--r--data/items/equipment/adamant_platebody.yaml39
-rw-r--r--data/items/equipment/adamant_sword.yaml43
-rw-r--r--data/items/equipment/binding_necklace.yaml8
-rw-r--r--data/items/equipment/black_axe.yaml35
-rw-r--r--data/items/equipment/bracelet_of_clay.yaml8
-rw-r--r--data/items/equipment/bracelet_of_slaughter.yaml8
-rw-r--r--data/items/equipment/bronze_dagger.yaml43
-rw-r--r--data/items/equipment/bronze_full_helm.yaml39
-rw-r--r--data/items/equipment/bronze_med_helm.yaml39
-rw-r--r--data/items/equipment/bronze_platebody.yaml39
-rw-r--r--data/items/equipment/bronze_sword.yaml43
-rw-r--r--data/items/equipment/capacitor_gloves.yaml19
-rw-r--r--data/items/equipment/cape_of_agility.yaml8
-rw-r--r--data/items/equipment/coif.yaml50
-rw-r--r--data/items/equipment/diamond_amulet_u.yaml3
-rw-r--r--data/items/equipment/diamond_bracelet.yaml43
-rw-r--r--data/items/equipment/diamond_necklace.yaml43
-rw-r--r--data/items/equipment/diamond_ring.yaml43
-rw-r--r--data/items/equipment/digsite_pendant.yaml8
-rw-r--r--data/items/equipment/dragon_axe.yaml33
-rw-r--r--data/items/equipment/emerald_amulet_u.yaml3
-rw-r--r--data/items/equipment/emerald_bracelet.yaml41
-rw-r--r--data/items/equipment/emerald_necklace.yaml41
-rw-r--r--data/items/equipment/emerald_ring.yaml41
-rw-r--r--data/items/equipment/gold_amulet_u.yaml3
-rw-r--r--data/items/equipment/gold_bracelet.yaml37
-rw-r--r--data/items/equipment/gold_necklace.yaml37
-rw-r--r--data/items/equipment/gold_ring.yaml37
-rw-r--r--data/items/equipment/graceful_boots.yaml8
-rw-r--r--data/items/equipment/graceful_cape.yaml8
-rw-r--r--data/items/equipment/graceful_gloves.yaml8
-rw-r--r--data/items/equipment/graceful_hat.yaml8
-rw-r--r--data/items/equipment/graceful_legs.yaml8
-rw-r--r--data/items/equipment/graceful_torso.yaml8
-rw-r--r--data/items/equipment/hard_leather_body.yaml45
-rw-r--r--data/items/equipment/hard_leather_shield.yaml45
-rw-r--r--data/items/equipment/inoculation_bracelet.yaml8
-rw-r--r--data/items/equipment/insulated_gloves.yaml12
-rw-r--r--data/items/equipment/iron_dagger.yaml43
-rw-r--r--data/items/equipment/iron_full_helm.yaml39
-rw-r--r--data/items/equipment/iron_med_helm.yaml39
-rw-r--r--data/items/equipment/iron_platebody.yaml39
-rw-r--r--data/items/equipment/iron_sword.yaml43
-rw-r--r--data/items/equipment/leather_body.yaml41
-rw-r--r--data/items/equipment/leather_boots.yaml41
-rw-r--r--data/items/equipment/leather_chaps.yaml41
-rw-r--r--data/items/equipment/leather_cowl.yaml41
-rw-r--r--data/items/equipment/leather_gloves.yaml41
-rw-r--r--data/items/equipment/leather_vambraces.yaml44
-rw-r--r--data/items/equipment/longbow.yaml36
-rw-r--r--data/items/equipment/longbow_u.yaml3
-rw-r--r--data/items/equipment/magic_longbow.yaml40
-rw-r--r--data/items/equipment/magic_longbow_u.yaml3
-rw-r--r--data/items/equipment/magic_shortbow.yaml40
-rw-r--r--data/items/equipment/magic_shortbow_u.yaml3
-rw-r--r--data/items/equipment/maple_longbow.yaml40
-rw-r--r--data/items/equipment/maple_longbow_u.yaml3
-rw-r--r--data/items/equipment/maple_shortbow.yaml40
-rw-r--r--data/items/equipment/maple_shortbow_u.yaml3
-rw-r--r--data/items/equipment/mithril_dagger.yaml45
-rw-r--r--data/items/equipment/mithril_full_helm.yaml41
-rw-r--r--data/items/equipment/mithril_med_helm.yaml41
-rw-r--r--data/items/equipment/mithril_platebody.yaml41
-rw-r--r--data/items/equipment/mithril_sword.yaml45
-rw-r--r--data/items/equipment/necklace_of_passage.yaml8
-rw-r--r--data/items/equipment/neural_headband.yaml19
-rw-r--r--data/items/equipment/oak_longbow.yaml38
-rw-r--r--data/items/equipment/oak_longbow_u.yaml3
-rw-r--r--data/items/equipment/oak_shortbow.yaml38
-rw-r--r--data/items/equipment/oak_shortbow_u.yaml3
-rw-r--r--data/items/equipment/party_hat_blue.yaml10
-rw-r--r--data/items/equipment/party_hat_green.yaml10
-rw-r--r--data/items/equipment/party_hat_pink.yaml10
-rw-r--r--data/items/equipment/party_hat_red.yaml10
-rw-r--r--data/items/equipment/party_hat_white.yaml10
-rw-r--r--data/items/equipment/party_hat_yellow.yaml10
-rw-r--r--data/items/equipment/phoenix_necklace.yaml8
-rw-r--r--data/items/equipment/power_amulet.yaml12
-rw-r--r--data/items/equipment/ring_of_dueling.yaml8
-rw-r--r--data/items/equipment/ring_of_forging.yaml8
-rw-r--r--data/items/equipment/ring_of_life.yaml8
-rw-r--r--data/items/equipment/ring_of_recoil.yaml8
-rw-r--r--data/items/equipment/ruby_amulet_u.yaml3
-rw-r--r--data/items/equipment/ruby_bracelet.yaml43
-rw-r--r--data/items/equipment/ruby_necklace.yaml43
-rw-r--r--data/items/equipment/ruby_ring.yaml43
-rw-r--r--data/items/equipment/rune_dagger.yaml43
-rw-r--r--data/items/equipment/rune_full_helm.yaml39
-rw-r--r--data/items/equipment/rune_med_helm.yaml39
-rw-r--r--data/items/equipment/rune_platebody.yaml39
-rw-r--r--data/items/equipment/rune_sword.yaml43
-rw-r--r--data/items/equipment/sapphire_amulet_u.yaml3
-rw-r--r--data/items/equipment/sapphire_bracelet.yaml41
-rw-r--r--data/items/equipment/sapphire_necklace.yaml41
-rw-r--r--data/items/equipment/sapphire_ring.yaml41
-rw-r--r--data/items/equipment/shortbow.yaml36
-rw-r--r--data/items/equipment/shortbow_u.yaml3
-rw-r--r--data/items/equipment/spectral_visor.yaml12
-rw-r--r--data/items/equipment/steel_dagger.yaml45
-rw-r--r--data/items/equipment/steel_full_helm.yaml41
-rw-r--r--data/items/equipment/steel_med_helm.yaml41
-rw-r--r--data/items/equipment/steel_platebody.yaml41
-rw-r--r--data/items/equipment/steel_sword.yaml45
-rw-r--r--data/items/equipment/willow_longbow.yaml40
-rw-r--r--data/items/equipment/willow_longbow_u.yaml3
-rw-r--r--data/items/equipment/willow_shortbow.yaml40
-rw-r--r--data/items/equipment/willow_shortbow_u.yaml3
-rw-r--r--data/items/equipment/yew_longbow.yaml40
-rw-r--r--data/items/equipment/yew_longbow_u.yaml3
-rw-r--r--data/items/equipment/yew_shortbow.yaml40
-rw-r--r--data/items/equipment/yew_shortbow_u.yaml3
-rw-r--r--data/items/materials/adamant_nails.yaml3
-rw-r--r--data/items/materials/adamantite_bar.yaml6
-rw-r--r--data/items/materials/avantoe.yaml6
-rw-r--r--data/items/materials/ball_of_wool.yaml3
-rw-r--r--data/items/materials/bowstring.yaml3
-rw-r--r--data/items/materials/bronze_bar.yaml6
-rw-r--r--data/items/materials/bronze_nails.yaml3
-rw-r--r--data/items/materials/cadantine.yaml6
-rw-r--r--data/items/materials/diamond.yaml3
-rw-r--r--data/items/materials/dwarf_weed.yaml6
-rw-r--r--data/items/materials/emerald.yaml3
-rw-r--r--data/items/materials/gold_bar.yaml6
-rw-r--r--data/items/materials/guam.yaml6
-rw-r--r--data/items/materials/harralander.yaml6
-rw-r--r--data/items/materials/irit.yaml6
-rw-r--r--data/items/materials/iron_bar.yaml6
-rw-r--r--data/items/materials/iron_nails.yaml3
-rw-r--r--data/items/materials/kwuarm.yaml6
-rw-r--r--data/items/materials/lantadyme.yaml6
-rw-r--r--data/items/materials/mahogany_planks.yaml3
-rw-r--r--data/items/materials/marrentill.yaml6
-rw-r--r--data/items/materials/mithril_bar.yaml6
-rw-r--r--data/items/materials/mithril_nails.yaml3
-rw-r--r--data/items/materials/oak_planks.yaml3
-rw-r--r--data/items/materials/planks.yaml3
-rw-r--r--data/items/materials/ranarr.yaml6
-rw-r--r--data/items/materials/ruby.yaml3
-rw-r--r--data/items/materials/rune_nails.yaml3
-rw-r--r--data/items/materials/runite_bar.yaml6
-rw-r--r--data/items/materials/sapphire.yaml3
-rw-r--r--data/items/materials/silver_bar.yaml6
-rw-r--r--data/items/materials/snapdragon.yaml6
-rw-r--r--data/items/materials/soft_clay.yaml2
-rw-r--r--data/items/materials/steel_bar.yaml6
-rw-r--r--data/items/materials/steel_nails.yaml3
-rw-r--r--data/items/materials/stim_cell.yaml3
-rw-r--r--data/items/materials/tarromin.yaml6
-rw-r--r--data/items/materials/teak_planks.yaml3
-rw-r--r--data/items/materials/toadflax.yaml6
-rw-r--r--data/items/materials/torstol.yaml6
-rw-r--r--data/items/misc/christmas_cracker.yaml1
-rw-r--r--data/items/misc/mahogany_table.yaml3
-rw-r--r--data/items/misc/oak_shelf.yaml3
-rw-r--r--data/items/misc/oak_table.yaml3
-rw-r--r--data/items/misc/teak_table.yaml3
-rw-r--r--data/items/misc/wooden_chair.yaml3
-rw-r--r--data/items/misc/wooden_shelf.yaml3
-rw-r--r--data/items/misc/wooden_table.yaml3
-rw-r--r--data/items/tools/adamant_axe.yaml33
-rw-r--r--data/items/tools/adamant_pickaxe.yaml33
-rw-r--r--data/items/tools/big_fishing_net.yaml24
-rw-r--r--data/items/tools/black_pickaxe.yaml33
-rw-r--r--data/items/tools/bronze_axe.yaml31
-rw-r--r--data/items/tools/bronze_pickaxe.yaml31
-rw-r--r--data/items/tools/chisel.yaml7
-rw-r--r--data/items/tools/dragon_pickaxe.yaml33
-rw-r--r--data/items/tools/empty_pot.yaml3
-rw-r--r--data/items/tools/firesteel.yaml11
-rw-r--r--data/items/tools/fishing_rod.yaml24
-rw-r--r--data/items/tools/fly_fishing_rod.yaml24
-rw-r--r--data/items/tools/hammer.yaml9
-rw-r--r--data/items/tools/harpoon.yaml24
-rw-r--r--data/items/tools/iron_axe.yaml31
-rw-r--r--data/items/tools/iron_pickaxe.yaml31
-rw-r--r--data/items/tools/knife.yaml7
-rw-r--r--data/items/tools/lighter.yaml19
-rw-r--r--data/items/tools/lobster_pot.yaml24
-rw-r--r--data/items/tools/matches.yaml15
-rw-r--r--data/items/tools/mithril_axe.yaml33
-rw-r--r--data/items/tools/mithril_pickaxe.yaml33
-rw-r--r--data/items/tools/needle.yaml7
-rw-r--r--data/items/tools/plant_pot.yaml3
-rw-r--r--data/items/tools/rake.yaml9
-rw-r--r--data/items/tools/rune_axe.yaml33
-rw-r--r--data/items/tools/rune_pickaxe.yaml33
-rw-r--r--data/items/tools/saw.yaml9
-rw-r--r--data/items/tools/shears.yaml7
-rw-r--r--data/items/tools/small_fishing_net.yaml24
-rw-r--r--data/items/tools/spade.yaml9
-rw-r--r--data/items/tools/steel_axe.yaml33
-rw-r--r--data/items/tools/steel_pickaxe.yaml33
-rw-r--r--data/items/tools/unfired_pie_dish.yaml3
-rw-r--r--data/items/tools/unfired_plant_pot.yaml3
-rw-r--r--data/items/tools/unfired_pot.yaml3
-rw-r--r--data/items/tools/watering_can.yaml9
-rw-r--r--data/rooms/intro/2001.yaml6
-rw-r--r--internal/admin/api_items.go1
-rw-r--r--internal/admin/server.go4
-rw-r--r--internal/admin/static/admin.css17
-rw-r--r--internal/admin/static/editor.js11
-rw-r--r--internal/admin/static/itemeditor.js1557
-rw-r--r--internal/admin/static/map.js13
-rw-r--r--internal/admin/static/objecteditor.js4
-rw-r--r--internal/admin/templates/items.html32
-rw-r--r--internal/admin/undo.go180
-rw-r--r--internal/admin/yaml_util.go2
-rw-r--r--internal/behavior/behavior.go2
-rw-r--r--internal/game/act_burn.go8
-rw-r--r--internal/game/act_clean.go12
-rw-r--r--internal/game/act_fletch.go8
-rw-r--r--internal/game/act_search.go10
-rw-r--r--internal/game/cmd_clean.go6
-rw-r--r--internal/game/cmd_craft.go6
-rw-r--r--internal/game/cmd_fletch.go10
-rw-r--r--internal/game/cmd_smelt.go2
-rw-r--r--internal/game/cmd_smith.go2
-rw-r--r--internal/game/cmd_stats.go14
-rw-r--r--internal/game/cmd_trigger_utility.go10
-rw-r--r--internal/game/cmd_use.go41
-rw-r--r--internal/game/cmd_verbs.go9
-rw-r--r--internal/game/cmd_wear.go18
-rw-r--r--internal/game/combat_attack.go32
-rw-r--r--internal/game/core_equip.go32
-rw-r--r--internal/game/core_production.go10
-rw-r--r--internal/game/look_target.go10
-rw-r--r--internal/game/production_engine.go32
-rw-r--r--internal/game/production_index.go30
-rw-r--r--internal/game/production_menu.go2
-rw-r--r--internal/game/sys_science.go6
-rw-r--r--internal/game/sys_technology.go2
-rw-r--r--internal/item/item.go255
-rw-r--r--internal/validate/checks.go26
329 files changed, 4698 insertions, 2841 deletions
diff --git a/building_guide/items.md b/building_guide/items.md
index 8f8d87d..eefdf9c 100644
--- a/building_guide/items.md
+++ b/building_guide/items.md
@@ -3,7 +3,7 @@
**The filename is the ID.** An item is looked up by its filename stem
(`bronze_pickaxe.yaml` → `bronze_pickaxe`); the loader derives `ItemDef.ID` from it. Do not
put an `id:` field in the file — it is ignored. Anything that references this item (drop
-tables, craft `consume`, room `item_spawns`) uses that filename.
+tables, craft `ingredients`, room `item_spawns`) uses that filename.
```yaml
name: bronze pickaxe
diff --git a/building_guide/recipes.md b/building_guide/recipes.md
index 364d375..e865fa6 100644
--- a/building_guide/recipes.md
+++ b/building_guide/recipes.md
@@ -2,20 +2,20 @@
## Crafting
-Crafting information lives on the **output item's YAML file** via the `craft:` block. There is no separate recipe directory. To find out how to make `bronze_dagger`, open `data/items/equipment/bronze_dagger.yaml` and look for the `craft:` block. (The item's **filename is its ID** — `bronze_dagger.yaml` → `bronze_dagger`; there is no `id:` field. `consume`/output entries reference items by filename.)
+Crafting information lives on the **output item's YAML file** via the `craft:` block. There is no separate recipe directory. To find out how to make `bronze_dagger`, open `data/items/equipment/bronze_dagger.yaml` and look for the `craft:` block. (The item's **filename is its ID** — `bronze_dagger.yaml` → `bronze_dagger`; there is no `id:` field. `ingredients`/output entries reference items by filename.)
### Craft YAML Reference
| Field | Type | Description |
| -------------- | -------------- | ----------------------------------------------- |
-| `type` | string | Craft type (pharmacy, smithing, cooking, smelting, crafting, fletching, construction, clean, or "" for skill-less) |
-| `skill` | string | Skill checked (defaults to type if omitted) |
+| `type` | string | Craft type (smithing, cooking, crafting, fletching, pharmacy, construction, combine, or "" for skill-less). Always matches a player skill name. |
+| `subtype` | string | Production method override (`smelt` for furnace recipes, `clean` for herb cleaning). Defaults empty (no override). |
| `level` | int | Required skill level |
| `xp` | int | XP awarded on success |
| `wait` | float64 | Ticks per craft cycle |
| `station` | []string | Station object IDs required (optional) |
| `tool` | string | Required tool_type (optional) |
-| `consume` | []ConsumeEntry | Ingredients consumed (see below) |
+| `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) |
@@ -34,12 +34,12 @@ All message fields (`message`, `fail_message`, `start_message`, `end_message`, a
| Variable | Expands to |
| -------- | ----------------------------------------------- |
| `%n` | Output item name (colored, e.g. "stim potion") |
-| `%i1` | 1st consume entry's matched item (colored) |
-| `%i2` | 2nd consume entry's matched item (colored) |
+| `%i1` | 1st ingredient entry's matched item (colored) |
+| `%i2` | 2nd ingredient entry's matched item (colored) |
| `%b1` | 1st byproduct item name (colored, success only) |
| `%b2` | 2nd byproduct item name (colored, success only) |
-Numbering follows YAML consume/byproduct order. If a consume entry has multiple alternative items (`items: [a, b]`), the variable expands to whichever the player actually possesses, colored with that item's `color:` field.
+Numbering follows YAML ingredient/byproduct order. If an ingredient entry has multiple alternative items (`items: [a, b]`), the variable expands to whichever the player actually possesses, colored with that item's `color:` field.
Variables work alongside inline color tags (`{C4}text{/}`) which are expanded after variable substitution.
@@ -50,7 +50,7 @@ If a message field is omitted from the YAML, the game uses a skill-level default
| Type | Default `message` | Default `start_message` |
|------|-------------------|------------------------|
| `cooking` | `"Cooked to perfection. %n looks great!"` | `"You start cooking %i1."` |
-| `smelting` | `"You remove a white hot %n!"` | `"You place the %i1 into the furnace."` |
+| `smelt` | `"You remove a white hot %n!"` | `"You place the %i1 into the furnace."` |
| `smithing` | `"You smith a %n."` | `"You begin smithing %i1."` |
| `crafting` | `"You craft a %n."` | `"You begin crafting %i1."` |
| `fletching` | `"You fletch a %n."` | `"You begin fletching %i1."` |
@@ -59,7 +59,7 @@ If a message field is omitted from the YAML, the game uses a skill-level default
| `clean` | `"You clean the %i1."` | `"You begin cleaning herbs."` |
| (unset) | `"You produce %n."` | `"You start <verb> %i1."` |
-Any of these can be overridden per-item by setting the field in YAML. For skills that never fail (smithing, crafting, fletching, pharmacy, construction), `fail_message` defaults to empty — no message is shown on failure.
+Default messages are keyed by `subtype` when set, otherwise by `type`. For skills that never fail (smithing, crafting, fletching, pharmacy, construction), `fail_message` defaults to empty — no message is shown on failure.
### CraftStep — Multi-Step Messages
@@ -69,7 +69,7 @@ Optional interim messages fired at specific tick offsets during the craft cycle.
craft:
type: ""
wait: 6
- consume:
+ ingredients:
- items: [map_piece_1]
quantity: 1
- items: [map_piece_2]
@@ -84,12 +84,12 @@ craft:
message: "You assemble the map!"
```
-### ConsumeEntry — Multi-Item Ingredient Slots
+### IngredientEntry — Multi-Item Ingredient Slots
-Each consume entry defines an ingredient slot with multiple valid items. The first matching item found in the player's inventory is consumed.
+Each ingredient entry defines an ingredient slot with multiple valid items. The first matching item found in the player's inventory is consumed.
```yaml
-consume:
+ingredients:
- items: [item_id, alternative_id, ...]
quantity: 1
byproducts: [byproduct_for_item, byproduct_for_alt, ...]
@@ -98,7 +98,7 @@ consume:
`byproducts` is optional. When present, it matches the `items` list by index — consuming `items[0]` returns `byproducts[0]` to inventory. Use `""` for items with no byproduct:
```yaml
-consume:
+ingredients:
- items: [bucket_of_water, vial_of_water]
quantity: 1
byproducts: [empty_bucket, ""] # bucket returns empty, vial is consumed entirely
@@ -112,11 +112,10 @@ consume:
# data/items/consumables/stim_potion.yaml
craft:
type: pharmacy
- skill: pharmacy
level: 3
xp: 25
wait: 4
- consume:
+ ingredients:
- items: [guam_potion_unf]
quantity: 1
- items: [eye_of_newt]
@@ -129,13 +128,13 @@ craft:
```yaml
# data/items/materials/bronze_bar.yaml
craft:
- type: smelting
- skill: smithing
+ type: smithing
+ subtype: smelt
level: 1
xp: 6
wait: 4
station: [furnace]
- consume:
+ ingredients:
- items: [copper_ore]
quantity: 1
- items: [tin_ore]
@@ -148,12 +147,11 @@ craft:
# data/items/equipment/bronze_dagger.yaml
craft:
type: smithing
- skill: smithing
level: 1
xp: 12
wait: 4
station: [anvil]
- consume:
+ ingredients:
- items: [bronze_bar]
quantity: 1
# message omitted — uses default "You smith a %n."
@@ -165,12 +163,11 @@ For stackable outputs, use `output_qty`:
# data/items/ammo/bronze_nails.yaml
craft:
type: smithing
- skill: smithing
level: 4
xp: 12
wait: 4
station: [anvil]
- consume:
+ ingredients:
- items: [bronze_bar]
quantity: 1
output_qty: 15
@@ -187,7 +184,7 @@ craft:
xp: 5
wait: 3
tool: knife
- consume:
+ ingredients:
- items: [logs, oak_logs, willow_logs]
quantity: 1
output_qty: 15
@@ -203,7 +200,7 @@ Omit `type` (or leave it empty) for combinations with no skill check, no XP, and
# data/items/consumables/bucket_of_water.yaml
craft:
wait: 0
- consume:
+ ingredients:
- items: [vial_of_water, jug_of_water]
quantity: 1
byproducts: [empty_vial, empty_jug]
@@ -218,7 +215,7 @@ craft:
# data/items/quest/ancient_map.yaml
craft:
wait: 0
- consume:
+ ingredients:
- items: [torn_page_1]
quantity: 1
- items: [torn_page_2]
@@ -235,17 +232,17 @@ craft:
### Clean Recipes
-Clean herb recipes use `type: clean` and are handled as background actions (one herb per cycle, directly mutating inventory):
+Clean herb recipes use `type: pharmacy` with `subtype: clean` and are handled as background actions (one herb per cycle, directly mutating inventory):
```yaml
# data/items/materials/guam.yaml (clean guam)
craft:
- type: clean
- skill: pharmacy
+ type: pharmacy
+ subtype: clean
level: 3
xp: 3
wait: 2
- consume:
+ ingredients:
- items: [grimy_guam]
quantity: 1
# message omitted — uses default "You clean the %i1."
@@ -267,10 +264,12 @@ eat_message: "You eat the bread. Warm and satisfying."
### Architecture
-The `CraftIndex` (`internal/game/craft_index.go`) is built once at startup from all item YAMLs that have a `craft:` block. It provides `O(1)` lookups by type, input item, and station. All crafting commands query the CraftIndex — no runtime file scanning.
+The `CraftIndex` (`internal/game/production_index.go`) is built once at startup from all item YAMLs that have a `craft:` block. It provides `O(1)` lookups by type, subtype, input item, and station. All crafting commands query the CraftIndex — no runtime file scanning.
**Indexes:**
- `byType["pharmacy"]` → all pharmacy-craftable items (used by `mix`)
-- `byInput["guam"]` → all items that consume guam (used by `use` to find combinations)
+- `bySubtype["clean"]` → all items with subtype `clean` (grimy herb cleaning)
+- `bySubtype["smelt"]` → all items with subtype `smelt` (furnace smelting)
+- `byInput["guam"]` → all items that use guam (used by `use` to find combinations)
- `byStation["anvil"]` → all items craftable at an anvil (used by `use bar on anvil`)
-- `FindByTwoInputs(a, b)` → items consuming both inputs in different consume entries
+- `FindByTwoInputs(a, b)` → items consuming both inputs in different ingredient entries
diff --git a/data/.admin_history.json b/data/.admin_history.json
index 364be38..8519fc5 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-30T17:43:45-04:00","description":"Update object copper_rock","file_path":"data/objects/copper_rock.yaml","old_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","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.55\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-30T17:43:55-04:00","description":"Update object copper_rock","file_path":"data/objects/copper_rock.yaml","old_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.55\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","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-30T17:45:35-04:00","description":"Update object copper_rock","file_path":"data/objects/copper_rock.yaml","old_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","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.55\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-30T17:45:40-04:00","description":"Update object copper_rock","file_path":"data/objects/copper_rock.yaml","old_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.55\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","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-30T17:57:25-04:00","description":"Update object copper_rock","file_path":"data/objects/copper_rock.yaml","old_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","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: woodcutting\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-30T18:11:31-04:00","description":"Update object copper_rock","file_path":"data/objects/copper_rock.yaml","old_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: woodcutting\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","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-30T18:11:34-04:00","description":"Create object copper_rock_copy","file_path":"data/objects/copper_rock_copy.yaml","old_content":"","new_content":"id: copper_rock_copy\n","is_delete":false,"is_create":true},{"time":"2026-06-30T18:11:34-04:00","description":"Update object copper_rock_copy","file_path":"data/objects/copper_rock_copy.yaml","old_content":"id: copper_rock_copy\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: woodcutting\n success:\n base: 0.5\n cap: 0.95\n per_level: 0.01\n tools:\n - pickaxe\nhidden: false\nid: copper_rock_copy\ninroom_description: \"\"\nname: copper rock\nremoval_item: \"\"\n","is_delete":false,"is_create":false},{"time":"2026-06-30T18:11:39-04:00","description":"Delete object copper_rock_copy","file_path":"data/objects/copper_rock_copy.yaml","old_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: woodcutting\n success:\n base: 0.5\n cap: 0.95\n per_level: 0.01\n tools:\n - pickaxe\nhidden: false\nid: copper_rock_copy\ninroom_description: \"\"\nname: copper rock\nremoval_item: \"\"\n","new_content":"","is_delete":true,"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}],"redo":null} \ No newline at end of file
diff --git a/data/drops/christmas_cracker_hat.yaml b/data/drops/christmas_cracker_hat.yaml
index e9f7a77..23f7231 100644
--- a/data/drops/christmas_cracker_hat.yaml
+++ b/data/drops/christmas_cracker_hat.yaml
@@ -11,3 +11,11 @@ drops:
weight: 1
- item_id: party_hat_white
weight: 1
+ - item_id: ashes
+ weight: 1
+ - item_id: bread
+ weight: 1
+ - item_id: bronze_pickaxe
+ weight: 1
+ - item_id: ""
+ weight: 3
diff --git a/data/drops/christmas_cracker_misc.yaml b/data/drops/christmas_cracker_misc.yaml
deleted file mode 100644
index 94debfd..0000000
--- a/data/drops/christmas_cracker_misc.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-drops:
- - item_id: ashes
- weight: 1
- - item_id: bread
- weight: 1
- - item_id: bronze_pickaxe
- weight: 1
- - item_id: ""
- weight: 3
diff --git a/data/items/ammo/adamant_arrow.yaml b/data/items/ammo/adamant_arrow.yaml
index a7cb647..3b1a750 100644
--- a/data/items/ammo/adamant_arrow.yaml
+++ b/data/items/ammo/adamant_arrow.yaml
@@ -1,23 +1,24 @@
color: "78"
craft:
- consume:
- - items:
- - headless_arrow
- quantity: 15
- - items:
- - adamant_arrowtips
- quantity: 15
- level: 60
- output_qty: 15
- skill: fletching
- type: fletching
- wait: 2
- xp: 10
+ ingredients:
+ - items:
+ - headless_arrow
+ quantity: 15
+ - items:
+ - adamant_arrowtips
+ quantity: 15
+ level: 60
+ output_qty: 15
+ type: fletching
+ wait: 2
+ xp: 10
description: An adamant-tipped arrow.
-equip_slot: ammo
+equipment:
+ other:
+ ranged: 31
+ slot: ammo
+ type: ammo
name: adamant arrow
-stackable: true
recoverable: true
-stats:
- ranged_strength: 31
+stackable: true
value: 20
diff --git a/data/items/ammo/adamant_arrowtips.yaml b/data/items/ammo/adamant_arrowtips.yaml
index 6a8bb76..8e83ec8 100644
--- a/data/items/ammo/adamant_arrowtips.yaml
+++ b/data/items/ammo/adamant_arrowtips.yaml
@@ -1,12 +1,11 @@
color: "78"
craft:
- consume:
+ ingredients:
- items:
- adamantite_bar
quantity: 1
level: 75
output_qty: 15
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/ammo/adamant_bolts.yaml b/data/items/ammo/adamant_bolts.yaml
index e61acd6..2a416ec 100644
--- a/data/items/ammo/adamant_bolts.yaml
+++ b/data/items/ammo/adamant_bolts.yaml
@@ -1,22 +1,23 @@
color: "78"
craft:
- consume:
- - items:
- - adamant_bolts_unf
- quantity: 10
- - items:
- - feather
- quantity: 10
- level: 61
- output_qty: 10
- skill: fletching
- type: fletching
- wait: 0
- xp: 70
+ ingredients:
+ - items:
+ - adamant_bolts_unf
+ quantity: 10
+ - items:
+ - feather
+ quantity: 10
+ level: 61
+ output_qty: 10
+ type: fletching
+ wait: 0
+ xp: 70
description: Adamant crossbow bolts.
-equip_slot: ammo
+equipment:
+ other:
+ ranged: 43
+ slot: ammo
+ type: ammo
name: adamant bolts
stackable: true
-stats:
- ranged_strength: 43
value: 24
diff --git a/data/items/ammo/adamant_bolts_unf.yaml b/data/items/ammo/adamant_bolts_unf.yaml
index 96d2fec..857dc34 100644
--- a/data/items/ammo/adamant_bolts_unf.yaml
+++ b/data/items/ammo/adamant_bolts_unf.yaml
@@ -1,12 +1,11 @@
color: "78"
craft:
- consume:
+ ingredients:
- items:
- adamantite_bar
quantity: 1
level: 76
output_qty: 10
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/ammo/arrow_shaft.yaml b/data/items/ammo/arrow_shaft.yaml
index b4500a9..ce44c90 100644
--- a/data/items/ammo/arrow_shaft.yaml
+++ b/data/items/ammo/arrow_shaft.yaml
@@ -1,67 +1,61 @@
color: "89"
craft:
- - consume:
+ - ingredients:
- items:
- logs
quantity: 1
level: 1
output_qty: 15
- skill: fletching
tool: knife
type: fletching
wait: 5
xp: 5
- - consume:
+ - ingredients:
- items:
- oak_logs
quantity: 1
level: 15
output_qty: 20
- skill: fletching
tool: knife
type: fletching
wait: 5
xp: 10
- - consume:
+ - ingredients:
- items:
- willow_logs
quantity: 1
level: 30
output_qty: 25
- skill: fletching
tool: knife
type: fletching
wait: 5
xp: 15
- - consume:
+ - ingredients:
- items:
- maple_logs
quantity: 1
level: 45
output_qty: 30
- skill: fletching
tool: knife
type: fletching
wait: 5
xp: 20
- - consume:
+ - ingredients:
- items:
- yew_logs
quantity: 1
level: 60
output_qty: 35
- skill: fletching
tool: knife
type: fletching
wait: 5
xp: 25
- - consume:
+ - ingredients:
- items:
- magic_logs
quantity: 1
level: 75
output_qty: 40
- skill: fletching
tool: knife
type: fletching
wait: 5
diff --git a/data/items/ammo/bronze_arrow.yaml b/data/items/ammo/bronze_arrow.yaml
index 85fac40..dfdac57 100644
--- a/data/items/ammo/bronze_arrow.yaml
+++ b/data/items/ammo/bronze_arrow.yaml
@@ -1,23 +1,24 @@
-color: "B2"
+color: B2
craft:
- consume:
- - items:
- - headless_arrow
- quantity: 15
- - items:
- - bronze_arrowtips
- quantity: 15
- level: 1
- output_qty: 15
- skill: fletching
- type: fletching
- wait: 2
- xp: 1
+ ingredients:
+ - items:
+ - headless_arrow
+ quantity: 15
+ - items:
+ - bronze_arrowtips
+ quantity: 15
+ level: 1
+ output_qty: 15
+ type: fletching
+ wait: 2
+ xp: 1
description: A bronze-tipped arrow.
-equip_slot: ammo
+equipment:
+ other:
+ ranged: 7
+ slot: ammo
+ type: ammo
name: bronze arrow
-stackable: true
recoverable: true
-stats:
- ranged_strength: 7
+stackable: true
value: 1
diff --git a/data/items/ammo/bronze_arrowtips.yaml b/data/items/ammo/bronze_arrowtips.yaml
index 3935852..d8dd71e 100644
--- a/data/items/ammo/bronze_arrowtips.yaml
+++ b/data/items/ammo/bronze_arrowtips.yaml
@@ -1,12 +1,11 @@
color: "B2"
craft:
- consume:
+ ingredients:
- items:
- bronze_bar
quantity: 1
level: 5
output_qty: 15
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/ammo/bronze_bolts.yaml b/data/items/ammo/bronze_bolts.yaml
index bd45e8b..3aeb21b 100644
--- a/data/items/ammo/bronze_bolts.yaml
+++ b/data/items/ammo/bronze_bolts.yaml
@@ -1,22 +1,23 @@
-color: "B2"
+color: B2
craft:
- consume:
- - items:
- - bronze_bolts_unf
- quantity: 10
- - items:
- - feather
- quantity: 10
- level: 9
- output_qty: 10
- skill: fletching
- type: fletching
- wait: 0
- xp: 5
+ ingredients:
+ - items:
+ - bronze_bolts_unf
+ quantity: 10
+ - items:
+ - feather
+ quantity: 10
+ level: 9
+ output_qty: 10
+ type: fletching
+ wait: 0
+ xp: 5
description: Bronze crossbow bolts.
-equip_slot: ammo
+equipment:
+ other:
+ ranged: 10
+ slot: ammo
+ type: ammo
name: bronze bolts
stackable: true
-stats:
- ranged_strength: 10
value: 1
diff --git a/data/items/ammo/bronze_bolts_unf.yaml b/data/items/ammo/bronze_bolts_unf.yaml
index 83ed723..ff0a8be 100644
--- a/data/items/ammo/bronze_bolts_unf.yaml
+++ b/data/items/ammo/bronze_bolts_unf.yaml
@@ -1,12 +1,11 @@
color: "B2"
craft:
- consume:
+ ingredients:
- items:
- bronze_bar
quantity: 1
level: 6
output_qty: 10
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/ammo/diamond_bolt_tips.yaml b/data/items/ammo/diamond_bolt_tips.yaml
index 4ad1809..96bc0eb 100644
--- a/data/items/ammo/diamond_bolt_tips.yaml
+++ b/data/items/ammo/diamond_bolt_tips.yaml
@@ -1,12 +1,11 @@
color: "FF"
craft:
- consume:
+ ingredients:
- items:
- uncut_diamond
quantity: 1
level: 65
output_qty: 12
- skill: fletching
tool: chisel
type: fletching
wait: 5
diff --git a/data/items/ammo/diamond_bolts.yaml b/data/items/ammo/diamond_bolts.yaml
index ea3e0af..1cbc3f6 100644
--- a/data/items/ammo/diamond_bolts.yaml
+++ b/data/items/ammo/diamond_bolts.yaml
@@ -1,22 +1,23 @@
-color: "FF"
+color: FF
craft:
- consume:
- - items:
- - adamant_bolts
- quantity: 10
- - items:
- - diamond_bolt_tips
- quantity: 10
- level: 65
- output_qty: 10
- skill: fletching
- type: fletching
- wait: 2
- xp: 7
+ ingredients:
+ - items:
+ - adamant_bolts
+ quantity: 10
+ - items:
+ - diamond_bolt_tips
+ quantity: 10
+ level: 65
+ output_qty: 10
+ type: fletching
+ wait: 2
+ xp: 7
description: Bolts tipped with diamond. Can be enchanted via science.
-equip_slot: ammo
+equipment:
+ attack:
+ ranged: 9
+ slot: ammo
+ type: ammo
name: diamond bolts
stackable: true
-stats:
- ranged_attack: 9
value: 130
diff --git a/data/items/ammo/diamond_bolts_e.yaml b/data/items/ammo/diamond_bolts_e.yaml
index 1243882..86209f2 100644
--- a/data/items/ammo/diamond_bolts_e.yaml
+++ b/data/items/ammo/diamond_bolts_e.yaml
@@ -1,8 +1,10 @@
+color: FF bold
+description: Enchanted diamond-tipped bolts. Have a chance to ignore the target's defense.
+equipment:
+ attack:
+ ranged: 10
+ slot: ammo
+ type: ammo
name: diamond bolts e
-color: "FF bold"
-description: "Enchanted diamond-tipped bolts. Have a chance to ignore the target's defense."
-value: 180
stackable: true
-equip_slot: ammo
-stats:
- ranged_attack: 10
+value: 180
diff --git a/data/items/ammo/emerald_bolt_tips.yaml b/data/items/ammo/emerald_bolt_tips.yaml
index 9d94694..871ac1d 100644
--- a/data/items/ammo/emerald_bolt_tips.yaml
+++ b/data/items/ammo/emerald_bolt_tips.yaml
@@ -1,12 +1,11 @@
color: "53"
craft:
- consume:
+ ingredients:
- items:
- uncut_emerald
quantity: 1
level: 66
output_qty: 12
- skill: fletching
tool: chisel
type: fletching
wait: 5
diff --git a/data/items/ammo/emerald_bolts.yaml b/data/items/ammo/emerald_bolts.yaml
index 15fde56..8cafc32 100644
--- a/data/items/ammo/emerald_bolts.yaml
+++ b/data/items/ammo/emerald_bolts.yaml
@@ -1,22 +1,23 @@
color: "22"
craft:
- consume:
- - items:
- - mithril_bolts
- quantity: 10
- - items:
- - emerald_bolt_tips
- quantity: 10
- level: 66
- output_qty: 10
- skill: fletching
- type: fletching
- wait: 2
- xp: 5
+ ingredients:
+ - items:
+ - mithril_bolts
+ quantity: 10
+ - items:
+ - emerald_bolt_tips
+ quantity: 10
+ level: 66
+ output_qty: 10
+ type: fletching
+ wait: 2
+ xp: 5
description: Bolts tipped with emerald. Can be enchanted via science.
-equip_slot: ammo
+equipment:
+ attack:
+ ranged: 5
+ slot: ammo
+ type: ammo
name: emerald bolts
stackable: true
-stats:
- ranged_attack: 5
value: 40
diff --git a/data/items/ammo/emerald_bolts_e.yaml b/data/items/ammo/emerald_bolts_e.yaml
index 390730f..59f3883 100644
--- a/data/items/ammo/emerald_bolts_e.yaml
+++ b/data/items/ammo/emerald_bolts_e.yaml
@@ -1,8 +1,10 @@
+color: 22 bold
+description: Enchanted emerald-tipped bolts. Have a chance to poison the target.
+equipment:
+ attack:
+ ranged: 6
+ slot: ammo
+ type: ammo
name: emerald bolts e
-color: "22 bold"
-description: "Enchanted emerald-tipped bolts. Have a chance to poison the target."
-value: 55
stackable: true
-equip_slot: ammo
-stats:
- ranged_attack: 6
+value: 55
diff --git a/data/items/ammo/headless_arrow.yaml b/data/items/ammo/headless_arrow.yaml
index d27cc1e..61ad40f 100644
--- a/data/items/ammo/headless_arrow.yaml
+++ b/data/items/ammo/headless_arrow.yaml
@@ -1,6 +1,6 @@
color: "89"
craft:
- consume:
+ ingredients:
- items:
- arrow_shaft
quantity: 15
@@ -10,7 +10,6 @@ craft:
level: 1
message: "You attach feathers to the %i1."
output_qty: 15
- skill: fletching
type: fletching
wait: 2
xp: 1
diff --git a/data/items/ammo/iron_arrow.yaml b/data/items/ammo/iron_arrow.yaml
index 9a17ff8..f0036d8 100644
--- a/data/items/ammo/iron_arrow.yaml
+++ b/data/items/ammo/iron_arrow.yaml
@@ -1,23 +1,24 @@
-color: "FA"
+color: FA
craft:
- consume:
- - items:
- - headless_arrow
- quantity: 15
- - items:
- - iron_arrowtips
- quantity: 15
- level: 15
- output_qty: 15
- skill: fletching
- type: fletching
- wait: 2
- xp: 2
+ ingredients:
+ - items:
+ - headless_arrow
+ quantity: 15
+ - items:
+ - iron_arrowtips
+ quantity: 15
+ level: 15
+ output_qty: 15
+ type: fletching
+ wait: 2
+ xp: 2
description: An iron-tipped arrow.
-equip_slot: ammo
+equipment:
+ other:
+ ranged: 10
+ slot: ammo
+ type: ammo
name: iron arrow
-stackable: true
recoverable: true
-stats:
- ranged_strength: 10
+stackable: true
value: 2
diff --git a/data/items/ammo/iron_arrowtips.yaml b/data/items/ammo/iron_arrowtips.yaml
index aa4cc33..8aec7d9 100644
--- a/data/items/ammo/iron_arrowtips.yaml
+++ b/data/items/ammo/iron_arrowtips.yaml
@@ -1,12 +1,11 @@
color: "FA"
craft:
- consume:
+ ingredients:
- items:
- iron_bar
quantity: 1
level: 25
output_qty: 15
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/ammo/iron_bolts.yaml b/data/items/ammo/iron_bolts.yaml
index f8ac883..30e5b28 100644
--- a/data/items/ammo/iron_bolts.yaml
+++ b/data/items/ammo/iron_bolts.yaml
@@ -1,22 +1,23 @@
-color: "FA"
+color: FA
craft:
- consume:
- - items:
- - iron_bolts_unf
- quantity: 10
- - items:
- - feather
- quantity: 10
- level: 39
- output_qty: 10
- skill: fletching
- type: fletching
- wait: 0
- xp: 15
+ ingredients:
+ - items:
+ - iron_bolts_unf
+ quantity: 10
+ - items:
+ - feather
+ quantity: 10
+ level: 39
+ output_qty: 10
+ type: fletching
+ wait: 0
+ xp: 15
description: Iron crossbow bolts.
-equip_slot: ammo
+equipment:
+ other:
+ ranged: 17
+ slot: ammo
+ type: ammo
name: iron bolts
stackable: true
-stats:
- ranged_strength: 17
value: 3
diff --git a/data/items/ammo/iron_bolts_unf.yaml b/data/items/ammo/iron_bolts_unf.yaml
index 32950fa..181a5e2 100644
--- a/data/items/ammo/iron_bolts_unf.yaml
+++ b/data/items/ammo/iron_bolts_unf.yaml
@@ -1,12 +1,11 @@
color: "FA"
craft:
- consume:
+ ingredients:
- items:
- iron_bar
quantity: 1
level: 26
output_qty: 10
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/ammo/mithril_arrow.yaml b/data/items/ammo/mithril_arrow.yaml
index 281f24a..853b38d 100644
--- a/data/items/ammo/mithril_arrow.yaml
+++ b/data/items/ammo/mithril_arrow.yaml
@@ -1,23 +1,24 @@
-color: "4B"
+color: 4B
craft:
- consume:
- - items:
- - headless_arrow
- quantity: 15
- - items:
- - mithril_arrowtips
- quantity: 15
- level: 45
- output_qty: 15
- skill: fletching
- type: fletching
- wait: 2
- xp: 7
+ ingredients:
+ - items:
+ - headless_arrow
+ quantity: 15
+ - items:
+ - mithril_arrowtips
+ quantity: 15
+ level: 45
+ output_qty: 15
+ type: fletching
+ wait: 2
+ xp: 7
description: A mithril-tipped arrow.
-equip_slot: ammo
+equipment:
+ other:
+ ranged: 22
+ slot: ammo
+ type: ammo
name: mithril arrow
-stackable: true
recoverable: true
-stats:
- ranged_strength: 22
+stackable: true
value: 10
diff --git a/data/items/ammo/mithril_arrowtips.yaml b/data/items/ammo/mithril_arrowtips.yaml
index d6acbf8..8f213ec 100644
--- a/data/items/ammo/mithril_arrowtips.yaml
+++ b/data/items/ammo/mithril_arrowtips.yaml
@@ -1,12 +1,11 @@
color: "4B"
craft:
- consume:
+ ingredients:
- items:
- mithril_bar
quantity: 1
level: 55
output_qty: 15
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/ammo/mithril_bolts.yaml b/data/items/ammo/mithril_bolts.yaml
index fb3f10c..5f64ba1 100644
--- a/data/items/ammo/mithril_bolts.yaml
+++ b/data/items/ammo/mithril_bolts.yaml
@@ -1,22 +1,23 @@
-color: "4B"
+color: 4B
craft:
- consume:
- - items:
- - mithril_bolts_unf
- quantity: 10
- - items:
- - feather
- quantity: 10
- level: 54
- output_qty: 10
- skill: fletching
- type: fletching
- wait: 0
- xp: 50
+ ingredients:
+ - items:
+ - mithril_bolts_unf
+ quantity: 10
+ - items:
+ - feather
+ quantity: 10
+ level: 54
+ output_qty: 10
+ type: fletching
+ wait: 0
+ xp: 50
description: Mithril crossbow bolts.
-equip_slot: ammo
+equipment:
+ other:
+ ranged: 32
+ slot: ammo
+ type: ammo
name: mithril bolts
stackable: true
-stats:
- ranged_strength: 32
value: 12
diff --git a/data/items/ammo/mithril_bolts_unf.yaml b/data/items/ammo/mithril_bolts_unf.yaml
index ea3879a..0ff8efe 100644
--- a/data/items/ammo/mithril_bolts_unf.yaml
+++ b/data/items/ammo/mithril_bolts_unf.yaml
@@ -1,12 +1,11 @@
color: "4B"
craft:
- consume:
+ ingredients:
- items:
- mithril_bar
quantity: 1
level: 56
output_qty: 10
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/ammo/ruby_bolt_tips.yaml b/data/items/ammo/ruby_bolt_tips.yaml
index e8a1e0b..a900f90 100644
--- a/data/items/ammo/ruby_bolt_tips.yaml
+++ b/data/items/ammo/ruby_bolt_tips.yaml
@@ -1,12 +1,11 @@
color: "C4"
craft:
- consume:
+ ingredients:
- items:
- uncut_ruby
quantity: 1
level: 73
output_qty: 12
- skill: fletching
tool: chisel
type: fletching
wait: 5
diff --git a/data/items/ammo/ruby_bolts.yaml b/data/items/ammo/ruby_bolts.yaml
index a30b5bb..354d595 100644
--- a/data/items/ammo/ruby_bolts.yaml
+++ b/data/items/ammo/ruby_bolts.yaml
@@ -1,22 +1,23 @@
-color: "C4"
+color: C4
craft:
- consume:
- - items:
- - adamant_bolts
- quantity: 10
- - items:
- - ruby_bolt_tips
- quantity: 10
- level: 73
- output_qty: 10
- skill: fletching
- type: fletching
- wait: 2
- xp: 6
+ ingredients:
+ - items:
+ - adamant_bolts
+ quantity: 10
+ - items:
+ - ruby_bolt_tips
+ quantity: 10
+ level: 73
+ output_qty: 10
+ type: fletching
+ wait: 2
+ xp: 6
description: Bolts tipped with ruby. Can be enchanted via science.
-equip_slot: ammo
+equipment:
+ attack:
+ ranged: 7
+ slot: ammo
+ type: ammo
name: ruby bolts
stackable: true
-stats:
- ranged_attack: 7
value: 75
diff --git a/data/items/ammo/ruby_bolts_e.yaml b/data/items/ammo/ruby_bolts_e.yaml
index 0aaa79e..618cf62 100644
--- a/data/items/ammo/ruby_bolts_e.yaml
+++ b/data/items/ammo/ruby_bolts_e.yaml
@@ -1,8 +1,10 @@
+color: C4 bold
+description: Enchanted ruby-tipped bolts. Have a chance to deal extra damage based on the target's remaining HP.
+equipment:
+ attack:
+ ranged: 8
+ slot: ammo
+ type: ammo
name: ruby bolts e
-color: "C4 bold"
-description: "Enchanted ruby-tipped bolts. Have a chance to deal extra damage based on the target's remaining HP."
-value: 100
stackable: true
-equip_slot: ammo
-stats:
- ranged_attack: 8
+value: 100
diff --git a/data/items/ammo/rune_arrow.yaml b/data/items/ammo/rune_arrow.yaml
index f6cee8e..e997871 100644
--- a/data/items/ammo/rune_arrow.yaml
+++ b/data/items/ammo/rune_arrow.yaml
@@ -1,23 +1,24 @@
color: "57"
craft:
- consume:
- - items:
- - headless_arrow
- quantity: 15
- - items:
- - rune_arrowtips
- quantity: 15
- level: 75
- output_qty: 15
- skill: fletching
- type: fletching
- wait: 2
- xp: 12
+ ingredients:
+ - items:
+ - headless_arrow
+ quantity: 15
+ - items:
+ - rune_arrowtips
+ quantity: 15
+ level: 75
+ output_qty: 15
+ type: fletching
+ wait: 2
+ xp: 12
description: A rune-tipped arrow.
-equip_slot: ammo
+equipment:
+ other:
+ ranged: 49
+ slot: ammo
+ type: ammo
name: rune arrow
-stackable: true
recoverable: true
-stats:
- ranged_strength: 49
+stackable: true
value: 40
diff --git a/data/items/ammo/rune_arrowtips.yaml b/data/items/ammo/rune_arrowtips.yaml
index d729996..354cf0e 100644
--- a/data/items/ammo/rune_arrowtips.yaml
+++ b/data/items/ammo/rune_arrowtips.yaml
@@ -1,12 +1,11 @@
color: "57"
craft:
- consume:
+ ingredients:
- items:
- runite_bar
quantity: 1
level: 90
output_qty: 15
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/ammo/rune_bolts.yaml b/data/items/ammo/rune_bolts.yaml
index 738cd4c..534dc94 100644
--- a/data/items/ammo/rune_bolts.yaml
+++ b/data/items/ammo/rune_bolts.yaml
@@ -1,22 +1,23 @@
color: "57"
craft:
- consume:
- - items:
- - rune_bolts_unf
- quantity: 10
- - items:
- - feather
- quantity: 10
- level: 69
- output_qty: 10
- skill: fletching
- type: fletching
- wait: 0
- xp: 100
+ ingredients:
+ - items:
+ - rune_bolts_unf
+ quantity: 10
+ - items:
+ - feather
+ quantity: 10
+ level: 69
+ output_qty: 10
+ type: fletching
+ wait: 0
+ xp: 100
description: Rune crossbow bolts.
-equip_slot: ammo
+equipment:
+ other:
+ ranged: 55
+ slot: ammo
+ type: ammo
name: rune bolts
stackable: true
-stats:
- ranged_strength: 55
value: 48
diff --git a/data/items/ammo/rune_bolts_unf.yaml b/data/items/ammo/rune_bolts_unf.yaml
index 0b824f6..a529c37 100644
--- a/data/items/ammo/rune_bolts_unf.yaml
+++ b/data/items/ammo/rune_bolts_unf.yaml
@@ -1,12 +1,11 @@
color: "57"
craft:
- consume:
+ ingredients:
- items:
- runite_bar
quantity: 1
level: 91
output_qty: 10
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/ammo/sapphire_bolt_tips.yaml b/data/items/ammo/sapphire_bolt_tips.yaml
index 92e04f0..bffee83 100644
--- a/data/items/ammo/sapphire_bolt_tips.yaml
+++ b/data/items/ammo/sapphire_bolt_tips.yaml
@@ -1,12 +1,11 @@
color: "45"
craft:
- consume:
+ ingredients:
- items:
- uncut_sapphire
quantity: 1
level: 63
output_qty: 12
- skill: fletching
tool: chisel
type: fletching
wait: 5
diff --git a/data/items/ammo/sapphire_bolts.yaml b/data/items/ammo/sapphire_bolts.yaml
index ec7ae3d..253de08 100644
--- a/data/items/ammo/sapphire_bolts.yaml
+++ b/data/items/ammo/sapphire_bolts.yaml
@@ -1,22 +1,23 @@
color: "27"
craft:
- consume:
- - items:
- - mithril_bolts
- quantity: 10
- - items:
- - sapphire_bolt_tips
- quantity: 10
- level: 63
- output_qty: 10
- skill: fletching
- type: fletching
- wait: 2
- xp: 4
+ ingredients:
+ - items:
+ - mithril_bolts
+ quantity: 10
+ - items:
+ - sapphire_bolt_tips
+ quantity: 10
+ level: 63
+ output_qty: 10
+ type: fletching
+ wait: 2
+ xp: 4
description: Bolts tipped with sapphire. Can be enchanted via science.
-equip_slot: ammo
+equipment:
+ attack:
+ ranged: 3
+ slot: ammo
+ type: ammo
name: sapphire bolts
stackable: true
-stats:
- ranged_attack: 3
value: 20
diff --git a/data/items/ammo/sapphire_bolts_e.yaml b/data/items/ammo/sapphire_bolts_e.yaml
index af05b9c..07694ea 100644
--- a/data/items/ammo/sapphire_bolts_e.yaml
+++ b/data/items/ammo/sapphire_bolts_e.yaml
@@ -1,8 +1,10 @@
+color: 27 bold
+description: Enchanted sapphire-tipped bolts. Have a chance to drain the target's Science level.
+equipment:
+ attack:
+ ranged: 4
+ slot: ammo
+ type: ammo
name: sapphire bolts e
-color: "27 bold"
-description: "Enchanted sapphire-tipped bolts. Have a chance to drain the target's Science level."
-value: 30
stackable: true
-equip_slot: ammo
-stats:
- ranged_attack: 4
+value: 30
diff --git a/data/items/ammo/steel_arrow.yaml b/data/items/ammo/steel_arrow.yaml
index 8d8f18c..4f0fe7d 100644
--- a/data/items/ammo/steel_arrow.yaml
+++ b/data/items/ammo/steel_arrow.yaml
@@ -1,23 +1,24 @@
-color: "FD"
+color: FD
craft:
- consume:
- - items:
- - headless_arrow
- quantity: 15
- - items:
- - steel_arrowtips
- quantity: 15
- level: 30
- output_qty: 15
- skill: fletching
- type: fletching
- wait: 2
- xp: 5
+ ingredients:
+ - items:
+ - headless_arrow
+ quantity: 15
+ - items:
+ - steel_arrowtips
+ quantity: 15
+ level: 30
+ output_qty: 15
+ type: fletching
+ wait: 2
+ xp: 5
description: A steel-tipped arrow.
-equip_slot: ammo
+equipment:
+ other:
+ ranged: 16
+ slot: ammo
+ type: ammo
name: steel arrow
-stackable: true
recoverable: true
-stats:
- ranged_strength: 16
+stackable: true
value: 5
diff --git a/data/items/ammo/steel_arrowtips.yaml b/data/items/ammo/steel_arrowtips.yaml
index 519ce09..b455f31 100644
--- a/data/items/ammo/steel_arrowtips.yaml
+++ b/data/items/ammo/steel_arrowtips.yaml
@@ -1,12 +1,11 @@
color: "FD"
craft:
- consume:
+ ingredients:
- items:
- steel_bar
quantity: 1
level: 35
output_qty: 15
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/ammo/steel_bolts.yaml b/data/items/ammo/steel_bolts.yaml
index 4dfa7d4..f91b024 100644
--- a/data/items/ammo/steel_bolts.yaml
+++ b/data/items/ammo/steel_bolts.yaml
@@ -1,22 +1,23 @@
-color: "FD"
+color: FD
craft:
- consume:
- - items:
- - steel_bolts_unf
- quantity: 10
- - items:
- - feather
- quantity: 10
- level: 46
- output_qty: 10
- skill: fletching
- type: fletching
- wait: 0
- xp: 35
+ ingredients:
+ - items:
+ - steel_bolts_unf
+ quantity: 10
+ - items:
+ - feather
+ quantity: 10
+ level: 46
+ output_qty: 10
+ type: fletching
+ wait: 0
+ xp: 35
description: Steel crossbow bolts.
-equip_slot: ammo
+equipment:
+ other:
+ ranged: 25
+ slot: ammo
+ type: ammo
name: steel bolts
stackable: true
-stats:
- ranged_strength: 25
value: 6
diff --git a/data/items/ammo/steel_bolts_unf.yaml b/data/items/ammo/steel_bolts_unf.yaml
index dd69c37..4532220 100644
--- a/data/items/ammo/steel_bolts_unf.yaml
+++ b/data/items/ammo/steel_bolts_unf.yaml
@@ -1,12 +1,11 @@
color: "FD"
craft:
- consume:
+ ingredients:
- items:
- steel_bar
quantity: 1
level: 36
output_qty: 10
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/consumables/amp_potion.yaml b/data/items/consumables/amp_potion.yaml
index 8afe6a8..22f9dd7 100644
--- a/data/items/consumables/amp_potion.yaml
+++ b/data/items/consumables/amp_potion.yaml
@@ -1,6 +1,6 @@
color: "DC"
craft:
- consume:
+ ingredients:
- items:
- tarromin_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- limpwurt_root
quantity: 1
level: 12
- skill: pharmacy
type: pharmacy
wait: 4
xp: 50
diff --git a/data/items/consumables/anchovies.yaml b/data/items/consumables/anchovies.yaml
index 51a2e99..1d1dfdd 100644
--- a/data/items/consumables/anchovies.yaml
+++ b/data/items/consumables/anchovies.yaml
@@ -1,13 +1,12 @@
color: "D1"
craft:
- consume:
+ ingredients:
- items:
- raw_anchovies
quantity: 1
fail: burnt_fish
fail_message: You accidentally burn the anchovies.
level: 1
- skill: cooking
station:
- fire
- cooking_range
diff --git a/data/items/consumables/avantoe_potion_unf.yaml b/data/items/consumables/avantoe_potion_unf.yaml
index 49c8122..8cee682 100644
--- a/data/items/consumables/avantoe_potion_unf.yaml
+++ b/data/items/consumables/avantoe_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- avantoe
quantity: 1
diff --git a/data/items/consumables/bio_serum.yaml b/data/items/consumables/bio_serum.yaml
index ba7a7f5..68147b1 100644
--- a/data/items/consumables/bio_serum.yaml
+++ b/data/items/consumables/bio_serum.yaml
@@ -1,6 +1,6 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- marrentill_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- antitoxin_powder
quantity: 1
level: 5
- skill: pharmacy
type: pharmacy
wait: 4
xp: 38
diff --git a/data/items/consumables/bread.yaml b/data/items/consumables/bread.yaml
index bde89dd..1f8a996 100644
--- a/data/items/consumables/bread.yaml
+++ b/data/items/consumables/bread.yaml
@@ -1,6 +1,6 @@
color: "DE"
craft:
- consume:
+ ingredients:
- items:
- bread_dough
quantity: 1
@@ -8,7 +8,6 @@ craft:
fail_message: You burn the bread to a crisp.
level: 1
message: "You bake the dough into a %n."
- skill: cooking
station:
- cooking_range
type: cooking
diff --git a/data/items/consumables/bread_dough.yaml b/data/items/consumables/bread_dough.yaml
index 2df433f..48fa986 100644
--- a/data/items/consumables/bread_dough.yaml
+++ b/data/items/consumables/bread_dough.yaml
@@ -1,21 +1,34 @@
-color: "DE"
+color: DE
craft:
+ - ingredients:
+ - byproducts:
+ - empty_pot
+ items:
+ - pot_of_flour
+ quantity: 1
+ - byproducts:
+ - empty_bucket
+ - empty_jug
+ items:
+ - bucket_of_water
+ - jug_of_water
+ quantity: 1
+ end_message: testend
+ fail: ""
+ level: 0
+ message: testmsg
+ output_qty: 0
+ start_message: teststart
+ station: []
+ subtype: ""
+ tool: ""
type: combine
- consume:
- - byproducts:
- - empty_pot
- items:
- - pot_of_flour
- quantity: 1
- - byproducts:
- - empty_bucket
- - empty_jug
- items:
- - bucket_of_water
- - jug_of_water
- quantity: 1
wait: 2
+ xp: 0
description: A ball of uncooked bread dough. It needs to be baked.
+id: bread_dough
+max_quality: 0
name: bread dough
+quality: 0
stackable: false
value: 3
diff --git a/data/items/consumables/cadantine_potion_unf.yaml b/data/items/consumables/cadantine_potion_unf.yaml
index 5198f36..d222ed4 100644
--- a/data/items/consumables/cadantine_potion_unf.yaml
+++ b/data/items/consumables/cadantine_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- cadantine
quantity: 1
diff --git a/data/items/consumables/dwarf_weed_potion_unf.yaml b/data/items/consumables/dwarf_weed_potion_unf.yaml
index c5b814c..084a434 100644
--- a/data/items/consumables/dwarf_weed_potion_unf.yaml
+++ b/data/items/consumables/dwarf_weed_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- dwarf_weed
quantity: 1
diff --git a/data/items/consumables/full_restore.yaml b/data/items/consumables/full_restore.yaml
index b817bc5..178914e 100644
--- a/data/items/consumables/full_restore.yaml
+++ b/data/items/consumables/full_restore.yaml
@@ -1,6 +1,6 @@
color: "D0"
craft:
- consume:
+ ingredients:
- items:
- snapdragon_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- arachnid_enzyme
quantity: 1
level: 63
- skill: pharmacy
type: pharmacy
wait: 4
xp: 143
diff --git a/data/items/consumables/guam_potion_unf.yaml b/data/items/consumables/guam_potion_unf.yaml
index 298c578..dc963ed 100644
--- a/data/items/consumables/guam_potion_unf.yaml
+++ b/data/items/consumables/guam_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- guam
quantity: 1
diff --git a/data/items/consumables/harralander_potion_unf.yaml b/data/items/consumables/harralander_potion_unf.yaml
index 29999ac..cf4873f 100644
--- a/data/items/consumables/harralander_potion_unf.yaml
+++ b/data/items/consumables/harralander_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- harralander
quantity: 1
diff --git a/data/items/consumables/herring.yaml b/data/items/consumables/herring.yaml
index 4ff4e19..165802b 100644
--- a/data/items/consumables/herring.yaml
+++ b/data/items/consumables/herring.yaml
@@ -1,13 +1,12 @@
color: "D1"
craft:
- consume:
+ ingredients:
- items:
- raw_herring
quantity: 1
fail: burnt_fish
fail_message: You accidentally burn the herring.
level: 5
- skill: cooking
station:
- fire
- cooking_range
diff --git a/data/items/consumables/irit_potion_unf.yaml b/data/items/consumables/irit_potion_unf.yaml
index 1c24007..b358c07 100644
--- a/data/items/consumables/irit_potion_unf.yaml
+++ b/data/items/consumables/irit_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- irit
quantity: 1
diff --git a/data/items/consumables/kwuarm_potion_unf.yaml b/data/items/consumables/kwuarm_potion_unf.yaml
index 9a54ac3..801d301 100644
--- a/data/items/consumables/kwuarm_potion_unf.yaml
+++ b/data/items/consumables/kwuarm_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- kwuarm
quantity: 1
diff --git a/data/items/consumables/lantadyme_potion_unf.yaml b/data/items/consumables/lantadyme_potion_unf.yaml
index d084ac1..069cf22 100644
--- a/data/items/consumables/lantadyme_potion_unf.yaml
+++ b/data/items/consumables/lantadyme_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- lantadyme
quantity: 1
diff --git a/data/items/consumables/marrentill_potion_unf.yaml b/data/items/consumables/marrentill_potion_unf.yaml
index 3a6d6d0..f39094e 100644
--- a/data/items/consumables/marrentill_potion_unf.yaml
+++ b/data/items/consumables/marrentill_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- marrentill
quantity: 1
diff --git a/data/items/consumables/nano_restore.yaml b/data/items/consumables/nano_restore.yaml
index 3afbe16..ddb355a 100644
--- a/data/items/consumables/nano_restore.yaml
+++ b/data/items/consumables/nano_restore.yaml
@@ -1,6 +1,6 @@
color: "D0"
craft:
- consume:
+ ingredients:
- items:
- harralander_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- arachnid_enzyme
quantity: 1
level: 22
- skill: pharmacy
type: pharmacy
wait: 4
xp: 63
diff --git a/data/items/consumables/pastry_dough.yaml b/data/items/consumables/pastry_dough.yaml
index a72c6c3..8c106e0 100644
--- a/data/items/consumables/pastry_dough.yaml
+++ b/data/items/consumables/pastry_dough.yaml
@@ -1,7 +1,7 @@
color: "DE"
craft:
type: combine
- consume:
+ ingredients:
- items:
- pot_of_flour
quantity: 1
diff --git a/data/items/consumables/pie_dish.yaml b/data/items/consumables/pie_dish.yaml
index b95a291..89d82e4 100644
--- a/data/items/consumables/pie_dish.yaml
+++ b/data/items/consumables/pie_dish.yaml
@@ -1,13 +1,12 @@
color: "82"
craft:
- consume:
+ ingredients:
- items:
- unfired_pie_dish
quantity: 1
fail_message: The pie dish cracks in the oven and is ruined.
level: 7
message: "You fire the %i1 in the oven."
- skill: crafting
station:
- pottery_oven
success:
diff --git a/data/items/consumables/pizza_dough.yaml b/data/items/consumables/pizza_dough.yaml
index 5223d9b..c8e29dc 100644
--- a/data/items/consumables/pizza_dough.yaml
+++ b/data/items/consumables/pizza_dough.yaml
@@ -1,7 +1,7 @@
color: "DE"
craft:
type: combine
- consume:
+ ingredients:
- items:
- pot_of_flour
quantity: 1
diff --git a/data/items/consumables/ranarr_potion_unf.yaml b/data/items/consumables/ranarr_potion_unf.yaml
index 08a2857..3adfbf7 100644
--- a/data/items/consumables/ranarr_potion_unf.yaml
+++ b/data/items/consumables/ranarr_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- ranarr
quantity: 1
diff --git a/data/items/consumables/restoration_compound.yaml b/data/items/consumables/restoration_compound.yaml
index b9c36cd..79eac7a 100644
--- a/data/items/consumables/restoration_compound.yaml
+++ b/data/items/consumables/restoration_compound.yaml
@@ -1,6 +1,6 @@
color: "33"
craft:
- consume:
+ ingredients:
- items:
- toadflax_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- crushed_nest
quantity: 1
level: 81
- skill: pharmacy
type: pharmacy
wait: 4
xp: 180
diff --git a/data/items/consumables/salmon.yaml b/data/items/consumables/salmon.yaml
index dc9a956..b29f935 100644
--- a/data/items/consumables/salmon.yaml
+++ b/data/items/consumables/salmon.yaml
@@ -1,13 +1,12 @@
color: "D1"
craft:
- consume:
+ ingredients:
- items:
- raw_salmon
quantity: 1
fail: burnt_fish
fail_message: You accidentally burn the salmon.
level: 25
- skill: cooking
station:
- fire
- cooking_range
diff --git a/data/items/consumables/sardine.yaml b/data/items/consumables/sardine.yaml
index ac33a49..0c4f53e 100644
--- a/data/items/consumables/sardine.yaml
+++ b/data/items/consumables/sardine.yaml
@@ -1,13 +1,12 @@
color: "D1"
craft:
- consume:
+ ingredients:
- items:
- raw_sardine
quantity: 1
fail: burnt_fish
fail_message: You accidentally burn the sardine.
level: 1
- skill: cooking
station:
- fire
- cooking_range
diff --git a/data/items/consumables/science_serum.yaml b/data/items/consumables/science_serum.yaml
index 39ab495..4915ea8 100644
--- a/data/items/consumables/science_serum.yaml
+++ b/data/items/consumables/science_serum.yaml
@@ -1,6 +1,6 @@
color: "8D"
craft:
- consume:
+ ingredients:
- items:
- lantadyme_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- potato_cactus
quantity: 1
level: 76
- skill: pharmacy
type: pharmacy
wait: 4
xp: 173
diff --git a/data/items/consumables/shield_potion.yaml b/data/items/consumables/shield_potion.yaml
index 92f4980..88d1967 100644
--- a/data/items/consumables/shield_potion.yaml
+++ b/data/items/consumables/shield_potion.yaml
@@ -1,6 +1,6 @@
color: "15"
craft:
- consume:
+ ingredients:
- items:
- ranarr_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- white_berries
quantity: 1
level: 30
- skill: pharmacy
type: pharmacy
wait: 4
xp: 75
diff --git a/data/items/consumables/shrimps.yaml b/data/items/consumables/shrimps.yaml
index 536c3f5..be11251 100644
--- a/data/items/consumables/shrimps.yaml
+++ b/data/items/consumables/shrimps.yaml
@@ -1,13 +1,12 @@
color: "D1"
craft:
- consume:
+ ingredients:
- items:
- raw_shrimps
quantity: 1
fail: burnt_fish
fail_message: You accidentally burn the shrimps.
level: 1
- skill: cooking
station:
- fire
- cooking_range
diff --git a/data/items/consumables/snapdragon_potion_unf.yaml b/data/items/consumables/snapdragon_potion_unf.yaml
index 5bc76b3..d3e8a9e 100644
--- a/data/items/consumables/snapdragon_potion_unf.yaml
+++ b/data/items/consumables/snapdragon_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- snapdragon
quantity: 1
diff --git a/data/items/consumables/stim_potion.yaml b/data/items/consumables/stim_potion.yaml
index a19d611..c61e4fb 100644
--- a/data/items/consumables/stim_potion.yaml
+++ b/data/items/consumables/stim_potion.yaml
@@ -1,6 +1,6 @@
color: "C4"
craft:
- consume:
+ ingredients:
- items:
- guam_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- eye_of_newt
quantity: 1
level: 1
- skill: pharmacy
type: pharmacy
wait: 4
xp: 25
diff --git a/data/items/consumables/super_amp.yaml b/data/items/consumables/super_amp.yaml
index 7f851c6..f8fd85c 100644
--- a/data/items/consumables/super_amp.yaml
+++ b/data/items/consumables/super_amp.yaml
@@ -1,6 +1,6 @@
color: "DC"
craft:
- consume:
+ ingredients:
- items:
- kwuarm_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- limpwurt_root
quantity: 1
level: 55
- skill: pharmacy
type: pharmacy
wait: 4
xp: 125
diff --git a/data/items/consumables/super_bio_serum.yaml b/data/items/consumables/super_bio_serum.yaml
index 08154d2..d4003a4 100644
--- a/data/items/consumables/super_bio_serum.yaml
+++ b/data/items/consumables/super_bio_serum.yaml
@@ -1,6 +1,6 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- irit_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- antitoxin_powder
quantity: 1
level: 48
- skill: pharmacy
type: pharmacy
wait: 4
xp: 106
diff --git a/data/items/consumables/super_shield.yaml b/data/items/consumables/super_shield.yaml
index 570af87..90389d6 100644
--- a/data/items/consumables/super_shield.yaml
+++ b/data/items/consumables/super_shield.yaml
@@ -1,6 +1,6 @@
color: "15"
craft:
- consume:
+ ingredients:
- items:
- cadantine_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- white_berries
quantity: 1
level: 66
- skill: pharmacy
type: pharmacy
wait: 4
xp: 150
diff --git a/data/items/consumables/super_stim.yaml b/data/items/consumables/super_stim.yaml
index 2b35d62..27662a5 100644
--- a/data/items/consumables/super_stim.yaml
+++ b/data/items/consumables/super_stim.yaml
@@ -1,6 +1,6 @@
color: "C4"
craft:
- consume:
+ ingredients:
- items:
- irit_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- eye_of_newt
quantity: 1
level: 45
- skill: pharmacy
type: pharmacy
wait: 4
xp: 100
diff --git a/data/items/consumables/super_stim_cell.yaml b/data/items/consumables/super_stim_cell.yaml
index 45dca94..37c4af2 100644
--- a/data/items/consumables/super_stim_cell.yaml
+++ b/data/items/consumables/super_stim_cell.yaml
@@ -1,6 +1,6 @@
color: "E2"
craft:
- consume:
+ ingredients:
- items:
- avantoe_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- blight_spore
quantity: 1
level: 52
- skill: pharmacy
type: pharmacy
wait: 4
xp: 118
diff --git a/data/items/consumables/targeting_serum.yaml b/data/items/consumables/targeting_serum.yaml
index e8b8864..74b3e73 100644
--- a/data/items/consumables/targeting_serum.yaml
+++ b/data/items/consumables/targeting_serum.yaml
@@ -1,6 +1,6 @@
color: "CA"
craft:
- consume:
+ ingredients:
- items:
- dwarf_weed_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- catalyst_wine
quantity: 1
level: 72
- skill: pharmacy
type: pharmacy
wait: 4
xp: 163
diff --git a/data/items/consumables/tarromin_potion_unf.yaml b/data/items/consumables/tarromin_potion_unf.yaml
index 1db2c0a..c8798f6 100644
--- a/data/items/consumables/tarromin_potion_unf.yaml
+++ b/data/items/consumables/tarromin_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- tarromin
quantity: 1
diff --git a/data/items/consumables/tech_serum.yaml b/data/items/consumables/tech_serum.yaml
index 79224ad..01ad080 100644
--- a/data/items/consumables/tech_serum.yaml
+++ b/data/items/consumables/tech_serum.yaml
@@ -1,6 +1,6 @@
color: "4B"
craft:
- consume:
+ ingredients:
- items:
- ranarr_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- snape_grass
quantity: 1
level: 38
- skill: pharmacy
type: pharmacy
wait: 4
xp: 88
diff --git a/data/items/consumables/toadflax_potion_unf.yaml b/data/items/consumables/toadflax_potion_unf.yaml
index ff9bf79..aa56ef7 100644
--- a/data/items/consumables/toadflax_potion_unf.yaml
+++ b/data/items/consumables/toadflax_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- toadflax
quantity: 1
diff --git a/data/items/consumables/torstol_potion_unf.yaml b/data/items/consumables/torstol_potion_unf.yaml
index 1008463..89e8f8c 100644
--- a/data/items/consumables/torstol_potion_unf.yaml
+++ b/data/items/consumables/torstol_potion_unf.yaml
@@ -1,7 +1,7 @@
color: "30"
craft:
type: pharmacy
- consume:
+ ingredients:
- items:
- torstol
quantity: 1
diff --git a/data/items/consumables/trout.yaml b/data/items/consumables/trout.yaml
index 4c5a2e1..91933d6 100644
--- a/data/items/consumables/trout.yaml
+++ b/data/items/consumables/trout.yaml
@@ -1,13 +1,12 @@
color: "D1"
craft:
- consume:
+ ingredients:
- items:
- raw_trout
quantity: 1
fail: burnt_fish
fail_message: You accidentally burn the trout.
level: 15
- skill: cooking
station:
- fire
- cooking_range
diff --git a/data/items/decks/advanced_bio_deck.yaml b/data/items/decks/advanced_bio_deck.yaml
index 6eb1559..dffbbe0 100644
--- a/data/items/decks/advanced_bio_deck.yaml
+++ b/data/items/decks/advanced_bio_deck.yaml
@@ -1,10 +1,11 @@
+color: C4 bold
+description: A high-powered bio deck with enhanced circuitry. Provides unlimited biojunk and removes the scrap requirement.
+equipment:
+ attack:
+ science: 20
+ junk: biojunk
+ slot: main_hand
+ speed: 5
+ type: tech_weapon
name: advanced bio deck
-color: "C4 bold"
-description: "A high-powered bio deck with enhanced circuitry. Provides unlimited biojunk and removes the scrap requirement."
value: 15000
-equip_slot: main_hand
-weapon_type: science
-speed: 5
-stats:
- science_attack: 20
-provides_junk: biojunk
diff --git a/data/items/decks/advanced_eco_deck.yaml b/data/items/decks/advanced_eco_deck.yaml
index 9c4d58a..419e157 100644
--- a/data/items/decks/advanced_eco_deck.yaml
+++ b/data/items/decks/advanced_eco_deck.yaml
@@ -1,10 +1,11 @@
+color: 22 bold
+description: A high-powered eco deck with enhanced circuitry. Provides unlimited ecojunk and removes the scrap requirement.
+equipment:
+ attack:
+ science: 20
+ junk: ecojunk
+ slot: main_hand
+ speed: 5
+ type: tech_weapon
name: advanced eco deck
-color: "22 bold"
-description: "A high-powered eco deck with enhanced circuitry. Provides unlimited ecojunk and removes the scrap requirement."
value: 15000
-equip_slot: main_hand
-weapon_type: science
-speed: 5
-stats:
- science_attack: 20
-provides_junk: ecojunk
diff --git a/data/items/decks/advanced_hydro_deck.yaml b/data/items/decks/advanced_hydro_deck.yaml
index bf165b8..e1c7181 100644
--- a/data/items/decks/advanced_hydro_deck.yaml
+++ b/data/items/decks/advanced_hydro_deck.yaml
@@ -1,10 +1,11 @@
+color: 27 bold
+description: A high-powered hydro deck with enhanced circuitry. Provides unlimited hydrojunk and removes the scrap requirement.
+equipment:
+ attack:
+ science: 20
+ junk: hydrojunk
+ slot: main_hand
+ speed: 5
+ type: tech_weapon
name: advanced hydro deck
-color: "27 bold"
-description: "A high-powered hydro deck with enhanced circuitry. Provides unlimited hydrojunk and removes the scrap requirement."
value: 15000
-equip_slot: main_hand
-weapon_type: science
-speed: 5
-stats:
- science_attack: 20
-provides_junk: hydrojunk
diff --git a/data/items/decks/advanced_solar_deck.yaml b/data/items/decks/advanced_solar_deck.yaml
index 98d4e76..25ce3d5 100644
--- a/data/items/decks/advanced_solar_deck.yaml
+++ b/data/items/decks/advanced_solar_deck.yaml
@@ -1,10 +1,11 @@
+color: DC bold
+description: A high-powered solar deck with enhanced circuitry. Provides unlimited solarjunk and removes the scrap requirement.
+equipment:
+ attack:
+ science: 20
+ junk: solarjunk
+ slot: main_hand
+ speed: 5
+ type: tech_weapon
name: advanced solar deck
-color: "DC bold"
-description: "A high-powered solar deck with enhanced circuitry. Provides unlimited solarjunk and removes the scrap requirement."
value: 15000
-equip_slot: main_hand
-weapon_type: science
-speed: 5
-stats:
- science_attack: 20
-provides_junk: solarjunk
diff --git a/data/items/decks/basic_deck.yaml b/data/items/decks/basic_deck.yaml
index da478b0..52bcea7 100644
--- a/data/items/decks/basic_deck.yaml
+++ b/data/items/decks/basic_deck.yaml
@@ -1,9 +1,10 @@
+color: F5
+description: A simple programmable deck. Removes the scrap requirement for triggering mods, but provides no elemental junk.
+equipment:
+ attack:
+ science: 5
+ slot: main_hand
+ speed: 5
+ type: tech_weapon
name: basic deck
-color: "F5"
-description: "A simple programmable deck. Removes the scrap requirement for triggering mods, but provides no elemental junk."
value: 500
-equip_slot: main_hand
-weapon_type: science
-speed: 5
-stats:
- science_attack: 5
diff --git a/data/items/decks/bio_deck.yaml b/data/items/decks/bio_deck.yaml
index 7211ddc..373202c 100644
--- a/data/items/decks/bio_deck.yaml
+++ b/data/items/decks/bio_deck.yaml
@@ -1,10 +1,11 @@
+color: C4
+description: A programmable deck infused with bio-synthetic membranes. Provides unlimited biojunk and removes the scrap requirement.
+equipment:
+ attack:
+ science: 10
+ junk: biojunk
+ slot: main_hand
+ speed: 5
+ type: tech_weapon
name: bio deck
-color: "C4"
-description: "A programmable deck infused with bio-synthetic membranes. Provides unlimited biojunk and removes the scrap requirement."
value: 1500
-equip_slot: main_hand
-weapon_type: science
-speed: 5
-stats:
- science_attack: 10
-provides_junk: biojunk
diff --git a/data/items/decks/eco_deck.yaml b/data/items/decks/eco_deck.yaml
index b922eea..9d679de 100644
--- a/data/items/decks/eco_deck.yaml
+++ b/data/items/decks/eco_deck.yaml
@@ -1,10 +1,11 @@
-name: eco deck
color: "22"
-description: "A programmable deck threaded with eco-organic circuits. Provides unlimited ecojunk and removes the scrap requirement."
+description: A programmable deck threaded with eco-organic circuits. Provides unlimited ecojunk and removes the scrap requirement.
+equipment:
+ attack:
+ science: 10
+ junk: ecojunk
+ slot: main_hand
+ speed: 5
+ type: tech_weapon
+name: eco deck
value: 1500
-equip_slot: main_hand
-weapon_type: science
-speed: 5
-stats:
- science_attack: 10
-provides_junk: ecojunk
diff --git a/data/items/decks/hydro_deck.yaml b/data/items/decks/hydro_deck.yaml
index 51e23e3..e49cedd 100644
--- a/data/items/decks/hydro_deck.yaml
+++ b/data/items/decks/hydro_deck.yaml
@@ -1,10 +1,11 @@
-name: hydro deck
color: "27"
-description: "A programmable deck infused with hydro circuitry. Provides unlimited hydrojunk and removes the scrap requirement."
+description: A programmable deck infused with hydro circuitry. Provides unlimited hydrojunk and removes the scrap requirement.
+equipment:
+ attack:
+ science: 10
+ junk: hydrojunk
+ slot: main_hand
+ speed: 5
+ type: tech_weapon
+name: hydro deck
value: 1500
-equip_slot: main_hand
-weapon_type: science
-speed: 5
-stats:
- science_attack: 10
-provides_junk: hydrojunk
diff --git a/data/items/decks/solar_deck.yaml b/data/items/decks/solar_deck.yaml
index c597ac6..e7816a1 100644
--- a/data/items/decks/solar_deck.yaml
+++ b/data/items/decks/solar_deck.yaml
@@ -1,10 +1,11 @@
+color: DC
+description: A programmable deck pulsing with solar energy. Provides unlimited solarjunk and removes the scrap requirement.
+equipment:
+ attack:
+ science: 10
+ junk: solarjunk
+ slot: main_hand
+ speed: 5
+ type: tech_weapon
name: solar deck
-color: "DC"
-description: "A programmable deck pulsing with solar energy. Provides unlimited solarjunk and removes the scrap requirement."
value: 1500
-equip_slot: main_hand
-weapon_type: science
-speed: 5
-stats:
- science_attack: 10
-provides_junk: solarjunk
diff --git a/data/items/equipment/abyssal_bracelet.yaml b/data/items/equipment/abyssal_bracelet.yaml
index df10e72..f20c1c4 100644
--- a/data/items/equipment/abyssal_bracelet.yaml
+++ b/data/items/equipment/abyssal_bracelet.yaml
@@ -1,5 +1,7 @@
+color: FF
+description: An enchanted diamond bracelet that increases scavenging output.
+equipment:
+ slot: hands
+ type: armor
name: abyssal bracelet
-color: "FF"
-description: "An enchanted diamond bracelet that increases scavenging output."
value: 6000
-equip_slot: hands
diff --git a/data/items/equipment/adamant_dagger.yaml b/data/items/equipment/adamant_dagger.yaml
index 3f8263c..7a4a832 100644
--- a/data/items/equipment/adamant_dagger.yaml
+++ b/data/items/equipment/adamant_dagger.yaml
@@ -1,28 +1,29 @@
-attack_type: stab
color: "78"
craft:
- consume:
- - items:
- - adamantite_bar
- quantity: 1
- level: 70
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 62
+ ingredients:
+ - items:
+ - adamantite_bar
+ quantity: 1
+ level: 70
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 62
description: A small adamant dagger.
-equip_slot: main_hand
+equipment:
+ attack:
+ crush: -1
+ slash: 14
+ stab: 23
+ attack_type: stab
+ other:
+ strength: 19
+ slot: main_hand
+ speed: 3
+ type: melee_weapon
name: adamant dagger
requirements:
- accuracy: 30
-speed: 3
+ accuracy: 30
stackable: false
-stats:
- crush_attack: -1
- slash_attack: 14
- stab_attack: 23
- strength_bonus: 19
value: 400
-weapon_type: melee
diff --git a/data/items/equipment/adamant_full_helm.yaml b/data/items/equipment/adamant_full_helm.yaml
index 9a8e2a2..5d4bd24 100644
--- a/data/items/equipment/adamant_full_helm.yaml
+++ b/data/items/equipment/adamant_full_helm.yaml
@@ -1,26 +1,27 @@
color: "78"
craft:
- consume:
- - items:
- - adamantite_bar
- quantity: 2
- level: 77
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 125
+ ingredients:
+ - items:
+ - adamantite_bar
+ quantity: 2
+ level: 77
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 125
description: An adamant full helmet.
-equip_slot: head
+equipment:
+ defense:
+ crush: 16
+ ranged: 22
+ science: -8
+ slash: 24
+ stab: 22
+ slot: head
+ type: armor
name: adamant full helm
requirements:
- defense: 30
+ defense: 30
stackable: false
-stats:
- crush_defense: 16
- ranged_defense: 22
- science_defense: -8
- slash_defense: 24
- stab_defense: 22
value: 1000
diff --git a/data/items/equipment/adamant_med_helm.yaml b/data/items/equipment/adamant_med_helm.yaml
index baa3cf3..3b06767 100644
--- a/data/items/equipment/adamant_med_helm.yaml
+++ b/data/items/equipment/adamant_med_helm.yaml
@@ -1,26 +1,27 @@
color: "78"
craft:
- consume:
- - items:
- - adamantite_bar
- quantity: 1
- level: 73
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 62
+ ingredients:
+ - items:
+ - adamantite_bar
+ quantity: 1
+ level: 73
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 62
description: An adamant medium helmet.
-equip_slot: head
+equipment:
+ defense:
+ crush: 10
+ ranged: 14
+ science: -3
+ slash: 16
+ stab: 14
+ slot: head
+ type: armor
name: adamant med helm
requirements:
- defense: 30
+ defense: 30
stackable: false
-stats:
- crush_defense: 10
- ranged_defense: 14
- science_defense: -3
- slash_defense: 16
- stab_defense: 14
value: 400
diff --git a/data/items/equipment/adamant_platebody.yaml b/data/items/equipment/adamant_platebody.yaml
index a80f375..144bf78 100644
--- a/data/items/equipment/adamant_platebody.yaml
+++ b/data/items/equipment/adamant_platebody.yaml
@@ -1,26 +1,27 @@
color: "78"
craft:
- consume:
- - items:
- - adamantite_bar
- quantity: 5
- level: 88
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 312
+ ingredients:
+ - items:
+ - adamantite_bar
+ quantity: 5
+ level: 88
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 312
description: An adamant platebody.
-equip_slot: torso
+equipment:
+ defense:
+ crush: 40
+ ranged: 49
+ science: -25
+ slash: 55
+ stab: 49
+ slot: torso
+ type: armor
name: adamant platebody
requirements:
- defense: 30
+ defense: 30
stackable: false
-stats:
- crush_defense: 40
- ranged_defense: 49
- science_defense: -25
- slash_defense: 55
- stab_defense: 49
value: 5000
diff --git a/data/items/equipment/adamant_sword.yaml b/data/items/equipment/adamant_sword.yaml
index 0033f76..052d501 100644
--- a/data/items/equipment/adamant_sword.yaml
+++ b/data/items/equipment/adamant_sword.yaml
@@ -1,28 +1,29 @@
-attack_type: slash
color: "78"
craft:
- consume:
- - items:
- - adamantite_bar
- quantity: 1
- level: 74
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 62
+ ingredients:
+ - items:
+ - adamantite_bar
+ quantity: 1
+ level: 74
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 62
description: An adamant sword.
-equip_slot: main_hand
+equipment:
+ attack:
+ crush: -2
+ slash: 40
+ stab: 20
+ attack_type: slash
+ other:
+ strength: 27
+ slot: main_hand
+ speed: 4
+ type: melee_weapon
name: adamant sword
requirements:
- accuracy: 30
-speed: 4
+ accuracy: 30
stackable: false
-stats:
- crush_attack: -2
- slash_attack: 40
- stab_attack: 20
- strength_bonus: 27
value: 700
-weapon_type: melee
diff --git a/data/items/equipment/binding_necklace.yaml b/data/items/equipment/binding_necklace.yaml
index 6518ce7..879271e 100644
--- a/data/items/equipment/binding_necklace.yaml
+++ b/data/items/equipment/binding_necklace.yaml
@@ -1,5 +1,7 @@
-name: binding necklace
color: "22"
-description: "An enchanted emerald necklace. Provides a 100 percent success rate when identifying junk at altars."
+description: An enchanted emerald necklace. Provides a 100 percent success rate when identifying junk at altars.
+equipment:
+ slot: neck
+ type: armor
+name: binding necklace
value: 1200
-equip_slot: neck
diff --git a/data/items/equipment/black_axe.yaml b/data/items/equipment/black_axe.yaml
index ffff723..e0bbad2 100644
--- a/data/items/equipment/black_axe.yaml
+++ b/data/items/equipment/black_axe.yaml
@@ -1,18 +1,21 @@
+color: F0
+description: A black axe, good for woodcutting.
+equipment:
+ attack:
+ crush: 14
+ slash: 18
+ stab: -2
+ attack_type: slash
+ other:
+ strength: 14
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: black axe
-color: "F0"
-description: "A black axe, good for woodcutting."
-value: 150
-stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
-stats:
- stab_attack: -2
- slash_attack: 18
- crush_attack: 14
- strength_bonus: 14
-speed: 5
-tool_type: axe
-tool_speed: 2.5
requirements:
- accuracy: 10
+ accuracy: 10
+stackable: false
+tool:
+ speed: 2.5
+ type: axe
+value: 150
diff --git a/data/items/equipment/bracelet_of_clay.yaml b/data/items/equipment/bracelet_of_clay.yaml
index ed9f92f..297538c 100644
--- a/data/items/equipment/bracelet_of_clay.yaml
+++ b/data/items/equipment/bracelet_of_clay.yaml
@@ -1,5 +1,7 @@
-name: bracelet of clay
color: "27"
-description: "An enchanted sapphire bracelet. Softens clay for easier crafting."
+description: An enchanted sapphire bracelet. Softens clay for easier crafting.
+equipment:
+ slot: hands
+ type: armor
+name: bracelet of clay
value: 500
-equip_slot: hands
diff --git a/data/items/equipment/bracelet_of_slaughter.yaml b/data/items/equipment/bracelet_of_slaughter.yaml
index 785a379..4ce08c9 100644
--- a/data/items/equipment/bracelet_of_slaughter.yaml
+++ b/data/items/equipment/bracelet_of_slaughter.yaml
@@ -1,5 +1,7 @@
-name: bracelet of slaughter
color: "22"
-description: "An enchanted emerald bracelet that provides bonus XP on kills."
+description: An enchanted emerald bracelet that provides bonus XP on kills.
+equipment:
+ slot: hands
+ type: armor
+name: bracelet of slaughter
value: 1200
-equip_slot: hands
diff --git a/data/items/equipment/bronze_dagger.yaml b/data/items/equipment/bronze_dagger.yaml
index d74276e..a1fa416 100644
--- a/data/items/equipment/bronze_dagger.yaml
+++ b/data/items/equipment/bronze_dagger.yaml
@@ -1,26 +1,27 @@
-attack_type: stab
-color: "B2"
+color: B2
craft:
- consume:
- - items:
- - bronze_bar
- quantity: 1
- level: 1
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 12
+ ingredients:
+ - items:
+ - bronze_bar
+ quantity: 1
+ level: 1
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 12
description: A small bronze dagger.
-equip_slot: main_hand
+equipment:
+ attack:
+ crush: -1
+ slash: 3
+ stab: 5
+ attack_type: stab
+ other:
+ strength: 4
+ slot: main_hand
+ speed: 3
+ type: melee_weapon
name: bronze dagger
-speed: 3
stackable: false
-stats:
- crush_attack: -1
- slash_attack: 3
- stab_attack: 5
- strength_bonus: 4
value: 8
-weapon_type: melee
diff --git a/data/items/equipment/bronze_full_helm.yaml b/data/items/equipment/bronze_full_helm.yaml
index 07db7f4..1d61b36 100644
--- a/data/items/equipment/bronze_full_helm.yaml
+++ b/data/items/equipment/bronze_full_helm.yaml
@@ -1,24 +1,25 @@
-color: "B2"
+color: B2
craft:
- consume:
- - items:
- - bronze_bar
- quantity: 2
- level: 7
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 25
+ ingredients:
+ - items:
+ - bronze_bar
+ quantity: 2
+ level: 7
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 25
description: A bronze full helmet.
-equip_slot: head
+equipment:
+ defense:
+ crush: 4
+ ranged: 5
+ science: -3
+ slash: 6
+ stab: 5
+ slot: head
+ type: armor
name: bronze full helm
stackable: false
-stats:
- crush_defense: 4
- ranged_defense: 5
- science_defense: -3
- slash_defense: 6
- stab_defense: 5
value: 20
diff --git a/data/items/equipment/bronze_med_helm.yaml b/data/items/equipment/bronze_med_helm.yaml
index d6a31ad..4832b3d 100644
--- a/data/items/equipment/bronze_med_helm.yaml
+++ b/data/items/equipment/bronze_med_helm.yaml
@@ -1,24 +1,25 @@
-color: "B2"
+color: B2
craft:
- consume:
- - items:
- - bronze_bar
- quantity: 1
- level: 3
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 12
+ ingredients:
+ - items:
+ - bronze_bar
+ quantity: 1
+ level: 3
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 12
description: A bronze medium helmet.
-equip_slot: head
+equipment:
+ defense:
+ crush: 2
+ ranged: 3
+ science: -1
+ slash: 4
+ stab: 3
+ slot: head
+ type: armor
name: bronze med helm
stackable: false
-stats:
- crush_defense: 2
- ranged_defense: 3
- science_defense: -1
- slash_defense: 4
- stab_defense: 3
value: 8
diff --git a/data/items/equipment/bronze_platebody.yaml b/data/items/equipment/bronze_platebody.yaml
index d6954a2..a642f14 100644
--- a/data/items/equipment/bronze_platebody.yaml
+++ b/data/items/equipment/bronze_platebody.yaml
@@ -1,24 +1,25 @@
-color: "B2"
+color: B2
craft:
- consume:
- - items:
- - bronze_bar
- quantity: 5
- level: 18
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 62
+ ingredients:
+ - items:
+ - bronze_bar
+ quantity: 5
+ level: 18
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 62
description: A bronze platebody.
-equip_slot: torso
+equipment:
+ defense:
+ crush: 10
+ ranged: 12
+ science: -10
+ slash: 15
+ stab: 12
+ slot: torso
+ type: armor
name: bronze platebody
stackable: false
-stats:
- crush_defense: 10
- ranged_defense: 12
- science_defense: -10
- slash_defense: 15
- stab_defense: 12
value: 60
diff --git a/data/items/equipment/bronze_sword.yaml b/data/items/equipment/bronze_sword.yaml
index 7a93c4c..56160f4 100644
--- a/data/items/equipment/bronze_sword.yaml
+++ b/data/items/equipment/bronze_sword.yaml
@@ -1,26 +1,27 @@
-attack_type: slash
-color: "B2"
+color: B2
craft:
- consume:
- - items:
- - bronze_bar
- quantity: 1
- level: 4
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 12
+ ingredients:
+ - items:
+ - bronze_bar
+ quantity: 1
+ level: 4
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 12
description: A basic bronze sword.
-equip_slot: main_hand
+equipment:
+ attack:
+ crush: -2
+ slash: 10
+ stab: 4
+ attack_type: slash
+ other:
+ strength: 7
+ slot: main_hand
+ speed: 4
+ type: melee_weapon
name: bronze sword
-speed: 4
stackable: false
-stats:
- crush_attack: -2
- slash_attack: 10
- stab_attack: 4
- strength_bonus: 7
value: 12
-weapon_type: melee
diff --git a/data/items/equipment/capacitor_gloves.yaml b/data/items/equipment/capacitor_gloves.yaml
index 85cccc0..4b30983 100644
--- a/data/items/equipment/capacitor_gloves.yaml
+++ b/data/items/equipment/capacitor_gloves.yaml
@@ -1,10 +1,13 @@
+color: FA
+description: Gloves woven with superconducting filaments.
+equipment:
+ defense:
+ crush: 2
+ slash: 2
+ stab: 2
+ other:
+ technology: 2
+ slot: hands
+ type: armor
name: Capacitor Gloves
-description: "Gloves woven with superconducting filaments."
-color: "FA"
value: 300
-equip_slot: hands
-stats:
- stab_defense: 2
- slash_defense: 2
- crush_defense: 2
- technology_bonus: 2
diff --git a/data/items/equipment/cape_of_agility.yaml b/data/items/equipment/cape_of_agility.yaml
index 62b8635..a64be10 100644
--- a/data/items/equipment/cape_of_agility.yaml
+++ b/data/items/equipment/cape_of_agility.yaml
@@ -1,5 +1,7 @@
+color: g:DF,E7,DF
+description: A lightweight cape that allows the wearer to move with extraordinary speed.
+equipment:
+ slot: back
+ type: armor
name: cape of agility
-color: "g:DF,E7,DF"
-description: "A lightweight cape that allows the wearer to move with extraordinary speed."
value: 500
-equip_slot: back
diff --git a/data/items/equipment/coif.yaml b/data/items/equipment/coif.yaml
index ea02d92..7aeab1b 100644
--- a/data/items/equipment/coif.yaml
+++ b/data/items/equipment/coif.yaml
@@ -1,29 +1,31 @@
-color: "5E"
+color: 5E
craft:
- consume:
- - items:
- - hard_leather
- quantity: 1
- - items:
- - thread
- quantity: 1
- level: 38
- skill: crafting
- tool: needle
- type: crafting
- wait: 4
- xp: 37
+ ingredients:
+ - items:
+ - hard_leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 38
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 37
description: A hard leather coif.
-equip_slot: head
+equipment:
+ attack:
+ ranged: 2
+ defense:
+ crush: 2
+ ranged: 6
+ science: 4
+ slash: 4
+ stab: 2
+ slot: head
+ type: armor
name: coif
requirements:
- defense: 20
- ranged: 20
-stats:
- crush_defense: 2
- ranged_attack: 2
- ranged_defense: 6
- science_defense: 4
- slash_defense: 4
- stab_defense: 2
+ defense: 20
+ ranged: 20
value: 45
diff --git a/data/items/equipment/diamond_amulet_u.yaml b/data/items/equipment/diamond_amulet_u.yaml
index 2959aac..e8be864 100644
--- a/data/items/equipment/diamond_amulet_u.yaml
+++ b/data/items/equipment/diamond_amulet_u.yaml
@@ -1,6 +1,6 @@
color: "FF"
craft:
- consume:
+ ingredients:
- items:
- gold_bar
quantity: 1
@@ -13,7 +13,6 @@ craft:
- amulet_mould
quantity: 1
level: 70
- skill: crafting
station:
- furnace
type: crafting
diff --git a/data/items/equipment/diamond_bracelet.yaml b/data/items/equipment/diamond_bracelet.yaml
index 13acc98..090f4f5 100644
--- a/data/items/equipment/diamond_bracelet.yaml
+++ b/data/items/equipment/diamond_bracelet.yaml
@@ -1,25 +1,26 @@
-color: "FF"
+color: FF
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - items:
- - diamond
- quantity: 1
- - byproducts:
- - bracelet_mould
- items:
- - bracelet_mould
- quantity: 1
- level: 58
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 95
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - diamond
+ quantity: 1
+ - byproducts:
+ - bracelet_mould
+ items:
+ - bracelet_mould
+ quantity: 1
+ level: 58
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 95
description: A bracelet set with a diamond. Can be enchanted.
-equip_slot: hands
+equipment:
+ slot: hands
+ type: armor
name: diamond bracelet
value: 1500
diff --git a/data/items/equipment/diamond_necklace.yaml b/data/items/equipment/diamond_necklace.yaml
index 19d485a..1c3510d 100644
--- a/data/items/equipment/diamond_necklace.yaml
+++ b/data/items/equipment/diamond_necklace.yaml
@@ -1,25 +1,26 @@
-color: "FF"
+color: FF
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - items:
- - diamond
- quantity: 1
- - byproducts:
- - necklace_mould
- items:
- - necklace_mould
- quantity: 1
- level: 56
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 90
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - diamond
+ quantity: 1
+ - byproducts:
+ - necklace_mould
+ items:
+ - necklace_mould
+ quantity: 1
+ level: 56
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 90
description: A necklace set with a diamond. Can be enchanted.
-equip_slot: neck
+equipment:
+ slot: neck
+ type: armor
name: diamond necklace
value: 1600
diff --git a/data/items/equipment/diamond_ring.yaml b/data/items/equipment/diamond_ring.yaml
index f8fc583..a8f8017 100644
--- a/data/items/equipment/diamond_ring.yaml
+++ b/data/items/equipment/diamond_ring.yaml
@@ -1,25 +1,26 @@
-color: "FF"
+color: FF
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - items:
- - diamond
- quantity: 1
- - byproducts:
- - ring_mould
- items:
- - ring_mould
- quantity: 1
- level: 43
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 85
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - diamond
+ quantity: 1
+ - byproducts:
+ - ring_mould
+ items:
+ - ring_mould
+ quantity: 1
+ level: 43
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 85
description: A ring set with a diamond. Can be enchanted.
-equip_slot: ring
+equipment:
+ slot: ring
+ type: armor
name: diamond ring
value: 1500
diff --git a/data/items/equipment/digsite_pendant.yaml b/data/items/equipment/digsite_pendant.yaml
index b8a2985..49942f1 100644
--- a/data/items/equipment/digsite_pendant.yaml
+++ b/data/items/equipment/digsite_pendant.yaml
@@ -1,5 +1,7 @@
+color: C4
+description: An enchanted ruby necklace that can teleport you to dig sites.
+equipment:
+ slot: neck
+ type: armor
name: digsite pendant
-color: "C4"
-description: "An enchanted ruby necklace that can teleport you to dig sites."
value: 2500
-equip_slot: neck
diff --git a/data/items/equipment/dragon_axe.yaml b/data/items/equipment/dragon_axe.yaml
index 127b343..81f7c48 100644
--- a/data/items/equipment/dragon_axe.yaml
+++ b/data/items/equipment/dragon_axe.yaml
@@ -1,18 +1,21 @@
+color: C4
+description: A powerful dragon axe.
+equipment:
+ attack:
+ crush: 32
+ slash: 43
+ stab: -2
+ attack_type: slash
+ other:
+ strength: 36
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: dragon axe
-color: "C4"
-description: "A powerful dragon axe."
-value: 50000
-stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
-stats:
- stab_attack: -2
- slash_attack: 43
- crush_attack: 32
- strength_bonus: 36
-speed: 5
-tool_type: axe
-tool_speed: 1
requirements:
accuracy: 60
+stackable: false
+tool:
+ speed: 1
+ type: axe
+value: 50000
diff --git a/data/items/equipment/emerald_amulet_u.yaml b/data/items/equipment/emerald_amulet_u.yaml
index 27421f2..0e0ac26 100644
--- a/data/items/equipment/emerald_amulet_u.yaml
+++ b/data/items/equipment/emerald_amulet_u.yaml
@@ -1,6 +1,6 @@
color: "53"
craft:
- consume:
+ ingredients:
- items:
- gold_bar
quantity: 1
@@ -13,7 +13,6 @@ craft:
- amulet_mould
quantity: 1
level: 31
- skill: crafting
station:
- furnace
type: crafting
diff --git a/data/items/equipment/emerald_bracelet.yaml b/data/items/equipment/emerald_bracelet.yaml
index 55ad471..990a7eb 100644
--- a/data/items/equipment/emerald_bracelet.yaml
+++ b/data/items/equipment/emerald_bracelet.yaml
@@ -1,25 +1,26 @@
color: "22"
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - items:
- - emerald
- quantity: 1
- - byproducts:
- - bracelet_mould
- items:
- - bracelet_mould
- quantity: 1
- level: 30
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 65
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - emerald
+ quantity: 1
+ - byproducts:
+ - bracelet_mould
+ items:
+ - bracelet_mould
+ quantity: 1
+ level: 30
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 65
description: A bracelet set with an emerald. Can be enchanted.
-equip_slot: hands
+equipment:
+ slot: hands
+ type: armor
name: emerald bracelet
value: 400
diff --git a/data/items/equipment/emerald_necklace.yaml b/data/items/equipment/emerald_necklace.yaml
index a0a8b95..99f1af2 100644
--- a/data/items/equipment/emerald_necklace.yaml
+++ b/data/items/equipment/emerald_necklace.yaml
@@ -1,25 +1,26 @@
color: "22"
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - items:
- - emerald
- quantity: 1
- - byproducts:
- - necklace_mould
- items:
- - necklace_mould
- quantity: 1
- level: 29
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 60
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - emerald
+ quantity: 1
+ - byproducts:
+ - necklace_mould
+ items:
+ - necklace_mould
+ quantity: 1
+ level: 29
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 60
description: A necklace set with an emerald. Can be enchanted.
-equip_slot: neck
+equipment:
+ slot: neck
+ type: armor
name: emerald necklace
value: 450
diff --git a/data/items/equipment/emerald_ring.yaml b/data/items/equipment/emerald_ring.yaml
index c51f9ea..401ac71 100644
--- a/data/items/equipment/emerald_ring.yaml
+++ b/data/items/equipment/emerald_ring.yaml
@@ -1,25 +1,26 @@
color: "22"
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - items:
- - emerald
- quantity: 1
- - byproducts:
- - ring_mould
- items:
- - ring_mould
- quantity: 1
- level: 27
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 55
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - emerald
+ quantity: 1
+ - byproducts:
+ - ring_mould
+ items:
+ - ring_mould
+ quantity: 1
+ level: 27
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 55
description: A ring set with an emerald. Can be enchanted.
-equip_slot: ring
+equipment:
+ slot: ring
+ type: armor
name: emerald ring
value: 400
diff --git a/data/items/equipment/gold_amulet_u.yaml b/data/items/equipment/gold_amulet_u.yaml
index 705042d..bc8041c 100644
--- a/data/items/equipment/gold_amulet_u.yaml
+++ b/data/items/equipment/gold_amulet_u.yaml
@@ -1,6 +1,6 @@
color: "DC"
craft:
- consume:
+ ingredients:
- items:
- gold_bar
quantity: 1
@@ -10,7 +10,6 @@ craft:
- amulet_mould
quantity: 1
level: 8
- skill: crafting
station:
- furnace
type: crafting
diff --git a/data/items/equipment/gold_bracelet.yaml b/data/items/equipment/gold_bracelet.yaml
index 874441a..56882c2 100644
--- a/data/items/equipment/gold_bracelet.yaml
+++ b/data/items/equipment/gold_bracelet.yaml
@@ -1,22 +1,23 @@
-color: "DC"
+color: DC
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - byproducts:
- - bracelet_mould
- items:
- - bracelet_mould
- quantity: 1
- level: 7
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 25
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - byproducts:
+ - bracelet_mould
+ items:
+ - bracelet_mould
+ quantity: 1
+ level: 7
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 25
description: A gold bracelet.
-equip_slot: hands
+equipment:
+ slot: hands
+ type: armor
name: gold bracelet
value: 65
diff --git a/data/items/equipment/gold_necklace.yaml b/data/items/equipment/gold_necklace.yaml
index 2366bd8..dc8d439 100644
--- a/data/items/equipment/gold_necklace.yaml
+++ b/data/items/equipment/gold_necklace.yaml
@@ -1,22 +1,23 @@
-color: "DC"
+color: DC
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - byproducts:
- - necklace_mould
- items:
- - necklace_mould
- quantity: 1
- level: 6
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 20
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - byproducts:
+ - necklace_mould
+ items:
+ - necklace_mould
+ quantity: 1
+ level: 6
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 20
description: A gold necklace.
-equip_slot: neck
+equipment:
+ slot: neck
+ type: armor
name: gold necklace
value: 60
diff --git a/data/items/equipment/gold_ring.yaml b/data/items/equipment/gold_ring.yaml
index 8b6dc9d..f9106ce 100644
--- a/data/items/equipment/gold_ring.yaml
+++ b/data/items/equipment/gold_ring.yaml
@@ -1,22 +1,23 @@
-color: "DC"
+color: DC
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - byproducts:
- - ring_mould
- items:
- - ring_mould
- quantity: 1
- level: 5
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 15
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - byproducts:
+ - ring_mould
+ items:
+ - ring_mould
+ quantity: 1
+ level: 5
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 15
description: A simple gold ring.
-equip_slot: ring
+equipment:
+ slot: ring
+ type: armor
name: gold ring
value: 50
diff --git a/data/items/equipment/graceful_boots.yaml b/data/items/equipment/graceful_boots.yaml
index 52c0e9a..c300bc6 100644
--- a/data/items/equipment/graceful_boots.yaml
+++ b/data/items/equipment/graceful_boots.yaml
@@ -1,5 +1,7 @@
+color: E6
+description: Lightweight boots. Part of the graceful set.
+equipment:
+ slot: feet
+ type: armor
name: graceful boots
-color: "E6"
-description: "Lightweight boots. Part of the graceful set."
value: 80
-equip_slot: feet
diff --git a/data/items/equipment/graceful_cape.yaml b/data/items/equipment/graceful_cape.yaml
index 88ebd9d..433be48 100644
--- a/data/items/equipment/graceful_cape.yaml
+++ b/data/items/equipment/graceful_cape.yaml
@@ -1,5 +1,7 @@
+color: E6
+description: A lightweight cape. Part of the graceful set.
+equipment:
+ slot: back
+ type: armor
name: graceful cape
-color: "E6"
-description: "A lightweight cape. Part of the graceful set."
value: 100
-equip_slot: back
diff --git a/data/items/equipment/graceful_gloves.yaml b/data/items/equipment/graceful_gloves.yaml
index c55d41b..265b321 100644
--- a/data/items/equipment/graceful_gloves.yaml
+++ b/data/items/equipment/graceful_gloves.yaml
@@ -1,5 +1,7 @@
+color: E6
+description: Lightweight gloves. Part of the graceful set.
+equipment:
+ slot: hands
+ type: armor
name: graceful gloves
-color: "E6"
-description: "Lightweight gloves. Part of the graceful set."
value: 80
-equip_slot: hands
diff --git a/data/items/equipment/graceful_hat.yaml b/data/items/equipment/graceful_hat.yaml
index 3c7a211..a0a524a 100644
--- a/data/items/equipment/graceful_hat.yaml
+++ b/data/items/equipment/graceful_hat.yaml
@@ -1,5 +1,7 @@
+color: E6
+description: A lightweight hat. Part of the graceful set.
+equipment:
+ slot: head
+ type: armor
name: graceful hat
-color: "E6"
-description: "A lightweight hat. Part of the graceful set."
value: 100
-equip_slot: head
diff --git a/data/items/equipment/graceful_legs.yaml b/data/items/equipment/graceful_legs.yaml
index 5b7eb44..5142692 100644
--- a/data/items/equipment/graceful_legs.yaml
+++ b/data/items/equipment/graceful_legs.yaml
@@ -1,5 +1,7 @@
+color: E6
+description: Lightweight leg armour. Part of the graceful set.
+equipment:
+ slot: legs
+ type: armor
name: graceful legs
-color: "E6"
-description: "Lightweight leg armour. Part of the graceful set."
value: 150
-equip_slot: legs
diff --git a/data/items/equipment/graceful_torso.yaml b/data/items/equipment/graceful_torso.yaml
index 656918a..502573e 100644
--- a/data/items/equipment/graceful_torso.yaml
+++ b/data/items/equipment/graceful_torso.yaml
@@ -1,5 +1,7 @@
+color: E6
+description: A lightweight top. Part of the graceful set.
+equipment:
+ slot: torso
+ type: armor
name: graceful torso
-color: "E6"
-description: "A lightweight top. Part of the graceful set."
value: 150
-equip_slot: torso
diff --git a/data/items/equipment/hard_leather_body.yaml b/data/items/equipment/hard_leather_body.yaml
index 8951f1d..f621852 100644
--- a/data/items/equipment/hard_leather_body.yaml
+++ b/data/items/equipment/hard_leather_body.yaml
@@ -1,27 +1,28 @@
-color: "5E"
+color: 5E
craft:
- consume:
- - items:
- - hard_leather
- quantity: 1
- - items:
- - thread
- quantity: 1
- level: 28
- skill: crafting
- tool: needle
- type: crafting
- wait: 4
- xp: 35
+ ingredients:
+ - items:
+ - hard_leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 28
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 35
description: A hardened leather body armour.
-equip_slot: torso
+equipment:
+ defense:
+ crush: 8
+ ranged: 14
+ science: 6
+ slash: 12
+ stab: 8
+ slot: torso
+ type: armor
name: hard leather body
requirements:
- defense: 10
-stats:
- crush_defense: 8
- ranged_defense: 14
- science_defense: 6
- slash_defense: 12
- stab_defense: 8
+ defense: 10
value: 60
diff --git a/data/items/equipment/hard_leather_shield.yaml b/data/items/equipment/hard_leather_shield.yaml
index 7ea33cb..5d4a0d8 100644
--- a/data/items/equipment/hard_leather_shield.yaml
+++ b/data/items/equipment/hard_leather_shield.yaml
@@ -1,27 +1,28 @@
-color: "5E"
+color: 5E
craft:
- consume:
- - items:
- - hard_leather
- quantity: 1
- - items:
- - thread
- quantity: 1
- level: 41
- skill: crafting
- tool: needle
- type: crafting
- wait: 4
- xp: 40
+ ingredients:
+ - items:
+ - hard_leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 41
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 40
description: A shield made of hardened leather.
-equip_slot: off_hand
+equipment:
+ defense:
+ crush: 5
+ ranged: 8
+ science: 4
+ slash: 7
+ stab: 5
+ slot: off_hand
+ type: armor
name: hard leather shield
requirements:
- defense: 10
-stats:
- crush_defense: 5
- ranged_defense: 8
- science_defense: 4
- slash_defense: 7
- stab_defense: 5
+ defense: 10
value: 55
diff --git a/data/items/equipment/inoculation_bracelet.yaml b/data/items/equipment/inoculation_bracelet.yaml
index ce55652..2029a71 100644
--- a/data/items/equipment/inoculation_bracelet.yaml
+++ b/data/items/equipment/inoculation_bracelet.yaml
@@ -1,5 +1,7 @@
+color: C4
+description: An enchanted ruby bracelet that provides resistance to poison.
+equipment:
+ slot: hands
+ type: armor
name: inoculation bracelet
-color: "C4"
-description: "An enchanted ruby bracelet that provides resistance to poison."
value: 2500
-equip_slot: hands
diff --git a/data/items/equipment/insulated_gloves.yaml b/data/items/equipment/insulated_gloves.yaml
index 3c50755..08740ce 100644
--- a/data/items/equipment/insulated_gloves.yaml
+++ b/data/items/equipment/insulated_gloves.yaml
@@ -1,8 +1,8 @@
+color: D6
+description: Heavy rubber gloves that protect against electrical attacks. Essential when fighting drones.
+equipment:
+ slot: hands
+ type: armor
name: insulated gloves
-color: "D6"
-description: "Heavy rubber gloves that protect against electrical attacks. Essential when fighting drones."
-value: 50
stackable: false
-equip_slot: hands
-stats:
- defense_bonus: 1
+value: 50
diff --git a/data/items/equipment/iron_dagger.yaml b/data/items/equipment/iron_dagger.yaml
index 80c0382..024e4e1 100644
--- a/data/items/equipment/iron_dagger.yaml
+++ b/data/items/equipment/iron_dagger.yaml
@@ -1,26 +1,27 @@
-attack_type: stab
-color: "FA"
+color: FA
craft:
- consume:
- - items:
- - iron_bar
- quantity: 1
- level: 20
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 25
+ ingredients:
+ - items:
+ - iron_bar
+ quantity: 1
+ level: 20
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 25
description: A small iron dagger.
-equip_slot: main_hand
+equipment:
+ attack:
+ crush: -1
+ slash: 5
+ stab: 8
+ attack_type: stab
+ other:
+ strength: 7
+ slot: main_hand
+ speed: 3
+ type: melee_weapon
name: iron dagger
-speed: 3
stackable: false
-stats:
- crush_attack: -1
- slash_attack: 5
- stab_attack: 8
- strength_bonus: 7
value: 20
-weapon_type: melee
diff --git a/data/items/equipment/iron_full_helm.yaml b/data/items/equipment/iron_full_helm.yaml
index 5e9e500..d828b95 100644
--- a/data/items/equipment/iron_full_helm.yaml
+++ b/data/items/equipment/iron_full_helm.yaml
@@ -1,24 +1,25 @@
-color: "FA"
+color: FA
craft:
- consume:
- - items:
- - iron_bar
- quantity: 2
- level: 27
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 50
+ ingredients:
+ - items:
+ - iron_bar
+ quantity: 2
+ level: 27
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 50
description: An iron full helmet.
-equip_slot: head
+equipment:
+ defense:
+ crush: 6
+ ranged: 8
+ science: -3
+ slash: 9
+ stab: 8
+ slot: head
+ type: armor
name: iron full helm
stackable: false
-stats:
- crush_defense: 6
- ranged_defense: 8
- science_defense: -3
- slash_defense: 9
- stab_defense: 8
value: 50
diff --git a/data/items/equipment/iron_med_helm.yaml b/data/items/equipment/iron_med_helm.yaml
index 6fea4b8..250327d 100644
--- a/data/items/equipment/iron_med_helm.yaml
+++ b/data/items/equipment/iron_med_helm.yaml
@@ -1,24 +1,25 @@
-color: "FA"
+color: FA
craft:
- consume:
- - items:
- - iron_bar
- quantity: 1
- level: 22
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 25
+ ingredients:
+ - items:
+ - iron_bar
+ quantity: 1
+ level: 22
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 25
description: An iron medium helmet.
-equip_slot: head
+equipment:
+ defense:
+ crush: 3
+ ranged: 5
+ science: -1
+ slash: 6
+ stab: 5
+ slot: head
+ type: armor
name: iron med helm
stackable: false
-stats:
- crush_defense: 3
- ranged_defense: 5
- science_defense: -1
- slash_defense: 6
- stab_defense: 5
value: 20
diff --git a/data/items/equipment/iron_platebody.yaml b/data/items/equipment/iron_platebody.yaml
index e7bfa17..73c40f1 100644
--- a/data/items/equipment/iron_platebody.yaml
+++ b/data/items/equipment/iron_platebody.yaml
@@ -1,24 +1,25 @@
-color: "FA"
+color: FA
craft:
- consume:
- - items:
- - iron_bar
- quantity: 5
- level: 33
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 125
+ ingredients:
+ - items:
+ - iron_bar
+ quantity: 5
+ level: 33
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 125
description: An iron platebody.
-equip_slot: torso
+equipment:
+ defense:
+ crush: 14
+ ranged: 18
+ science: -10
+ slash: 22
+ stab: 18
+ slot: torso
+ type: armor
name: iron platebody
stackable: false
-stats:
- crush_defense: 14
- ranged_defense: 18
- science_defense: -10
- slash_defense: 22
- stab_defense: 18
value: 150
diff --git a/data/items/equipment/iron_sword.yaml b/data/items/equipment/iron_sword.yaml
index cdc6017..392fe53 100644
--- a/data/items/equipment/iron_sword.yaml
+++ b/data/items/equipment/iron_sword.yaml
@@ -1,26 +1,27 @@
-attack_type: slash
-color: "FA"
+color: FA
craft:
- consume:
- - items:
- - iron_bar
- quantity: 1
- level: 24
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 25
+ ingredients:
+ - items:
+ - iron_bar
+ quantity: 1
+ level: 24
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 25
description: An iron sword.
-equip_slot: main_hand
+equipment:
+ attack:
+ crush: -2
+ slash: 15
+ stab: 7
+ attack_type: slash
+ other:
+ strength: 10
+ slot: main_hand
+ speed: 4
+ type: melee_weapon
name: iron sword
-speed: 4
stackable: false
-stats:
- crush_attack: -2
- slash_attack: 15
- stab_attack: 7
- strength_bonus: 10
value: 30
-weapon_type: melee
diff --git a/data/items/equipment/leather_body.yaml b/data/items/equipment/leather_body.yaml
index e201f10..6611ce2 100644
--- a/data/items/equipment/leather_body.yaml
+++ b/data/items/equipment/leather_body.yaml
@@ -1,25 +1,26 @@
color: "82"
craft:
- consume:
- - items:
- - leather
- quantity: 1
- - items:
- - thread
- quantity: 1
- level: 14
- skill: crafting
- tool: needle
- type: crafting
- wait: 4
- xp: 25
+ ingredients:
+ - items:
+ - leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 14
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 25
description: A leather body armour.
-equip_slot: torso
+equipment:
+ defense:
+ crush: 5
+ ranged: 10
+ science: 4
+ slash: 7
+ stab: 4
+ slot: torso
+ type: armor
name: leather body
-stats:
- crush_defense: 5
- ranged_defense: 10
- science_defense: 4
- slash_defense: 7
- stab_defense: 4
value: 25
diff --git a/data/items/equipment/leather_boots.yaml b/data/items/equipment/leather_boots.yaml
index a917637..2d8d280 100644
--- a/data/items/equipment/leather_boots.yaml
+++ b/data/items/equipment/leather_boots.yaml
@@ -1,25 +1,26 @@
color: "82"
craft:
- consume:
- - items:
- - leather
- quantity: 1
- - items:
- - thread
- quantity: 1
- level: 7
- skill: crafting
- tool: needle
- type: crafting
- wait: 4
- xp: 16
+ ingredients:
+ - items:
+ - leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 7
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 16
description: A pair of leather boots.
-equip_slot: feet
+equipment:
+ defense:
+ crush: 1
+ ranged: 2
+ science: 1
+ slash: 1
+ stab: 1
+ slot: feet
+ type: armor
name: leather boots
-stats:
- crush_defense: 1
- ranged_defense: 2
- science_defense: 1
- slash_defense: 1
- stab_defense: 1
value: 12
diff --git a/data/items/equipment/leather_chaps.yaml b/data/items/equipment/leather_chaps.yaml
index 8917732..8246c8a 100644
--- a/data/items/equipment/leather_chaps.yaml
+++ b/data/items/equipment/leather_chaps.yaml
@@ -1,25 +1,26 @@
color: "82"
craft:
- consume:
- - items:
- - leather
- quantity: 1
- - items:
- - thread
- quantity: 1
- level: 18
- skill: crafting
- tool: needle
- type: crafting
- wait: 4
- xp: 27
+ ingredients:
+ - items:
+ - leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 18
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 27
description: A pair of leather chaps.
-equip_slot: legs
+equipment:
+ defense:
+ crush: 3
+ ranged: 6
+ science: 3
+ slash: 4
+ stab: 2
+ slot: legs
+ type: armor
name: leather chaps
-stats:
- crush_defense: 3
- ranged_defense: 6
- science_defense: 3
- slash_defense: 4
- stab_defense: 2
value: 20
diff --git a/data/items/equipment/leather_cowl.yaml b/data/items/equipment/leather_cowl.yaml
index b48ba23..c918ef0 100644
--- a/data/items/equipment/leather_cowl.yaml
+++ b/data/items/equipment/leather_cowl.yaml
@@ -1,25 +1,26 @@
color: "82"
craft:
- consume:
- - items:
- - leather
- quantity: 1
- - items:
- - thread
- quantity: 1
- level: 9
- skill: crafting
- tool: needle
- type: crafting
- wait: 4
- xp: 19
+ ingredients:
+ - items:
+ - leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 9
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 19
description: A leather cowl.
-equip_slot: head
+equipment:
+ defense:
+ crush: 1
+ ranged: 4
+ science: 2
+ slash: 2
+ stab: 1
+ slot: head
+ type: armor
name: leather cowl
-stats:
- crush_defense: 1
- ranged_defense: 4
- science_defense: 2
- slash_defense: 2
- stab_defense: 1
value: 15
diff --git a/data/items/equipment/leather_gloves.yaml b/data/items/equipment/leather_gloves.yaml
index 04efee9..65a0168 100644
--- a/data/items/equipment/leather_gloves.yaml
+++ b/data/items/equipment/leather_gloves.yaml
@@ -1,25 +1,26 @@
color: "82"
craft:
- consume:
- - items:
- - leather
- quantity: 1
- - items:
- - thread
- quantity: 1
- level: 1
- skill: crafting
- tool: needle
- type: crafting
- wait: 4
- xp: 14
+ ingredients:
+ - items:
+ - leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 1
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 14
description: A pair of leather gloves.
-equip_slot: hands
+equipment:
+ defense:
+ crush: 1
+ ranged: 2
+ science: 1
+ slash: 1
+ stab: 1
+ slot: hands
+ type: armor
name: leather gloves
-stats:
- crush_defense: 1
- ranged_defense: 2
- science_defense: 1
- slash_defense: 1
- stab_defense: 1
value: 10
diff --git a/data/items/equipment/leather_vambraces.yaml b/data/items/equipment/leather_vambraces.yaml
index 7b8022d..26b0dea 100644
--- a/data/items/equipment/leather_vambraces.yaml
+++ b/data/items/equipment/leather_vambraces.yaml
@@ -1,26 +1,28 @@
color: "82"
craft:
- consume:
- - items:
- - leather
- quantity: 1
- - items:
- - thread
- quantity: 1
- level: 11
- skill: crafting
- tool: needle
- type: crafting
- wait: 4
- xp: 22
+ ingredients:
+ - items:
+ - leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 11
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 22
description: A pair of leather vambraces.
-equip_slot: hands
+equipment:
+ attack:
+ ranged: 4
+ defense:
+ crush: 2
+ ranged: 4
+ science: 2
+ slash: 2
+ stab: 2
+ slot: hands
+ type: armor
name: leather vambraces
-stats:
- crush_defense: 2
- ranged_attack: 4
- ranged_defense: 4
- science_defense: 2
- slash_defense: 2
- stab_defense: 2
value: 18
diff --git a/data/items/equipment/longbow.yaml b/data/items/equipment/longbow.yaml
index 8c2aac5..5529c1c 100644
--- a/data/items/equipment/longbow.yaml
+++ b/data/items/equipment/longbow.yaml
@@ -1,23 +1,23 @@
-attack_type: ranged
color: "89"
craft:
- consume:
- - items:
- - longbow_u
- quantity: 1
- - items:
- - bowstring
- quantity: 1
- level: 10
- skill: fletching
- type: fletching
- wait: 2
- xp: 10
+ ingredients:
+ - items:
+ - longbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 10
+ type: fletching
+ wait: 2
+ xp: 10
description: A longbow.
-equip_slot: main_hand
+equipment:
+ attack:
+ ranged: 8
+ attack_type: ranged
+ slot: main_hand
+ speed: 6
+ type: ranged_weapon
name: longbow
-speed: 6
-stats:
- ranged_attack: 8
value: 20
-weapon_type: ranged
diff --git a/data/items/equipment/longbow_u.yaml b/data/items/equipment/longbow_u.yaml
index b6f2c44..9597919 100644
--- a/data/items/equipment/longbow_u.yaml
+++ b/data/items/equipment/longbow_u.yaml
@@ -1,11 +1,10 @@
color: "89"
craft:
- consume:
+ ingredients:
- items:
- logs
quantity: 1
level: 10
- skill: fletching
tool: knife
type: fletching
wait: 3
diff --git a/data/items/equipment/magic_longbow.yaml b/data/items/equipment/magic_longbow.yaml
index becbf99..fd39e94 100644
--- a/data/items/equipment/magic_longbow.yaml
+++ b/data/items/equipment/magic_longbow.yaml
@@ -1,25 +1,25 @@
-attack_type: ranged
-color: "3F"
+color: 3F
craft:
- consume:
- - items:
- - magic_longbow_u
- quantity: 1
- - items:
- - bowstring
- quantity: 1
- level: 85
- skill: fletching
- type: fletching
- wait: 2
- xp: 91
+ ingredients:
+ - items:
+ - magic_longbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 85
+ type: fletching
+ wait: 2
+ xp: 91
description: A magic longbow.
-equip_slot: main_hand
+equipment:
+ attack:
+ ranged: 69
+ attack_type: ranged
+ slot: main_hand
+ speed: 6
+ type: ranged_weapon
name: magic longbow
requirements:
- ranged: 50
-speed: 6
-stats:
- ranged_attack: 69
+ ranged: 50
value: 6000
-weapon_type: ranged
diff --git a/data/items/equipment/magic_longbow_u.yaml b/data/items/equipment/magic_longbow_u.yaml
index ac81260..fae49b7 100644
--- a/data/items/equipment/magic_longbow_u.yaml
+++ b/data/items/equipment/magic_longbow_u.yaml
@@ -1,11 +1,10 @@
color: "3F"
craft:
- consume:
+ ingredients:
- items:
- magic_logs
quantity: 1
level: 85
- skill: fletching
tool: knife
type: fletching
wait: 3
diff --git a/data/items/equipment/magic_shortbow.yaml b/data/items/equipment/magic_shortbow.yaml
index 210dffe..424a504 100644
--- a/data/items/equipment/magic_shortbow.yaml
+++ b/data/items/equipment/magic_shortbow.yaml
@@ -1,25 +1,25 @@
-attack_type: ranged
-color: "3F"
+color: 3F
craft:
- consume:
- - items:
- - magic_shortbow_u
- quantity: 1
- - items:
- - bowstring
- quantity: 1
- level: 80
- skill: fletching
- type: fletching
- wait: 2
- xp: 83
+ ingredients:
+ - items:
+ - magic_shortbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 80
+ type: fletching
+ wait: 2
+ xp: 83
description: A magic shortbow.
-equip_slot: main_hand
+equipment:
+ attack:
+ ranged: 69
+ attack_type: ranged
+ slot: main_hand
+ speed: 4
+ type: ranged_weapon
name: magic shortbow
requirements:
- ranged: 50
-speed: 4
-stats:
- ranged_attack: 69
+ ranged: 50
value: 4000
-weapon_type: ranged
diff --git a/data/items/equipment/magic_shortbow_u.yaml b/data/items/equipment/magic_shortbow_u.yaml
index aa9e455..4ba09d6 100644
--- a/data/items/equipment/magic_shortbow_u.yaml
+++ b/data/items/equipment/magic_shortbow_u.yaml
@@ -1,11 +1,10 @@
color: "3F"
craft:
- consume:
+ ingredients:
- items:
- magic_logs
quantity: 1
level: 80
- skill: fletching
tool: knife
type: fletching
wait: 3
diff --git a/data/items/equipment/maple_longbow.yaml b/data/items/equipment/maple_longbow.yaml
index 66ee5a0..994a60e 100644
--- a/data/items/equipment/maple_longbow.yaml
+++ b/data/items/equipment/maple_longbow.yaml
@@ -1,25 +1,25 @@
-attack_type: ranged
-color: "AC"
+color: AC
craft:
- consume:
- - items:
- - maple_longbow_u
- quantity: 1
- - items:
- - bowstring
- quantity: 1
- level: 55
- skill: fletching
- type: fletching
- wait: 2
- xp: 58
+ ingredients:
+ - items:
+ - maple_longbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 55
+ type: fletching
+ wait: 2
+ xp: 58
description: A maple longbow.
-equip_slot: main_hand
+equipment:
+ attack:
+ ranged: 29
+ attack_type: ranged
+ slot: main_hand
+ speed: 6
+ type: ranged_weapon
name: maple longbow
requirements:
- ranged: 30
-speed: 6
-stats:
- ranged_attack: 29
+ ranged: 30
value: 1280
-weapon_type: ranged
diff --git a/data/items/equipment/maple_longbow_u.yaml b/data/items/equipment/maple_longbow_u.yaml
index b26bae6..85d558a 100644
--- a/data/items/equipment/maple_longbow_u.yaml
+++ b/data/items/equipment/maple_longbow_u.yaml
@@ -1,11 +1,10 @@
color: "AC"
craft:
- consume:
+ ingredients:
- items:
- maple_logs
quantity: 1
level: 55
- skill: fletching
tool: knife
type: fletching
wait: 3
diff --git a/data/items/equipment/maple_shortbow.yaml b/data/items/equipment/maple_shortbow.yaml
index 385cbd0..cd3a02c 100644
--- a/data/items/equipment/maple_shortbow.yaml
+++ b/data/items/equipment/maple_shortbow.yaml
@@ -1,25 +1,25 @@
-attack_type: ranged
-color: "AC"
+color: AC
craft:
- consume:
- - items:
- - maple_shortbow_u
- quantity: 1
- - items:
- - bowstring
- quantity: 1
- level: 50
- skill: fletching
- type: fletching
- wait: 2
- xp: 50
+ ingredients:
+ - items:
+ - maple_shortbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 50
+ type: fletching
+ wait: 2
+ xp: 50
description: A maple shortbow.
-equip_slot: main_hand
+equipment:
+ attack:
+ ranged: 29
+ attack_type: ranged
+ slot: main_hand
+ speed: 4
+ type: ranged_weapon
name: maple shortbow
requirements:
- ranged: 30
-speed: 4
-stats:
- ranged_attack: 29
+ ranged: 30
value: 640
-weapon_type: ranged
diff --git a/data/items/equipment/maple_shortbow_u.yaml b/data/items/equipment/maple_shortbow_u.yaml
index 5c58cbf..93e7d6c 100644
--- a/data/items/equipment/maple_shortbow_u.yaml
+++ b/data/items/equipment/maple_shortbow_u.yaml
@@ -1,11 +1,10 @@
color: "AC"
craft:
- consume:
+ ingredients:
- items:
- maple_logs
quantity: 1
level: 50
- skill: fletching
tool: knife
type: fletching
wait: 3
diff --git a/data/items/equipment/mithril_dagger.yaml b/data/items/equipment/mithril_dagger.yaml
index 3b258cf..0f717e7 100644
--- a/data/items/equipment/mithril_dagger.yaml
+++ b/data/items/equipment/mithril_dagger.yaml
@@ -1,28 +1,29 @@
-attack_type: stab
-color: "4B"
+color: 4B
craft:
- consume:
- - items:
- - mithril_bar
- quantity: 1
- level: 50
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 50
+ ingredients:
+ - items:
+ - mithril_bar
+ quantity: 1
+ level: 50
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 50
description: A small mithril dagger.
-equip_slot: main_hand
+equipment:
+ attack:
+ crush: -1
+ slash: 10
+ stab: 17
+ attack_type: stab
+ other:
+ strength: 14
+ slot: main_hand
+ speed: 3
+ type: melee_weapon
name: mithril dagger
requirements:
- accuracy: 20
-speed: 3
+ accuracy: 20
stackable: false
-stats:
- crush_attack: -1
- slash_attack: 10
- stab_attack: 17
- strength_bonus: 14
value: 150
-weapon_type: melee
diff --git a/data/items/equipment/mithril_full_helm.yaml b/data/items/equipment/mithril_full_helm.yaml
index 9b0787b..6d40856 100644
--- a/data/items/equipment/mithril_full_helm.yaml
+++ b/data/items/equipment/mithril_full_helm.yaml
@@ -1,26 +1,27 @@
-color: "4B"
+color: 4B
craft:
- consume:
- - items:
- - mithril_bar
- quantity: 2
- level: 57
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 100
+ ingredients:
+ - items:
+ - mithril_bar
+ quantity: 2
+ level: 57
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 100
description: A mithril full helmet.
-equip_slot: head
+equipment:
+ defense:
+ crush: 12
+ ranged: 16
+ science: -6
+ slash: 18
+ stab: 16
+ slot: head
+ type: armor
name: mithril full helm
requirements:
- defense: 20
+ defense: 20
stackable: false
-stats:
- crush_defense: 12
- ranged_defense: 16
- science_defense: -6
- slash_defense: 18
- stab_defense: 16
value: 400
diff --git a/data/items/equipment/mithril_med_helm.yaml b/data/items/equipment/mithril_med_helm.yaml
index 0c5c936..8bf7ac1 100644
--- a/data/items/equipment/mithril_med_helm.yaml
+++ b/data/items/equipment/mithril_med_helm.yaml
@@ -1,26 +1,27 @@
-color: "4B"
+color: 4B
craft:
- consume:
- - items:
- - mithril_bar
- quantity: 1
- level: 53
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 50
+ ingredients:
+ - items:
+ - mithril_bar
+ quantity: 1
+ level: 53
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 50
description: A mithril medium helmet.
-equip_slot: head
+equipment:
+ defense:
+ crush: 7
+ ranged: 10
+ science: -3
+ slash: 12
+ stab: 10
+ slot: head
+ type: armor
name: mithril med helm
requirements:
- defense: 20
+ defense: 20
stackable: false
-stats:
- crush_defense: 7
- ranged_defense: 10
- science_defense: -3
- slash_defense: 12
- stab_defense: 10
value: 150
diff --git a/data/items/equipment/mithril_platebody.yaml b/data/items/equipment/mithril_platebody.yaml
index 4683a50..04ca44e 100644
--- a/data/items/equipment/mithril_platebody.yaml
+++ b/data/items/equipment/mithril_platebody.yaml
@@ -1,26 +1,27 @@
-color: "4B"
+color: 4B
craft:
- consume:
- - items:
- - mithril_bar
- quantity: 5
- level: 68
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 250
+ ingredients:
+ - items:
+ - mithril_bar
+ quantity: 5
+ level: 68
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 250
description: A mithril platebody.
-equip_slot: torso
+equipment:
+ defense:
+ crush: 30
+ ranged: 36
+ science: -20
+ slash: 42
+ stab: 36
+ slot: torso
+ type: armor
name: mithril platebody
requirements:
- defense: 20
+ defense: 20
stackable: false
-stats:
- crush_defense: 30
- ranged_defense: 36
- science_defense: -20
- slash_defense: 42
- stab_defense: 36
value: 1500
diff --git a/data/items/equipment/mithril_sword.yaml b/data/items/equipment/mithril_sword.yaml
index aee233a..fa87e56 100644
--- a/data/items/equipment/mithril_sword.yaml
+++ b/data/items/equipment/mithril_sword.yaml
@@ -1,28 +1,29 @@
-attack_type: slash
-color: "4B"
+color: 4B
craft:
- consume:
- - items:
- - mithril_bar
- quantity: 1
- level: 54
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 50
+ ingredients:
+ - items:
+ - mithril_bar
+ quantity: 1
+ level: 54
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 50
description: A mithril sword.
-equip_slot: main_hand
+equipment:
+ attack:
+ crush: -2
+ slash: 30
+ stab: 14
+ attack_type: slash
+ other:
+ strength: 20
+ slot: main_hand
+ speed: 4
+ type: melee_weapon
name: mithril sword
requirements:
- accuracy: 20
-speed: 4
+ accuracy: 20
stackable: false
-stats:
- crush_attack: -2
- slash_attack: 30
- stab_attack: 14
- strength_bonus: 20
value: 250
-weapon_type: melee
diff --git a/data/items/equipment/necklace_of_passage.yaml b/data/items/equipment/necklace_of_passage.yaml
index 6fefd6b..b6ebe71 100644
--- a/data/items/equipment/necklace_of_passage.yaml
+++ b/data/items/equipment/necklace_of_passage.yaml
@@ -1,5 +1,7 @@
-name: necklace of passage
color: "27"
-description: "An enchanted sapphire necklace that can teleport the wearer to various locations."
+description: An enchanted sapphire necklace that can teleport the wearer to various locations.
+equipment:
+ slot: neck
+ type: armor
+name: necklace of passage
value: 750
-equip_slot: neck
diff --git a/data/items/equipment/neural_headband.yaml b/data/items/equipment/neural_headband.yaml
index aede26e..14a1a34 100644
--- a/data/items/equipment/neural_headband.yaml
+++ b/data/items/equipment/neural_headband.yaml
@@ -1,10 +1,13 @@
-name: Neural Headband
-description: "A thin band of circuitry that amplifies neural signals, reducing battery drain."
color: "27"
+description: A thin band of circuitry that amplifies neural signals, reducing battery drain.
+equipment:
+ defense:
+ crush: 1
+ slash: 1
+ stab: 1
+ other:
+ technology: 4
+ slot: head
+ type: armor
+name: Neural Headband
value: 500
-equip_slot: head
-stats:
- stab_defense: 1
- slash_defense: 1
- crush_defense: 1
- technology_bonus: 4
diff --git a/data/items/equipment/oak_longbow.yaml b/data/items/equipment/oak_longbow.yaml
index bf2c259..45ef2fc 100644
--- a/data/items/equipment/oak_longbow.yaml
+++ b/data/items/equipment/oak_longbow.yaml
@@ -1,25 +1,25 @@
-attack_type: ranged
color: "88"
craft:
- consume:
- - items:
- - oak_longbow_u
- quantity: 1
- - items:
- - bowstring
- quantity: 1
- level: 25
- skill: fletching
- type: fletching
- wait: 2
- xp: 25
+ ingredients:
+ - items:
+ - oak_longbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 25
+ type: fletching
+ wait: 2
+ xp: 25
description: An oak longbow.
-equip_slot: main_hand
+equipment:
+ attack:
+ ranged: 14
+ attack_type: ranged
+ slot: main_hand
+ speed: 6
+ type: ranged_weapon
name: oak longbow
requirements:
- ranged: 5
-speed: 6
-stats:
- ranged_attack: 14
+ ranged: 5
value: 80
-weapon_type: ranged
diff --git a/data/items/equipment/oak_longbow_u.yaml b/data/items/equipment/oak_longbow_u.yaml
index 89e7aa7..9d3cf68 100644
--- a/data/items/equipment/oak_longbow_u.yaml
+++ b/data/items/equipment/oak_longbow_u.yaml
@@ -1,11 +1,10 @@
color: "88"
craft:
- consume:
+ ingredients:
- items:
- oak_logs
quantity: 1
level: 25
- skill: fletching
tool: knife
type: fletching
wait: 3
diff --git a/data/items/equipment/oak_shortbow.yaml b/data/items/equipment/oak_shortbow.yaml
index 879dead..767bb43 100644
--- a/data/items/equipment/oak_shortbow.yaml
+++ b/data/items/equipment/oak_shortbow.yaml
@@ -1,25 +1,25 @@
-attack_type: ranged
color: "88"
craft:
- consume:
- - items:
- - oak_shortbow_u
- quantity: 1
- - items:
- - bowstring
- quantity: 1
- level: 20
- skill: fletching
- type: fletching
- wait: 2
- xp: 16
+ ingredients:
+ - items:
+ - oak_shortbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 20
+ type: fletching
+ wait: 2
+ xp: 16
description: An oak shortbow.
-equip_slot: main_hand
+equipment:
+ attack:
+ ranged: 14
+ attack_type: ranged
+ slot: main_hand
+ speed: 4
+ type: ranged_weapon
name: oak shortbow
requirements:
- ranged: 5
-speed: 4
-stats:
- ranged_attack: 14
+ ranged: 5
value: 40
-weapon_type: ranged
diff --git a/data/items/equipment/oak_shortbow_u.yaml b/data/items/equipment/oak_shortbow_u.yaml
index 553b9bd..0b567c2 100644
--- a/data/items/equipment/oak_shortbow_u.yaml
+++ b/data/items/equipment/oak_shortbow_u.yaml
@@ -1,11 +1,10 @@
color: "88"
craft:
- consume:
+ ingredients:
- items:
- oak_logs
quantity: 1
level: 20
- skill: fletching
tool: knife
type: fletching
wait: 3
diff --git a/data/items/equipment/party_hat_blue.yaml b/data/items/equipment/party_hat_blue.yaml
index 87fe732..0d2f33e 100644
--- a/data/items/equipment/party_hat_blue.yaml
+++ b/data/items/equipment/party_hat_blue.yaml
@@ -1,6 +1,8 @@
-name: blue party hat
color: "45"
-description: "A festive blue party hat."
-value: 2
-equip_slot: head
+description: A festive blue party hat.
+equipment:
+ slot: head
+ type: armor
+name: blue party hat
stackable: false
+value: 2
diff --git a/data/items/equipment/party_hat_green.yaml b/data/items/equipment/party_hat_green.yaml
index 39efc53..e8b6349 100644
--- a/data/items/equipment/party_hat_green.yaml
+++ b/data/items/equipment/party_hat_green.yaml
@@ -1,6 +1,8 @@
-name: green party hat
color: "53"
-description: "A festive green party hat."
-value: 2
-equip_slot: head
+description: A festive green party hat.
+equipment:
+ slot: head
+ type: armor
+name: green party hat
stackable: false
+value: 2
diff --git a/data/items/equipment/party_hat_pink.yaml b/data/items/equipment/party_hat_pink.yaml
index 37473d5..da52fb7 100644
--- a/data/items/equipment/party_hat_pink.yaml
+++ b/data/items/equipment/party_hat_pink.yaml
@@ -1,6 +1,8 @@
+color: D5
+description: A festive pink party hat.
+equipment:
+ slot: head
+ type: armor
name: pink party hat
-color: "D5"
-description: "A festive pink party hat."
-value: 2
-equip_slot: head
stackable: false
+value: 2
diff --git a/data/items/equipment/party_hat_red.yaml b/data/items/equipment/party_hat_red.yaml
index a26b690..322c008 100644
--- a/data/items/equipment/party_hat_red.yaml
+++ b/data/items/equipment/party_hat_red.yaml
@@ -1,6 +1,8 @@
+color: C4
+description: A festive red party hat.
+equipment:
+ slot: head
+ type: armor
name: red party hat
-color: "C4"
-description: "A festive red party hat."
-value: 2
-equip_slot: head
stackable: false
+value: 2
diff --git a/data/items/equipment/party_hat_white.yaml b/data/items/equipment/party_hat_white.yaml
index f1e748c..138a1ab 100644
--- a/data/items/equipment/party_hat_white.yaml
+++ b/data/items/equipment/party_hat_white.yaml
@@ -1,6 +1,8 @@
+color: FF
+description: A festive white party hat.
+equipment:
+ slot: head
+ type: armor
name: white party hat
-color: "FF"
-description: "A festive white party hat."
-value: 2
-equip_slot: head
stackable: false
+value: 2
diff --git a/data/items/equipment/party_hat_yellow.yaml b/data/items/equipment/party_hat_yellow.yaml
index eda601b..3ec9303 100644
--- a/data/items/equipment/party_hat_yellow.yaml
+++ b/data/items/equipment/party_hat_yellow.yaml
@@ -1,6 +1,8 @@
+color: E2
+description: A festive yellow party hat.
+equipment:
+ slot: head
+ type: armor
name: yellow party hat
-color: "E2"
-description: "A festive yellow party hat."
-value: 2
-equip_slot: head
stackable: false
+value: 2
diff --git a/data/items/equipment/phoenix_necklace.yaml b/data/items/equipment/phoenix_necklace.yaml
index 8db7ed8..c163b1f 100644
--- a/data/items/equipment/phoenix_necklace.yaml
+++ b/data/items/equipment/phoenix_necklace.yaml
@@ -1,5 +1,7 @@
+color: FF
+description: An enchanted diamond necklace that restores HP when you drop below 20 percent health.
+equipment:
+ slot: neck
+ type: armor
name: phoenix necklace
-color: "FF"
-description: "An enchanted diamond necklace that restores HP when you drop below 20 percent health."
value: 5500
-equip_slot: neck
diff --git a/data/items/equipment/power_amulet.yaml b/data/items/equipment/power_amulet.yaml
index 6039d03..c32063c 100644
--- a/data/items/equipment/power_amulet.yaml
+++ b/data/items/equipment/power_amulet.yaml
@@ -1,7 +1,9 @@
+color: DC
+description: A pendant containing a miniature power cell that augments tech efficiency.
+equipment:
+ other:
+ technology: 3
+ slot: neck
+ type: armor
name: Power Amulet
-description: "A pendant containing a miniature power cell that augments tech efficiency."
-color: "DC"
value: 800
-equip_slot: neck
-stats:
- technology_bonus: 3
diff --git a/data/items/equipment/ring_of_dueling.yaml b/data/items/equipment/ring_of_dueling.yaml
index 7b2f5a1..206d088 100644
--- a/data/items/equipment/ring_of_dueling.yaml
+++ b/data/items/equipment/ring_of_dueling.yaml
@@ -1,5 +1,7 @@
-name: ring of dueling
color: "22"
-description: "An enchanted emerald ring used for teleporting to dueling arenas."
+description: An enchanted emerald ring used for teleporting to dueling arenas.
+equipment:
+ slot: ring
+ type: armor
+name: ring of dueling
value: 1000
-equip_slot: ring
diff --git a/data/items/equipment/ring_of_forging.yaml b/data/items/equipment/ring_of_forging.yaml
index 0b76249..41770db 100644
--- a/data/items/equipment/ring_of_forging.yaml
+++ b/data/items/equipment/ring_of_forging.yaml
@@ -1,5 +1,7 @@
+color: C4
+description: An enchanted ruby ring that prevents ore from failing to smelt.
+equipment:
+ slot: ring
+ type: armor
name: ring of forging
-color: "C4"
-description: "An enchanted ruby ring that prevents ore from failing to smelt."
value: 2000
-equip_slot: ring
diff --git a/data/items/equipment/ring_of_life.yaml b/data/items/equipment/ring_of_life.yaml
index fa831e4..5304578 100644
--- a/data/items/equipment/ring_of_life.yaml
+++ b/data/items/equipment/ring_of_life.yaml
@@ -1,5 +1,7 @@
+color: FF
+description: An enchanted diamond ring that teleports you to safety when your HP drops critically low.
+equipment:
+ slot: ring
+ type: armor
name: ring of life
-color: "FF"
-description: "An enchanted diamond ring that teleports you to safety when your HP drops critically low."
value: 5000
-equip_slot: ring
diff --git a/data/items/equipment/ring_of_recoil.yaml b/data/items/equipment/ring_of_recoil.yaml
index 337d4cd..b7d08ec 100644
--- a/data/items/equipment/ring_of_recoil.yaml
+++ b/data/items/equipment/ring_of_recoil.yaml
@@ -1,5 +1,7 @@
-name: ring of recoil
color: "27"
-description: "An enchanted sapphire ring that reflects a portion of melee damage back to the attacker."
+description: An enchanted sapphire ring that reflects a portion of melee damage back to the attacker.
+equipment:
+ slot: ring
+ type: armor
+name: ring of recoil
value: 500
-equip_slot: ring
diff --git a/data/items/equipment/ruby_amulet_u.yaml b/data/items/equipment/ruby_amulet_u.yaml
index 79cb716..261d2e9 100644
--- a/data/items/equipment/ruby_amulet_u.yaml
+++ b/data/items/equipment/ruby_amulet_u.yaml
@@ -1,6 +1,6 @@
color: "C4"
craft:
- consume:
+ ingredients:
- items:
- gold_bar
quantity: 1
@@ -13,7 +13,6 @@ craft:
- amulet_mould
quantity: 1
level: 50
- skill: crafting
station:
- furnace
type: crafting
diff --git a/data/items/equipment/ruby_bracelet.yaml b/data/items/equipment/ruby_bracelet.yaml
index 5964aa6..cc7a99a 100644
--- a/data/items/equipment/ruby_bracelet.yaml
+++ b/data/items/equipment/ruby_bracelet.yaml
@@ -1,25 +1,26 @@
-color: "C4"
+color: C4
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - items:
- - ruby
- quantity: 1
- - byproducts:
- - bracelet_mould
- items:
- - bracelet_mould
- quantity: 1
- level: 42
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 80
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - ruby
+ quantity: 1
+ - byproducts:
+ - bracelet_mould
+ items:
+ - bracelet_mould
+ quantity: 1
+ level: 42
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 80
description: A bracelet set with a ruby. Can be enchanted.
-equip_slot: hands
+equipment:
+ slot: hands
+ type: armor
name: ruby bracelet
value: 800
diff --git a/data/items/equipment/ruby_necklace.yaml b/data/items/equipment/ruby_necklace.yaml
index ec7250e..b4e6cfb 100644
--- a/data/items/equipment/ruby_necklace.yaml
+++ b/data/items/equipment/ruby_necklace.yaml
@@ -1,25 +1,26 @@
-color: "C4"
+color: C4
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - items:
- - ruby
- quantity: 1
- - byproducts:
- - necklace_mould
- items:
- - necklace_mould
- quantity: 1
- level: 40
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 75
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - ruby
+ quantity: 1
+ - byproducts:
+ - necklace_mould
+ items:
+ - necklace_mould
+ quantity: 1
+ level: 40
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 75
description: A necklace set with a ruby. Can be enchanted.
-equip_slot: neck
+equipment:
+ slot: neck
+ type: armor
name: ruby necklace
value: 850
diff --git a/data/items/equipment/ruby_ring.yaml b/data/items/equipment/ruby_ring.yaml
index a7159c8..812a969 100644
--- a/data/items/equipment/ruby_ring.yaml
+++ b/data/items/equipment/ruby_ring.yaml
@@ -1,25 +1,26 @@
-color: "C4"
+color: C4
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - items:
- - ruby
- quantity: 1
- - byproducts:
- - ring_mould
- items:
- - ring_mould
- quantity: 1
- level: 63
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 70
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - ruby
+ quantity: 1
+ - byproducts:
+ - ring_mould
+ items:
+ - ring_mould
+ quantity: 1
+ level: 63
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 70
description: A ring set with a ruby. Can be enchanted.
-equip_slot: ring
+equipment:
+ slot: ring
+ type: armor
name: ruby ring
value: 800
diff --git a/data/items/equipment/rune_dagger.yaml b/data/items/equipment/rune_dagger.yaml
index d96e2d2..005fdc2 100644
--- a/data/items/equipment/rune_dagger.yaml
+++ b/data/items/equipment/rune_dagger.yaml
@@ -1,28 +1,29 @@
-attack_type: stab
color: "57"
craft:
- consume:
- - items:
- - runite_bar
- quantity: 1
- level: 85
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 75
+ ingredients:
+ - items:
+ - runite_bar
+ quantity: 1
+ level: 85
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 75
description: A small rune dagger.
-equip_slot: main_hand
+equipment:
+ attack:
+ crush: -1
+ slash: 18
+ stab: 30
+ attack_type: stab
+ other:
+ strength: 24
+ slot: main_hand
+ speed: 3
+ type: melee_weapon
name: rune dagger
requirements:
- accuracy: 40
-speed: 3
+ accuracy: 40
stackable: false
-stats:
- crush_attack: -1
- slash_attack: 18
- stab_attack: 30
- strength_bonus: 24
value: 5000
-weapon_type: melee
diff --git a/data/items/equipment/rune_full_helm.yaml b/data/items/equipment/rune_full_helm.yaml
index e25f22e..92fe0d5 100644
--- a/data/items/equipment/rune_full_helm.yaml
+++ b/data/items/equipment/rune_full_helm.yaml
@@ -1,26 +1,27 @@
color: "57"
craft:
- consume:
- - items:
- - runite_bar
- quantity: 2
- level: 92
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 150
+ ingredients:
+ - items:
+ - runite_bar
+ quantity: 2
+ level: 92
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 150
description: A rune full helmet.
-equip_slot: head
+equipment:
+ defense:
+ crush: 22
+ ranged: 30
+ science: -11
+ slash: 33
+ stab: 30
+ slot: head
+ type: armor
name: rune full helm
requirements:
- defense: 40
+ defense: 40
stackable: false
-stats:
- crush_defense: 22
- ranged_defense: 30
- science_defense: -11
- slash_defense: 33
- stab_defense: 30
value: 12000
diff --git a/data/items/equipment/rune_med_helm.yaml b/data/items/equipment/rune_med_helm.yaml
index 0823947..24a8992 100644
--- a/data/items/equipment/rune_med_helm.yaml
+++ b/data/items/equipment/rune_med_helm.yaml
@@ -1,26 +1,27 @@
color: "57"
craft:
- consume:
- - items:
- - runite_bar
- quantity: 1
- level: 88
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 75
+ ingredients:
+ - items:
+ - runite_bar
+ quantity: 1
+ level: 88
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 75
description: A rune medium helmet.
-equip_slot: head
+equipment:
+ defense:
+ crush: 13
+ ranged: 19
+ science: -4
+ slash: 22
+ stab: 19
+ slot: head
+ type: armor
name: rune med helm
requirements:
- defense: 40
+ defense: 40
stackable: false
-stats:
- crush_defense: 13
- ranged_defense: 19
- science_defense: -4
- slash_defense: 22
- stab_defense: 19
value: 5000
diff --git a/data/items/equipment/rune_platebody.yaml b/data/items/equipment/rune_platebody.yaml
index 7702976..c5686d1 100644
--- a/data/items/equipment/rune_platebody.yaml
+++ b/data/items/equipment/rune_platebody.yaml
@@ -1,26 +1,27 @@
color: "57"
craft:
- consume:
- - items:
- - runite_bar
- quantity: 5
- level: 99
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 375
+ ingredients:
+ - items:
+ - runite_bar
+ quantity: 5
+ level: 99
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 375
description: A rune platebody.
-equip_slot: torso
+equipment:
+ defense:
+ crush: 52
+ ranged: 65
+ science: -30
+ slash: 72
+ stab: 65
+ slot: torso
+ type: armor
name: rune platebody
requirements:
- defense: 40
+ defense: 40
stackable: false
-stats:
- crush_defense: 52
- ranged_defense: 65
- science_defense: -30
- slash_defense: 72
- stab_defense: 65
value: 40000
diff --git a/data/items/equipment/rune_sword.yaml b/data/items/equipment/rune_sword.yaml
index f1c9f14..d0adcdb 100644
--- a/data/items/equipment/rune_sword.yaml
+++ b/data/items/equipment/rune_sword.yaml
@@ -1,28 +1,29 @@
-attack_type: slash
color: "57"
craft:
- consume:
- - items:
- - runite_bar
- quantity: 1
- level: 89
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 75
+ ingredients:
+ - items:
+ - runite_bar
+ quantity: 1
+ level: 89
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 75
description: A rune sword.
-equip_slot: main_hand
+equipment:
+ attack:
+ crush: -2
+ slash: 52
+ stab: 27
+ attack_type: slash
+ other:
+ strength: 36
+ slot: main_hand
+ speed: 4
+ type: melee_weapon
name: rune sword
requirements:
- accuracy: 40
-speed: 4
+ accuracy: 40
stackable: false
-stats:
- crush_attack: -2
- slash_attack: 52
- stab_attack: 27
- strength_bonus: 36
value: 10000
-weapon_type: melee
diff --git a/data/items/equipment/sapphire_amulet_u.yaml b/data/items/equipment/sapphire_amulet_u.yaml
index 20d7581..241d463 100644
--- a/data/items/equipment/sapphire_amulet_u.yaml
+++ b/data/items/equipment/sapphire_amulet_u.yaml
@@ -1,6 +1,6 @@
color: "45"
craft:
- consume:
+ ingredients:
- items:
- gold_bar
quantity: 1
@@ -13,7 +13,6 @@ craft:
- amulet_mould
quantity: 1
level: 24
- skill: crafting
station:
- furnace
type: crafting
diff --git a/data/items/equipment/sapphire_bracelet.yaml b/data/items/equipment/sapphire_bracelet.yaml
index e128639..f5cf3fb 100644
--- a/data/items/equipment/sapphire_bracelet.yaml
+++ b/data/items/equipment/sapphire_bracelet.yaml
@@ -1,25 +1,26 @@
color: "27"
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - items:
- - sapphire
- quantity: 1
- - byproducts:
- - bracelet_mould
- items:
- - bracelet_mould
- quantity: 1
- level: 23
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 60
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - sapphire
+ quantity: 1
+ - byproducts:
+ - bracelet_mould
+ items:
+ - bracelet_mould
+ quantity: 1
+ level: 23
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 60
description: A bracelet set with a sapphire. Can be enchanted.
-equip_slot: hands
+equipment:
+ slot: hands
+ type: armor
name: sapphire bracelet
value: 200
diff --git a/data/items/equipment/sapphire_necklace.yaml b/data/items/equipment/sapphire_necklace.yaml
index 5d59df6..cd93467 100644
--- a/data/items/equipment/sapphire_necklace.yaml
+++ b/data/items/equipment/sapphire_necklace.yaml
@@ -1,25 +1,26 @@
color: "27"
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - items:
- - sapphire
- quantity: 1
- - byproducts:
- - necklace_mould
- items:
- - necklace_mould
- quantity: 1
- level: 22
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 55
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - sapphire
+ quantity: 1
+ - byproducts:
+ - necklace_mould
+ items:
+ - necklace_mould
+ quantity: 1
+ level: 22
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 55
description: A necklace set with a sapphire. Can be enchanted.
-equip_slot: neck
+equipment:
+ slot: neck
+ type: armor
name: sapphire necklace
value: 250
diff --git a/data/items/equipment/sapphire_ring.yaml b/data/items/equipment/sapphire_ring.yaml
index aca01dc..da1cd43 100644
--- a/data/items/equipment/sapphire_ring.yaml
+++ b/data/items/equipment/sapphire_ring.yaml
@@ -1,25 +1,26 @@
color: "27"
craft:
- consume:
- - items:
- - gold_bar
- quantity: 1
- - items:
- - sapphire
- quantity: 1
- - byproducts:
- - ring_mould
- items:
- - ring_mould
- quantity: 1
- level: 20
- skill: crafting
- station:
- - furnace
- type: crafting
- wait: 4
- xp: 40
+ ingredients:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - sapphire
+ quantity: 1
+ - byproducts:
+ - ring_mould
+ items:
+ - ring_mould
+ quantity: 1
+ level: 20
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 40
description: A ring set with a sapphire. Can be enchanted.
-equip_slot: ring
+equipment:
+ slot: ring
+ type: armor
name: sapphire ring
value: 200
diff --git a/data/items/equipment/shortbow.yaml b/data/items/equipment/shortbow.yaml
index cec7661..cd79769 100644
--- a/data/items/equipment/shortbow.yaml
+++ b/data/items/equipment/shortbow.yaml
@@ -1,23 +1,23 @@
-attack_type: ranged
color: "89"
craft:
- consume:
- - items:
- - shortbow_u
- quantity: 1
- - items:
- - bowstring
- quantity: 1
- level: 5
- skill: fletching
- type: fletching
- wait: 2
- xp: 5
+ ingredients:
+ - items:
+ - shortbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 5
+ type: fletching
+ wait: 2
+ xp: 5
description: A shortbow.
-equip_slot: main_hand
+equipment:
+ attack:
+ ranged: 8
+ attack_type: ranged
+ slot: main_hand
+ speed: 4
+ type: ranged_weapon
name: shortbow
-speed: 4
-stats:
- ranged_attack: 8
value: 10
-weapon_type: ranged
diff --git a/data/items/equipment/shortbow_u.yaml b/data/items/equipment/shortbow_u.yaml
index 07d5fb9..9715273 100644
--- a/data/items/equipment/shortbow_u.yaml
+++ b/data/items/equipment/shortbow_u.yaml
@@ -1,11 +1,10 @@
color: "89"
craft:
- consume:
+ ingredients:
- items:
- logs
quantity: 1
level: 5
- skill: fletching
tool: knife
type: fletching
wait: 3
diff --git a/data/items/equipment/spectral_visor.yaml b/data/items/equipment/spectral_visor.yaml
index e69a785..79bd829 100644
--- a/data/items/equipment/spectral_visor.yaml
+++ b/data/items/equipment/spectral_visor.yaml
@@ -1,8 +1,8 @@
+color: 8D
+description: A visor fitted with spectral frequency filters. Dampens psychic attacks from phantoms.
+equipment:
+ slot: head
+ type: armor
name: spectral visor
-color: "8D"
-description: "A visor fitted with spectral frequency filters. Dampens psychic attacks from phantoms."
-value: 75
stackable: false
-equip_slot: head
-stats:
- defense_bonus: 2
+value: 75
diff --git a/data/items/equipment/steel_dagger.yaml b/data/items/equipment/steel_dagger.yaml
index 66265e2..4a1cbc5 100644
--- a/data/items/equipment/steel_dagger.yaml
+++ b/data/items/equipment/steel_dagger.yaml
@@ -1,28 +1,29 @@
-attack_type: stab
-color: "FD"
+color: FD
craft:
- consume:
- - items:
- - steel_bar
- quantity: 1
- level: 30
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 37
+ ingredients:
+ - items:
+ - steel_bar
+ quantity: 1
+ level: 30
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 37
description: A small steel dagger.
-equip_slot: main_hand
+equipment:
+ attack:
+ crush: -1
+ slash: 7
+ stab: 12
+ attack_type: stab
+ other:
+ strength: 10
+ slot: main_hand
+ speed: 3
+ type: melee_weapon
name: steel dagger
requirements:
- accuracy: 5
-speed: 3
+ accuracy: 5
stackable: false
-stats:
- crush_attack: -1
- slash_attack: 7
- stab_attack: 12
- strength_bonus: 10
value: 60
-weapon_type: melee
diff --git a/data/items/equipment/steel_full_helm.yaml b/data/items/equipment/steel_full_helm.yaml
index 01c1e39..da282fa 100644
--- a/data/items/equipment/steel_full_helm.yaml
+++ b/data/items/equipment/steel_full_helm.yaml
@@ -1,26 +1,27 @@
-color: "FD"
+color: FD
craft:
- consume:
- - items:
- - steel_bar
- quantity: 2
- level: 37
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 75
+ ingredients:
+ - items:
+ - steel_bar
+ quantity: 2
+ level: 37
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 75
description: A steel full helmet.
-equip_slot: head
+equipment:
+ defense:
+ crush: 9
+ ranged: 12
+ science: -5
+ slash: 13
+ stab: 12
+ slot: head
+ type: armor
name: steel full helm
requirements:
- defense: 5
+ defense: 5
stackable: false
-stats:
- crush_defense: 9
- ranged_defense: 12
- science_defense: -5
- slash_defense: 13
- stab_defense: 12
value: 150
diff --git a/data/items/equipment/steel_med_helm.yaml b/data/items/equipment/steel_med_helm.yaml
index 0c4b666..99186d0 100644
--- a/data/items/equipment/steel_med_helm.yaml
+++ b/data/items/equipment/steel_med_helm.yaml
@@ -1,26 +1,27 @@
-color: "FD"
+color: FD
craft:
- consume:
- - items:
- - steel_bar
- quantity: 1
- level: 33
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 37
+ ingredients:
+ - items:
+ - steel_bar
+ quantity: 1
+ level: 33
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 37
description: A steel medium helmet.
-equip_slot: head
+equipment:
+ defense:
+ crush: 5
+ ranged: 7
+ science: -2
+ slash: 9
+ stab: 7
+ slot: head
+ type: armor
name: steel med helm
requirements:
- defense: 5
+ defense: 5
stackable: false
-stats:
- crush_defense: 5
- ranged_defense: 7
- science_defense: -2
- slash_defense: 9
- stab_defense: 7
value: 60
diff --git a/data/items/equipment/steel_platebody.yaml b/data/items/equipment/steel_platebody.yaml
index 6c6dfd2..cb80601 100644
--- a/data/items/equipment/steel_platebody.yaml
+++ b/data/items/equipment/steel_platebody.yaml
@@ -1,26 +1,27 @@
-color: "FD"
+color: FD
craft:
- consume:
- - items:
- - steel_bar
- quantity: 5
- level: 48
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 187
+ ingredients:
+ - items:
+ - steel_bar
+ quantity: 5
+ level: 48
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 187
description: A steel platebody.
-equip_slot: torso
+equipment:
+ defense:
+ crush: 22
+ ranged: 27
+ science: -15
+ slash: 32
+ stab: 27
+ slot: torso
+ type: armor
name: steel platebody
requirements:
- defense: 5
+ defense: 5
stackable: false
-stats:
- crush_defense: 22
- ranged_defense: 27
- science_defense: -15
- slash_defense: 32
- stab_defense: 27
value: 500
diff --git a/data/items/equipment/steel_sword.yaml b/data/items/equipment/steel_sword.yaml
index 01ceb5c..b590210 100644
--- a/data/items/equipment/steel_sword.yaml
+++ b/data/items/equipment/steel_sword.yaml
@@ -1,28 +1,29 @@
-attack_type: slash
-color: "FD"
+color: FD
craft:
- consume:
- - items:
- - steel_bar
- quantity: 1
- level: 34
- skill: smithing
- station:
- - anvil
- type: smithing
- wait: 4
- xp: 37
+ ingredients:
+ - items:
+ - steel_bar
+ quantity: 1
+ level: 34
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 37
description: A steel sword.
-equip_slot: main_hand
+equipment:
+ attack:
+ crush: -2
+ slash: 22
+ stab: 10
+ attack_type: slash
+ other:
+ strength: 14
+ slot: main_hand
+ speed: 4
+ type: melee_weapon
name: steel sword
requirements:
- accuracy: 5
-speed: 4
+ accuracy: 5
stackable: false
-stats:
- crush_attack: -2
- slash_attack: 22
- stab_attack: 10
- strength_bonus: 14
value: 100
-weapon_type: melee
diff --git a/data/items/equipment/willow_longbow.yaml b/data/items/equipment/willow_longbow.yaml
index 90f8ba6..f177552 100644
--- a/data/items/equipment/willow_longbow.yaml
+++ b/data/items/equipment/willow_longbow.yaml
@@ -1,25 +1,25 @@
-attack_type: ranged
-color: "6B"
+color: 6B
craft:
- consume:
- - items:
- - willow_longbow_u
- quantity: 1
- - items:
- - bowstring
- quantity: 1
- level: 40
- skill: fletching
- type: fletching
- wait: 2
- xp: 41
+ ingredients:
+ - items:
+ - willow_longbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 40
+ type: fletching
+ wait: 2
+ xp: 41
description: A willow longbow.
-equip_slot: main_hand
+equipment:
+ attack:
+ ranged: 20
+ attack_type: ranged
+ slot: main_hand
+ speed: 6
+ type: ranged_weapon
name: willow longbow
requirements:
- ranged: 20
-speed: 6
-stats:
- ranged_attack: 20
+ ranged: 20
value: 320
-weapon_type: ranged
diff --git a/data/items/equipment/willow_longbow_u.yaml b/data/items/equipment/willow_longbow_u.yaml
index 9d4db3d..25194d1 100644
--- a/data/items/equipment/willow_longbow_u.yaml
+++ b/data/items/equipment/willow_longbow_u.yaml
@@ -1,11 +1,10 @@
color: "6B"
craft:
- consume:
+ ingredients:
- items:
- willow_logs
quantity: 1
level: 40
- skill: fletching
tool: knife
type: fletching
wait: 3
diff --git a/data/items/equipment/willow_shortbow.yaml b/data/items/equipment/willow_shortbow.yaml
index a663353..f7028e4 100644
--- a/data/items/equipment/willow_shortbow.yaml
+++ b/data/items/equipment/willow_shortbow.yaml
@@ -1,25 +1,25 @@
-attack_type: ranged
-color: "6B"
+color: 6B
craft:
- consume:
- - items:
- - willow_shortbow_u
- quantity: 1
- - items:
- - bowstring
- quantity: 1
- level: 35
- skill: fletching
- type: fletching
- wait: 2
- xp: 33
+ ingredients:
+ - items:
+ - willow_shortbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 35
+ type: fletching
+ wait: 2
+ xp: 33
description: A willow shortbow.
-equip_slot: main_hand
+equipment:
+ attack:
+ ranged: 20
+ attack_type: ranged
+ slot: main_hand
+ speed: 4
+ type: ranged_weapon
name: willow shortbow
requirements:
- ranged: 20
-speed: 4
-stats:
- ranged_attack: 20
+ ranged: 20
value: 160
-weapon_type: ranged
diff --git a/data/items/equipment/willow_shortbow_u.yaml b/data/items/equipment/willow_shortbow_u.yaml
index e737292..534c50d 100644
--- a/data/items/equipment/willow_shortbow_u.yaml
+++ b/data/items/equipment/willow_shortbow_u.yaml
@@ -1,11 +1,10 @@
color: "6B"
craft:
- consume:
+ ingredients:
- items:
- willow_logs
quantity: 1
level: 35
- skill: fletching
tool: knife
type: fletching
wait: 3
diff --git a/data/items/equipment/yew_longbow.yaml b/data/items/equipment/yew_longbow.yaml
index ee365d3..1deab6d 100644
--- a/data/items/equipment/yew_longbow.yaml
+++ b/data/items/equipment/yew_longbow.yaml
@@ -1,25 +1,25 @@
-attack_type: ranged
-color: "5E"
+color: 5E
craft:
- consume:
- - items:
- - yew_longbow_u
- quantity: 1
- - items:
- - bowstring
- quantity: 1
- level: 65
- skill: fletching
- type: fletching
- wait: 2
- xp: 75
+ ingredients:
+ - items:
+ - yew_longbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 65
+ type: fletching
+ wait: 2
+ xp: 75
description: A yew longbow.
-equip_slot: main_hand
+equipment:
+ attack:
+ ranged: 47
+ attack_type: ranged
+ slot: main_hand
+ speed: 6
+ type: ranged_weapon
name: yew longbow
requirements:
- ranged: 40
-speed: 6
-stats:
- ranged_attack: 47
+ ranged: 40
value: 2000
-weapon_type: ranged
diff --git a/data/items/equipment/yew_longbow_u.yaml b/data/items/equipment/yew_longbow_u.yaml
index ff15392..61eeac4 100644
--- a/data/items/equipment/yew_longbow_u.yaml
+++ b/data/items/equipment/yew_longbow_u.yaml
@@ -1,11 +1,10 @@
color: "5E"
craft:
- consume:
+ ingredients:
- items:
- yew_logs
quantity: 1
level: 65
- skill: fletching
tool: knife
type: fletching
wait: 3
diff --git a/data/items/equipment/yew_shortbow.yaml b/data/items/equipment/yew_shortbow.yaml
index a94b78f..b9f3eb5 100644
--- a/data/items/equipment/yew_shortbow.yaml
+++ b/data/items/equipment/yew_shortbow.yaml
@@ -1,25 +1,25 @@
-attack_type: ranged
-color: "5E"
+color: 5E
craft:
- consume:
- - items:
- - yew_shortbow_u
- quantity: 1
- - items:
- - bowstring
- quantity: 1
- level: 60
- skill: fletching
- type: fletching
- wait: 2
- xp: 67
+ ingredients:
+ - items:
+ - yew_shortbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 60
+ type: fletching
+ wait: 2
+ xp: 67
description: A yew shortbow.
-equip_slot: main_hand
+equipment:
+ attack:
+ ranged: 47
+ attack_type: ranged
+ slot: main_hand
+ speed: 4
+ type: ranged_weapon
name: yew shortbow
requirements:
- ranged: 40
-speed: 4
-stats:
- ranged_attack: 47
+ ranged: 40
value: 1600
-weapon_type: ranged
diff --git a/data/items/equipment/yew_shortbow_u.yaml b/data/items/equipment/yew_shortbow_u.yaml
index 77cb117..ee4ed7d 100644
--- a/data/items/equipment/yew_shortbow_u.yaml
+++ b/data/items/equipment/yew_shortbow_u.yaml
@@ -1,11 +1,10 @@
color: "5E"
craft:
- consume:
+ ingredients:
- items:
- yew_logs
quantity: 1
level: 60
- skill: fletching
tool: knife
type: fletching
wait: 3
diff --git a/data/items/materials/adamant_nails.yaml b/data/items/materials/adamant_nails.yaml
index e47e79c..25fb80b 100644
--- a/data/items/materials/adamant_nails.yaml
+++ b/data/items/materials/adamant_nails.yaml
@@ -1,12 +1,11 @@
color: "78"
craft:
- consume:
+ ingredients:
- items:
- adamantite_bar
quantity: 1
level: 74
output_qty: 15
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/materials/adamantite_bar.yaml b/data/items/materials/adamantite_bar.yaml
index d5095e2..4fed7fc 100644
--- a/data/items/materials/adamantite_bar.yaml
+++ b/data/items/materials/adamantite_bar.yaml
@@ -1,6 +1,6 @@
color: "47"
craft:
- consume:
+ ingredients:
- items:
- adamantite_ore
quantity: 1
@@ -9,10 +9,10 @@ craft:
quantity: 6
fail_message: You fail to smelt a usable bar.
level: 65
- skill: smithing
+ subtype: smelt
station:
- furnace
- type: smelting
+ type: smithing
wait: 4
xp: 37
description: A bar of adamantite metal, extremely durable.
diff --git a/data/items/materials/avantoe.yaml b/data/items/materials/avantoe.yaml
index 340a8d0..75727af 100644
--- a/data/items/materials/avantoe.yaml
+++ b/data/items/materials/avantoe.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_avantoe
quantity: 1
level: 48
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 10
description: A clean avantoe leaf, ready for use in pharmacy.
diff --git a/data/items/materials/ball_of_wool.yaml b/data/items/materials/ball_of_wool.yaml
index de6bc98..d074203 100644
--- a/data/items/materials/ball_of_wool.yaml
+++ b/data/items/materials/ball_of_wool.yaml
@@ -1,12 +1,11 @@
color: "FF"
craft:
- consume:
+ ingredients:
- items:
- wool
quantity: 1
level: 1
message: "You spin the wool into a %n."
- skill: crafting
station:
- spinning_wheel
type: crafting
diff --git a/data/items/materials/bowstring.yaml b/data/items/materials/bowstring.yaml
index 30b01ae..fab0510 100644
--- a/data/items/materials/bowstring.yaml
+++ b/data/items/materials/bowstring.yaml
@@ -1,12 +1,11 @@
color: "E6"
craft:
- consume:
+ ingredients:
- items:
- flax
quantity: 1
level: 1
message: "You spin the flax into a %n."
- skill: crafting
station:
- spinning_wheel
type: crafting
diff --git a/data/items/materials/bronze_bar.yaml b/data/items/materials/bronze_bar.yaml
index fabdf07..c7a999e 100644
--- a/data/items/materials/bronze_bar.yaml
+++ b/data/items/materials/bronze_bar.yaml
@@ -1,6 +1,6 @@
color: "B2"
craft:
- consume:
+ ingredients:
- items:
- copper_ore
quantity: 1
@@ -9,10 +9,10 @@ craft:
quantity: 1
fail_message: You fail to smelt a usable bar.
level: 1
- skill: smithing
+ subtype: smelt
station:
- furnace
- type: smelting
+ type: smithing
wait: 4
xp: 6
description: A bar of bronze metal, smelted from copper and tin ore.
diff --git a/data/items/materials/bronze_nails.yaml b/data/items/materials/bronze_nails.yaml
index 5b0ddfd..748f848 100644
--- a/data/items/materials/bronze_nails.yaml
+++ b/data/items/materials/bronze_nails.yaml
@@ -1,12 +1,11 @@
color: "B2"
craft:
- consume:
+ ingredients:
- items:
- bronze_bar
quantity: 1
level: 4
output_qty: 15
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/materials/cadantine.yaml b/data/items/materials/cadantine.yaml
index 6f97167..24513ce 100644
--- a/data/items/materials/cadantine.yaml
+++ b/data/items/materials/cadantine.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_cadantine
quantity: 1
level: 65
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 13
description: A clean cadantine leaf, ready for use in pharmacy.
diff --git a/data/items/materials/diamond.yaml b/data/items/materials/diamond.yaml
index 20e38b3..4bf610a 100644
--- a/data/items/materials/diamond.yaml
+++ b/data/items/materials/diamond.yaml
@@ -1,12 +1,11 @@
color: "FF"
craft:
- consume:
+ ingredients:
- items:
- uncut_diamond
quantity: 1
level: 43
message: "You cut the %i1 into a beautiful %n."
- skill: crafting
tool: chisel
type: crafting
wait: 3
diff --git a/data/items/materials/dwarf_weed.yaml b/data/items/materials/dwarf_weed.yaml
index 486ea18..edb1058 100644
--- a/data/items/materials/dwarf_weed.yaml
+++ b/data/items/materials/dwarf_weed.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_dwarf_weed
quantity: 1
level: 70
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 14
description: A clean dwarf weed leaf, ready for use in pharmacy.
diff --git a/data/items/materials/emerald.yaml b/data/items/materials/emerald.yaml
index 218a54a..5d729cd 100644
--- a/data/items/materials/emerald.yaml
+++ b/data/items/materials/emerald.yaml
@@ -1,12 +1,11 @@
color: "53"
craft:
- consume:
+ ingredients:
- items:
- uncut_emerald
quantity: 1
level: 27
message: "You cut the %i1 into a beautiful %n."
- skill: crafting
tool: chisel
type: crafting
wait: 3
diff --git a/data/items/materials/gold_bar.yaml b/data/items/materials/gold_bar.yaml
index 749584a..895db74 100644
--- a/data/items/materials/gold_bar.yaml
+++ b/data/items/materials/gold_bar.yaml
@@ -1,15 +1,15 @@
color: "DC"
craft:
- consume:
+ ingredients:
- items:
- gold_ore
quantity: 1
fail_message: You fail to smelt a usable bar.
level: 45
- skill: smithing
+ subtype: smelt
station:
- furnace
- type: smelting
+ type: smithing
wait: 4
xp: 22
description: A bar of gold metal.
diff --git a/data/items/materials/guam.yaml b/data/items/materials/guam.yaml
index c0b2592..c0ad838 100644
--- a/data/items/materials/guam.yaml
+++ b/data/items/materials/guam.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_guam
quantity: 1
level: 1
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 3
description: A clean guam leaf, ready for use in pharmacy.
diff --git a/data/items/materials/harralander.yaml b/data/items/materials/harralander.yaml
index b595db5..276c676 100644
--- a/data/items/materials/harralander.yaml
+++ b/data/items/materials/harralander.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_harralander
quantity: 1
level: 20
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 6
description: A clean harralander leaf, ready for use in pharmacy.
diff --git a/data/items/materials/irit.yaml b/data/items/materials/irit.yaml
index c6d5028..90f41b5 100644
--- a/data/items/materials/irit.yaml
+++ b/data/items/materials/irit.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_irit
quantity: 1
level: 40
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 9
description: A clean irit leaf, ready for use in pharmacy.
diff --git a/data/items/materials/iron_bar.yaml b/data/items/materials/iron_bar.yaml
index a6f89c8..4ef4c11 100644
--- a/data/items/materials/iron_bar.yaml
+++ b/data/items/materials/iron_bar.yaml
@@ -1,19 +1,19 @@
color: "FA"
craft:
- consume:
+ ingredients:
- items:
- iron_ore
quantity: 1
fail_message: The iron is too impure to make a workable bar.
level: 20
- skill: smithing
+ subtype: smelt
station:
- furnace
success:
base: 0.5
cap: 0.5
per_level: 0
- type: smelting
+ type: smithing
wait: 4
xp: 12
description: A bar of iron metal.
diff --git a/data/items/materials/iron_nails.yaml b/data/items/materials/iron_nails.yaml
index 39384e8..dc647f5 100644
--- a/data/items/materials/iron_nails.yaml
+++ b/data/items/materials/iron_nails.yaml
@@ -1,12 +1,11 @@
color: "FA"
craft:
- consume:
+ ingredients:
- items:
- iron_bar
quantity: 1
level: 24
output_qty: 15
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/materials/kwuarm.yaml b/data/items/materials/kwuarm.yaml
index c31610a..77e0ead 100644
--- a/data/items/materials/kwuarm.yaml
+++ b/data/items/materials/kwuarm.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_kwuarm
quantity: 1
level: 54
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 11
description: A clean kwuarm leaf, ready for use in pharmacy.
diff --git a/data/items/materials/lantadyme.yaml b/data/items/materials/lantadyme.yaml
index 3ed6ef5..67663ac 100644
--- a/data/items/materials/lantadyme.yaml
+++ b/data/items/materials/lantadyme.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_lantadyme
quantity: 1
level: 67
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 13
description: A clean lantadyme leaf, ready for use in pharmacy.
diff --git a/data/items/materials/mahogany_planks.yaml b/data/items/materials/mahogany_planks.yaml
index a0e4ed7..a3f3e0b 100644
--- a/data/items/materials/mahogany_planks.yaml
+++ b/data/items/materials/mahogany_planks.yaml
@@ -1,12 +1,11 @@
color: "60"
craft:
- consume:
+ ingredients:
- items:
- mahogany_logs
quantity: 1
level: 50
message: "You saw the mahogany logs into %n."
- skill: construction
station:
- workbench
tool: saw
diff --git a/data/items/materials/marrentill.yaml b/data/items/materials/marrentill.yaml
index cb37431..17730b7 100644
--- a/data/items/materials/marrentill.yaml
+++ b/data/items/materials/marrentill.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_marrentill
quantity: 1
level: 5
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 4
description: A clean marrentill leaf, ready for use in pharmacy.
diff --git a/data/items/materials/mithril_bar.yaml b/data/items/materials/mithril_bar.yaml
index f625c97..0cc0102 100644
--- a/data/items/materials/mithril_bar.yaml
+++ b/data/items/materials/mithril_bar.yaml
@@ -1,6 +1,6 @@
color: "4B"
craft:
- consume:
+ ingredients:
- items:
- mithril_ore
quantity: 1
@@ -9,10 +9,10 @@ craft:
quantity: 4
fail_message: You fail to smelt a usable bar.
level: 50
- skill: smithing
+ subtype: smelt
station:
- furnace
- type: smelting
+ type: smithing
wait: 4
xp: 30
description: A bar of mithril metal, light and strong.
diff --git a/data/items/materials/mithril_nails.yaml b/data/items/materials/mithril_nails.yaml
index 43d39d2..f747da2 100644
--- a/data/items/materials/mithril_nails.yaml
+++ b/data/items/materials/mithril_nails.yaml
@@ -1,12 +1,11 @@
color: "4B"
craft:
- consume:
+ ingredients:
- items:
- mithril_bar
quantity: 1
level: 54
output_qty: 15
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/materials/oak_planks.yaml b/data/items/materials/oak_planks.yaml
index 6f6b793..82e5504 100644
--- a/data/items/materials/oak_planks.yaml
+++ b/data/items/materials/oak_planks.yaml
@@ -1,12 +1,11 @@
color: "66"
craft:
- consume:
+ ingredients:
- items:
- oak_logs
quantity: 1
level: 15
message: "You saw the oak logs into %n."
- skill: construction
station:
- workbench
tool: saw
diff --git a/data/items/materials/planks.yaml b/data/items/materials/planks.yaml
index 1c1b8cf..d2ad6c2 100644
--- a/data/items/materials/planks.yaml
+++ b/data/items/materials/planks.yaml
@@ -1,12 +1,11 @@
color: "89"
craft:
- consume:
+ ingredients:
- items:
- logs
quantity: 1
level: 1
message: "You saw the logs into %n."
- skill: construction
station:
- workbench
tool: saw
diff --git a/data/items/materials/ranarr.yaml b/data/items/materials/ranarr.yaml
index 94b36aa..4527c52 100644
--- a/data/items/materials/ranarr.yaml
+++ b/data/items/materials/ranarr.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_ranarr
quantity: 1
level: 25
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 8
description: A clean ranarr leaf, ready for use in pharmacy.
diff --git a/data/items/materials/ruby.yaml b/data/items/materials/ruby.yaml
index a2875f1..eb7968b 100644
--- a/data/items/materials/ruby.yaml
+++ b/data/items/materials/ruby.yaml
@@ -1,12 +1,11 @@
color: "C4"
craft:
- consume:
+ ingredients:
- items:
- uncut_ruby
quantity: 1
level: 63
message: "You cut the %i1 into a beautiful %n."
- skill: crafting
tool: chisel
type: crafting
wait: 3
diff --git a/data/items/materials/rune_nails.yaml b/data/items/materials/rune_nails.yaml
index c49949f..5cea6e0 100644
--- a/data/items/materials/rune_nails.yaml
+++ b/data/items/materials/rune_nails.yaml
@@ -1,12 +1,11 @@
color: "57"
craft:
- consume:
+ ingredients:
- items:
- runite_bar
quantity: 1
level: 89
output_qty: 15
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/materials/runite_bar.yaml b/data/items/materials/runite_bar.yaml
index d1c7bb0..1b4823d 100644
--- a/data/items/materials/runite_bar.yaml
+++ b/data/items/materials/runite_bar.yaml
@@ -1,6 +1,6 @@
color: "27"
craft:
- consume:
+ ingredients:
- items:
- runite_ore
quantity: 1
@@ -9,10 +9,10 @@ craft:
quantity: 8
fail_message: You fail to smelt a usable bar.
level: 80
- skill: smithing
+ subtype: smelt
station:
- furnace
- type: smelting
+ type: smithing
wait: 4
xp: 50
description: A bar of runite metal, the finest alloy known.
diff --git a/data/items/materials/sapphire.yaml b/data/items/materials/sapphire.yaml
index fc11f76..bc73cd9 100644
--- a/data/items/materials/sapphire.yaml
+++ b/data/items/materials/sapphire.yaml
@@ -1,12 +1,11 @@
color: "45"
craft:
- consume:
+ ingredients:
- items:
- uncut_sapphire
quantity: 1
level: 20
message: "You cut the %i1 into a beautiful %n."
- skill: crafting
tool: chisel
type: crafting
wait: 3
diff --git a/data/items/materials/silver_bar.yaml b/data/items/materials/silver_bar.yaml
index ec4ae8d..cf85b31 100644
--- a/data/items/materials/silver_bar.yaml
+++ b/data/items/materials/silver_bar.yaml
@@ -1,15 +1,15 @@
color: "FF"
craft:
- consume:
+ ingredients:
- items:
- silver_ore
quantity: 1
fail_message: You fail to smelt a usable bar.
level: 25
- skill: smithing
+ subtype: smelt
station:
- furnace
- type: smelting
+ type: smithing
wait: 4
xp: 14
description: A bar of silver metal.
diff --git a/data/items/materials/snapdragon.yaml b/data/items/materials/snapdragon.yaml
index dec19d6..a05c87d 100644
--- a/data/items/materials/snapdragon.yaml
+++ b/data/items/materials/snapdragon.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_snapdragon
quantity: 1
level: 59
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 12
description: A clean snapdragon leaf, ready for use in pharmacy.
diff --git a/data/items/materials/soft_clay.yaml b/data/items/materials/soft_clay.yaml
index b55ed16..d9935dd 100644
--- a/data/items/materials/soft_clay.yaml
+++ b/data/items/materials/soft_clay.yaml
@@ -1,7 +1,7 @@
color: "AD"
craft:
type: combine
- consume:
+ ingredients:
- items:
- clay
quantity: 1
diff --git a/data/items/materials/steel_bar.yaml b/data/items/materials/steel_bar.yaml
index 5788911..31d8224 100644
--- a/data/items/materials/steel_bar.yaml
+++ b/data/items/materials/steel_bar.yaml
@@ -1,6 +1,6 @@
color: "FE"
craft:
- consume:
+ ingredients:
- items:
- iron_ore
quantity: 1
@@ -9,10 +9,10 @@ craft:
quantity: 2
fail_message: You fail to smelt a usable bar.
level: 40
- skill: smithing
+ subtype: smelt
station:
- furnace
- type: smelting
+ type: smithing
wait: 4
xp: 17
description: A bar of steel, forged from iron ore and coal.
diff --git a/data/items/materials/steel_nails.yaml b/data/items/materials/steel_nails.yaml
index ef1fe33..12bab1e 100644
--- a/data/items/materials/steel_nails.yaml
+++ b/data/items/materials/steel_nails.yaml
@@ -1,12 +1,11 @@
color: "FD"
craft:
- consume:
+ ingredients:
- items:
- steel_bar
quantity: 1
level: 34
output_qty: 15
- skill: smithing
station:
- anvil
type: smithing
diff --git a/data/items/materials/stim_cell.yaml b/data/items/materials/stim_cell.yaml
index c04bc05..6637cc7 100644
--- a/data/items/materials/stim_cell.yaml
+++ b/data/items/materials/stim_cell.yaml
@@ -1,6 +1,6 @@
color: "E2"
craft:
- consume:
+ ingredients:
- items:
- harralander_potion_unf
quantity: 1
@@ -8,7 +8,6 @@ craft:
- chocolate_dust
quantity: 1
level: 26
- skill: pharmacy
type: pharmacy
wait: 4
xp: 68
diff --git a/data/items/materials/tarromin.yaml b/data/items/materials/tarromin.yaml
index fd77fcd..4783bed 100644
--- a/data/items/materials/tarromin.yaml
+++ b/data/items/materials/tarromin.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_tarromin
quantity: 1
level: 11
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 5
description: A clean tarromin leaf, ready for use in pharmacy.
diff --git a/data/items/materials/teak_planks.yaml b/data/items/materials/teak_planks.yaml
index 3d41e0b..400c9e5 100644
--- a/data/items/materials/teak_planks.yaml
+++ b/data/items/materials/teak_planks.yaml
@@ -1,12 +1,11 @@
color: "64"
craft:
- consume:
+ ingredients:
- items:
- teak_logs
quantity: 1
level: 35
message: "You saw the teak logs into %n."
- skill: construction
station:
- workbench
tool: saw
diff --git a/data/items/materials/toadflax.yaml b/data/items/materials/toadflax.yaml
index ba63139..9405301 100644
--- a/data/items/materials/toadflax.yaml
+++ b/data/items/materials/toadflax.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_toadflax
quantity: 1
level: 30
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 8
description: A clean toadflax leaf, ready for use in pharmacy.
diff --git a/data/items/materials/torstol.yaml b/data/items/materials/torstol.yaml
index c2cc1e8..f220b28 100644
--- a/data/items/materials/torstol.yaml
+++ b/data/items/materials/torstol.yaml
@@ -1,12 +1,12 @@
color: "30"
craft:
- consume:
+ ingredients:
- items:
- grimy_torstol
quantity: 1
level: 75
- skill: pharmacy
- type: clean
+ subtype: clean
+ type: pharmacy
wait: 2
xp: 15
description: A clean torstol leaf, ready for use in pharmacy.
diff --git a/data/items/misc/christmas_cracker.yaml b/data/items/misc/christmas_cracker.yaml
index 21b63be..c3eeba3 100644
--- a/data/items/misc/christmas_cracker.yaml
+++ b/data/items/misc/christmas_cracker.yaml
@@ -4,6 +4,5 @@ description: "A festive party cracker. Pull it apart to see what's inside!"
value: 10
stackable: true
search_table: christmas_cracker_hat
-search_misc_table: christmas_cracker_misc
search_ticks: 3
search_message: "digging through the christmas cracker"
diff --git a/data/items/misc/mahogany_table.yaml b/data/items/misc/mahogany_table.yaml
index d5b23b8..aa31b1f 100644
--- a/data/items/misc/mahogany_table.yaml
+++ b/data/items/misc/mahogany_table.yaml
@@ -1,11 +1,10 @@
color: "60"
craft:
- consume:
+ ingredients:
- items:
- mahogany_planks
quantity: 3
level: 52
- skill: construction
station:
- workbench
type: construction
diff --git a/data/items/misc/oak_shelf.yaml b/data/items/misc/oak_shelf.yaml
index 422e2eb..94c9a7e 100644
--- a/data/items/misc/oak_shelf.yaml
+++ b/data/items/misc/oak_shelf.yaml
@@ -1,11 +1,10 @@
color: "5E"
craft:
- consume:
+ ingredients:
- items:
- oak_planks
quantity: 2
level: 18
- skill: construction
station:
- workbench
type: construction
diff --git a/data/items/misc/oak_table.yaml b/data/items/misc/oak_table.yaml
index d3c66b7..f096c3b 100644
--- a/data/items/misc/oak_table.yaml
+++ b/data/items/misc/oak_table.yaml
@@ -1,11 +1,10 @@
color: "5E"
craft:
- consume:
+ ingredients:
- items:
- oak_planks
quantity: 3
level: 20
- skill: construction
station:
- workbench
type: construction
diff --git a/data/items/misc/teak_table.yaml b/data/items/misc/teak_table.yaml
index e0dba33..b75f506 100644
--- a/data/items/misc/teak_table.yaml
+++ b/data/items/misc/teak_table.yaml
@@ -1,11 +1,10 @@
color: "64"
craft:
- consume:
+ ingredients:
- items:
- teak_planks
quantity: 3
level: 38
- skill: construction
station:
- workbench
type: construction
diff --git a/data/items/misc/wooden_chair.yaml b/data/items/misc/wooden_chair.yaml
index 128ab98..77de78d 100644
--- a/data/items/misc/wooden_chair.yaml
+++ b/data/items/misc/wooden_chair.yaml
@@ -1,11 +1,10 @@
color: "82"
craft:
- consume:
+ ingredients:
- items:
- planks
quantity: 2
level: 8
- skill: construction
station:
- workbench
type: construction
diff --git a/data/items/misc/wooden_shelf.yaml b/data/items/misc/wooden_shelf.yaml
index 5363790..bb04fc8 100644
--- a/data/items/misc/wooden_shelf.yaml
+++ b/data/items/misc/wooden_shelf.yaml
@@ -1,11 +1,10 @@
color: "82"
craft:
- consume:
+ ingredients:
- items:
- planks
quantity: 2
level: 4
- skill: construction
station:
- workbench
type: construction
diff --git a/data/items/misc/wooden_table.yaml b/data/items/misc/wooden_table.yaml
index f76deaa..88a996f 100644
--- a/data/items/misc/wooden_table.yaml
+++ b/data/items/misc/wooden_table.yaml
@@ -1,11 +1,10 @@
color: "82"
craft:
- consume:
+ ingredients:
- items:
- planks
quantity: 3
level: 6
- skill: construction
station:
- workbench
type: construction
diff --git a/data/items/tools/adamant_axe.yaml b/data/items/tools/adamant_axe.yaml
index 865bec0..78118be 100644
--- a/data/items/tools/adamant_axe.yaml
+++ b/data/items/tools/adamant_axe.yaml
@@ -1,18 +1,21 @@
-name: adamant axe
color: "78"
-description: "A sturdy adamant axe, good for woodcutting."
-value: 500
-stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
-stats:
- stab_attack: -2
- slash_attack: 29
- crush_attack: 22
- strength_bonus: 24
-speed: 5
-tool_type: axe
-tool_speed: 2
+description: A sturdy adamant axe, good for woodcutting.
+equipment:
+ attack:
+ crush: 22
+ slash: 29
+ stab: -2
+ attack_type: slash
+ other:
+ strength: 24
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
+name: adamant axe
requirements:
accuracy: 30
+stackable: false
+tool:
+ speed: 2
+ type: axe
+value: 500
diff --git a/data/items/tools/adamant_pickaxe.yaml b/data/items/tools/adamant_pickaxe.yaml
index d2ea9bb..95ca1f5 100644
--- a/data/items/tools/adamant_pickaxe.yaml
+++ b/data/items/tools/adamant_pickaxe.yaml
@@ -1,18 +1,21 @@
-name: adamant pickaxe
color: "78"
-description: "A sturdy adamant pickaxe, good for mining."
-value: 500
-stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
-stats:
- stab_attack: 23
- slash_attack: -2
- crush_attack: 10
- strength_bonus: 16
-speed: 5
-tool_type: pickaxe
-tool_speed: 0.75
+description: A sturdy adamant pickaxe, good for mining.
+equipment:
+ attack:
+ crush: 10
+ slash: -2
+ stab: 23
+ attack_type: stab
+ other:
+ strength: 16
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
+name: adamant pickaxe
requirements:
accuracy: 30
+stackable: false
+tool:
+ speed: 0.75
+ type: pickaxe
+value: 500
diff --git a/data/items/tools/big_fishing_net.yaml b/data/items/tools/big_fishing_net.yaml
index 4398587..10f3f57 100644
--- a/data/items/tools/big_fishing_net.yaml
+++ b/data/items/tools/big_fishing_net.yaml
@@ -1,14 +1,14 @@
+color: E6
+description: A larger fishing net for catching bigger fish.
+equipment:
+ other:
+ strength: 0
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: big fishing net
-color: "E6"
-description: "A larger fishing net for catching bigger fish."
-value: 20
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-stats:
- attack_bonus: 0
- strength_bonus: 0
- defense_bonus: 0
-speed: 5
-tool_type: big_net
-tool_speed: 1
+tool:
+ speed: 1
+ type: big_net
+value: 20
diff --git a/data/items/tools/black_pickaxe.yaml b/data/items/tools/black_pickaxe.yaml
index 22d5363..5b06f32 100644
--- a/data/items/tools/black_pickaxe.yaml
+++ b/data/items/tools/black_pickaxe.yaml
@@ -1,18 +1,21 @@
+color: F0
+description: A black pickaxe, good for mining.
+equipment:
+ attack:
+ crush: 6
+ slash: -2
+ stab: 14
+ attack_type: stab
+ other:
+ strength: 9
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: black pickaxe
-color: "F0"
-description: "A black pickaxe, good for mining."
-value: 150
-stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
-stats:
- stab_attack: 14
- slash_attack: -2
- crush_attack: 6
- strength_bonus: 9
-speed: 5
-tool_type: pickaxe
-tool_speed: 1
requirements:
accuracy: 10
+stackable: false
+tool:
+ speed: 1
+ type: pickaxe
+value: 150
diff --git a/data/items/tools/bronze_axe.yaml b/data/items/tools/bronze_axe.yaml
index 863c34a..91c0b43 100644
--- a/data/items/tools/bronze_axe.yaml
+++ b/data/items/tools/bronze_axe.yaml
@@ -1,16 +1,19 @@
+color: B2
+description: A sturdy bronze axe, good for woodcutting.
+equipment:
+ attack:
+ crush: 5
+ slash: 7
+ stab: -2
+ attack_type: slash
+ other:
+ strength: 5
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: bronze axe
-color: "B2"
-description: "A sturdy bronze axe, good for woodcutting."
-value: 10
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
-stats:
- stab_attack: -2
- slash_attack: 7
- crush_attack: 5
- strength_bonus: 5
-speed: 5
-tool_type: axe
-tool_speed: 4
+tool:
+ speed: 4
+ type: axe
+value: 10
diff --git a/data/items/tools/bronze_pickaxe.yaml b/data/items/tools/bronze_pickaxe.yaml
index e280020..807cadb 100644
--- a/data/items/tools/bronze_pickaxe.yaml
+++ b/data/items/tools/bronze_pickaxe.yaml
@@ -1,16 +1,19 @@
+color: B2
+description: A sturdy bronze pickaxe, good for mining.
+equipment:
+ attack:
+ crush: 2
+ slash: -2
+ stab: 4
+ attack_type: stab
+ other:
+ strength: 3
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: bronze pickaxe
-color: "B2"
-description: "A sturdy bronze pickaxe, good for mining."
-value: 10
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
-stats:
- stab_attack: 4
- slash_attack: -2
- crush_attack: 2
- strength_bonus: 3
-speed: 5
-tool_type: pickaxe
-tool_speed: 2
+tool:
+ speed: 2
+ type: pickaxe
+value: 10
diff --git a/data/items/tools/chisel.yaml b/data/items/tools/chisel.yaml
index c4d67b7..c5b9ca6 100644
--- a/data/items/tools/chisel.yaml
+++ b/data/items/tools/chisel.yaml
@@ -1,5 +1,6 @@
+color: FA
+description: A chisel used for cutting gems.
name: chisel
-color: "FA"
-description: "A chisel used for cutting gems."
+tool:
+ type: chisel
value: 5
-tool_type: chisel
diff --git a/data/items/tools/dragon_pickaxe.yaml b/data/items/tools/dragon_pickaxe.yaml
index 0dd3cd4..25c5cce 100644
--- a/data/items/tools/dragon_pickaxe.yaml
+++ b/data/items/tools/dragon_pickaxe.yaml
@@ -1,18 +1,21 @@
+color: C4
+description: A powerful dragon pickaxe.
+equipment:
+ attack:
+ crush: 16
+ slash: -2
+ stab: 34
+ attack_type: stab
+ other:
+ strength: 26
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: dragon pickaxe
-color: "C4"
-description: "A powerful dragon pickaxe."
-value: 50000
-stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
-stats:
- stab_attack: 34
- slash_attack: -2
- crush_attack: 16
- strength_bonus: 26
-speed: 5
-tool_type: pickaxe
-tool_speed: 0.25
requirements:
accuracy: 60
+stackable: false
+tool:
+ speed: 0.25
+ type: pickaxe
+value: 50000
diff --git a/data/items/tools/empty_pot.yaml b/data/items/tools/empty_pot.yaml
index 93c9a04..371a783 100644
--- a/data/items/tools/empty_pot.yaml
+++ b/data/items/tools/empty_pot.yaml
@@ -1,13 +1,12 @@
color: "82"
craft:
- consume:
+ ingredients:
- items:
- unfired_pot
quantity: 1
fail_message: The pot cracks in the oven and is ruined.
level: 1
message: "You fire the %i1 in the oven."
- skill: crafting
station:
- pottery_oven
success:
diff --git a/data/items/tools/firesteel.yaml b/data/items/tools/firesteel.yaml
index bccd390..861e879 100644
--- a/data/items/tools/firesteel.yaml
+++ b/data/items/tools/firesteel.yaml
@@ -1,7 +1,8 @@
+color: FA
+description: A rugged firesteel that never wears out.
name: firesteel
-color: "FA"
-description: "A rugged firesteel that never wears out."
-value: 20
stackable: false
-tool_type: fire
-tool_speed: 4
+tool:
+ speed: 4
+ type: fire
+value: 20
diff --git a/data/items/tools/fishing_rod.yaml b/data/items/tools/fishing_rod.yaml
index e7e3023..b9e5610 100644
--- a/data/items/tools/fishing_rod.yaml
+++ b/data/items/tools/fishing_rod.yaml
@@ -1,14 +1,14 @@
+color: 6B
+description: A simple fishing rod. Use with fishing bait.
+equipment:
+ other:
+ strength: 0
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: fishing rod
-color: "6B"
-description: "A simple fishing rod. Use with fishing bait."
-value: 5
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-stats:
- attack_bonus: 0
- strength_bonus: 0
- defense_bonus: 0
-speed: 5
-tool_type: fishing_rod
-tool_speed: 0
+tool:
+ speed: 0
+ type: fishing_rod
+value: 5
diff --git a/data/items/tools/fly_fishing_rod.yaml b/data/items/tools/fly_fishing_rod.yaml
index a65b90c..fc4655d 100644
--- a/data/items/tools/fly_fishing_rod.yaml
+++ b/data/items/tools/fly_fishing_rod.yaml
@@ -1,14 +1,14 @@
+color: 6B
+description: A flexible rod for fly fishing with artificial flies.
+equipment:
+ other:
+ strength: 0
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: fly fishing rod
-color: "6B"
-description: "A flexible rod for fly fishing with artificial flies."
-value: 20
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-stats:
- attack_bonus: 0
- strength_bonus: 0
- defense_bonus: 0
-speed: 5
-tool_type: fly_rod
-tool_speed: 1
+tool:
+ speed: 1
+ type: fly_rod
+value: 20
diff --git a/data/items/tools/hammer.yaml b/data/items/tools/hammer.yaml
index a017160..3f156aa 100644
--- a/data/items/tools/hammer.yaml
+++ b/data/items/tools/hammer.yaml
@@ -1,6 +1,7 @@
+color: FA
+description: A sturdy hammer for smithing metal on an anvil.
name: hammer
-color: "FA"
-description: "A sturdy hammer for smithing metal on an anvil."
-value: 10
stackable: false
-tool_type: hammer
+tool:
+ type: hammer
+value: 10
diff --git a/data/items/tools/harpoon.yaml b/data/items/tools/harpoon.yaml
index 639f978..51163f5 100644
--- a/data/items/tools/harpoon.yaml
+++ b/data/items/tools/harpoon.yaml
@@ -1,14 +1,14 @@
+color: FA
+description: A barbed harpoon for spearing large fish.
+equipment:
+ other:
+ strength: 0
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: harpoon
-color: "FA"
-description: "A barbed harpoon for spearing large fish."
-value: 30
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-stats:
- attack_bonus: 0
- strength_bonus: 0
- defense_bonus: 0
-speed: 5
-tool_type: harpoon
-tool_speed: 0
+tool:
+ speed: 0
+ type: harpoon
+value: 30
diff --git a/data/items/tools/iron_axe.yaml b/data/items/tools/iron_axe.yaml
index bac2530..bb633f5 100644
--- a/data/items/tools/iron_axe.yaml
+++ b/data/items/tools/iron_axe.yaml
@@ -1,16 +1,19 @@
+color: FA
+description: A sturdy iron axe, good for woodcutting.
+equipment:
+ attack:
+ crush: 7
+ slash: 10
+ stab: -2
+ attack_type: slash
+ other:
+ strength: 8
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: iron axe
-color: "FA"
-description: "A sturdy iron axe, good for woodcutting."
-value: 25
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
-stats:
- stab_attack: -2
- slash_attack: 10
- crush_attack: 7
- strength_bonus: 8
-speed: 5
-tool_type: axe
-tool_speed: 3.5
+tool:
+ speed: 3.5
+ type: axe
+value: 25
diff --git a/data/items/tools/iron_pickaxe.yaml b/data/items/tools/iron_pickaxe.yaml
index bdb4531..e003c93 100644
--- a/data/items/tools/iron_pickaxe.yaml
+++ b/data/items/tools/iron_pickaxe.yaml
@@ -1,16 +1,19 @@
+color: FA
+description: A sturdy iron pickaxe, good for mining.
+equipment:
+ attack:
+ crush: 3
+ slash: -2
+ stab: 7
+ attack_type: stab
+ other:
+ strength: 5
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: iron pickaxe
-color: "FA"
-description: "A sturdy iron pickaxe, good for mining."
-value: 25
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
-stats:
- stab_attack: 7
- slash_attack: -2
- crush_attack: 3
- strength_bonus: 5
-speed: 5
-tool_type: pickaxe
-tool_speed: 1.5
+tool:
+ speed: 1.5
+ type: pickaxe
+value: 25
diff --git a/data/items/tools/knife.yaml b/data/items/tools/knife.yaml
index ce27095..353d859 100644
--- a/data/items/tools/knife.yaml
+++ b/data/items/tools/knife.yaml
@@ -1,5 +1,6 @@
+color: FA
+description: A small knife used for cutting wood and fletching.
name: knife
-color: "FA"
-description: "A small knife used for cutting wood and fletching."
+tool:
+ type: knife
value: 5
-tool_type: knife
diff --git a/data/items/tools/lighter.yaml b/data/items/tools/lighter.yaml
index ff38aa4..9f47a45 100644
--- a/data/items/tools/lighter.yaml
+++ b/data/items/tools/lighter.yaml
@@ -1,10 +1,13 @@
-name: lighter
color: "51"
-description: "A reliable butane lighter."
-value: 15
-stackable: false
-equip_slot: main_hand
-tool_type: fire
-tool_speed: 4
-quality: 100
+description: A reliable butane lighter.
+equipment:
+ slot: main_hand
+ type: tool
max_quality: 100
+name: lighter
+quality: 100
+stackable: false
+tool:
+ speed: 4
+ type: fire
+value: 15
diff --git a/data/items/tools/lobster_pot.yaml b/data/items/tools/lobster_pot.yaml
index 6e4a1d4..56760de 100644
--- a/data/items/tools/lobster_pot.yaml
+++ b/data/items/tools/lobster_pot.yaml
@@ -1,14 +1,14 @@
+color: FA
+description: A wire cage for trapping lobsters.
+equipment:
+ other:
+ strength: 0
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: lobster pot
-color: "FA"
-description: "A wire cage for trapping lobsters."
-value: 20
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-stats:
- attack_bonus: 0
- strength_bonus: 0
- defense_bonus: 0
-speed: 5
-tool_type: lobster_pot
-tool_speed: 0
+tool:
+ speed: 0
+ type: lobster_pot
+value: 20
diff --git a/data/items/tools/matches.yaml b/data/items/tools/matches.yaml
index c7cf9dd..75f8c1a 100644
--- a/data/items/tools/matches.yaml
+++ b/data/items/tools/matches.yaml
@@ -1,8 +1,11 @@
+color: C4
+description: A small box of matches for starting fires.
+equipment:
+ slot: main_hand
+ type: tool
name: matches
-color: "C4"
-description: "A small box of matches for starting fires."
-value: 5
stackable: true
-equip_slot: main_hand
-tool_type: fire
-tool_speed: 4
+tool:
+ speed: 4
+ type: fire
+value: 5
diff --git a/data/items/tools/mithril_axe.yaml b/data/items/tools/mithril_axe.yaml
index f0976a9..31aef89 100644
--- a/data/items/tools/mithril_axe.yaml
+++ b/data/items/tools/mithril_axe.yaml
@@ -1,18 +1,21 @@
+color: 4B
+description: A sturdy mithril axe, good for woodcutting.
+equipment:
+ attack:
+ crush: 16
+ slash: 21
+ stab: -2
+ attack_type: slash
+ other:
+ strength: 17
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: mithril axe
-color: "4B"
-description: "A sturdy mithril axe, good for woodcutting."
-value: 200
-stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
-stats:
- stab_attack: -2
- slash_attack: 21
- crush_attack: 16
- strength_bonus: 17
-speed: 5
-tool_type: axe
-tool_speed: 2.5
requirements:
accuracy: 20
+stackable: false
+tool:
+ speed: 2.5
+ type: axe
+value: 200
diff --git a/data/items/tools/mithril_pickaxe.yaml b/data/items/tools/mithril_pickaxe.yaml
index e525c21..ab2996d 100644
--- a/data/items/tools/mithril_pickaxe.yaml
+++ b/data/items/tools/mithril_pickaxe.yaml
@@ -1,18 +1,21 @@
+color: 4B
+description: A sturdy mithril pickaxe, good for mining.
+equipment:
+ attack:
+ crush: 8
+ slash: -2
+ stab: 17
+ attack_type: stab
+ other:
+ strength: 12
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: mithril pickaxe
-color: "4B"
-description: "A sturdy mithril pickaxe, good for mining."
-value: 200
-stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
-stats:
- stab_attack: 17
- slash_attack: -2
- crush_attack: 8
- strength_bonus: 12
-speed: 5
-tool_type: pickaxe
-tool_speed: 1
requirements:
accuracy: 20
+stackable: false
+tool:
+ speed: 1
+ type: pickaxe
+value: 200
diff --git a/data/items/tools/needle.yaml b/data/items/tools/needle.yaml
index a5adf2a..bc12202 100644
--- a/data/items/tools/needle.yaml
+++ b/data/items/tools/needle.yaml
@@ -1,5 +1,6 @@
+color: FA
+description: A needle for crafting leather goods.
name: needle
-color: "FA"
-description: "A needle for crafting leather goods."
+tool:
+ type: needle
value: 5
-tool_type: needle
diff --git a/data/items/tools/plant_pot.yaml b/data/items/tools/plant_pot.yaml
index ede859c..e758167 100644
--- a/data/items/tools/plant_pot.yaml
+++ b/data/items/tools/plant_pot.yaml
@@ -1,13 +1,12 @@
color: "82"
craft:
- consume:
+ ingredients:
- items:
- unfired_plant_pot
quantity: 1
fail_message: The plant pot cracks in the oven and is ruined.
level: 19
message: "You fire the %i1 in the oven."
- skill: crafting
station:
- pottery_oven
success:
diff --git a/data/items/tools/rake.yaml b/data/items/tools/rake.yaml
index 3848104..ca5e44b 100644
--- a/data/items/tools/rake.yaml
+++ b/data/items/tools/rake.yaml
@@ -1,6 +1,7 @@
+color: 5E
+description: A sturdy rake for clearing weeds and dead plants from farming patches.
name: rake
-color: "5E"
-description: "A sturdy rake for clearing weeds and dead plants from farming patches."
-value: 8
stackable: false
-tool_type: rake
+tool:
+ type: rake
+value: 8
diff --git a/data/items/tools/rune_axe.yaml b/data/items/tools/rune_axe.yaml
index e239a0a..46cc948 100644
--- a/data/items/tools/rune_axe.yaml
+++ b/data/items/tools/rune_axe.yaml
@@ -1,18 +1,21 @@
-name: rune axe
color: "57"
-description: "A sturdy rune axe, good for woodcutting."
-value: 5000
-stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
-stats:
- stab_attack: -2
- slash_attack: 38
- crush_attack: 29
- strength_bonus: 32
-speed: 5
-tool_type: axe
-tool_speed: 1.5
+description: A sturdy rune axe, good for woodcutting.
+equipment:
+ attack:
+ crush: 29
+ slash: 38
+ stab: -2
+ attack_type: slash
+ other:
+ strength: 32
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
+name: rune axe
requirements:
accuracy: 40
+stackable: false
+tool:
+ speed: 1.5
+ type: axe
+value: 5000
diff --git a/data/items/tools/rune_pickaxe.yaml b/data/items/tools/rune_pickaxe.yaml
index b4e9ac1..d580df5 100644
--- a/data/items/tools/rune_pickaxe.yaml
+++ b/data/items/tools/rune_pickaxe.yaml
@@ -1,18 +1,21 @@
-name: rune pickaxe
color: "57"
-description: "A sturdy rune pickaxe, good for mining."
-value: 5000
-stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
-stats:
- stab_attack: 30
- slash_attack: -2
- crush_attack: 14
- strength_bonus: 22
-speed: 5
-tool_type: pickaxe
-tool_speed: 0.5
+description: A sturdy rune pickaxe, good for mining.
+equipment:
+ attack:
+ crush: 14
+ slash: -2
+ stab: 30
+ attack_type: stab
+ other:
+ strength: 22
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
+name: rune pickaxe
requirements:
accuracy: 40
+stackable: false
+tool:
+ speed: 0.5
+ type: pickaxe
+value: 5000
diff --git a/data/items/tools/saw.yaml b/data/items/tools/saw.yaml
index d671923..8f6a287 100644
--- a/data/items/tools/saw.yaml
+++ b/data/items/tools/saw.yaml
@@ -1,6 +1,7 @@
-name: saw
-tool_type: saw
color: "98"
-description: "A sharp-toothed saw for cutting planks from logs at a workbench."
-value: 30
+description: A sharp-toothed saw for cutting planks from logs at a workbench.
+name: saw
stackable: false
+tool:
+ type: saw
+value: 30
diff --git a/data/items/tools/shears.yaml b/data/items/tools/shears.yaml
index 0fbbafb..c9739f4 100644
--- a/data/items/tools/shears.yaml
+++ b/data/items/tools/shears.yaml
@@ -1,5 +1,6 @@
+color: FA
+description: A pair of shears for shearing sheep.
name: shears
-color: "FA"
-description: "A pair of shears for shearing sheep."
+tool:
+ type: shears
value: 5
-tool_type: shears
diff --git a/data/items/tools/small_fishing_net.yaml b/data/items/tools/small_fishing_net.yaml
index 2d3c6bd..9b127b6 100644
--- a/data/items/tools/small_fishing_net.yaml
+++ b/data/items/tools/small_fishing_net.yaml
@@ -1,14 +1,14 @@
+color: E6
+description: A small hand-held fishing net.
+equipment:
+ other:
+ strength: 0
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: small fishing net
-color: "E6"
-description: "A small hand-held fishing net."
-value: 5
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-stats:
- attack_bonus: 0
- strength_bonus: 0
- defense_bonus: 0
-speed: 5
-tool_type: small_net
-tool_speed: 0
+tool:
+ speed: 0
+ type: small_net
+value: 5
diff --git a/data/items/tools/spade.yaml b/data/items/tools/spade.yaml
index aab5ee8..e755981 100644
--- a/data/items/tools/spade.yaml
+++ b/data/items/tools/spade.yaml
@@ -1,6 +1,7 @@
+color: F1
+description: A metal spade used for planting seeds and harvesting crops.
name: spade
-color: "F1"
-description: "A metal spade used for planting seeds and harvesting crops."
-value: 8
stackable: false
-tool_type: spade
+tool:
+ type: spade
+value: 8
diff --git a/data/items/tools/steel_axe.yaml b/data/items/tools/steel_axe.yaml
index 929bc9e..c0f5a7f 100644
--- a/data/items/tools/steel_axe.yaml
+++ b/data/items/tools/steel_axe.yaml
@@ -1,18 +1,21 @@
+color: FD
+description: A sturdy steel axe, good for woodcutting.
+equipment:
+ attack:
+ crush: 11
+ slash: 15
+ stab: -2
+ attack_type: slash
+ other:
+ strength: 12
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: steel axe
-color: "FD"
-description: "A sturdy steel axe, good for woodcutting."
-value: 75
-stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
-stats:
- stab_attack: -2
- slash_attack: 15
- crush_attack: 11
- strength_bonus: 12
-speed: 5
-tool_type: axe
-tool_speed: 3
requirements:
accuracy: 5
+stackable: false
+tool:
+ speed: 3
+ type: axe
+value: 75
diff --git a/data/items/tools/steel_pickaxe.yaml b/data/items/tools/steel_pickaxe.yaml
index 2acd5e5..7560612 100644
--- a/data/items/tools/steel_pickaxe.yaml
+++ b/data/items/tools/steel_pickaxe.yaml
@@ -1,18 +1,21 @@
+color: FD
+description: A sturdy steel pickaxe, good for mining.
+equipment:
+ attack:
+ crush: 4
+ slash: -2
+ stab: 10
+ attack_type: stab
+ other:
+ strength: 7
+ slot: main_hand
+ speed: 5
+ type: melee_weapon
name: steel pickaxe
-color: "FD"
-description: "A sturdy steel pickaxe, good for mining."
-value: 75
-stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
-stats:
- stab_attack: 10
- slash_attack: -2
- crush_attack: 4
- strength_bonus: 7
-speed: 5
-tool_type: pickaxe
-tool_speed: 1.25
requirements:
accuracy: 5
+stackable: false
+tool:
+ speed: 1.25
+ type: pickaxe
+value: 75
diff --git a/data/items/tools/unfired_pie_dish.yaml b/data/items/tools/unfired_pie_dish.yaml
index eb5d2b6..bc0c5a5 100644
--- a/data/items/tools/unfired_pie_dish.yaml
+++ b/data/items/tools/unfired_pie_dish.yaml
@@ -1,12 +1,11 @@
color: "AD"
craft:
- consume:
+ ingredients:
- items:
- soft_clay
quantity: 1
level: 7
message: "You shape the clay into a %n."
- skill: crafting
station:
- pottery_wheel
type: crafting
diff --git a/data/items/tools/unfired_plant_pot.yaml b/data/items/tools/unfired_plant_pot.yaml
index 3c6c28f..30ccd83 100644
--- a/data/items/tools/unfired_plant_pot.yaml
+++ b/data/items/tools/unfired_plant_pot.yaml
@@ -1,12 +1,11 @@
color: "AD"
craft:
- consume:
+ ingredients:
- items:
- soft_clay
quantity: 1
level: 19
message: "You shape the clay into a %n."
- skill: crafting
station:
- pottery_wheel
type: crafting
diff --git a/data/items/tools/unfired_pot.yaml b/data/items/tools/unfired_pot.yaml
index 26e8771..9dc2406 100644
--- a/data/items/tools/unfired_pot.yaml
+++ b/data/items/tools/unfired_pot.yaml
@@ -1,12 +1,11 @@
color: "AD"
craft:
- consume:
+ ingredients:
- items:
- soft_clay
quantity: 1
level: 1
message: "You shape the clay into a %n."
- skill: crafting
station:
- pottery_wheel
type: crafting
diff --git a/data/items/tools/watering_can.yaml b/data/items/tools/watering_can.yaml
index eab9b0d..4d747a6 100644
--- a/data/items/tools/watering_can.yaml
+++ b/data/items/tools/watering_can.yaml
@@ -1,6 +1,7 @@
-name: watering can
color: "27"
-description: "A watering can for hydrating farming patches. Watering eliminates disease risk."
-value: 12
+description: A watering can for hydrating farming patches. Watering eliminates disease risk.
+name: watering can
stackable: false
-tool_type: watering_can
+tool:
+ type: watering_can
+value: 12
diff --git a/data/rooms/intro/2001.yaml b/data/rooms/intro/2001.yaml
index 0bb9c16..4b21ab8 100644
--- a/data/rooms/intro/2001.yaml
+++ b/data/rooms/intro/2001.yaml
@@ -17,6 +17,12 @@ item_spawns:
- id: bronze_axe
quantity: 1
respawn_ticks: 1
+ - id: pot_of_flour
+ quantity: 1
+ respawn_ticks: 0
+ - id: bucket_of_water
+ quantity: 1
+ respawn_ticks: 0
mobs: []
name: Test Room
objects:
diff --git a/internal/admin/api_items.go b/internal/admin/api_items.go
index d5e707e..721a6f8 100644
--- a/internal/admin/api_items.go
+++ b/internal/admin/api_items.go
@@ -95,6 +95,7 @@ func (s *AdminServer) updateItem(w http.ResponseWriter, r *http.Request, id stri
http.Error(w, `{"error":"failed to write item"}`, http.StatusInternalServerError)
return
}
+ m["_raw"] = string(newContent)
s.undoStack.Push(ChangeDesc{
Description: fmt.Sprintf("Update item %s", id),
diff --git a/internal/admin/server.go b/internal/admin/server.go
index df6d5d9..ff2bebd 100644
--- a/internal/admin/server.go
+++ b/internal/admin/server.go
@@ -403,7 +403,7 @@ func (s *AdminServer) doUndo(w http.ResponseWriter, r *http.Request) {
return
}
s.rebuildAfterUndo(change)
- writeJSON(w, map[string]any{"message": "Undid: " + change.Description})
+ writeJSON(w, map[string]any{"message": "Undid: " + change.ShortDesc()})
}
func (s *AdminServer) doRedo(w http.ResponseWriter, r *http.Request) {
@@ -417,7 +417,7 @@ func (s *AdminServer) doRedo(w http.ResponseWriter, r *http.Request) {
return
}
s.rebuildAfterUndo(change)
- writeJSON(w, map[string]any{"message": "Redid: " + change.Description})
+ writeJSON(w, map[string]any{"message": "Redid: " + change.ShortDesc()})
}
func (s *AdminServer) rebuildAfterUndo(change *ChangeDesc) {
diff --git a/internal/admin/static/admin.css b/internal/admin/static/admin.css
index 6da338e..bdf9b60 100644
--- a/internal/admin/static/admin.css
+++ b/internal/admin/static/admin.css
@@ -11,6 +11,7 @@ body{font-family:monospace;background:var(--bg);color:var(--text);min-height:100
.nav .undo-bar span{color:#888;flex:1;text-align:right;margin-right:8px}
.layout{display:flex;height:calc(100vh - 38px)}
.main{flex:1;overflow:auto;position:relative;user-select:none;-webkit-user-select:none}
+#mapSvg{display:block}
.panel{width:300px;background:var(--panel);border-left:1px solid var(--border);overflow-y:auto;padding:16px;flex-shrink:0;position:relative}
.panel-resize-handle{position:absolute;left:-3px;top:0;bottom:0;width:6px;cursor:col-resize;z-index:5;background:transparent}
.panel-resize-handle:hover{background:rgba(255,255,255,.06)}
@@ -153,6 +154,7 @@ g.ud-hover:hover text{font-weight:bold}
.panel-tab-content .mini-label{font-size:9px;color:#888;display:block;margin-bottom:1px;margin-top:3px}
.panel-tab-content .mini-input{width:60px!important;display:inline-block;margin-right:4px}
.obj-card{background:rgba(255,255,255,.02);border:1px solid var(--border);border-radius:5px;margin-bottom:10px;overflow:hidden;flex:1 1 calc(50% - 5px);min-width:340px;max-width:calc(50% - 5px)}
+.obj-card.obj-card-full{flex:1 1 100%;max-width:100%}
.obj-fields-wrap{display:flex;flex-wrap:wrap;gap:10px}
.obj-card-header{display:flex;align-items:center;gap:8px;padding:8px 12px;background:rgba(15,52,96,.3);cursor:pointer;user-select:none;font-size:13px;font-weight:bold}
.obj-card-header:hover{background:rgba(15,52,96,.5)}
@@ -166,7 +168,7 @@ g.ud-hover:hover text{font-weight:bold}
.obj-field input,.obj-field textarea,.obj-field select{padding:4px 6px;font-size:11px;background:var(--input-bg);color:var(--text);border:1px solid var(--input-border);border-radius:3px;font-family:monospace;width:100%}
.obj-field textarea{resize:vertical;min-height:28px}
.obj-field.obj-wide{grid-column:1/-1}
-.obj-field.obj-check{flex-direction:row;align-items:flex-end;gap:4px;padding-bottom:3px}
+.obj-field.obj-check{flex-direction:row;align-items:center;gap:4px;padding-bottom:3px}
.obj-field.obj-check input{width:auto}
.obj-field.obj-check span{font-size:11px;color:var(--text)}
.obj-color-row{display:flex;align-items:center;gap:4px}
@@ -210,3 +212,16 @@ g.ud-hover:hover text{font-weight:bold}
.obj-sub-row-line+.obj-sub-row-line{margin-top:4px}
.obj-inline-compact .obj-card-grid{grid-template-columns:repeat(auto-fill,minmax(60px,1fr))!important}
.obj-inline-compact .obj-narrow input{width:50px}
+.ingredients-table{border:1px solid var(--border);border-radius:4px;overflow:hidden;margin-top:4px}
+.ingredients-header-row{display:flex;gap:4px;padding:4px 6px;background:rgba(255,255,255,.03);font-size:9px;color:#888;text-transform:uppercase;letter-spacing:.5px;border-bottom:1px solid var(--border)}
+.ingredients-ingredient{border-bottom:1px solid rgba(255,255,255,.04)}
+.ingredients-ingredient:last-child{border-bottom:none}
+.ingredients-pair{display:flex;gap:4px;padding:3px 6px;align-items:center;border-top:1px solid rgba(255,255,255,.03)}
+.ingredients-pair:first-child{border-top:none}
+.ingredients-pair-add{padding:2px 6px 4px}
+.ingredients-col{flex-shrink:0}
+.ingredients-col-qty{width:48px;display:flex;align-items:center}
+.ingredients-col-items{flex:1;min-width:0}
+.ingredients-col-byproducts{flex:1;min-width:0}
+.ingredients-col-x{width:26px;display:flex;align-items:center;justify-content:center}
+.ingredients-col-num{width:24px;text-align:center;color:#888;font-size:10px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
diff --git a/internal/admin/static/editor.js b/internal/admin/static/editor.js
index 1ed0653..37bc797 100644
--- a/internal/admin/static/editor.js
+++ b/internal/admin/static/editor.js
@@ -59,7 +59,7 @@ function renderDirGroup(name, ids, filter) {
displayIds = ids.filter(function(id) { return String(id).toLowerCase().indexOf(filter) >= 0; });
if (displayIds.length === 0) return '';
}
- var expanded = isFiltering ? true : (collapsedDirs[name] === false || (!name && collapsedDirs[''] === undefined));
+ var expanded = isFiltering ? true : (collapsedDirs[name] === true);
var displayName = name || editorType.charAt(0).toUpperCase() + editorType.slice(1);
var h = '<div class="dir-group">';
if (name) {
@@ -79,7 +79,7 @@ function renderDirGroup(name, ids, filter) {
}
function toggleDir(name) {
- collapsedDirs[name] = collapsedDirs[name] === false ? true : false;
+ collapsedDirs[name] = collapsedDirs[name] !== true;
renderList('');
}
@@ -206,6 +206,13 @@ function switchTab(e, tab) {
tabs.forEach(function(t) { t.style.display = 'none'; });
var target = document.getElementById('tab' + tab.charAt(0).toUpperCase() + tab.slice(1));
if (target) target.style.display = 'block';
+
+ if (tab === 'yaml' && currentID) {
+ API.get('/api/' + editorType + '/' + encodeURIComponent(currentID)).then(function(data) {
+ var pre = document.querySelector('#tabYaml pre');
+ if (pre) pre.textContent = data._raw || JSON.stringify(data, null, 2);
+ });
+ }
}
function getNested(obj, path) {
diff --git a/internal/admin/static/itemeditor.js b/internal/admin/static/itemeditor.js
new file mode 100644
index 0000000..d02e7ab
--- /dev/null
+++ b/internal/admin/static/itemeditor.js
@@ -0,0 +1,1557 @@
+var itemData = null;
+var itemID = null;
+var expandedCards = {};
+var addedSections = {};
+
+var SECTIONS = [
+ {
+ id: 'core', label: 'Core', always: true,
+ fields: [
+ ['name', 'Name', 'text', 'Copper Ore'],
+ ['color', 'Color', 'color', 'B2'],
+ ['value', 'Value', 'number', '0'],
+ ['stackable', 'Stackable', 'checkbox'],
+ ['quality', 'Quality', 'number', ''],
+ ['max_quality', 'Max Quality', 'number', ''],
+ ['aliases', 'Aliases', 'text', 'comma separated'],
+ ['description', 'Description', 'textarea', 'A chunk of copper ore.'],
+ ]
+ },
+ {
+ id: 'equipment', label: 'Equipment', path: 'equipment',
+ detect: function(d) { return d.equipment; },
+ fields: []
+ },
+ {
+ id: 'tool', label: 'Tool', path: 'tool',
+ detect: function(d) { return d.tool; },
+ fields: [
+ ['type', 'Type', 'text', 'pickaxe, axe, hammer'],
+ ['speed', 'Speed', 'number', '1.0', 'narrow'],
+ ]
+ },
+ {
+ id: 'firemaking', label: 'Firemaking',
+ detect: function(d) { return d.burn_ticks || d.fire_level || d.fire_xp; },
+ fields: [
+ ['burn_ticks', 'Burn Ticks', 'number', '100', 'narrow'],
+ ['fire_level', 'Fire Level', 'number', '1', 'narrow'],
+ ['fire_xp', 'Fire XP', 'number', '40', 'narrow'],
+ ]
+ },
+ {
+ id: 'crafting', label: 'Crafting',
+ detect: function(d) { return d.craft; },
+ fields: [],
+ subtables: [
+ {
+ label: 'Recipes to Craft This Item', key: 'craft',
+ fields: [
+ ['type', 'Type', 'select', 'smithing|cooking|crafting|fletching|combine|pharmacy|construction'],
+ ['subtype', 'Subtype', 'select', '|smelt|clean'],
+ ['level', 'Level', 'number', '', 'narrow'],
+ ['xp', 'XP', 'number', '', 'narrow'],
+ ['wait', 'Wait', 'number', '', 'narrow'],
+ ['output_qty', 'Output Qty', 'number', '', 'narrow'],
+ ['station', 'Station', 'select', '|anvil|cooking_range|fire|furnace|pottery_oven|pottery_wheel|spinning_wheel|workbench'],
+ ['tool', 'Tool', 'select', '|pickaxe|axe|fire|chisel|hammer|knife|needle|rake|saw|shears|spade|watering_can|fishing_rod|fly_rod|harpoon|small_net|big_net|lobster_pot'],
+ ['fail', 'Fail Item', 'search', '', '', null, 'items'],
+ ['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'],
+ ],
+ 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'],
+ ],
+ subSubtables: [
+ {label:'Ingredients', key:'ingredients', fields:[
+ ['quantity', 'Qty', 'number', '1', 'narrow'],
+ ]},
+ {label:'Steps', key:'steps', fields:[
+ ['tick', 'Ticks', 'number', '', 'narrow'],
+ ['message', 'Message', 'text', '', 'wide'],
+ ]},
+ ],
+ }
+ ]
+ },
+ {
+ id: 'search', label: 'Search',
+ detect: function(d) { return d.search_table || d.search_ticks; },
+ fields: [
+ ['search_table', 'Search Table', 'search', '', '', null, 'drops'],
+ ['search_ticks', 'Search Ticks', 'number', '', 'narrow'],
+ ['search_message', 'Search Message', 'text', 'You search...', 'wide'],
+ ]
+ },
+ {
+ id: 'food', label: 'Food',
+ detect: function(d) { return d.heal_value || d.eat_message; },
+ fields: [
+ ['heal_value', '+HP', 'number', '', 'narrow'],
+ ['eat_message', 'Eat Message', 'text', 'You eat the...', 'wide'],
+ ]
+ },
+ {
+ id: 'farming', label: 'Farming',
+ detect: function(d) { return d.farm_patch_type; },
+ fields: [
+ ['farm_patch_type', 'Patch Type', 'select', 'herb|allotment|flower|bush|tree'],
+ ['farm_level', 'Farm Level', 'number', '', 'narrow'],
+ ['farm_plant_xp', 'Plant XP', 'number', '', 'narrow'],
+ ['farm_harvest_xp', 'Harvest XP', 'number', '', 'narrow'],
+ ['farm_stages', 'Stages', 'number', '', 'narrow'],
+ ['farm_product', 'Product', 'search', '', '', null, 'items'],
+ ['farm_min_yield', 'Min Yield', 'number', '', 'narrow'],
+ ['farm_max_yield', 'Max Yield', 'number', '', 'narrow'],
+ ]
+ },
+ {
+ id: 'potions', label: 'Potions',
+ detect: function(d) { return d.potion_effect; },
+ fields: [
+ ['potion_effect', 'Effect', 'text', 'strength, accuracy, heal'],
+ ['potion_bonus', 'Bonus', 'number', '', 'narrow'],
+ ['potion_duration', 'Duration', 'number', '', 'narrow'],
+ ]
+ },
+];
+
+function initItemEditor() {
+ editorType = 'items';
+ editorFields = [];
+ loadList();
+ if (window.location.hash) loadItemEditor(window.location.hash.substring(1));
+}
+
+function cardPath(sec) {
+ return sec.path || '';
+}
+
+function getVal(data, sec, key) {
+ var p = cardPath(sec);
+ if (sec.isArray) {
+ return data[p] && data[p][key] !== undefined ? data[p][key] : '';
+ }
+ var fp = p ? p + '.' + key : key;
+ var v = getNested(data, fp);
+ return v === undefined || v === null ? '' : v;
+}
+
+function fieldID(sec, key, idx) {
+ if (idx !== undefined && idx >= 0) return 'f_' + sec.id + '_' + idx + '_' + key.replace(/\./g, '_');
+ return 'f_' + sec.id + '_' + key.replace(/\./g, '_');
+}
+
+function fieldIDSub(sec, subKey, idx, fieldKey) {
+ return 'f_' + sec.id + '_' + subKey + '_' + idx + '_' + fieldKey;
+}
+
+function fieldIDSubSub(sec, subKey, rowIdx, subSubKey, subSubIdx, fieldKey) {
+ return 'f_' + sec.id + '_' + subKey + '_' + rowIdx + '__' + subSubKey + '_' + subSubIdx + '_' + fieldKey;
+}
+
+function loadItemEditor(id) {
+ itemID = id;
+ currentID = id;
+ renderList('');
+ window.location.hash = id;
+ API.get('/api/items/' + encodeURIComponent(id)).then(function(data) {
+ itemData = data;
+ renderItemEditor(id, data);
+ }).catch(function(e) {
+ $('#editorMain').innerHTML = '<p style="color:var(--danger)">Failed to load: ' + esc(e.message) + '</p>';
+ });
+}
+
+function renderItemEditor(id, data) {
+ var html = '<h2>Item: ' + esc(id) + '</h2>';
+ html += '<div class="tabs"><button class="tab active" onclick="switchTab(event,\'fields\')">Fields</button>';
+ html += '<button class="tab" onclick="switchTab(event,\'yaml\')">Raw YAML</button></div>';
+
+ html += '<div class="tab-content" id="tabFields">';
+ html += '<div class="obj-fields-wrap">';
+
+ SECTIONS.forEach(function(sec) {
+ if (!sec.always && sec.detect && !sec.detect(data) && !addedSections[sec.id]) return;
+ html += renderCard(sec, data);
+ });
+
+ html += '</div>';
+
+ html += '<div class="add-section-bar">';
+ html += '<select id="addSectionSelect">';
+ html += '<option value="">+ Add Section...</option>';
+ SECTIONS.forEach(function(sec) {
+ if (sec.always) return;
+ if (sec.detect && sec.detect(data)) return;
+ if (addedSections[sec.id]) return;
+ html += '<option value="' + sec.id + '">' + esc(sec.label) + '</option>';
+ });
+ html += '</select>';
+ html += '<button class="btn btn-primary btn-sm" onclick="addSection()">Add</button>';
+ html += '</div>';
+ html += '</div>';
+
+ html += '<div class="tab-content" id="tabYaml" style="display:none">';
+ html += '<pre>' + esc(data._raw || JSON.stringify(data, null, 2)) + '</pre>';
+ html += '</div>';
+
+ html += '<div class="btn-row">';
+ html += '<button class="btn btn-primary" onclick="saveItem()">Save</button>';
+ html += '<button class="btn" onclick="duplicateItem()">Duplicate</button>';
+ html += '<button class="btn btn-danger" onclick="deleteItem()">Delete Item</button>';
+ html += '</div>';
+
+ $('#editorMain').innerHTML = html;
+ setupSearchFields();
+ setupCraftTypeChange();
+ setTimeout(function() {
+ document.querySelectorAll('.color-field').forEach(function(el) { bindColorField(el); });
+ }, 50);
+}
+
+function renderCurrent() {
+ if (!itemData || !itemID) return;
+ renderItemEditor(itemID, itemData);
+}
+
+// =========================================================================
+// Card rendering
+// =========================================================================
+
+function renderCard(sec, data) {
+ var collapsed = expandedCards[sec.id] === false;
+ var h = '<div class="obj-card' + (sec.id === 'crafting' ? ' obj-card-full' : '') + '" data-card="' + sec.id + '">';
+ h += '<div class="obj-card-header" onclick="toggleCard(\'' + sec.id + '\')">';
+ h += '<span class="obj-card-arrow">' + (collapsed ? '&#9654;' : '&#9660;') + '</span>';
+ h += '<span class="obj-card-label">' + sec.label + '</span>';
+ if (!sec.always) {
+ h += '<button class="btn btn-sm btn-danger obj-card-remove" onclick="event.stopPropagation();removeSection(\'' + sec.id + '\')">X</button>';
+ }
+ h += '</div>';
+ if (!collapsed) {
+ h += '<div class="obj-card-body">';
+
+ if (sec.isArray) {
+ h += renderArraySection(sec, data);
+ } else if (sec.id === 'core') {
+ h += renderCoreCard(data);
+ } else if (sec.id === 'equipment') {
+ h += renderEquipmentCard(data);
+ } else {
+ h += '<div class="obj-card-grid">';
+ if (sec.fields) sec.fields.forEach(function(f) {
+ h += renderField(sec, f, data, -1);
+ });
+ h += '</div>';
+
+ if (sec.inline) {
+ sec.inline.forEach(function(il) {
+ if (il.type === 'tags') {
+ h += renderTags(sec, il, data);
+ } else if (il.type === 'kv') {
+ h += renderKV(sec, il, data);
+ } else {
+ h += '<div class="obj-inline-group">';
+ h += '<span class="obj-inline-label">' + esc(il.label) + '</span>';
+ h += '<div style="display:flex;gap:8px;flex-wrap:nowrap;margin-top:4px">';
+ il.fields.forEach(function(f) {
+ h += renderField(sec, f, data, -1, il.path);
+ });
+ h += '</div>';
+ h += '</div>';
+ }
+ });
+ }
+
+ if (sec.subtables) {
+ sec.subtables.forEach(function(st) {
+ h += renderSubtable(sec, st, data);
+ });
+ }
+ }
+ h += '</div>';
+ }
+ h += '</div>';
+ return h;
+}
+
+function renderCoreCard(data) {
+ var sec = SECTIONS[0];
+ var h = '<div class="obj-core-row">';
+ h += renderField(sec, sec.fields[0], data, -1, null, 'flex:1');
+ h += renderField(sec, sec.fields[1], data, -1);
+ h += renderField(sec, sec.fields[6], data, -1, null, 'flex:1');
+ h += '</div>';
+ h += '<div class="obj-core-row" style="align-items:center">';
+ h += renderField(sec, sec.fields[2], data, -1);
+ h += '<div style="display:flex;align-items:center;gap:6px">';
+ h += renderField(sec, sec.fields[3], data, -1);
+ h += '</div>';
+ h += '<div style="display:flex;gap:8px;margin-left:auto">';
+ h += renderField(sec, sec.fields[4], data, -1);
+ h += renderField(sec, sec.fields[5], data, -1);
+ h += '</div>';
+ h += '</div>';
+ var descFid = fieldID(sec, 'description', -1);
+ var descVal = getVal(itemData, sec, 'description');
+ var dstr = typeof descVal === 'object' ? (Array.isArray(descVal) ? descVal.map(function(v){return v.text||'';}).join('\n') : JSON.stringify(descVal)) : String(descVal);
+ h += '<label class="obj-field obj-wide"><span>Description</span><textarea id="' + descFid + '" rows="6" style="min-height:56px">' + esc(dstr) + '</textarea></label>';
+ return h;
+}
+
+// =========================================================================
+// Equipment card (custom render with 3 stat blocks)
+// =========================================================================
+
+var EQUIP_STAT_FIELDS = {
+ attack: [
+ ['stab', 'Stab', 'number', '', 'narrow'],
+ ['slash', 'Slash', 'number', '', 'narrow'],
+ ['crush', 'Crush', 'number', '', 'narrow'],
+ ['ranged', 'Ranged', 'number', '', 'narrow'],
+ ['science', 'Science', 'number', '', 'narrow'],
+ ],
+ defense: [
+ ['stab', 'Stab', 'number', '', 'narrow'],
+ ['slash', 'Slash', 'number', '', 'narrow'],
+ ['crush', 'Crush', 'number', '', 'narrow'],
+ ['ranged', 'Ranged', 'number', '', 'narrow'],
+ ['science', 'Science', 'number', '', 'narrow'],
+ ],
+ other: [
+ ['strength', 'Strength', 'number', '', 'narrow'],
+ ['ranged', 'Ranged', 'number', '', 'narrow'],
+ ['science', 'Science', 'number', '', 'narrow'],
+ ['technology', 'Tech', 'number', '', 'narrow'],
+ ],
+};
+
+function equipFieldID(key) {
+ return 'f_equipment_' + key;
+}
+
+function renderEquipField(key, label, type, hint, val, wide, searchEntity) {
+ var fid = equipFieldID(key);
+ val = val === undefined || val === null ? '' : val;
+
+ function wrap(cls, inner) {
+ var w = (wide === 'wide') ? ' obj-wide' : '';
+ if (wide === 'narrow') w = ' obj-narrow';
+ return '<label class="' + cls + w + '">' + inner + '</label>';
+ }
+
+ if (type === 'select') {
+ var opts = hint ? hint.split('|') : [];
+ var inner = '<span>' + esc(label) + '</span><select id="' + fid + '">';
+ opts.forEach(function(o) {
+ inner += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + esc(o) + '</option>';
+ });
+ inner += '</select>';
+ return wrap('obj-field', inner);
+ }
+ if (type === 'search') {
+ var entity = searchEntity || 'items';
+ var ph = hint ? ' placeholder="' + esc(hint) + '"' : ' placeholder="Search ' + entity + '..."';
+ return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '"' + ph + '><div class="search-results" style="display:none"></div></div>');
+ }
+ var ph = hint ? ' placeholder="' + esc(hint) + '"' : '';
+ return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + '>');
+}
+
+function renderEquipmentCard(data) {
+ var eq = data.equipment || {};
+ var h = '';
+ var isAmmo = eq.type === 'ammo';
+
+ // Row 1: type, slot
+ h += '<div class="obj-core-row">';
+ h += renderEquipField('type', 'Type', 'select', 'melee_weapon|ranged_weapon|tech_weapon|armor|tool|ammo', eq.type);
+ if (!isAmmo) {
+ h += renderEquipField('slot', 'Slot', 'select', 'head|neck|torso|legs|hands|feet|back|ammo|main_hand|off_hand|ring', eq.slot);
+ } else {
+ h += '<input type="hidden" id="' + equipFieldID('slot') + '" value="ammo">';
+ }
+ h += '</div>';
+
+ // Ammo: recoverable checkbox
+ if (isAmmo) {
+ h += '<label class="obj-field obj-check" style="margin-top:4px"><input type="checkbox" id="f_core_recoverable"' + (data.recoverable ? ' checked' : '') + '> <span>Recoverable</span></label>';
+ }
+
+ // Row 2: attack_type, speed, junk
+ var isMelee = eq.type === 'melee_weapon';
+ var isWeapon = ['melee_weapon', 'ranged_weapon', 'tech_weapon'].indexOf(eq.type) >= 0;
+ h += '<div class="obj-core-row"' + (!isMelee && !isWeapon ? ' style="display:none"' : '') + '>';
+ if (isMelee) {
+ h += renderEquipField('attack_type', 'Attack Type', 'select', 'stab|slash|crush', eq.attack_type);
+ }
+ if (isWeapon) {
+ h += renderEquipField('speed', 'Speed', 'number', '', eq.speed, 'narrow');
+ }
+ h += renderEquipField('junk', 'Junk', 'search', '', eq.junk, '', 'items');
+ h += '</div>';
+
+ // 3 buttons row
+ var btns = [];
+ if (!eq.attack) btns.push('<button class="btn btn-sm obj-sub-add" style="flex:1" onclick="addEquipBlock(\'attack\')">+ Add Attack Stats</button>');
+ if (!eq.defense) btns.push('<button class="btn btn-sm obj-sub-add" style="flex:1" onclick="addEquipBlock(\'defense\')">+ Add Defense Stats</button>');
+ if (!eq.other) btns.push('<button class="btn btn-sm obj-sub-add" style="flex:1" onclick="addEquipBlock(\'other\')">+ Add Other Stats</button>');
+ if (btns.length > 0) {
+ h += '<div style="display:flex;gap:6px;margin-top:10px">' + btns.join('') + '</div>';
+ }
+
+ // Stat blocks
+ if (eq.attack) h += renderEquipStatBlock('Attack Stats', 'attack', eq.attack, EQUIP_STAT_FIELDS.attack);
+ if (eq.defense) h += renderEquipStatBlock('Defense Stats', 'defense', eq.defense, EQUIP_STAT_FIELDS.defense);
+ if (eq.other) h += renderEquipStatBlock('Other Stats', 'other', eq.other, EQUIP_STAT_FIELDS.other);
+
+ // Requirements KV (at root level, stored in itemData.requirements)
+ h += '<div style="margin-top:8px">';
+ h += renderKV({id: 'equipment_req'}, {label:'Requirements', key:'requirements', type:'kv', valType:'number'}, data);
+ h += '</div>';
+
+ return h;
+}
+
+function renderEquipStatBlock(label, blockKey, blockData, fields) {
+ var h = '<div class="obj-inline-group" style="margin-top:8px">';
+ h += '<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">';
+ h += '<span class="obj-inline-label" style="margin-bottom:0;flex:1">' + esc(label) + '</span>';
+ h += '<button class="btn btn-sm btn-danger" onclick="removeEquipBlock(\'' + blockKey + '\')">X</button>';
+ h += '</div>';
+ h += '<div style="display:flex;gap:8px;flex-wrap:nowrap;margin-top:4px">';
+ fields.forEach(function(f) {
+ var fid = 'f_equipment_' + blockKey + '_' + f[0];
+ var val = blockData[f[0]];
+ val = val === undefined || val === null ? '' : val;
+ var extraCls = f[4] === 'narrow' ? ' obj-narrow' : '';
+ h += '<label class="obj-field' + extraCls + '"><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"></label>';
+ });
+ h += '</div>';
+ h += '</div>';
+ return h;
+}
+
+function addEquipBlock(blockKey) {
+ if (!itemData.equipment) itemData.equipment = {};
+ var empty = {};
+ EQUIP_STAT_FIELDS[blockKey].forEach(function(f) { empty[f[0]] = 0; });
+ itemData.equipment[blockKey] = empty;
+ renderCurrent();
+}
+
+function removeEquipBlock(blockKey) {
+ if (itemData.equipment) delete itemData.equipment[blockKey];
+ renderCurrent();
+}
+
+// =========================================================================
+// Generic field render (unchanged)
+// =========================================================================
+
+function renderField(sec, f, data, idx, subPath, optStyle) {
+ var key = f[0], label = f[1], type = f[2], hint = f[3], wide = f[4], showIf = f[5], searchEntity = f[6];
+ var fp = subPath ? subPath + '.' + key : key;
+ var fid = fieldID(sec, fp, idx);
+ var val;
+
+ if (sec.isArray && idx >= 0) {
+ var arr = itemData[cardPath(sec)] || [];
+ var item = arr[idx] || {};
+ val = item[key];
+ } else if (subPath) {
+ var sectionRoot = itemData[cardPath(sec)] || {};
+ var subObj = sectionRoot[subPath] || {};
+ val = subObj[key];
+ } else {
+ val = getVal(itemData, sec, key);
+ }
+ val = val === undefined || val === null ? '' : val;
+
+ var hiddenStyle = '';
+ if (showIf) {
+ var sectionData = itemData[cardPath(sec)] || {};
+ if (!showIf(sectionData)) hiddenStyle = ' style="display:none"';
+ }
+
+ function wrap(cls, inner) {
+ var w = (wide === 'wide') ? ' obj-wide' : '';
+ if (wide === 'narrow') w = ' obj-narrow';
+ var s = optStyle ? ' style="' + optStyle + '"' : '';
+ return '<label class="' + cls + w + '"' + hiddenStyle + s + '>' + inner + '</label>';
+ }
+
+ if (type === 'checkbox') {
+ return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(label) + '</span>');
+ }
+ if (type === 'json') {
+ var jstr = typeof val === 'object' ? JSON.stringify(val) : String(val);
+ return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}">' + esc(jstr) + '</textarea>');
+ }
+ if (type === 'textarea') {
+ var dstr = typeof val === 'object' ? (Array.isArray(val) ? val.map(function(v){return v.text||'';}).join('\n') : JSON.stringify(val)) : String(val);
+ return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3">' + esc(dstr) + '</textarea>');
+ }
+ if (type === 'select') {
+ var opts = hint ? hint.split('|') : [];
+ var inner = '<span>' + esc(label) + '</span><select id="' + fid + '">';
+ opts.forEach(function(o) {
+ inner += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + esc(o) + '</option>';
+ });
+ inner += '</select>';
+ return wrap('obj-field', inner);
+ }
+ if (type === 'color') {
+ var swatch = '<span class="color-swatch" style="background:#' + escAttr(String(val) || '808080') + '"></span>';
+ return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"></div>');
+ }
+ if (type === 'search') {
+ var entity = searchEntity || 'items';
+ var ph = hint ? ' placeholder="' + esc(hint) + '"' : ' placeholder="Search ' + entity + '..."';
+ return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '"' + ph + '><div class="search-results" style="display:none"></div></div>');
+ }
+ var ph = hint ? ' placeholder="' + esc(hint) + '"' : '';
+ return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + '>');
+}
+
+// =========================================================================
+// Array section rendering
+// =========================================================================
+
+function renderArraySection(sec, data) {
+ var arr = data[cardPath(sec)] || [];
+ var h = '';
+ arr.forEach(function(item, idx) {
+ h += '<div class="obj-sub-row" data-idx="' + idx + '">';
+ h += '<div class="obj-card-grid">';
+ sec.fields.forEach(function(f) {
+ h += renderField(sec, f, data, idx);
+ });
+ h += '</div>';
+ h += '<button class="btn btn-sm btn-danger obj-sub-remove" onclick="removeArrayItem(\'' + sec.id + '\',' + idx + ')">X</button>';
+ h += '</div>';
+ });
+ h += '<button class="btn btn-sm obj-sub-add" onclick="addArrayItem(\'' + sec.id + '\')">+ Add ' + esc(sec.label).slice(0,-1) + '</button>';
+ return h;
+}
+
+// =========================================================================
+// Subtable rendering
+// =========================================================================
+
+function renderSubFieldHTML(sec, stKey, idx, f, val) {
+ var fid = fieldIDSub(sec, stKey, idx, f[0]);
+ val = val === undefined || val === null ? '' : val;
+ var extraCls = '';
+ var extraStyle = '';
+ 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%"';
+ if (f[2] === 'search') {
+ var entity = f[6] || 'items';
+ var ph = f[3] ? ' placeholder="' + escAttr(f[3]) + '"' : ' placeholder="Search ' + entity + '..."';
+ return '<label class="obj-field obj-search' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>';
+ }
+ if (f[2] === 'checkbox') {
+ return '<label class="obj-field obj-check' + extraCls + '"' + extraStyle + '><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(f[1]) + '</span></label>';
+ }
+ if (f[2] === 'select') {
+ var opts = f[3] ? f[3].split('|') : [];
+ var sel = '<label class="obj-field' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><select id="' + fid + '">';
+ opts.forEach(function(o) {
+ sel += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + esc(o || '(none)') + '</option>';
+ });
+ 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>';
+}
+
+function renderSubtable(sec, st, data) {
+ var p = cardPath(sec);
+ var sectionRoot = p ? (itemData[p] || {}) : itemData;
+ var raw = sectionRoot[st.key];
+ var arr;
+ if (st.single) {
+ arr = raw ? [raw] : [];
+ } else if (Array.isArray(raw)) {
+ arr = raw;
+ } else if (raw && typeof raw === 'object') {
+ arr = [raw];
+ } else {
+ arr = [];
+ }
+ var hasSubSubtables = st.subSubtables && st.subSubtables.length > 0;
+ var h = '<div class="obj-subtable">';
+ h += '<div class="obj-subtable-header">' + esc(st.label) + '</div>';
+ arr.forEach(function(item, idx) {
+ h += '<div class="obj-sub-row" style="flex-direction:column;align-items:stretch">';
+
+ function renderGroup(fieldKeys) {
+ fieldKeys.forEach(function(fieldKey) {
+ var f = st.fields.find(function(sf) { return sf[0] === fieldKey; });
+ if (!f) return;
+ if (f[5] && !f[5](item)) return;
+ var val = item[f[0]];
+ h += renderSubFieldHTML(sec, st.key, idx, f, val);
+ });
+ }
+
+ if (st.fieldGroups) {
+ st.fieldGroups.forEach(function(group) {
+ if (group.label) {
+ if (group.label === 'Success') {
+ var hasSuccess = !!(item.fail || item.success_base || item.success_per_level || item.success_cap);
+ var gid = 'sg_' + sec.id + '_' + st.key + '_' + idx;
+ h += '<div class="obj-inline-group success-group" id="' + gid + '" style="margin:2px 0;padding:4px 8px' + (hasSuccess ? '' : ';display:none') + '">';
+ h += '<div style="display:flex;align-items:center;margin-bottom:2px">';
+ h += '<span class="obj-inline-label" style="margin-bottom:0;flex:1">Success</span>';
+ h += '<button class="btn btn-sm btn-danger" style="padding:2px 6px;font-size:10px;min-width:auto" onclick="var g=document.getElementById(\'' + gid + '\');g.style.display=\'none\';var b=document.getElementById(\'' + gid + '_btn\');b.style.display=\'\'">X</button>';
+ h += '</div>';
+ h += '<div class="obj-sub-row-line">';
+ renderGroup(group.fields);
+ h += '</div>';
+ h += '</div>';
+ h += '<button class="btn btn-sm obj-sub-add" id="' + gid + '_btn" style="margin:2px 0' + (hasSuccess ? ';display:none' : '') + '" onclick="var b=document.getElementById(\'' + gid + '_btn\');b.style.display=\'none\';var g=document.getElementById(\'' + gid + '\');g.style.display=\'\'">+ Add Success Rates</button>';
+ } else {
+ h += '<div class="obj-inline-group" style="margin:2px 0;padding:4px 8px">';
+ h += '<span class="obj-inline-label" style="margin-bottom:2px">' + esc(group.label) + '</span>';
+ h += '<div class="obj-sub-row-line">';
+ renderGroup(group.fields);
+ h += '</div>';
+ h += '</div>';
+ }
+ } else {
+ var rightStyle = (group && group.right) ? ' style="justify-content:flex-end"' : '';
+ h += '<div class="obj-sub-row-line"' + rightStyle + '>';
+ var keys = Array.isArray(group) ? group : group.fields;
+ renderGroup(keys);
+ h += '</div>';
+ }
+ });
+ } else {
+ h += '<div class="obj-sub-row-line">';
+ st.fields.forEach(function(f) {
+ if (f[5] && !f[5](item)) return;
+ var val = item[f[0]];
+ h += renderSubFieldHTML(sec, st.key, idx, f, val);
+ });
+ h += '</div>';
+ }
+
+ if (hasSubSubtables) {
+ st.subSubtables.forEach(function(sst) {
+ 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>';
+ return;
+ }
+ }
+ h += renderSubSubtable(sec, st, idx, sst, item);
+ });
+ }
+
+ if (!st.single) {
+ h += '<button class="btn btn-sm btn-danger obj-sub-remove" style="align-self:flex-end;margin:6px 10px" onclick="removeSubRow(\'' + sec.id + '\',\'' + st.key + '\',' + idx + ')">Remove Recipe</button>';
+ }
+ h += '</div>';
+ });
+ if (!st.single || arr.length === 0) {
+ h += '<button class="btn btn-sm obj-sub-add" onclick="addSubRow(\'' + sec.id + '\',\'' + st.key + '\')">+ Add Recipe</button>';
+ }
+ h += '</div>';
+ return h;
+}
+
+function ingredientPairID(sec, subKey, rowIdx, subSubKey, subIdx, pairIdx, fieldKey) {
+ return 'f_' + sec.id + '_' + subKey + '_' + rowIdx + '__' + subSubKey + '_' + subIdx + '_' + fieldKey + '_' + pairIdx;
+}
+
+function renderConsumeSearchHTML(fid, val, entity, placeholder) {
+ val = val === undefined || val === null ? '' : val;
+ return '<div class="obj-field obj-search" style="flex:1;min-width:0"><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" placeholder="' + escAttr(placeholder) + '"><div class="search-results" style="display:none"></div></div></div>';
+}
+
+function addConsumePair(secID, subKey, rowIdx, subSubKey, subIdx) {
+ var sec = SECTIONS.find(function(s) { return s.id === secID; });
+ var existing = document.querySelectorAll('.ingredients-pair[data-sec="' + secID + '"][data-subkey="' + subKey + '"][data-row="' + rowIdx + '"][data-subsub="' + subSubKey + '"][data-subidx="' + subIdx + '"]');
+ var pairIdx = existing.length;
+ var btnRow = document.querySelector('.ingredients-pair-add[data-sec="' + secID + '"][data-subkey="' + subKey + '"][data-row="' + rowIdx + '"][data-subsub="' + subSubKey + '"][data-subidx="' + subIdx + '"]');
+ var itemFid = ingredientPairID(sec, subKey, rowIdx, subSubKey, subIdx, pairIdx, 'items');
+ var bypFid = ingredientPairID(sec, subKey, rowIdx, subSubKey, subIdx, pairIdx, 'byproducts');
+ var row = document.createElement('div');
+ row.className = 'ingredients-pair';
+ row.setAttribute('data-sec', secID);
+ row.setAttribute('data-subkey', subKey);
+ row.setAttribute('data-row', rowIdx);
+ row.setAttribute('data-subsub', subSubKey);
+ row.setAttribute('data-subidx', subIdx);
+ row.setAttribute('data-pair', pairIdx);
+ row.innerHTML = '<div class="ingredients-col ingredients-col-num"></div>'
+ + '<div class="ingredients-col ingredients-col-qty"></div>'
+ + '<div class="ingredients-col ingredients-col-items">' + renderConsumeSearchHTML(itemFid, '', 'items', 'Search items...') + '</div>'
+ + '<div class="ingredients-col ingredients-col-byproducts">' + renderConsumeSearchHTML(bypFid, '', 'items', '(optional)') + '</div>'
+ + '<div class="ingredients-col ingredients-col-x"><button class="btn btn-sm btn-danger" style="padding:2px 6px;font-size:10px;background:var(--danger);color:#fff;border:none;border-radius:3px;cursor:pointer" onclick="removeConsumePair(\'' + secID + '\',\'' + subKey + '\',' + rowIdx + ',\'' + subSubKey + '\',' + subIdx + ',' + pairIdx + ')">X</button></div>';
+ btnRow.parentNode.insertBefore(row, btnRow);
+ setupSearchFields();
+}
+
+function removeConsumePair(secID, subKey, rowIdx, subSubKey, subIdx, pairIdx) {
+ var el = document.querySelector('.ingredients-pair[data-sec="' + secID + '"][data-subkey="' + subKey + '"][data-row="' + rowIdx + '"][data-subsub="' + subSubKey + '"][data-subidx="' + subIdx + '"][data-pair="' + pairIdx + '"]');
+ if (!el) return;
+ var ingredient = el.parentElement;
+ var remaining = ingredient.querySelectorAll('.ingredients-pair');
+ if (remaining.length <= 1) {
+ ingredient.remove();
+ } else {
+ el.remove();
+ }
+}
+
+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)">';
+ if (!isIngredients) {
+ h += '<div class="obj-subtable-header">' + esc(sst.label) + '</div>';
+ }
+
+ if (isIngredients) {
+ h += '<div class="ingredients-table">';
+ h += '<div class="ingredients-header-row">';
+ h += '<div class="ingredients-col ingredients-col-num">#</div>';
+ h += '<div class="ingredients-col ingredients-col-qty">Qty</div>';
+ h += '<div class="ingredients-col ingredients-col-items">Items</div>';
+ h += '<div class="ingredients-col ingredients-col-byproducts">Byproducts</div>';
+ h += '<div class="ingredients-col ingredients-col-x"></div>';
+ h += '</div>';
+
+ var ingredientNum = 0;
+ arr.forEach(function(subItem, subIdx) {
+ ingredientNum++;
+ var itemsArr = Array.isArray(subItem.items) ? subItem.items : (subItem.items ? [subItem.items] : ['']);
+ var bypArr = Array.isArray(subItem.byproducts) ? subItem.byproducts : [];
+ var pairs = Math.max(itemsArr.length, bypArr.length, 1);
+
+ h += '<div class="ingredients-ingredient" data-click-pair="' + subIdx + '">';
+
+ var qtyVal = subItem.quantity !== undefined && subItem.quantity !== null ? subItem.quantity : '';
+ var qtyFid = fieldIDSubSub(sec, st.key, rowIdx, sst.key, subIdx, 'quantity');
+
+ for (var p = 0; p < pairs; p++) {
+ h += '<div class="ingredients-pair" data-sec="' + sec.id + '" data-subkey="' + st.key + '" data-row="' + rowIdx + '" data-subsub="' + sst.key + '" data-subidx="' + subIdx + '" data-pair="' + p + '">';
+
+ if (p === 0) {
+ h += '<div class="ingredients-col ingredients-col-num">' + ingredientNum + '</div>';
+ } else {
+ h += '<div class="ingredients-col ingredients-col-num"></div>';
+ }
+
+ if (p === 0) {
+ h += '<div class="ingredients-col ingredients-col-qty"><label class="obj-field obj-narrow"><input id="' + qtyFid + '" value="' + escAttr(String(qtyVal)) + '" style="width:36px"></label></div>';
+ } else {
+ h += '<div class="ingredients-col ingredients-col-qty"></div>';
+ }
+
+ var itemVal = itemsArr[p] || '';
+ var itemFid = ingredientPairID(sec, st.key, rowIdx, sst.key, subIdx, p, 'items');
+ h += '<div class="ingredients-col ingredients-col-items">' + renderConsumeSearchHTML(itemFid, itemVal, 'items', 'Search items...') + '</div>';
+
+ var bypVal = bypArr[p] || '';
+ var bypFid = ingredientPairID(sec, st.key, rowIdx, sst.key, subIdx, p, 'byproducts');
+ h += '<div class="ingredients-col ingredients-col-byproducts">' + renderConsumeSearchHTML(bypFid, bypVal, 'items', '(optional)') + '</div>';
+
+ h += '<div class="ingredients-col ingredients-col-x"><button class="btn btn-sm btn-danger" style="padding:2px 6px;font-size:10px;background:var(--danger);color:#fff;border:none;border-radius:3px;cursor:pointer" onclick="removeConsumePair(\'' + sec.id + '\',\'' + st.key + '\',' + rowIdx + ',\'' + sst.key + '\',' + subIdx + ',' + p + ')">X</button></div>';
+
+ h += '</div>';
+ }
+
+ h += '<div class="ingredients-pair-add" data-sec="' + sec.id + '" data-subkey="' + st.key + '" data-row="' + rowIdx + '" data-subsub="' + sst.key + '" data-subidx="' + subIdx + '">';
+ h += '<button class="btn btn-sm" style="padding:2px 8px;font-size:11px;background:var(--accent);color:var(--text);border:1px solid #1a5a8e;border-radius:3px;cursor:pointer;margin-left:76px" onclick="addConsumePair(\'' + sec.id + '\',\'' + st.key + '\',' + rowIdx + ',\'' + sst.key + '\',' + subIdx + ')">+ Add Alternative Item</button>';
+ h += '</div>';
+
+ h += '</div>';
+ });
+
+ h += '</div>';
+ h += '<button class="btn btn-sm obj-sub-add" onclick="addSubSubRow(\'' + sec.id + '\',\'' + st.key + '\',' + rowIdx + ',\'' + sst.key + '\')">+ Add New Ingredient</button>';
+ } else {
+ arr.forEach(function(subItem, subIdx) {
+ h += '<div class="obj-sub-row" style="align-items:flex-start">';
+ h += '<div class="obj-sub-row-line" style="flex:1">';
+ sst.fields.forEach(function(f) {
+ var fid = fieldIDSubSub(sec, st.key, rowIdx, sst.key, subIdx, f[0]);
+ var val = subItem[f[0]];
+ val = val === undefined || val === null ? '' : val;
+ var extraCls = '';
+ if (f[4] === 'narrow') extraCls = ' obj-narrow';
+ if (f[2] === 'search') {
+ var entity = f[6] || 'items';
+ var ph = f[3] ? ' placeholder="' + escAttr(f[3]) + '"' : ' placeholder="Search ' + entity + '..."';
+ h += '<label class="obj-field obj-search' + extraCls + '"><span>' + esc(f[1]) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>';
+ } else if (f[2] === 'checkbox') {
+ h += '<label class="obj-field obj-check' + extraCls + '"><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(f[1]) + '</span></label>';
+ } else {
+ h += '<label class="obj-field' + extraCls + '"><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"></label>';
+ }
+ });
+ h += '</div>';
+ h += '<button class="btn btn-sm btn-danger" style="align-self:flex-start;padding:2px 6px;font-size:10px;background:var(--danger);color:#fff;border:none;border-radius:3px;cursor:pointer;margin-top:2px" onclick="removeSubSubRow(\'' + sec.id + '\',\'' + st.key + '\',' + rowIdx + ',\'' + sst.key + '\',' + subIdx + ')">X</button>';
+ h += '</div>';
+ });
+ h += '<button class="btn btn-sm obj-sub-add" onclick="addSubSubRow(\'' + sec.id + '\',\'' + st.key + '\',' + rowIdx + ',\'' + sst.key + '\')">+ Add Custom Delay/Message</button>';
+ }
+
+ h += '</div>';
+ return h;
+}
+
+// =========================================================================
+// Tags / KV rendering
+// =========================================================================
+
+function renderTags(sec, il, data) {
+ var p = cardPath(sec);
+ var sectionRoot = p ? (itemData[p] || {}) : itemData;
+ var arr = sectionRoot[il.key] || [];
+ var str = Array.isArray(arr) ? arr.join(', ') : String(arr || '');
+ var fid = fieldID(sec, il.key);
+ return '<label class="obj-field obj-wide"><span>' + esc(il.label) + '</span><input id="' + fid + '" value="' + escAttr(str) + '" placeholder="comma separated"></label>';
+}
+
+function renderKV(sec, il, data) {
+ var p = cardPath(sec);
+ var sectionRoot = p ? (itemData[p] || {}) : itemData;
+ var map = sectionRoot[il.key] || {};
+ var h = '<div class="obj-inline-group">';
+ h += '<span class="obj-inline-label">' + esc(il.label) + '</span>';
+ h += '<div class="obj-kv-list" data-sec="' + sec.id + '" data-key="' + il.key + '">';
+ Object.keys(map).forEach(function(k, idx) {
+ h += '<div class="obj-kv-row"><input value="' + escAttr(k) + '" placeholder="key" class="obj-kv-key">';
+ h += '<input value="' + escAttr(String(map[k])) + '" placeholder="val" class="obj-kv-val">';
+ h += '<button class="btn btn-sm btn-danger" onclick="this.parentElement.remove()">X</button></div>';
+ });
+ h += '</div>';
+ h += '<button class="btn btn-sm obj-sub-add" onclick="addKVRow(\'' + sec.id + '\',\'' + il.key + '\')">+ Add</button>';
+ h += '</div>';
+ return h;
+}
+
+// =========================================================================
+// Card toggle / section add-remove
+// =========================================================================
+
+function toggleCard(id) {
+ expandedCards[id] = expandedCards[id] === false ? true : false;
+ renderCurrent();
+}
+
+function removeSection(id) {
+ var sec = SECTIONS.find(function(s) { return s.id === id; });
+ if (!sec) return;
+ if (sec.path) {
+ delete itemData[sec.path];
+ } else {
+ if (sec.fields) sec.fields.forEach(function(f) { delete itemData[f[0]]; });
+ if (sec.inline) sec.inline.forEach(function(il) { if (il.key) delete itemData[il.key]; });
+ if (sec.subtables) sec.subtables.forEach(function(st) { delete itemData[st.key]; });
+ }
+ delete addedSections[id];
+ renderCurrent();
+}
+
+function addSection() {
+ var sel = $('#addSectionSelect');
+ var id = sel.value;
+ if (!id) return;
+ var sec = SECTIONS.find(function(s) { return s.id === id; });
+ if (!sec) return;
+
+ if (sec.path) {
+ if (sec.id === 'equipment') {
+ itemData.equipment = {type:'', slot:'', attack_type:'', speed:0, junk:''};
+ } else {
+ itemData[sec.path] = {};
+ }
+ }
+ addedSections[id] = true;
+ expandedCards[id] = true;
+ renderCurrent();
+}
+
+// =========================================================================
+// Array item add/remove
+// =========================================================================
+
+function addArrayItem(cardID) {
+ var sec = SECTIONS.find(function(s) { return s.id === cardID; });
+ if (!sec || !sec.isArray) return;
+ var arr = itemData[sec.path] || [];
+ var empty = {};
+ sec.fields.forEach(function(f) {
+ empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ });
+ arr.push(empty);
+ itemData[sec.path] = arr;
+ renderCurrent();
+}
+
+function removeArrayItem(cardID, idx) {
+ var sec = SECTIONS.find(function(s) { return s.id === cardID; });
+ if (!sec || !sec.isArray) return;
+ var arr = itemData[sec.path] || [];
+ arr.splice(idx, 1);
+ renderCurrent();
+}
+
+// =========================================================================
+// Subtable row add/remove
+// =========================================================================
+
+function addSubRow(cardID, subKey) {
+ var sec = SECTIONS.find(function(s) { return s.id === cardID; });
+ if (!sec) return;
+ var p = cardPath(sec);
+ var sectionRoot = p ? (itemData[p] || {}) : itemData;
+ var raw = sectionRoot[subKey];
+ var arr;
+ if (Array.isArray(raw)) {
+ arr = raw;
+ } else if (raw && typeof raw === 'object') {
+ arr = [raw];
+ } else {
+ arr = [];
+ }
+ var empty = {};
+ var st = (sec.subtables || []).find(function(s) { return s.key === subKey; });
+ if (st) {
+ st.fields.forEach(function(f) {
+ empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ });
+ }
+ arr.push(empty);
+ sectionRoot[subKey] = arr;
+ if (sectionRoot === itemData) { delete itemData._raw; delete itemData._path; }
+ renderCurrent();
+}
+
+function removeSubRow(cardID, subKey, idx) {
+ var sec = SECTIONS.find(function(s) { return s.id === cardID; });
+ if (!sec) return;
+ var p = cardPath(sec);
+ var sectionRoot = p ? (itemData[p] || {}) : itemData;
+ var raw = sectionRoot[subKey];
+ var arr;
+ if (Array.isArray(raw)) {
+ arr = raw;
+ } else if (raw && typeof raw === 'object') {
+ arr = [raw];
+ } else {
+ arr = [];
+ }
+ arr.splice(idx, 1);
+ sectionRoot[subKey] = arr;
+ renderCurrent();
+}
+
+// =========================================================================
+// Sub-subtable row add/remove
+// =========================================================================
+
+function addSubSubRow(cardID, subKey, rowIdx, subSubKey) {
+ try {
+ var sec = SECTIONS.find(function(s) { return s.id === cardID; });
+ if (!sec) { console.warn('addSubSubRow: section not found', cardID); return; }
+ var p = cardPath(sec);
+ var sectionRoot = p ? (itemData[p] || {}) : itemData;
+ var arr = sectionRoot[subKey] || [];
+ if (rowIdx >= arr.length) { console.warn('addSubSubRow: row index out of range', rowIdx, arr.length); return; }
+ if (!arr[rowIdx]) { arr[rowIdx] = {}; }
+ var subArr = arr[rowIdx][subSubKey] || [];
+ var empty = {};
+ var st = (sec.subtables || []).find(function(s) { return s.key === subKey; });
+ if (st && st.subSubtables) {
+ var sst = st.subSubtables.find(function(ss) { return ss.key === subSubKey; });
+ if (sst) {
+ sst.fields.forEach(function(f) {
+ empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ });
+ }
+ }
+ subArr.push(empty);
+ arr[rowIdx][subSubKey] = subArr;
+ sectionRoot[subKey] = arr;
+ renderCurrent();
+ } catch(e) {
+ console.error('addSubSubRow error:', e);
+ notify('Failed to add row: ' + e.message, 'error');
+ }
+}
+
+function removeSubSubRow(cardID, subKey, rowIdx, subSubKey, subSubIdx) {
+ var sec = SECTIONS.find(function(s) { return s.id === cardID; });
+ if (!sec) return;
+ var p = cardPath(sec);
+ var sectionRoot = p ? (itemData[p] || {}) : itemData;
+ var arr = sectionRoot[subKey] || [];
+ if (rowIdx >= arr.length) return;
+ var subArr = arr[rowIdx][subSubKey] || [];
+ subArr.splice(subSubIdx, 1);
+ renderCurrent();
+}
+
+// =========================================================================
+// KV row add
+// =========================================================================
+
+function addKVRow(cardID, subKey) {
+ var el = document.querySelector('.obj-kv-list[data-sec="' + cardID + '"][data-key="' + subKey + '"]');
+ if (!el) return;
+ var row = document.createElement('div');
+ row.className = 'obj-kv-row';
+ row.innerHTML = '<input value="" placeholder="key" class="obj-kv-key"><input value="0" placeholder="val" class="obj-kv-val"><button class="btn btn-sm btn-danger" onclick="this.parentElement.remove()">X</button>';
+ el.appendChild(row);
+}
+
+// =========================================================================
+// Search autocomplete
+// =========================================================================
+
+function setupCraftTypeChange() {
+ document.querySelectorAll('.obj-subtable select[id$="_type"]').forEach(function(typeSelect) {
+ var id = typeSelect.id;
+ var subtypeSel = document.getElementById(id.replace(/_type$/, '_subtype'));
+ var levelField = document.getElementById(id.replace(/_type$/, '_level'));
+ var xpField = document.getElementById(id.replace(/_type$/, '_xp'));
+ var stationField = document.getElementById(id.replace(/_type$/, '_station'));
+
+ function update() {
+ var t = typeSelect.value;
+
+ [levelField, xpField].forEach(function(el) {
+ if (!el) return;
+ var lbl = el.closest('.obj-field');
+ if (lbl) lbl.style.display = t === 'combine' ? 'none' : '';
+ });
+
+ if (stationField) {
+ var sLbl = stationField.closest('.obj-field');
+ if (sLbl) sLbl.style.display = (t === 'fletching' || t === 'combine' || t === 'pharmacy') ? 'none' : '';
+ }
+
+ if (subtypeSel) {
+ var sLbl = subtypeSel.closest('.obj-field');
+ if (t === 'smithing') {
+ if (sLbl) sLbl.style.display = '';
+ subtypeSel.innerHTML = '<option value="">none</option><option value="smelt">smelt</option>';
+ } else if (t === 'pharmacy') {
+ if (sLbl) sLbl.style.display = '';
+ subtypeSel.innerHTML = '<option value="">none</option><option value="clean">clean</option>';
+ } else {
+ if (sLbl) sLbl.style.display = 'none';
+ subtypeSel.value = '';
+ }
+ }
+ }
+
+ typeSelect.addEventListener('change', update);
+ update();
+ });
+}
+
+function setupSearchFields() {
+ document.querySelectorAll('.search-input').forEach(function(input) {
+ if (input._searchSetup) return;
+ input._searchSetup = true;
+ var entityType = input.getAttribute('data-search-type') || 'items';
+ var results = input.parentElement.querySelector('.search-results');
+ if (!results) return;
+
+ results._targetInput = input;
+
+ input.addEventListener('input', function() {
+ var val = input.value.trim();
+ if (!val) { results.style.display = 'none'; results.innerHTML = ''; return; }
+ API.get('/api/search?q=' + encodeURIComponent(val)).then(function(data) {
+ var items = [];
+ if (entityType === 'items' && data.items) items = data.items;
+ if (entityType === 'objects' && data.objects) items = data.objects;
+ if (entityType === 'mobs' && data.mobs) items = data.mobs;
+ if (entityType === 'drops' && data.drops) items = data.drops;
+ if (items.length === 0) {
+ results.style.display = 'block';
+ results.innerHTML = '<div class="sr-item" style="color:#888">No results</div>';
+ return;
+ }
+ results.style.display = 'block';
+ results.innerHTML = items.map(function(r) {
+ return '<div class="sr-item" onclick="event.stopPropagation();selectSearchResult(this)">' + esc(r.id) + ' <span style="color:#aaa">' + esc(r.name || '') + '</span></div>';
+ }).join('');
+ results._items = items;
+ }).catch(function() {
+ results.style.display = 'none';
+ });
+ });
+
+ input.addEventListener('focus', function() {
+ if (input.value.trim()) {
+ input.dispatchEvent(new Event('input'));
+ }
+ });
+
+ input.addEventListener('blur', function() {
+ setTimeout(function() { results.style.display = 'none'; }, 200);
+ });
+ });
+
+ document.addEventListener('click', function(e) {
+ if (!e.target.closest('.search-results') && !e.target.closest('.search-input')) {
+ document.querySelectorAll('.search-results').forEach(function(r) { r.style.display = 'none'; });
+ }
+ });
+}
+
+function selectSearchResult(el) {
+ var results = el.parentElement;
+ var idx = Array.prototype.indexOf.call(results.children, el);
+ if (results._items && results._items[idx] && results._targetInput) {
+ results._targetInput.value = results._items[idx].id;
+ results._targetInput.dispatchEvent(new Event('input'));
+ results.style.display = 'none';
+ results.innerHTML = '';
+ }
+}
+
+// =========================================================================
+// Validation
+// =========================================================================
+
+function validateFields() {
+ var errors = [];
+ document.querySelectorAll('.obj-field-error').forEach(function(field) {
+ field.classList.remove('obj-field-error');
+ });
+
+ document.querySelectorAll('input[id]:not([type="checkbox"])').forEach(function(input) {
+ var id = input.id;
+ if (!id) return;
+ var labelEl = input.closest('.obj-field');
+ if (!labelEl) return;
+
+ if (input.classList.contains('search-input') || input.classList.contains('color-input')) return;
+
+ var isNumber = false;
+ var labelText = '';
+
+ var fieldDiv = labelEl.closest('[data-card]');
+ var cardID = fieldDiv ? fieldDiv.getAttribute('data-card') : '';
+ var sec = SECTIONS.find(function(s) { return s.id === cardID; });
+ if (!sec) return;
+
+ var baseMatch = id.match(/^f_(\w+)_(\w+)$/);
+ var subMatch = id.match(/^f_(\w+)_(\w+)_(\d+)_(\w.+)$/);
+ var subSubMatch = id.match(/^f_(\w+)_(\w+)_(\d+)__(\w+)_(\d+)_(\w+)$/);
+ var equipStatMatch = id.match(/^f_equipment_(\w+)_(\w+)$/);
+
+ if (subSubMatch) {
+ var ssSecId = subSubMatch[1], ssSubKey = subSubMatch[2];
+ var ssSubSubKey = subSubMatch[4], ssFieldKey = subSubMatch[6];
+ var st = (sec.subtables || []).find(function(s) { return s.key === ssSubKey; });
+ if (st && st.subSubtables) {
+ var sst = st.subSubtables.find(function(ss) { return ss.key === ssSubSubKey; });
+ if (sst) {
+ var nf = sst.fields.find(function(f) { return f[0] === ssFieldKey && f[2] === 'number'; });
+ if (nf) { isNumber = true; labelText = nf[1]; }
+ }
+ }
+ } else if (subMatch) {
+ var subKey = subMatch[2], fieldKey = subMatch[4];
+ var st = (sec.subtables || []).find(function(s) { return s.key === subKey; });
+ if (st) {
+ var nf = st.fields.find(function(f) { return f[0] === fieldKey && f[2] === 'number'; });
+ if (nf) { isNumber = true; labelText = nf[1]; }
+ }
+ } else if (equipStatMatch) {
+ var blockKey = equipStatMatch[1], statField = equipStatMatch[2];
+ var fields = EQUIP_STAT_FIELDS[blockKey];
+ if (fields) {
+ var nf = fields.find(function(f) { return f[0] === statField; });
+ if (nf) { isNumber = true; labelText = nf[1]; }
+ }
+ } else if (baseMatch) {
+ var fieldKey = baseMatch[2];
+ var nf = sec.fields ? sec.fields.find(function(f) {
+ var fid = fieldID(sec, f[0], -1);
+ return fid === id && f[2] === 'number';
+ }) : null;
+ if (!nf && sec.inline) {
+ sec.inline.forEach(function(il) {
+ if (il.fields) {
+ var inf = il.fields.find(function(f) {
+ var fid = fieldID(sec, il.path + '.' + f[0], -1);
+ return fid === id && f[2] === 'number';
+ });
+ if (inf) nf = inf;
+ }
+ });
+ }
+ if (nf) { isNumber = true; labelText = nf[1]; }
+ }
+
+ if (isNumber) {
+ var rawVal = input.value.trim();
+ if (rawVal !== '' && !/^-?\d*\.?\d*$/.test(rawVal)) {
+ labelEl.classList.add('obj-field-error');
+ input.focus();
+ input.select();
+ labelText = labelText || (labelEl.querySelector('span') || {}).textContent || '';
+ errors.push('Invalid value in "' + esc(labelText) + '": "' + esc(rawVal) + '"');
+ }
+ }
+ });
+
+ if (errors.length > 0) {
+ notify(errors.join('; '), 'error');
+ return false;
+ }
+ return true;
+}
+
+// =========================================================================
+// Field value collector
+// =========================================================================
+
+function collectFieldValue(el, type) {
+ if (type === 'checkbox') return el.checked;
+ if (type === 'select') return el.value;
+ if (type === 'search') return el.value;
+ if (type === 'number') { var n = parseFloat(el.value); return isNaN(n) ? 0 : n; }
+ if (type === 'json') {
+ var raw = el.value.trim();
+ if (!raw) return null;
+ try { return JSON.parse(raw); } catch(e) { return raw; }
+ }
+ if (type === 'textarea') return el.value;
+ return el.value;
+}
+
+// =========================================================================
+// Save
+// =========================================================================
+
+function saveItem() {
+ if (!validateFields()) return;
+
+ var out = {};
+
+ SECTIONS.forEach(function(sec) {
+ if (sec.isArray) {
+ var arr = [];
+ var card = document.querySelector('.obj-card[data-card="' + sec.id + '"]');
+ if (!card) return;
+ var rows = card.querySelectorAll('.obj-sub-row');
+ rows.forEach(function(row, idx) {
+ var item = {};
+ sec.fields.forEach(function(f) {
+ var fid = fieldID(sec, f[0], idx);
+ var el = document.getElementById(fid);
+ if (el) item[f[0]] = collectFieldValue(el, f[2]);
+ });
+ if (Object.keys(item).length > 0) arr.push(item);
+ });
+ if (arr.length > 0) out[sec.path] = arr;
+ return;
+ }
+
+ if (sec.id === 'equipment') {
+ var eq = {};
+ var hasEq = false;
+
+ ['type', 'slot', 'attack_type', 'junk'].forEach(function(k) {
+ var el = document.getElementById('f_equipment_' + k);
+ if (!el) return;
+ var val = collectFieldValue(el, k === 'type' || k === 'slot' ? 'select' : (k === 'junk' ? 'search' : 'text'));
+ if (val !== '' && val !== false && val !== 0 && val !== null) hasEq = true;
+ eq[k] = val;
+ });
+
+ if (eq.type === 'ammo') eq.slot = 'ammo';
+
+ var speedEl = document.getElementById('f_equipment_speed');
+ if (speedEl) {
+ var spd = parseFloat(speedEl.value);
+ if (!isNaN(spd) && spd !== 0) { eq.speed = spd; hasEq = true; }
+ }
+
+ EQUIP_STAT_FIELDS.attack.forEach(function(f) { saveEquipBlock(eq, 'attack', f[0]); });
+ EQUIP_STAT_FIELDS.defense.forEach(function(f) { saveEquipBlock(eq, 'defense', f[0]); });
+ EQUIP_STAT_FIELDS.other.forEach(function(f) { saveEquipBlock(eq, 'other', f[0]); });
+
+ if (hasEq && Object.keys(eq).length > 0) out.equipment = eq;
+
+ var recovEl = document.getElementById('f_core_recoverable');
+ if (recovEl) out.recoverable = recovEl.checked;
+
+ var kvEl = document.querySelector('.obj-kv-list[data-sec="equipment_req"][data-key="requirements"]');
+ if (kvEl) {
+ var map = {};
+ kvEl.querySelectorAll('.obj-kv-row').forEach(function(row) {
+ var keyEl = row.querySelector('.obj-kv-key');
+ var valEl = row.querySelector('.obj-kv-val');
+ if (keyEl && keyEl.value.trim() && valEl) {
+ map[keyEl.value.trim()] = parseFloat(valEl.value) || 0;
+ }
+ });
+ if (Object.keys(map).length > 0) out.requirements = map;
+ }
+ return;
+ }
+
+ var sectionObj = {};
+ var hasValues = sec.always;
+
+ if (sec.fields) sec.fields.forEach(function(f) {
+ var fid = fieldID(sec, f[0]);
+ var el = document.getElementById(fid);
+ if (!el) return;
+ var val = collectFieldValue(el, f[2]);
+ if (val !== '' && val !== false && val !== 0 && val !== null) hasValues = true;
+ sectionObj[f[0]] = val;
+ });
+
+ if (sec.subtables) {
+ sec.subtables.forEach(function(st) {
+ var items = [];
+ for (var i = 0; ; i++) {
+ var item = {};
+ var found = false;
+ st.fields.forEach(function(f) {
+ var fid = fieldIDSub(sec, st.key, i, f[0]);
+ var el = document.getElementById(fid);
+ if (el) found = true;
+ if (el) item[f[0]] = collectFieldValue(el, f[2]);
+ });
+ if (!found) break;
+
+ if (item.success_base !== undefined || item.success_per_level !== undefined || item.success_cap !== undefined) {
+ var sBase = Number(item.success_base) || 0;
+ var sPer = Number(item.success_per_level) || 0;
+ var sCap = Number(item.success_cap) || 0;
+ delete item.success_base;
+ delete item.success_per_level;
+ delete item.success_cap;
+ if (sBase || sPer || sCap) {
+ item.success = {base: sBase, per_level: sPer, cap: sCap};
+ }
+ }
+
+ if (item.station !== undefined && typeof item.station === 'string') {
+ item.station = item.station.split(',').map(function(s) { return s.trim(); }).filter(function(s) { return s; });
+ }
+
+ if (st.subSubtables) {
+ st.subSubtables.forEach(function(sst) {
+ var isIngredients = sst.key === 'ingredients';
+ var subItems = [];
+ for (var j = 0; ; j++) {
+ var subItem = {};
+ var subFound = false;
+ sst.fields.forEach(function(f) {
+ var fid = fieldIDSubSub(sec, st.key, i, sst.key, j, f[0]);
+ var el = document.getElementById(fid);
+ if (el) subFound = true;
+ if (el) subItem[f[0]] = collectFieldValue(el, f[2]);
+ });
+ if (!subFound) break;
+
+ if (isIngredients) {
+ var itemsArr = [];
+ var bypArr = [];
+ for (var k = 0; ; k++) {
+ var itemFid = ingredientPairID(sec, st.key, i, sst.key, j, k, 'items');
+ var itemEl = document.getElementById(itemFid);
+ if (!itemEl) break;
+ var itemVal = itemEl.value.trim();
+ itemsArr.push(itemVal);
+
+ var bypFid = ingredientPairID(sec, st.key, i, sst.key, j, k, 'byproducts');
+ var bypEl = document.getElementById(bypFid);
+ var bypVal = bypEl ? bypEl.value.trim() : '';
+ bypArr.push(bypVal);
+ }
+ var hasItems = itemsArr.some(function(v) { return v !== ''; });
+ if (hasItems) {
+ subItem.items = itemsArr.filter(function(v) { return v !== ''; });
+ if (bypArr.some(function(v) { return v !== ''; })) subItem.byproducts = bypArr;
+ }
+ if (!subItem.items) { subFound = false; }
+ } else {
+ if (subItem.items !== undefined && typeof subItem.items === 'string') {
+ subItem.items = subItem.items.split(',').map(function(s) { return s.trim(); }).filter(function(s) { return s; });
+ }
+ if (subItem.byproducts !== undefined && typeof subItem.byproducts === 'string') {
+ subItem.byproducts = subItem.byproducts.split(',').map(function(s) { return s.trim(); }).filter(function(s) { return s; });
+ }
+ }
+
+ if (Object.keys(subItem).length > 0) subItems.push(subItem);
+ }
+ if (subItems.length > 0) item[sst.key] = subItems;
+ });
+ }
+
+ if (Object.keys(item).length > 0) items.push(item);
+ }
+ if (st.single) {
+ if (items.length > 0) { sectionObj[st.key] = items[0]; hasValues = true; }
+ } else {
+ if (items.length > 0) { sectionObj[st.key] = items; hasValues = true; }
+ }
+ });
+ }
+
+ if (sec.inline) {
+ sec.inline.forEach(function(il) {
+ if (il.type === 'tags') {
+ var fid = fieldID(sec, il.key);
+ var el = document.getElementById(fid);
+ if (el && el.value.trim()) {
+ sectionObj[il.key] = el.value.split(',').map(function(s) { return s.trim(); }).filter(function(s) { return s; });
+ hasValues = true;
+ }
+ } else if (il.type === 'kv') {
+ var kvEl = document.querySelector('.obj-kv-list[data-sec="' + sec.id + '"][data-key="' + il.key + '"]');
+ if (kvEl) {
+ var map = {};
+ kvEl.querySelectorAll('.obj-kv-row').forEach(function(row) {
+ var keyEl = row.querySelector('.obj-kv-key');
+ var valEl = row.querySelector('.obj-kv-val');
+ if (keyEl && keyEl.value.trim() && valEl) {
+ map[keyEl.value.trim()] = parseFloat(valEl.value) || 0;
+ }
+ });
+ if (Object.keys(map).length > 0) { sectionObj[il.key] = map; hasValues = true; }
+ }
+ } else {
+ var ssec = {};
+ var sHas = false;
+ il.fields.forEach(function(f) {
+ var fp = il.path + '.' + f[0];
+ var fid = fieldID(sec, fp);
+ var el = document.getElementById(fid);
+ if (el) sHas = true;
+ if (el) ssec[f[0]] = collectFieldValue(el, f[2]);
+ });
+ if (sHas) { sectionObj[il.path] = ssec; hasValues = true; }
+ }
+ });
+ }
+
+ if (sec.always) {
+ Object.keys(sectionObj).forEach(function(k) { out[k] = sectionObj[k]; });
+ } else if (hasValues && sec.path) {
+ out[sec.path] = sectionObj;
+ } else if (hasValues && !sec.path) {
+ Object.keys(sectionObj).forEach(function(k) { out[k] = sectionObj[k]; });
+ }
+ });
+
+ cleanOutput(out);
+ out.id = itemID;
+
+ API.put('/api/items/' + encodeURIComponent(itemID), out).then(function(resp) {
+ notify('Item ' + itemID + ' saved', 'success');
+ updateUndoBar();
+ if (resp && resp._raw) itemData._raw = resp._raw;
+ }).catch(function(e) { notify('Save failed: ' + e.message, 'error'); });
+}
+
+function saveEquipBlock(eq, blockKey, fieldKey) {
+ var fid = 'f_equipment_' + blockKey + '_' + fieldKey;
+ var el = document.getElementById(fid);
+ if (!el) return;
+ var val = parseFloat(el.value);
+ if (isNaN(val)) return;
+ if (!eq[blockKey]) eq[blockKey] = {};
+ eq[blockKey][fieldKey] = val;
+}
+
+function cleanOutput(obj) {
+ for (var key in obj) {
+ var val = obj[key];
+ if (val === '' || val === null || val === undefined) {
+ delete obj[key];
+ } else if (typeof val === 'object' && val !== null && !Array.isArray(val)) {
+ cleanOutput(val);
+ if (Object.keys(val).length === 0) delete obj[key];
+ } else if (Array.isArray(val) && val.length === 0) {
+ delete obj[key];
+ }
+ }
+}
+
+// =========================================================================
+// Duplicate
+// =========================================================================
+
+function duplicateItem() {
+ var baseID = itemID + '_copy';
+ var newID = baseID;
+ var counter = 2;
+ while (true) {
+ var exists = false;
+ for (var i = 0; i < allIDs.length; i++) {
+ if (allIDs[i] === newID) { exists = true; break; }
+ }
+ if (!exists) break;
+ newID = baseID + '_' + counter;
+ counter++;
+ }
+
+ var copy = JSON.parse(JSON.stringify(itemData));
+ delete copy._raw;
+ delete copy._path;
+ copy.id = newID;
+
+ API.post('/api/items', {id: newID}).then(function() {
+ return API.put('/api/items/' + encodeURIComponent(newID), copy);
+ }).then(function() {
+ notify('Duplicated as ' + newID, 'success');
+ updateUndoBar();
+ loadList();
+ loadItem(newID);
+ }).catch(function(e) { notify('Duplicate failed: ' + e.message, 'error'); });
+}
+
+// =========================================================================
+// Delete
+// =========================================================================
+
+function deleteItem() {
+ if (!confirm('Delete item "' + itemID + '"?')) return;
+ API.del('/api/items/' + encodeURIComponent(itemID)).then(function() {
+ notify('Item ' + itemID + ' deleted', 'success');
+ updateUndoBar();
+ itemID = null;
+ currentID = null;
+ itemData = null;
+ $('#editorMain').innerHTML = '<p style="color:#888;text-align:center;margin-top:60px">Deleted</p>';
+ loadList();
+ }).catch(function(e) { notify('Delete failed: ' + e.message, 'error'); });
+}
+
+// =========================================================================
+// Editor.js compatibility re-exports
+// =========================================================================
+
+function loadItem(id) { loadItemEditor(id); }
diff --git a/internal/admin/static/map.js b/internal/admin/static/map.js
index 5bff9d7..e5d9a19 100644
--- a/internal/admin/static/map.js
+++ b/internal/admin/static/map.js
@@ -595,8 +595,10 @@ function renderItemsTab(spawns) {
h += '<div class="entry-row" data-idx="' + i + '">';
h += '<div class="entry-fields">';
h += '<span class="entry-id">' + esc(spawn.id || '') + '</span>';
- h += '<label class="mini-label">Qty</label><input class="mini-input spawn-qty" type="number" value="' + (spawn.quantity || 0) + '" onchange="updateSpawn(' + i + ')">';
- h += '<label class="mini-label">Respawn</label><input class="mini-input spawn-respawn" type="number" value="' + (spawn.respawn_ticks || 0) + '" onchange="updateSpawn(' + i + ')">';
+ h += '<div style="display:flex;gap:8px;align-items:center;">';
+ h += '<div><label class="mini-label">Quantity</label><input class="mini-input spawn-qty" type="text" inputmode="numeric" pattern="[0-9]*" value="' + (spawn.quantity || 0) + '" onchange="updateSpawn(' + i + ')"></div>';
+ h += '<div><label class="mini-label">Respawn</label><input class="mini-input spawn-respawn" type="text" inputmode="numeric" pattern="[0-9]*" value="' + (spawn.respawn_ticks || 0) + '" onchange="updateSpawn(' + i + ')"></div>';
+ h += '</div>';
h += '</div>';
h += '<button class="btn btn-sm" onclick="openEditor(\'items\', \'' + escAttr(spawn.id || '') + '\')">Edit</button>';
h += '<button class="btn btn-sm btn-danger" onclick="removeSpawn(' + i + ')">X</button>';
@@ -666,7 +668,7 @@ function editExitCondition(dir) {
overlay.className = 'cm-overlay exit-overlay';
var menu = document.createElement('div');
menu.className = 'cm-menu exit-modal';
- menu.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);min-width:420px;max-width:90vw;max-height:85vh;overflow-y:auto;overflow-x:hidden;padding:14px;z-index:201';
+ menu.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);min-width:520px;max-width:90vw;max-height:85vh;overflow-y:auto;overflow-x:hidden;padding:14px;z-index:201';
var h = '<h3 style="margin-bottom:8px;font-size:13px">Exit: ' + esc(dir) + ' &rarr; #' + exit.room + '</h3>';
h += '<div class="form-group"><label>Set Flags (key=value, comma separated)</label><input id="exitFlags" value="' + escAttr(flagsStr) + '"></div>';
@@ -742,7 +744,7 @@ function renderConditionClauses(menu) {
} else {
h += '<input class="cond-val" value="' + escAttr(clause.value || '') + '" onchange="updateClauseData(this)" style="flex:1">';
}
- h += '<label style="font-size:10px;color:#aaa;white-space:nowrap"><input type="checkbox" class="cond-not"' + (clause.not ? ' checked' : '') + ' onchange="updateClauseData(this)"> not</label>';
+ h += '<label style="font-size:10px;color:#aaa;white-space:nowrap"><input type="checkbox" class="cond-not"' + (clause.not ? ' checked' : '') + ' onchange="updateClauseData(this)"> =Not True</label>';
h += '<button class="btn btn-sm btn-danger" onclick="removeConditionClause(this)" style="flex-shrink:0">X</button>';
h += '</div>';
});
@@ -833,7 +835,8 @@ function saveExitCondition() {
}
function buildSingleCondition(clause) {
- var obj = {not: !!clause.not};
+ var obj = {};
+ if (clause.not) obj.not = true;
if (clause.type === 'value') {
var parts = (clause.value || '').split('=', 2);
obj.value = {key: (parts[0] || '').trim(), value: (parts[1] || '').trim()};
diff --git a/internal/admin/static/objecteditor.js b/internal/admin/static/objecteditor.js
index 3e944c4..13a983e 100644
--- a/internal/admin/static/objecteditor.js
+++ b/internal/admin/static/objecteditor.js
@@ -588,7 +588,7 @@ function addArrayItem(cardID) {
var arr = objectData[sec.path] || [];
var empty = {};
sec.fields.forEach(function(f) {
- empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' || f[2] === 'search' ? 0 : '');
+ empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
});
arr.push(empty);
objectData[sec.path] = arr;
@@ -612,7 +612,7 @@ function addSubRow(cardID, subKey) {
var st = (sec.subtables || []).find(function(s) { return s.key === subKey; });
if (st) {
st.fields.forEach(function(f) {
- empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' || f[2] === 'search' ? 0 : '');
+ empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
});
}
arr.push(empty);
diff --git a/internal/admin/templates/items.html b/internal/admin/templates/items.html
index f8f3b66..4a74ff5 100644
--- a/internal/admin/templates/items.html
+++ b/internal/admin/templates/items.html
@@ -10,36 +10,8 @@
</div>
</div>
<script src="/static/editor.js"></script>
+<script src="/static/itemeditor.js"></script>
<script>
-initEditor('items', [
- {key:'name',label:'Name',type:'text'},
- {key:'color',label:'Color',type:'color'},
- {key:'description',label:'Description',type:'textarea'},
- {key:'value',label:'Value',type:'number'},
- {key:'stackable',label:'Stackable',type:'checkbox'},
- {key:'equip_slot',label:'Equip Slot',type:'text'},
- {key:'weapon_type',label:'Weapon Type',type:'text'},
- {key:'attack_type',label:'Attack Type',type:'text'},
- {key:'speed',label:'Speed',type:'number'},
- {key:'tool_type',label:'Tool Type',type:'text'},
- {key:'tool_speed',label:'Tool Speed',type:'number'},
- {key:'burn_ticks',label:'Burn Ticks',type:'number'},
- {key:'fire_level',label:'Fire Level',type:'number'},
- {key:'fire_xp',label:'Fire XP',type:'number'},
- {key:'quality',label:'Quality',type:'number'},
- {key:'max_quality',label:'Max Quality',type:'number'},
- {key:'search_table',label:'Search Table',type:'text'},
- {key:'search_ticks',label:'Search Ticks',type:'number'},
- {key:'heal_value',label:'Heal Value',type:'number'},
- {key:'eat_message',label:'Eat Message',type:'text'},
- {key:'farm_patch_type',label:'Farm Patch',type:'text'},
- {key:'farm_level',label:'Farm Level',type:'number'},
- {key:'farm_stages',label:'Farm Stages',type:'number'},
- {key:'farm_product',label:'Farm Product',type:'text'},
- {key:'potion_effect',label:'Potion Effect',type:'text'},
- {key:'potion_bonus',label:'Potion Bonus',type:'number'},
- {key:'potion_duration',label:'Potion Duration',type:'number'},
- {key:'recoverable',label:'Recoverable',type:'checkbox'}
-]);
+initItemEditor();
</script>
{{end}}
diff --git a/internal/admin/undo.go b/internal/admin/undo.go
index 4921ae5..bec3c35 100644
--- a/internal/admin/undo.go
+++ b/internal/admin/undo.go
@@ -2,6 +2,7 @@ package admin
import (
"encoding/json"
+ "fmt"
"log"
"os"
"path/filepath"
@@ -27,6 +28,19 @@ type ChangeDesc struct {
ExtraFiles []ExtraFile `json:"extra_files,omitempty"`
}
+func (c ChangeDesc) ShortDesc() string {
+ switch {
+ case c.IsCreate:
+ return fmt.Sprintf("Created %s", filepath.Base(c.FilePath))
+ case c.IsDelete:
+ return fmt.Sprintf("Deleted %s", filepath.Base(c.FilePath))
+ case c.NewFilePath != "":
+ return fmt.Sprintf("Renamed %s", filepath.Base(c.NewFilePath))
+ default:
+ return fmt.Sprintf("Edited %s", filepath.Base(c.FilePath))
+ }
+}
+
type UndoInfo struct {
CanUndo bool `json:"can_undo"`
CanRedo bool `json:"can_redo"`
@@ -52,6 +66,7 @@ func NewUndoStack(dataDir string) *UndoStack {
filePath: filepath.Join(dataDir, ".admin_history.json"),
}
us.load()
+ us.redo = nil
return us
}
@@ -65,6 +80,7 @@ func (us *UndoStack) Push(desc ChangeDesc) {
us.history = us.history[len(us.history)-us.maxSize:]
}
us.save()
+ log.Printf("undo: push %s", desc.ShortDesc())
}
func (us *UndoStack) Undo() *ChangeDesc {
@@ -74,47 +90,16 @@ func (us *UndoStack) Undo() *ChangeDesc {
return nil
}
last := us.history[len(us.history)-1]
- us.history = us.history[:len(us.history)-1]
- if last.NewFilePath != "" {
- os.Remove(last.NewFilePath)
- }
- if last.IsDelete {
- if err := os.WriteFile(last.FilePath, last.OldContent, 0644); err != nil {
- log.Printf("undo: failed to restore deleted file %s: %v", last.FilePath, err)
- return nil
- }
- for _, ef := range last.ExtraFiles {
- if err := os.WriteFile(ef.FilePath, ef.OldContent, 0644); err != nil {
- log.Printf("undo: failed to restore extra file %s: %v", ef.FilePath, err)
- }
- }
- } else if last.IsCreate {
- os.Remove(last.FilePath)
- for _, ef := range last.ExtraFiles {
- os.Remove(ef.FilePath)
- }
- } else {
- if last.NewFilePath != "" {
- if err := os.WriteFile(last.FilePath, last.OldContent, 0644); err != nil {
- log.Printf("undo: failed to revert move %s: %v", last.FilePath, err)
- return nil
- }
- } else {
- if err := os.WriteFile(last.FilePath, last.OldContent, 0644); err != nil {
- log.Printf("undo: failed to revert file %s: %v", last.FilePath, err)
- return nil
- }
- }
- for _, ef := range last.ExtraFiles {
- if err := os.WriteFile(ef.FilePath, ef.OldContent, 0644); err != nil {
- log.Printf("undo: failed to restore extra file %s: %v", ef.FilePath, err)
- }
- }
+ if err := us.applyUndo(last); err != nil {
+ log.Printf("undo: failed %s: %v", last.ShortDesc(), err)
+ return nil
}
+ us.history = us.history[:len(us.history)-1]
us.redo = append(us.redo, last)
us.save()
+ log.Printf("undo: undid %s", last.ShortDesc())
return &last
}
@@ -125,57 +110,88 @@ func (us *UndoStack) Redo() *ChangeDesc {
return nil
}
last := us.redo[len(us.redo)-1]
+
+ if err := us.applyRedo(last); err != nil {
+ log.Printf("redo: failed %s: %v", last.ShortDesc(), err)
+ return nil
+ }
+
us.redo = us.redo[:len(us.redo)-1]
+ us.history = append(us.history, last)
+ us.save()
+ log.Printf("redo: redid %s", last.ShortDesc())
+ return &last
+}
- if last.NewFilePath != "" {
- os.Remove(last.FilePath)
+func (us *UndoStack) applyUndo(c ChangeDesc) error {
+ if c.NewFilePath != "" {
+ os.Remove(c.NewFilePath)
}
- if last.IsCreate {
- if last.NewFilePath != "" {
- if err := os.WriteFile(last.NewFilePath, last.NewContent, 0644); err != nil {
- log.Printf("redo: failed to recreate file %s: %v", last.NewFilePath, err)
- return nil
- }
- } else {
- if err := os.WriteFile(last.FilePath, last.NewContent, 0644); err != nil {
- log.Printf("redo: failed to recreate file %s: %v", last.FilePath, err)
- return nil
- }
+ if c.IsDelete {
+ if c.OldContent == nil {
+ return fmt.Errorf("no backup content to restore for %s", c.FilePath)
}
- for _, ef := range last.ExtraFiles {
- if err := os.WriteFile(ef.FilePath, ef.NewContent, 0644); err != nil {
- log.Printf("redo: failed to create extra file %s: %v", ef.FilePath, err)
- }
+ if err := os.WriteFile(c.FilePath, c.OldContent, 0644); err != nil {
+ return fmt.Errorf("restore deleted %s: %w", c.FilePath, err)
}
- } else if last.IsDelete {
- os.Remove(last.FilePath)
- for _, ef := range last.ExtraFiles {
- if err := os.WriteFile(ef.FilePath, ef.NewContent, 0644); err != nil {
- log.Printf("redo: failed to write extra file %s: %v", ef.FilePath, err)
- }
+ for _, ef := range c.ExtraFiles {
+ os.WriteFile(ef.FilePath, ef.OldContent, 0644)
+ }
+ } else if c.IsCreate {
+ os.Remove(c.FilePath)
+ for _, ef := range c.ExtraFiles {
+ os.Remove(ef.FilePath)
}
} else {
- if last.NewFilePath != "" {
- if err := os.WriteFile(last.NewFilePath, last.NewContent, 0644); err != nil {
- log.Printf("redo: failed to reapply move %s: %v", last.NewFilePath, err)
- return nil
- }
- } else {
- if err := os.WriteFile(last.FilePath, last.NewContent, 0644); err != nil {
- log.Printf("redo: failed to reapply file %s: %v", last.FilePath, err)
- return nil
- }
+ target := c.FilePath
+ content := c.OldContent
+ if c.NewFilePath != "" {
+ target = c.FilePath
}
- for _, ef := range last.ExtraFiles {
- if err := os.WriteFile(ef.FilePath, ef.NewContent, 0644); err != nil {
- log.Printf("redo: failed to write extra file %s: %v", ef.FilePath, err)
- }
+ if err := os.WriteFile(target, content, 0644); err != nil {
+ return fmt.Errorf("revert %s: %w", target, err)
+ }
+ for _, ef := range c.ExtraFiles {
+ os.WriteFile(ef.FilePath, ef.OldContent, 0644)
}
}
+ return nil
+}
- us.history = append(us.history, last)
- us.save()
- return &last
+func (us *UndoStack) applyRedo(c ChangeDesc) error {
+ if c.NewFilePath != "" {
+ os.Remove(c.FilePath)
+ }
+ if c.IsCreate {
+ target := c.FilePath
+ if c.NewFilePath != "" {
+ target = c.NewFilePath
+ }
+ if err := os.WriteFile(target, c.NewContent, 0644); err != nil {
+ return fmt.Errorf("recreate %s: %w", target, err)
+ }
+ for _, ef := range c.ExtraFiles {
+ os.WriteFile(ef.FilePath, ef.NewContent, 0644)
+ }
+ } else if c.IsDelete {
+ os.Remove(c.FilePath)
+ for _, ef := range c.ExtraFiles {
+ os.WriteFile(ef.FilePath, ef.NewContent, 0644)
+ }
+ } else {
+ target := c.FilePath
+ content := c.NewContent
+ if c.NewFilePath != "" {
+ target = c.NewFilePath
+ }
+ if err := os.WriteFile(target, content, 0644); err != nil {
+ return fmt.Errorf("reapply %s: %w", target, err)
+ }
+ for _, ef := range c.ExtraFiles {
+ os.WriteFile(ef.FilePath, ef.NewContent, 0644)
+ }
+ }
+ return nil
}
func (us *UndoStack) Info() UndoInfo {
@@ -187,11 +203,11 @@ func (us *UndoStack) Info() UndoInfo {
}
if len(us.history) > 0 {
info.CanUndo = true
- info.UndoDesc = us.history[len(us.history)-1].Description
+ info.UndoDesc = us.history[len(us.history)-1].ShortDesc()
}
if len(us.redo) > 0 {
info.CanRedo = true
- info.RedoDesc = us.redo[len(us.redo)-1].Description
+ info.RedoDesc = us.redo[len(us.redo)-1].ShortDesc()
}
return info
}
@@ -251,7 +267,9 @@ func (us *UndoStack) save() {
log.Printf("undo: failed to marshal history: %v", err)
return
}
- os.WriteFile(us.filePath, b, 0644)
+ if err := os.WriteFile(us.filePath, b, 0644); err != nil {
+ log.Printf("undo: failed to write history file: %v", err)
+ }
}
func (us *UndoStack) load() {
@@ -259,6 +277,7 @@ func (us *UndoStack) load() {
if err != nil {
return
}
+ log.Printf("undo: loaded history from %s", us.filePath)
type extraEntry struct {
FilePath string `json:"file_path"`
OldContent string `json:"old_content"`
@@ -280,6 +299,7 @@ func (us *UndoStack) load() {
Redo []entry `json:"redo"`
}
if err := json.Unmarshal(data, &saveData); err != nil {
+ log.Printf("undo: failed to unmarshal history: %v", err)
return
}
fromEntries := func(entries []entry) []ChangeDesc {
@@ -308,5 +328,5 @@ func (us *UndoStack) load() {
return changes
}
us.history = fromEntries(saveData.History)
- us.redo = fromEntries(saveData.Redo)
+ log.Printf("undo: loaded %d history entries", len(us.history))
}
diff --git a/internal/admin/yaml_util.go b/internal/admin/yaml_util.go
index 0bc2b4a..4d0a814 100644
--- a/internal/admin/yaml_util.go
+++ b/internal/admin/yaml_util.go
@@ -145,7 +145,7 @@ func listYAMLFilesDeep(dataDir, subdir string) ([]string, error) {
func backupFile(path string) []byte {
data, err := os.ReadFile(path)
if err != nil {
- log.Printf("backup: failed to read %s: %v", path, err)
+ log.Printf("backup: WARNING failed to read %s: %v (undo will not be able to restore this file)", path, err)
return nil
}
return data
diff --git a/internal/behavior/behavior.go b/internal/behavior/behavior.go
index f64b4b2..8333a05 100644
--- a/internal/behavior/behavior.go
+++ b/internal/behavior/behavior.go
@@ -73,7 +73,7 @@ type ShopItem struct {
type Condition struct {
Flag string `yaml:"flag"`
Value any `yaml:"value"`
- Not bool `yaml:"not"`
+ Not bool `yaml:"not"`
PlayerFlag string `yaml:"player_flag"`
HasItem string `yaml:"has_item"`
MinCredits int `yaml:"min_credits"`
diff --git a/internal/game/act_burn.go b/internal/game/act_burn.go
index 7420dff..c24d3d7 100644
--- a/internal/game/act_burn.go
+++ b/internal/game/act_burn.go
@@ -309,8 +309,8 @@ func (g *Game) findFireTool(sess *net.Session, p *player.Player) (toolSpeed floa
if itemID, equipped := p.Equipment[item.SlotMainHand]; equipped {
def, err := g.ItemStore.Load(itemID)
- if err == nil && def.ToolType == "fire" {
- toolSpeed = def.ToolSpeed
+ if err == nil && def.ToolType() == "fire" {
+ toolSpeed = def.ToolSpeed()
toolItemID = itemID
}
}
@@ -321,10 +321,10 @@ func (g *Game) findFireTool(sess *net.Session, p *player.Player) (toolSpeed floa
continue
}
def, err := g.ItemStore.Load(slot.ItemID)
- if err != nil || def.ToolType != "fire" {
+ if err != nil || def.ToolType() != "fire" {
continue
}
- toolSpeed = def.ToolSpeed
+ toolSpeed = def.ToolSpeed()
toolItemID = slot.ItemID
break
}
diff --git a/internal/game/act_clean.go b/internal/game/act_clean.go
index 0c128a1..021a07c 100644
--- a/internal/game/act_clean.go
+++ b/internal/game/act_clean.go
@@ -25,16 +25,16 @@ func (g *Game) advanceClean(sess *net.Session, p *player.Player) {
return
}
- items := g.CraftIndex.ByType("clean")
+ items := g.CraftIndex.BySubtype("clean")
var matchItem *item.ItemDef
for _, item := range items {
c := item.FirstCraft()
if filter != "" {
- if len(c.Consume) == 0 || len(c.Consume[0].Items) == 0 {
+ if len(c.Ingredients) == 0 || len(c.Ingredients[0].Items) == 0 {
continue
}
- if !strings.Contains(c.Consume[0].Items[0], filter) &&
+ if !strings.Contains(c.Ingredients[0].Items[0], filter) &&
!strings.Contains(item.ID, filter) {
continue
}
@@ -56,7 +56,7 @@ func (g *Game) advanceClean(sess *net.Session, p *player.Player) {
}
craft := matchItem.FirstCraft()
- consumeID := craft.Consume[0].Items[0]
+ consumeID := craft.Ingredients[0].Items[0]
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
@@ -81,10 +81,10 @@ func (g *Game) advanceClean(sess *net.Session, p *player.Player) {
for _, item := range items {
c := item.FirstCraft()
if filter != "" {
- if len(c.Consume) == 0 || len(c.Consume[0].Items) == 0 {
+ if len(c.Ingredients) == 0 || len(c.Ingredients[0].Items) == 0 {
continue
}
- if !strings.Contains(c.Consume[0].Items[0], filter) &&
+ if !strings.Contains(c.Ingredients[0].Items[0], filter) &&
!strings.Contains(item.ID, filter) {
continue
}
diff --git a/internal/game/act_fletch.go b/internal/game/act_fletch.go
index e85f2c1..800bc8e 100644
--- a/internal/game/act_fletch.go
+++ b/internal/game/act_fletch.go
@@ -44,7 +44,7 @@ func (g *Game) doInstantFletch(sess *net.Session, p *player.Player, def *item.It
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
if slot != nil && slot.ItemID == def.ID {
- craftConsumeAll(c, p.HasItem, p.RemoveItem)
+ consumeIngredients(c, p.HasItem, p.RemoveItem)
slot.Quantity += outputQty
placed = true
break
@@ -52,7 +52,7 @@ func (g *Game) doInstantFletch(sess *net.Session, p *player.Player, def *item.It
}
}
if !placed {
- craftConsumeAll(c, p.HasItem, p.RemoveItem)
+ consumeIngredients(c, p.HasItem, p.RemoveItem)
freeSlot := p.FirstFreeSlot()
if freeSlot == -1 {
break
@@ -165,7 +165,7 @@ func (g *Game) advanceFletch(sess *net.Session, p *player.Player) {
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
if slot != nil && slot.ItemID == def.ID {
- craftConsumeAll(c, p.HasItem, p.RemoveItem)
+ consumeIngredients(c, p.HasItem, p.RemoveItem)
slot.Quantity += outputQty
placed = true
break
@@ -173,7 +173,7 @@ func (g *Game) advanceFletch(sess *net.Session, p *player.Player) {
}
}
if !placed {
- craftConsumeAll(c, p.HasItem, p.RemoveItem)
+ consumeIngredients(c, p.HasItem, p.RemoveItem)
freeSlot := p.FirstFreeSlot()
if freeSlot == -1 {
sess.WriteLine("Your inventory is too full!")
diff --git a/internal/game/act_search.go b/internal/game/act_search.go
index 6f5fa65..83ea534 100644
--- a/internal/game/act_search.go
+++ b/internal/game/act_search.go
@@ -82,16 +82,6 @@ func (g *Game) advanceSearch(sess *net.Session, p *player.Player) {
}
}
- if def.SearchMiscTable != "" {
- dt2, err := behavior.LoadDropTable(g.DataDir, def.SearchMiscTable)
- if err == nil && len(dt2.Drops) > 0 {
- drop := behavior.ResolveDrop(g.DataDir, dt2.Drops)
- if drop != nil && drop.ItemID != "" {
- g.giveSearchLoot(sess, p, drop)
- }
- }
- }
-
g.AccountStore.SaveCharacter(p)
g.cancelAction(p)
}
diff --git a/internal/game/cmd_clean.go b/internal/game/cmd_clean.go
index a43295a..f77ff57 100644
--- a/internal/game/cmd_clean.go
+++ b/internal/game/cmd_clean.go
@@ -22,7 +22,7 @@ func (g *Game) doClean(sess *net.Session, input string) {
return
}
- items := g.CraftIndex.ByType("clean")
+ items := g.CraftIndex.BySubtype("clean")
filter := strings.TrimSpace(input)
@@ -78,8 +78,8 @@ func (g *Game) doClean(sess *net.Session, input string) {
}
func matchesCleanFilter(item *item.ItemDef, filter string) bool {
- if len(item.Craft) > 0 && len(item.FirstCraft().Consume) > 0 && len(item.FirstCraft().Consume[0].Items) > 0 {
- if strings.Contains(item.FirstCraft().Consume[0].Items[0], filter) {
+ if len(item.Craft) > 0 && len(item.FirstCraft().Ingredients) > 0 && len(item.FirstCraft().Ingredients[0].Items) > 0 {
+ if strings.Contains(item.FirstCraft().Ingredients[0].Items[0], filter) {
return true
}
}
diff --git a/internal/game/cmd_craft.go b/internal/game/cmd_craft.go
index 0fe3d5b..3d23f85 100644
--- a/internal/game/cmd_craft.go
+++ b/internal/game/cmd_craft.go
@@ -171,7 +171,7 @@ func (g *Game) startItem(sess *net.Session, p *player.Player, def *item.ItemDef,
}
func (g *Game) hasGrimyHerbs(p *player.Player) bool {
- items := g.CraftIndex.ByType("clean")
+ items := g.CraftIndex.BySubtype("clean")
for _, def := range items {
if def.FindCraft(func(c item.CraftDef) bool {
return craftHasAllItems(&c, p.HasItem)
@@ -188,10 +188,10 @@ func (g *Game) findCraftItems(p *player.Player, itemAID, itemBID string) []*item
toolTypeA := ""
toolTypeB := ""
if defA, _ := g.ItemStore.Load(itemAID); defA != nil {
- toolTypeA = defA.ToolType
+ toolTypeA = defA.ToolType()
}
if defB, _ := g.ItemStore.Load(itemBID); defB != nil {
- toolTypeB = defB.ToolType
+ toolTypeB = defB.ToolType()
}
var matched []*item.ItemDef
diff --git a/internal/game/cmd_fletch.go b/internal/game/cmd_fletch.go
index 0da8129..d638bc0 100644
--- a/internal/game/cmd_fletch.go
+++ b/internal/game/cmd_fletch.go
@@ -28,7 +28,7 @@ func fletchNeedsKnife(def *item.ItemDef) bool {
if c.Tool == "knife" {
return true
}
- for _, e := range c.Consume {
+ for _, e := range c.Ingredients {
for _, id := range e.Items {
if fletchLogItems[id] {
if strings.Contains(def.ID, "shaft") || strings.Contains(def.ID, "bow") {
@@ -46,7 +46,7 @@ func fletchNeedsChisel(def *item.ItemDef) bool {
if c.Tool == "chisel" {
return true
}
- for _, e := range c.Consume {
+ for _, e := range c.Ingredients {
for _, id := range e.Items {
if fletchGemItems[id] {
return true
@@ -179,10 +179,10 @@ func (g *Game) findFletchItems(p *player.Player, itemAID, itemBID string) []*ite
toolTypeA := ""
toolTypeB := ""
if defA, _ := g.ItemStore.Load(itemAID); defA != nil {
- toolTypeA = defA.ToolType
+ toolTypeA = defA.ToolType()
}
if defB, _ := g.ItemStore.Load(itemBID); defB != nil {
- toolTypeB = defB.ToolType
+ toolTypeB = defB.ToolType()
}
isToolA := toolTypeA == "knife" || toolTypeA == "chisel"
isToolB := toolTypeB == "knife" || toolTypeB == "chisel"
@@ -204,7 +204,7 @@ func (g *Game) findFletchItems(p *player.Player, itemAID, itemBID string) []*ite
aMatch := false
bMatch := false
- for _, e := range c.Consume {
+ for _, e := range c.Ingredients {
for _, id := range e.Items {
if id == itemAID {
aMatch = true
diff --git a/internal/game/cmd_smelt.go b/internal/game/cmd_smelt.go
index 59e52db..b551e76 100644
--- a/internal/game/cmd_smelt.go
+++ b/internal/game/cmd_smelt.go
@@ -23,7 +23,7 @@ func (g *Game) doSmelt(sess *net.Session, input string) {
return
}
- items := g.CraftIndex.ByType("smelting")
+ items := g.CraftIndex.BySubtype("smelt")
if input == "" {
g.showSmeltMenu(sess, p, items, stationDefID, stationName)
diff --git a/internal/game/cmd_smith.go b/internal/game/cmd_smith.go
index 9722896..4330b1d 100644
--- a/internal/game/cmd_smith.go
+++ b/internal/game/cmd_smith.go
@@ -118,7 +118,7 @@ func hasSmithingItems(items []*item.ItemDef, barID string) bool {
func findSmithableBars(items []*item.ItemDef, p *player.Player) []string {
barSet := make(map[string]bool)
for _, item := range items {
- for _, e := range item.FirstCraft().Consume {
+ for _, e := range item.FirstCraft().Ingredients {
for _, id := range e.Items {
if p.HasItem(id) {
barSet[id] = true
diff --git a/internal/game/cmd_stats.go b/internal/game/cmd_stats.go
index ef5e2ba..0430c6e 100644
--- a/internal/game/cmd_stats.go
+++ b/internal/game/cmd_stats.go
@@ -19,18 +19,18 @@ func (g *Game) doStats(sess *net.Session) {
attackType := "crush"
weaponName := "unarmed"
- var weaponType item.WeaponType
+ var weaponType item.EquipmentType
var weaponDef *item.ItemDef
if itemID, ok := p.Equipment[item.SlotMainHand]; ok {
if def, err := g.ItemStore.Load(itemID); err == nil {
weaponDef = def
weaponName = def.Name
- weaponType = def.WeaponType
- if def.AttackType != "" {
- attackType = def.AttackType
- } else if def.WeaponType == item.WeaponRanged {
+ weaponType = def.EquipmentType()
+ if def.AttackType() != "" {
+ attackType = def.AttackType()
+ } else if def.EquipmentType() == item.EquipRangedWeapon {
attackType = "ranged"
- } else if def.WeaponType == item.WeaponScience {
+ } else if def.EquipmentType() == item.EquipScienceWeapon {
attackType = "science"
}
}
@@ -58,7 +58,7 @@ func (g *Game) doStats(sess *net.Session) {
)
var attRoll, maxHitVal int
- if weaponType == item.WeaponRanged {
+ if weaponType == item.EquipRangedWeapon {
rangedBonus, _ := combat.RangedStyleBonus(string(p.AttackStyle))
attRoll = combat.EffectiveRoll(p.Level(player.Ranged), rangedBonus, totals.RangedAttack)
maxHitVal = combat.MaxHit(p.Level(player.Ranged), rangedBonus, totals.RangedStrength)
diff --git a/internal/game/cmd_trigger_utility.go b/internal/game/cmd_trigger_utility.go
index 0dae505..1697417 100644
--- a/internal/game/cmd_trigger_utility.go
+++ b/internal/game/cmd_trigger_utility.go
@@ -170,7 +170,7 @@ func (g *Game) triggerSuperheat(sess *net.Session, p *player.Player, mod *ModDef
}
var match *item.ItemDef
- for _, item := range g.CraftIndex.ByType("smelting") {
+ for _, item := range g.CraftIndex.BySubtype("smelt") {
if craftMatchesEntry(item.FirstCraft(), inv.ItemID) {
match = item
break
@@ -182,12 +182,12 @@ func (g *Game) triggerSuperheat(sess *net.Session, p *player.Player, mod *ModDef
}
c := match.FirstCraft()
- if c.Level > 0 && p.Level(player.SkillName(c.Skill)) < c.Level {
- sess.WriteLine(fmt.Sprintf("You need level %d %s to smelt that.", c.Level, c.Skill))
+ if c.Level > 0 && p.Level(player.SkillName(c.Type)) < c.Level {
+ sess.WriteLine(fmt.Sprintf("You need level %d %s to smelt that.", c.Level, c.Type))
return
}
- for _, e := range c.Consume {
+ for _, e := range c.Ingredients {
for _, itemID := range e.Items {
qty := e.Quantity
if qty <= 0 {
@@ -209,7 +209,7 @@ func (g *Game) triggerSuperheat(sess *net.Session, p *player.Player, mod *ModDef
g.cancelAction(p)
}
- craftConsumeAll(c, p.HasItem, p.RemoveItem)
+ consumeIngredients(c, p.HasItem, p.RemoveItem)
outputQty := c.OutputQty
if outputQty <= 0 {
diff --git a/internal/game/cmd_use.go b/internal/game/cmd_use.go
index d399e8f..033fd62 100644
--- a/internal/game/cmd_use.go
+++ b/internal/game/cmd_use.go
@@ -137,6 +137,19 @@ func (g *Game) stationRecipes(p *player.Player, stationDefID string) []*item.Ite
return results
}
+func craftLabel(c *item.CraftDef) string {
+ if c == nil {
+ return ""
+ }
+ if c.Subtype != "" {
+ return c.Subtype
+ }
+ if c.Type != "" {
+ return c.Type
+ }
+ return "combine"
+}
+
func (g *Game) showRecipeMenu(sess *net.Session, p *player.Player, items []*item.ItemDef, stationName string) {
if len(items) == 0 {
sess.WriteLine(fmt.Sprintf("You don't have anything you can make at the %s.", stationName))
@@ -149,7 +162,7 @@ func (g *Game) showRecipeMenu(sess *net.Session, p *player.Player, items []*item
needTypes := false
for i := range items {
for j := i + 1; j < len(items); j++ {
- if len(items[i].Craft) > 0 && len(items[j].Craft) > 0 && items[i].FirstCraft().Type != items[j].FirstCraft().Type {
+ if craftLabel(items[i].FirstCraft()) != craftLabel(items[j].FirstCraft()) {
needTypes = true
break
}
@@ -163,14 +176,8 @@ func (g *Game) showRecipeMenu(sess *net.Session, p *player.Player, items []*item
sess.State = net.StateRecipeChoice
var names []string
for _, item := range items {
- cType := ""
- if len(item.Craft) > 0 {
- cType = item.FirstCraft().Type
- }
- if cType == "" {
- cType = "combine"
- }
- typeLabel := strings.ToUpper(cType[:1]) + cType[1:]
+ label := craftLabel(item.FirstCraft())
+ typeLabel := strings.ToUpper(label[:1]) + label[1:]
if needTypes {
names = append(names, fmt.Sprintf("%s (%s)", g.itemName(sess, item), typeLabel))
} else {
@@ -209,15 +216,15 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName,
recipes := g.CraftIndex.FindByStationInput(def.ID, itemAID)
if len(recipes) > 0 {
- cType := ""
+ var firstSubtype string
if len(recipes[0].Craft) > 0 {
- cType = recipes[0].FirstCraft().Type
+ firstSubtype = recipes[0].FirstCraft().Subtype
}
- if cType == "smelting" {
+ if firstSubtype == "smelt" {
var filtered []*item.ItemDef
for _, item := range recipes {
c := item.FirstCraft()
- if c.Type != "smelting" {
+ if c.Subtype != "smelt" {
continue
}
if !craftHasAllItemsQty(c, p.CountItem) {
@@ -246,6 +253,10 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName,
g.showMenuTable(sess, "What would you like to smelt?", names)
return
}
+ var cType string
+ if len(recipes[0].Craft) > 0 {
+ cType = recipes[0].FirstCraft().Type
+ }
if cType == "smithing" {
if !g.hasToolType(p, "hammer") {
sess.WriteLine("You need a hammer to smith.")
@@ -396,9 +407,9 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName,
}
func (g *Game) isGrimyHerb(itemID string) bool {
- items := g.CraftIndex.ByType("clean")
+ items := g.CraftIndex.BySubtype("clean")
for _, item := range items {
- for _, e := range item.FirstCraft().Consume {
+ for _, e := range item.FirstCraft().Ingredients {
for _, id := range e.Items {
if id == itemID {
return true
diff --git a/internal/game/cmd_verbs.go b/internal/game/cmd_verbs.go
index 70b2fb2..cad90c5 100644
--- a/internal/game/cmd_verbs.go
+++ b/internal/game/cmd_verbs.go
@@ -90,12 +90,17 @@ func (g *Game) executeVerbs(sess *net.Session, args []string, rawInput string) {
if len(recipes) > 0 {
addUnique(&sectionObjs, &seen, "use "+name)
for _, r := range recipes {
- if info, ok := productionTypes[r.FirstCraft().Type]; ok {
+ c := r.FirstCraft()
+ key := c.Subtype
+ if key == "" {
+ key = c.Type
+ }
+ if info, ok := productionTypes[key]; ok {
if info.ActionType != behavior.TypeCombine && info.ActionType != behavior.TypeFletch && info.ActionType != behavior.TypeMix {
addUnique(&sectionObjs, &seen, string(info.ActionType))
}
}
- for _, c := range r.FirstCraft().Consume {
+ for _, c := range r.FirstCraft().Ingredients {
for _, itemID := range c.Items {
if itemDef, err := g.ItemStore.Load(itemID); err == nil {
addUnique(&sectionObjs, &seen, "use "+itemDef.Name+" on "+name)
diff --git a/internal/game/cmd_wear.go b/internal/game/cmd_wear.go
index 64069bc..c52321c 100644
--- a/internal/game/cmd_wear.go
+++ b/internal/game/cmd_wear.go
@@ -75,7 +75,7 @@ func (g *Game) doWear(sess *net.Session, input string) {
}
def, err := g.ItemStore.Load(slot.ItemID)
- if err != nil || def.EquipSlot == "" {
+ if err != nil || def.EquipSlot() == "" {
sess.WriteLine(fmt.Sprintf("You can't wear %s.", g.itemColorize(sess, def, match.Name)))
return
}
@@ -85,12 +85,12 @@ func (g *Game) doWear(sess *net.Session, input string) {
return
}
- if def.EquipSlot == item.SlotAmmo && def.Stackable {
+ if def.EquipSlot() == item.SlotAmmo && def.Stackable {
g.equipAmmo(sess, p, match.Slot, slot, def, qty)
return
}
- existingItemID, slotOccupied := p.Equipment[def.EquipSlot]
+ existingItemID, slotOccupied := p.Equipment[def.EquipSlot()]
if slotOccupied {
freeSlot := p.FirstFreeSlot()
@@ -116,11 +116,11 @@ func (g *Game) doWear(sess *net.Session, input string) {
p.EquipQuality[slot.ItemID] = slot.Quality
}
- p.Equipment[def.EquipSlot] = slot.ItemID
+ p.Equipment[def.EquipSlot()] = slot.ItemID
g.AccountStore.SaveCharacter(p)
verb := "wear"
- if def.WeaponType != "" {
+ if def.EquipmentType() != "" {
verb = "wield"
}
sess.WriteLine(fmt.Sprintf("You %s %s.", verb, g.itemColorize(sess, def, match.Name)))
@@ -193,19 +193,19 @@ func (g *Game) doWearAll(sess *net.Session) {
continue
}
def, err := g.ItemStore.Load(slot.ItemID)
- if err != nil || def.EquipSlot == "" {
+ if err != nil || def.EquipSlot() == "" {
continue
}
if g.checkRequirements(sess, p, def) != "" {
continue
}
- current, exists := bestPerSlot[def.EquipSlot]
+ current, exists := bestPerSlot[def.EquipSlot()]
val := def.Value
- if def.EquipSlot == item.SlotAmmo && def.Stackable {
+ if def.EquipSlot() == item.SlotAmmo && def.Stackable {
val = def.Value * slot.Quantity
}
if !exists || val > current.value {
- bestPerSlot[def.EquipSlot] = struct {
+ bestPerSlot[def.EquipSlot()] = struct {
itemID string
value int
invSlot int
diff --git a/internal/game/combat_attack.go b/internal/game/combat_attack.go
index bae210b..ddc3b8f 100644
--- a/internal/game/combat_attack.go
+++ b/internal/game/combat_attack.go
@@ -59,7 +59,7 @@ func (g *Game) doAttack(sess *net.Session, input string) {
}
if itemID, ok := p.Equipment[item.SlotMainHand]; ok {
- if def, err := g.ItemStore.Load(itemID); err == nil && def.WeaponType == item.WeaponRanged {
+ if def, err := g.ItemStore.Load(itemID); err == nil && def.EquipmentType() == item.EquipRangedWeapon {
if _, hasAmmo := p.Equipment[item.SlotAmmo]; !hasAmmo || p.AmmoQty <= 0 {
sess.WriteLine("You don't have any ammo equipped.")
return
@@ -289,23 +289,23 @@ func (g *Game) playerAttack(sess *net.Session, p *player.Player, mob *world.MobI
}
}
-func (g *Game) calculatePlayerAttackRoll(p *player.Player, mob *world.MobInstance) (attackType string, weaponType item.WeaponType, attRoll int, defRoll int, maxHit int) {
+func (g *Game) calculatePlayerAttackRoll(p *player.Player, mob *world.MobInstance) (attackType string, weaponType item.EquipmentType, attRoll int, defRoll int, maxHit int) {
attackType = "crush"
if itemID, ok := p.Equipment[item.SlotMainHand]; ok {
if def, err := g.ItemStore.Load(itemID); err == nil {
- if def.AttackType != "" {
- attackType = def.AttackType
- } else if def.WeaponType == item.WeaponRanged {
+ if def.AttackType() != "" {
+ attackType = def.AttackType()
+ } else if def.EquipmentType() == item.EquipRangedWeapon {
attackType = "ranged"
- } else if def.WeaponType == item.WeaponScience {
+ } else if def.EquipmentType() == item.EquipScienceWeapon {
attackType = "science"
}
- weaponType = def.WeaponType
+ weaponType = def.EquipmentType()
}
}
totals := g.playerEquipBonuses(p)
- isRanged := weaponType == item.WeaponRanged
+ isRanged := weaponType == item.EquipRangedWeapon
if isRanged {
rangedBonus, _ := combat.RangedStyleBonus(string(p.AttackStyle))
@@ -343,15 +343,15 @@ func (g *Game) playerAttackUnarmed(sess *net.Session, p *player.Player, mob *wor
attackType, _, attRoll, defRoll, maxHit := g.calculateUnarmedAttackRoll(p, mob)
if combat.HitCheck(attRoll, defRoll) {
- g.applyPlayerHit(sess, p, mob, attackType, item.WeaponMelee, maxHit)
+ g.applyPlayerHit(sess, p, mob, attackType, item.EquipMeleeWeapon, maxHit)
} else {
- g.applyPlayerMiss(sess, p, mob, item.WeaponMelee)
+ g.applyPlayerMiss(sess, p, mob, item.EquipMeleeWeapon)
}
}
-func (g *Game) calculateUnarmedAttackRoll(p *player.Player, mob *world.MobInstance) (attackType string, weaponType item.WeaponType, attRoll int, defRoll int, maxHit int) {
+func (g *Game) calculateUnarmedAttackRoll(p *player.Player, mob *world.MobInstance) (attackType string, weaponType item.EquipmentType, attRoll int, defRoll int, maxHit int) {
attackType = "crush"
- weaponType = item.WeaponMelee
+ weaponType = item.EquipMeleeWeapon
attBonus, strBonus, _ := combat.AttackStyleBonus(string(p.AttackStyle))
effectiveAccuracy := p.Level(player.Accuracy) + g.techLevelBonus(p, "accuracy") + g.buffLevelBonus(p, "accuracy")
@@ -365,7 +365,7 @@ func (g *Game) calculateUnarmedAttackRoll(p *player.Player, mob *world.MobInstan
return
}
-func (g *Game) applyPlayerHit(sess *net.Session, p *player.Player, mob *world.MobInstance, attackType string, weaponType item.WeaponType, maxHit int) {
+func (g *Game) applyPlayerHit(sess *net.Session, p *player.Player, mob *world.MobInstance, attackType string, weaponType item.EquipmentType, maxHit int) {
dmg := combat.RollDamage(maxHit)
mob.HP -= dmg
@@ -390,7 +390,7 @@ func (g *Game) applyPlayerHit(sess *net.Session, p *player.Player, mob *world.Mo
mobDisplayName(mob, false), fbName)))
}
- isRanged := weaponType == item.WeaponRanged
+ isRanged := weaponType == item.EquipRangedWeapon
gains := g.awardCombatXP(sess, p, dmg, isRanged)
if isRanged {
@@ -425,10 +425,10 @@ func (g *Game) applyPlayerHit(sess *net.Session, p *player.Player, mob *world.Mo
}
}
-func (g *Game) applyPlayerMiss(sess *net.Session, p *player.Player, mob *world.MobInstance, weaponType item.WeaponType) {
+func (g *Game) applyPlayerMiss(sess *net.Session, p *player.Player, mob *world.MobInstance, weaponType item.EquipmentType) {
sess.WriteLine(g.colorize(sess, "miss", fmt.Sprintf("You miss %s.", mobDisplayName(mob, true))))
- if weaponType == item.WeaponRanged {
+ if weaponType == item.EquipRangedWeapon {
g.consumeAmmo(sess, p)
if p.AmmoQty <= 0 {
sess.WriteLine("You've run out of ammo!")
diff --git a/internal/game/core_equip.go b/internal/game/core_equip.go
index cc8016d..a297466 100644
--- a/internal/game/core_equip.go
+++ b/internal/game/core_equip.go
@@ -12,20 +12,20 @@ func (g *Game) playerEquipBonuses(p *player.Player) item.ItemStats {
if err != nil {
continue
}
- totals.StabAttack += def.Stats.StabAttack
- totals.SlashAttack += def.Stats.SlashAttack
- totals.CrushAttack += def.Stats.CrushAttack
- totals.ScienceAttack += def.Stats.ScienceAttack
- totals.RangedAttack += def.Stats.RangedAttack
- totals.StabDefense += def.Stats.StabDefense
- totals.SlashDefense += def.Stats.SlashDefense
- totals.CrushDefense += def.Stats.CrushDefense
- totals.ScienceDefense += def.Stats.ScienceDefense
- totals.RangedDefense += def.Stats.RangedDefense
- totals.StrengthBonus += def.Stats.StrengthBonus
- totals.RangedStrength += def.Stats.RangedStrength
- totals.ScienceDamage += def.Stats.ScienceDamage
- totals.TechnologyBonus += def.Stats.TechnologyBonus
+ totals.StabAttack += def.Stats().StabAttack
+ totals.SlashAttack += def.Stats().SlashAttack
+ totals.CrushAttack += def.Stats().CrushAttack
+ totals.ScienceAttack += def.Stats().ScienceAttack
+ totals.RangedAttack += def.Stats().RangedAttack
+ totals.StabDefense += def.Stats().StabDefense
+ totals.SlashDefense += def.Stats().SlashDefense
+ totals.CrushDefense += def.Stats().CrushDefense
+ totals.ScienceDefense += def.Stats().ScienceDefense
+ totals.RangedDefense += def.Stats().RangedDefense
+ totals.StrengthBonus += def.Stats().StrengthBonus
+ totals.RangedStrength += def.Stats().RangedStrength
+ totals.ScienceDamage += def.Stats().ScienceDamage
+ totals.TechnologyBonus += def.Stats().TechnologyBonus
}
return totals
}
@@ -33,8 +33,8 @@ func (g *Game) playerEquipBonuses(p *player.Player) item.ItemStats {
func (g *Game) playerWeaponSpeed(p *player.Player) float64 {
if itemID, ok := p.Equipment[item.SlotMainHand]; ok {
def, err := g.ItemStore.Load(itemID)
- if err == nil && def.Speed > 0 {
- return def.Speed
+ if err == nil && def.Speed()> 0 {
+ return def.Speed()
}
}
return 5.0
diff --git a/internal/game/core_production.go b/internal/game/core_production.go
index 8a38f36..da9466f 100644
--- a/internal/game/core_production.go
+++ b/internal/game/core_production.go
@@ -11,8 +11,8 @@ func (g *Game) findTool(p *player.Player, toolTypes []string) (toolSpeed float64
if itemID, ok := p.Equipment[item.SlotMainHand]; ok {
if def, err := g.ItemStore.Load(itemID); err == nil {
for _, t := range toolTypes {
- if def.ToolType == t {
- return def.ToolSpeed, def.Name, true
+ if def.ToolType() == t {
+ return def.ToolSpeed(), def.Name, true
}
}
}
@@ -27,8 +27,8 @@ func (g *Game) findTool(p *player.Player, toolTypes []string) (toolSpeed float64
continue
}
for _, t := range toolTypes {
- if def.ToolType == t {
- return def.ToolSpeed, def.Name, true
+ if def.ToolType() == t {
+ return def.ToolSpeed(), def.Name, true
}
}
}
@@ -58,7 +58,7 @@ var productionTypes = map[string]productionTypeInfo{
EndMessage: "You've finished cooking.",
FailMessage: "You accidentally burn the %i1.",
},
- "smelting": {
+ "smelt": {
ActionType: behavior.TypeSmelt,
DisplayVerb: "smelting",
StartMessage: "You place the %i1 into the furnace.",
diff --git a/internal/game/look_target.go b/internal/game/look_target.go
index 915e4a7..a93d969 100644
--- a/internal/game/look_target.go
+++ b/internal/game/look_target.go
@@ -311,7 +311,7 @@ func showPlayerInfo(g *Game, sess *net.Session, p *player.Player) {
}
func (g *Game) showItemStats(sess *net.Session, def *item.ItemDef) {
- s := def.Stats
+ s := def.Stats()
hasAttack := s.StabAttack != 0 || s.SlashAttack != 0 || s.CrushAttack != 0 ||
s.ScienceAttack != 0 || s.RangedAttack != 0
hasDefense := s.StabDefense != 0 || s.SlashDefense != 0 || s.CrushDefense != 0 ||
@@ -348,11 +348,11 @@ func (g *Game) showItemStats(sess *net.Session, def *item.ItemDef) {
sess.WriteLine(fmt.Sprintf(" Technology: %+d", s.TechnologyBonus))
}
}
- if def.AttackType != "" {
- sess.WriteLine(fmt.Sprintf("Attack type: %s", def.AttackType))
+ if def.AttackType() != "" {
+ sess.WriteLine(fmt.Sprintf("Attack type: %s", def.AttackType()))
}
- if def.Speed > 0 {
- sess.WriteLine(fmt.Sprintf("Speed: %.0f", def.Speed))
+ if def.Speed()> 0 {
+ sess.WriteLine(fmt.Sprintf("Speed: %.0f", def.Speed()))
}
if len(def.Requirements) > 0 {
sess.WriteLine("")
diff --git a/internal/game/production_engine.go b/internal/game/production_engine.go
index c30e7bb..048e152 100644
--- a/internal/game/production_engine.go
+++ b/internal/game/production_engine.go
@@ -25,7 +25,7 @@ func (g *Game) resolveCraftVar(sess *net.Session, varSpec string, craft *item.Cr
}
varSpec = strings.ReplaceAll(varSpec, "%n", outputName)
- for i, e := range craft.Consume {
+ for i, e := range craft.Ingredients {
key := fmt.Sprintf("%%i%d", i+1)
if !strings.Contains(varSpec, key) {
continue
@@ -134,14 +134,19 @@ func (g *Game) startProductionFromItem(sess *net.Session, p *player.Player, item
stationName = "inventory"
}
- info, ok := productionTypes[craft.Type]
+ cKey := craft.Subtype
+ if cKey == "" {
+ cKey = craft.Type
+ }
+
+ info, ok := productionTypes[cKey]
if !ok {
info = productionTypeInfo{
- ActionType: behavior.ActionType(craft.Type),
- DisplayVerb: craft.Type,
- StartMessage: "You start " + craft.Type + " %i1.",
+ ActionType: behavior.ActionType(cKey),
+ DisplayVerb: cKey,
+ StartMessage: "You start " + cKey + " %i1.",
SuccessMessage: "You produce %n.",
- EndMessage: "You've finished " + craft.Type + ".",
+ EndMessage: "You've finished " + cKey + ".",
}
}
@@ -215,7 +220,12 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool {
chance = behavior.SuccessChance(behavior.SuccessFormula(*craft.Success), skillLevel, craft.Level)
}
- info, _ := productionTypes[craft.Type]
+ cKey := craft.Subtype
+ if cKey == "" {
+ cKey = craft.Type
+ }
+
+ info, _ := productionTypes[cKey]
if rand.Float64() < chance {
outputQty := craft.OutputQty
@@ -231,7 +241,7 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool {
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
if slot != nil && slot.ItemID == item.ID {
- craftConsumeAll(craft, p.HasItem, p.RemoveItem)
+ consumeIngredients(craft, p.HasItem, p.RemoveItem)
slot.Quantity += outputQty
placed = true
break
@@ -239,7 +249,7 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool {
}
}
if !placed {
- craftConsumeAll(craft, p.HasItem, p.RemoveItem)
+ consumeIngredients(craft, p.HasItem, p.RemoveItem)
freeSlot := p.FirstFreeSlot()
if freeSlot == -1 {
sess.WriteLine("Your inventory is too full!")
@@ -274,7 +284,7 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool {
}
sess.WriteLine(msg)
} else {
- craftConsumeAll(craft, p.HasItem, p.RemoveItem)
+ consumeIngredients(craft, p.HasItem, p.RemoveItem)
if craft.Fail != "" {
freeSlot := p.FirstFreeSlot()
@@ -320,7 +330,7 @@ func (g *Game) collectByproducts(p *player.Player, item *item.ItemDef) []string
return nil
}
var byproducts []string
- for _, e := range craft.Consume {
+ for _, e := range craft.Ingredients {
if len(e.Byproducts) == 0 {
continue
}
diff --git a/internal/game/production_index.go b/internal/game/production_index.go
index 648ccc8..77fc08a 100644
--- a/internal/game/production_index.go
+++ b/internal/game/production_index.go
@@ -9,6 +9,7 @@ import (
type CraftIndex struct {
mu sync.RWMutex
byType map[string][]*item.ItemDef
+ bySubtype map[string][]*item.ItemDef
byInput map[string][]*item.ItemDef
byStation map[string][]*item.ItemDef
}
@@ -16,6 +17,7 @@ type CraftIndex struct {
func NewCraftIndex() *CraftIndex {
return &CraftIndex{
byType: make(map[string][]*item.ItemDef),
+ bySubtype: make(map[string][]*item.ItemDef),
byInput: make(map[string][]*item.ItemDef),
byStation: make(map[string][]*item.ItemDef),
}
@@ -26,6 +28,7 @@ func (idx *CraftIndex) Build(items []*item.ItemDef) {
defer idx.mu.Unlock()
idx.byType = make(map[string][]*item.ItemDef)
+ idx.bySubtype = make(map[string][]*item.ItemDef)
idx.byInput = make(map[string][]*item.ItemDef)
idx.byStation = make(map[string][]*item.ItemDef)
@@ -41,7 +44,11 @@ func (idx *CraftIndex) Build(items []*item.ItemDef) {
}
idx.byType[t] = appendUnique(idx.byType[t], def)
- for _, e := range craft.Consume {
+ if craft.Subtype != "" {
+ idx.bySubtype[craft.Subtype] = appendUnique(idx.bySubtype[craft.Subtype], def)
+ }
+
+ for _, e := range craft.Ingredients {
for _, id := range e.Items {
idx.byInput[id] = appendUnique(idx.byInput[id], def)
}
@@ -72,6 +79,15 @@ func (idx *CraftIndex) ByType(craftType string) []*item.ItemDef {
return out
}
+func (idx *CraftIndex) BySubtype(subtype string) []*item.ItemDef {
+ idx.mu.RLock()
+ defer idx.mu.RUnlock()
+ items := idx.bySubtype[subtype]
+ out := make([]*item.ItemDef, len(items))
+ copy(out, items)
+ return out
+}
+
func (idx *CraftIndex) ByStation(stationDefID string) []*item.ItemDef {
idx.mu.RLock()
defer idx.mu.RUnlock()
@@ -147,7 +163,7 @@ func craftEntryFor(def *item.ItemDef, itemID string) int {
}
func craftEntry(craft item.CraftDef, itemID string) int {
- for ei, e := range craft.Consume {
+ for ei, e := range craft.Ingredients {
for _, id := range e.Items {
if id == itemID {
return ei
@@ -161,7 +177,7 @@ func craftMatchesEntry(c *item.CraftDef, itemID string) bool {
if c == nil {
return false
}
- for _, e := range c.Consume {
+ for _, e := range c.Ingredients {
for _, id := range e.Items {
if id == itemID {
return true
@@ -175,7 +191,7 @@ func craftHasAllItems(c *item.CraftDef, hasItem func(string) bool) bool {
if c == nil {
return false
}
- for _, e := range c.Consume {
+ for _, e := range c.Ingredients {
found := false
for _, id := range e.Items {
if hasItem(id) {
@@ -194,7 +210,7 @@ func craftHasAllItemsQty(c *item.CraftDef, countItem func(string) int) bool {
if c == nil {
return false
}
- for _, e := range c.Consume {
+ for _, e := range c.Ingredients {
found := false
for _, id := range e.Items {
qty := e.Quantity
@@ -213,11 +229,11 @@ func craftHasAllItemsQty(c *item.CraftDef, countItem func(string) int) bool {
return true
}
-func craftConsumeAll(c *item.CraftDef, hasItem func(string) bool, removeItem func(string, int) bool) bool {
+func consumeIngredients(c *item.CraftDef, hasItem func(string) bool, removeItem func(string, int) bool) bool {
if c == nil {
return false
}
- for _, e := range c.Consume {
+ for _, e := range c.Ingredients {
found := false
for _, id := range e.Items {
if hasItem(id) {
diff --git a/internal/game/production_menu.go b/internal/game/production_menu.go
index bc2fccc..ff9fc51 100644
--- a/internal/game/production_menu.go
+++ b/internal/game/production_menu.go
@@ -106,7 +106,7 @@ func (g *Game) formatMaterials(sess *net.Session, item *item.ItemDef) string {
return ""
}
var parts []string
- for _, e := range craft.Consume {
+ for _, e := range craft.Ingredients {
itemName := e.Items[0]
if def, err := g.ItemStore.Load(e.Items[0]); err == nil {
itemName = g.itemColorize(sess, def, def.Name)
diff --git a/internal/game/sys_science.go b/internal/game/sys_science.go
index b04072f..bded8c4 100644
--- a/internal/game/sys_science.go
+++ b/internal/game/sys_science.go
@@ -182,7 +182,7 @@ func (g *Game) hasDeckEquipped(p *player.Player) bool {
if err != nil {
return false
}
- return def.WeaponType == item.WeaponScience
+ return def.EquipmentType() == item.EquipScienceWeapon
}
func (g *Game) providesJunk(p *player.Player) string {
@@ -194,7 +194,7 @@ func (g *Game) providesJunk(p *player.Player) string {
if err != nil {
return ""
}
- return def.ProvidesJunk
+ return def.ProvidesJunk()
}
func (g *Game) effectiveJunkCost(p *player.Player, mod *ModDef) map[string]int {
@@ -296,7 +296,7 @@ func (g *Game) totalScienceAttack(p *player.Player) int {
for _, itemID := range p.Equipment {
def, err := g.ItemStore.Load(itemID)
if err == nil {
- total += def.Stats.ScienceAttack
+ total += def.Stats().ScienceAttack
}
}
return total
diff --git a/internal/game/sys_technology.go b/internal/game/sys_technology.go
index 7df68d4..9e6a635 100644
--- a/internal/game/sys_technology.go
+++ b/internal/game/sys_technology.go
@@ -128,7 +128,7 @@ func (g *Game) totalTechBonus(p *player.Player) int {
for _, itemID := range p.Equipment {
def, err := g.ItemStore.Load(itemID)
if err == nil {
- total += def.Stats.TechnologyBonus
+ total += def.Stats().TechnologyBonus
}
}
return total
diff --git a/internal/item/item.go b/internal/item/item.go
index 890fa8d..0e63156 100644
--- a/internal/item/item.go
+++ b/internal/item/item.go
@@ -24,54 +24,195 @@ const (
SlotRing EquipSlot = "ring"
)
-type WeaponType string
+type EquipmentType string
const (
- WeaponMelee WeaponType = "melee"
- WeaponRanged WeaponType = "ranged"
- WeaponScience WeaponType = "science"
+ EquipMeleeWeapon EquipmentType = "melee_weapon"
+ EquipRangedWeapon EquipmentType = "ranged_weapon"
+ EquipScienceWeapon EquipmentType = "tech_weapon"
+ EquipArmor EquipmentType = "armor"
+ EquipTool EquipmentType = "tool"
+ EquipAmmo EquipmentType = "ammo"
)
+func (et EquipmentType) IsWeapon() bool {
+ return et == EquipMeleeWeapon || et == EquipRangedWeapon || et == EquipScienceWeapon
+}
+
+type AttackBonuses struct {
+ Stab int `yaml:"stab,omitempty"`
+ Slash int `yaml:"slash,omitempty"`
+ Crush int `yaml:"crush,omitempty"`
+ Ranged int `yaml:"ranged,omitempty"`
+ Science int `yaml:"science,omitempty"`
+}
+
+type DefenseBonuses struct {
+ Stab int `yaml:"stab,omitempty"`
+ Slash int `yaml:"slash,omitempty"`
+ Crush int `yaml:"crush,omitempty"`
+ Ranged int `yaml:"ranged,omitempty"`
+ Science int `yaml:"science,omitempty"`
+}
+
+type OtherBonuses struct {
+ Strength int `yaml:"strength,omitempty"`
+ Ranged int `yaml:"ranged,omitempty"`
+ Science int `yaml:"science,omitempty"`
+ Technology int `yaml:"technology,omitempty"`
+}
+
+type EquipmentDef struct {
+ Type EquipmentType `yaml:"type"`
+ Slot EquipSlot `yaml:"slot,omitempty"`
+ AttackType string `yaml:"attack_type,omitempty"`
+ Speed float64 `yaml:"speed,omitempty"`
+ Junk string `yaml:"junk,omitempty"`
+ Attack *AttackBonuses `yaml:"attack,omitempty"`
+ Defense *DefenseBonuses `yaml:"defense,omitempty"`
+ Other *OtherBonuses `yaml:"other,omitempty"`
+}
+
+type ToolDef struct {
+ Type string `yaml:"type"`
+ Speed float64 `yaml:"speed,omitempty"`
+}
+
type ItemDef struct {
- ID string `yaml:"id"`
- Name string `yaml:"name"`
- Color string `yaml:"color"`
- Description string `yaml:"description"`
- Value int `yaml:"value"`
- Stackable bool `yaml:"stackable"`
- EquipSlot EquipSlot `yaml:"equip_slot"`
- WeaponType WeaponType `yaml:"weapon_type"`
- AttackType string `yaml:"attack_type"`
- Stats ItemStats `yaml:"stats"`
- Speed float64 `yaml:"speed"`
- Requirements map[string]int `yaml:"requirements,omitempty"`
- ToolType string `yaml:"tool_type"`
- ToolSpeed float64 `yaml:"tool_speed"`
- BurnTicks float64 `yaml:"burn_ticks"`
- FireLevel int `yaml:"fire_level"`
- FireXP int `yaml:"fire_xp"`
- Quality int `yaml:"quality"`
- MaxQuality int `yaml:"max_quality"`
- SearchTable string `yaml:"search_table"`
- SearchMiscTable string `yaml:"search_misc_table"`
- SearchTicks float64 `yaml:"search_ticks"`
- SearchMessage string `yaml:"search_message"`
- HealValue int `yaml:"heal_value"`
- EatMessage string `yaml:"eat_message"`
- Craft CraftList `yaml:"craft,omitempty"`
- ProvidesJunk string `yaml:"provides_junk,omitempty"`
- FarmPatchType string `yaml:"farm_patch_type"`
- FarmLevel int `yaml:"farm_level"`
- FarmPlantXP int `yaml:"farm_plant_xp"`
- FarmHarvestXP int `yaml:"farm_harvest_xp"`
- FarmStages int `yaml:"farm_stages"`
- FarmProduct string `yaml:"farm_product"`
- FarmMinYield int `yaml:"farm_min_yield"`
- FarmMaxYield int `yaml:"farm_max_yield"`
- PotionEffect string `yaml:"potion_effect"`
- PotionBonus int `yaml:"potion_bonus"`
- PotionDuration float64 `yaml:"potion_duration"`
- Recoverable bool `yaml:"recoverable"`
+ ID string `yaml:"id"`
+ Name string `yaml:"name"`
+ Color string `yaml:"color"`
+ Description string `yaml:"description"`
+ Value int `yaml:"value"`
+ Stackable bool `yaml:"stackable"`
+ Recoverable bool `yaml:"recoverable"`
+
+ Equipment *EquipmentDef `yaml:"equipment,omitempty"`
+ Tool *ToolDef `yaml:"tool,omitempty"`
+ Requirements map[string]int `yaml:"requirements,omitempty"`
+
+ Quality int `yaml:"quality"`
+ MaxQuality int `yaml:"max_quality"`
+ Craft CraftList `yaml:"craft,omitempty"`
+
+ SearchTable string `yaml:"search_table"`
+ SearchTicks float64 `yaml:"search_ticks"`
+ SearchMessage string `yaml:"search_message"`
+
+ HealValue int `yaml:"heal_value"`
+ EatMessage string `yaml:"eat_message"`
+
+ FarmPatchType string `yaml:"farm_patch_type"`
+ FarmLevel int `yaml:"farm_level"`
+ FarmPlantXP int `yaml:"farm_plant_xp"`
+ FarmHarvestXP int `yaml:"farm_harvest_xp"`
+ FarmStages int `yaml:"farm_stages"`
+ FarmProduct string `yaml:"farm_product"`
+ FarmMinYield int `yaml:"farm_min_yield"`
+ FarmMaxYield int `yaml:"farm_max_yield"`
+
+ PotionEffect string `yaml:"potion_effect"`
+ PotionBonus int `yaml:"potion_bonus"`
+ PotionDuration float64 `yaml:"potion_duration"`
+
+ BurnTicks float64 `yaml:"burn_ticks"`
+ FireLevel int `yaml:"fire_level"`
+ FireXP int `yaml:"fire_xp"`
+}
+
+func (d *ItemDef) EquipmentType() EquipmentType {
+ if d.Equipment != nil {
+ return d.Equipment.Type
+ }
+ return ""
+}
+
+func (d *ItemDef) EquipSlot() EquipSlot {
+ if d.Equipment != nil {
+ return d.Equipment.Slot
+ }
+ return ""
+}
+
+func (d *ItemDef) Speed() float64 {
+ if d.Equipment != nil {
+ return d.Equipment.Speed
+ }
+ return 0
+}
+
+func (d *ItemDef) AttackType() string {
+ if d.Equipment != nil {
+ return d.Equipment.AttackType
+ }
+ return ""
+}
+
+func (d *ItemDef) ToolType() string {
+ if d.Tool != nil {
+ return d.Tool.Type
+ }
+ return ""
+}
+
+func (d *ItemDef) ToolSpeed() float64 {
+ if d.Tool != nil {
+ return d.Tool.Speed
+ }
+ return 0
+}
+
+func (d *ItemDef) ProvidesJunk() string {
+ if d.Equipment != nil {
+ return d.Equipment.Junk
+ }
+ return ""
+}
+
+type ItemStats struct {
+ StabAttack int
+ SlashAttack int
+ CrushAttack int
+ ScienceAttack int
+ RangedAttack int
+
+ StabDefense int
+ SlashDefense int
+ CrushDefense int
+ ScienceDefense int
+ RangedDefense int
+
+ StrengthBonus int
+ RangedStrength int
+ ScienceDamage int
+ TechnologyBonus int
+}
+
+func (d *ItemDef) Stats() ItemStats {
+ var s ItemStats
+ if d.Equipment != nil {
+ if d.Equipment.Attack != nil {
+ s.StabAttack = d.Equipment.Attack.Stab
+ s.SlashAttack = d.Equipment.Attack.Slash
+ s.CrushAttack = d.Equipment.Attack.Crush
+ s.RangedAttack = d.Equipment.Attack.Ranged
+ s.ScienceAttack = d.Equipment.Attack.Science
+ }
+ if d.Equipment.Defense != nil {
+ s.StabDefense = d.Equipment.Defense.Stab
+ s.SlashDefense = d.Equipment.Defense.Slash
+ s.CrushDefense = d.Equipment.Defense.Crush
+ s.RangedDefense = d.Equipment.Defense.Ranged
+ s.ScienceDefense = d.Equipment.Defense.Science
+ }
+ if d.Equipment.Other != nil {
+ s.StrengthBonus = d.Equipment.Other.Strength
+ s.RangedStrength = d.Equipment.Other.Ranged
+ s.ScienceDamage = d.Equipment.Other.Science
+ s.TechnologyBonus = d.Equipment.Other.Technology
+ }
+ }
+ return s
}
type CraftList []CraftDef
@@ -117,7 +258,7 @@ func (d *ItemDef) MatchingCrafts(fn func(CraftDef) bool) []*CraftDef {
return result
}
-type ConsumeEntry struct {
+type IngredientEntry struct {
Items []string `yaml:"items"`
Quantity int `yaml:"quantity"`
Byproducts []string `yaml:"byproducts"`
@@ -136,13 +277,13 @@ type SuccessFormula struct {
type CraftDef struct {
Type string `yaml:"type"`
- Skill string `yaml:"skill"`
+ Subtype string `yaml:"subtype,omitempty"`
Level int `yaml:"level"`
XP int `yaml:"xp"`
Wait float64 `yaml:"wait"`
Station []string `yaml:"station"`
Tool string `yaml:"tool"`
- Consume []ConsumeEntry `yaml:"consume"`
+ Ingredients []IngredientEntry `yaml:"ingredients"`
OutputQty int `yaml:"output_qty"`
Fail string `yaml:"fail"`
Message string `yaml:"message"`
@@ -154,31 +295,9 @@ type CraftDef struct {
}
func (c *CraftDef) EffectiveSkill() string {
- if c.Skill != "" {
- return c.Skill
- }
return c.Type
}
-type ItemStats struct {
- StabAttack int `yaml:"stab_attack"`
- SlashAttack int `yaml:"slash_attack"`
- CrushAttack int `yaml:"crush_attack"`
- ScienceAttack int `yaml:"science_attack"`
- RangedAttack int `yaml:"ranged_attack"`
-
- StabDefense int `yaml:"stab_defense"`
- SlashDefense int `yaml:"slash_defense"`
- CrushDefense int `yaml:"crush_defense"`
- ScienceDefense int `yaml:"science_defense"`
- RangedDefense int `yaml:"ranged_defense"`
-
- StrengthBonus int `yaml:"strength_bonus"`
- RangedStrength int `yaml:"ranged_strength"`
- ScienceDamage int `yaml:"science_damage"`
- TechnologyBonus int `yaml:"technology_bonus"`
-}
-
func (d *ItemDef) MatchesName(input string) bool {
lower := strings.ToLower(strings.TrimSpace(input))
if lower == "" {
diff --git a/internal/validate/checks.go b/internal/validate/checks.go
index 50fd9a5..b35a3de 100644
--- a/internal/validate/checks.go
+++ b/internal/validate/checks.go
@@ -535,15 +535,6 @@ func validateItems(s Source) []Issue {
})
}
- if def.SearchMiscTable != "" && !dropIDs[def.SearchMiscTable] {
- issues = append(issues, Issue{
- Level: "ERROR",
- Type: "reference",
- Message: fmt.Sprintf("Item %q: search_misc_table %q does not exist",
- id, def.SearchMiscTable),
- })
- }
-
if def.FarmProduct != "" && !itemIDs[def.FarmProduct] {
issues = append(issues, Issue{
Level: "ERROR",
@@ -579,14 +570,19 @@ func validateCraftBlocks(s Source) []Issue {
for ci := range item.Craft {
c := &item.Craft[ci]
- for _, e := range c.Consume {
- for _, consumeItem := range e.Items {
- if consumeItem != "" && !itemIDs[consumeItem] {
+ craftLabel := c.Type
+ if c.Subtype != "" {
+ craftLabel = c.Type + "/" + c.Subtype
+ }
+
+ for _, e := range c.Ingredients {
+ for _, ingredientID := range e.Items {
+ if ingredientID != "" && !itemIDs[ingredientID] {
issues = append(issues, Issue{
Level: "ERROR",
Type: "reference",
Message: fmt.Sprintf("Item %q (craft: %s): consumes nonexistent item %q",
- item.ID, c.Type, consumeItem),
+ item.ID, craftLabel, ingredientID),
})
}
}
@@ -597,7 +593,7 @@ func validateCraftBlocks(s Source) []Issue {
Level: "ERROR",
Type: "reference",
Message: fmt.Sprintf("Item %q (craft: %s): fail product %q does not exist",
- item.ID, c.Type, c.Fail),
+ item.ID, craftLabel, c.Fail),
})
}
@@ -607,7 +603,7 @@ func validateCraftBlocks(s Source) []Issue {
Level: "ERROR",
Type: "reference",
Message: fmt.Sprintf("Item %q (craft: %s): station object %q does not exist",
- item.ID, c.Type, sid),
+ item.ID, craftLabel, sid),
})
}
}