aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AGENTS.md59
-rw-r--r--data/items/ammo/adamant_arrow.yaml24
-rw-r--r--data/items/ammo/adamant_arrowtips.yaml19
-rw-r--r--data/items/ammo/adamant_bolts.yaml24
-rw-r--r--data/items/ammo/adamant_bolts_unf.yaml19
-rw-r--r--data/items/ammo/arrow_shaft.yaml73
-rw-r--r--data/items/ammo/bronze_arrow.yaml24
-rw-r--r--data/items/ammo/bronze_arrowtips.yaml19
-rw-r--r--data/items/ammo/bronze_bolts.yaml24
-rw-r--r--data/items/ammo/bronze_bolts_unf.yaml19
-rw-r--r--data/items/ammo/diamond_bolt_tips.yaml18
-rw-r--r--data/items/ammo/diamond_bolts.yaml24
-rw-r--r--data/items/ammo/emerald_bolt_tips.yaml18
-rw-r--r--data/items/ammo/emerald_bolts.yaml24
-rw-r--r--data/items/ammo/headless_arrow.yaml21
-rw-r--r--data/items/ammo/iron_arrow.yaml24
-rw-r--r--data/items/ammo/iron_arrowtips.yaml19
-rw-r--r--data/items/ammo/iron_bolts.yaml24
-rw-r--r--data/items/ammo/iron_bolts_unf.yaml19
-rw-r--r--data/items/ammo/mithril_arrow.yaml24
-rw-r--r--data/items/ammo/mithril_arrowtips.yaml19
-rw-r--r--data/items/ammo/mithril_bolts.yaml24
-rw-r--r--data/items/ammo/mithril_bolts_unf.yaml19
-rw-r--r--data/items/ammo/ruby_bolt_tips.yaml18
-rw-r--r--data/items/ammo/ruby_bolts.yaml24
-rw-r--r--data/items/ammo/rune_arrow.yaml24
-rw-r--r--data/items/ammo/rune_arrowtips.yaml19
-rw-r--r--data/items/ammo/rune_bolts.yaml24
-rw-r--r--data/items/ammo/rune_bolts_unf.yaml19
-rw-r--r--data/items/ammo/sapphire_bolt_tips.yaml18
-rw-r--r--data/items/ammo/sapphire_bolts.yaml24
-rw-r--r--data/items/ammo/steel_arrow.yaml24
-rw-r--r--data/items/ammo/steel_arrowtips.yaml19
-rw-r--r--data/items/ammo/steel_bolts.yaml24
-rw-r--r--data/items/ammo/steel_bolts_unf.yaml19
-rw-r--r--data/items/consumables/amp_potion.yaml26
-rw-r--r--data/items/consumables/anchovies.yaml26
-rw-r--r--data/items/consumables/avantoe_potion_unf.yaml24
-rw-r--r--data/items/consumables/bio_serum.yaml26
-rw-r--r--data/items/consumables/bread.yaml25
-rw-r--r--data/items/consumables/bread_dough.yaml30
-rw-r--r--data/items/consumables/cadantine_potion_unf.yaml24
-rw-r--r--data/items/consumables/dwarf_weed_potion_unf.yaml24
-rw-r--r--data/items/consumables/full_restore.yaml26
-rw-r--r--data/items/consumables/guam_potion_unf.yaml24
-rw-r--r--data/items/consumables/harralander_potion_unf.yaml24
-rw-r--r--data/items/consumables/herring.yaml26
-rw-r--r--data/items/consumables/irit_potion_unf.yaml24
-rw-r--r--data/items/consumables/kwuarm_potion_unf.yaml24
-rw-r--r--data/items/consumables/lantadyme_potion_unf.yaml24
-rw-r--r--data/items/consumables/marrentill_potion_unf.yaml24
-rw-r--r--data/items/consumables/nano_restore.yaml26
-rw-r--r--data/items/consumables/pastry_dough.yaml21
-rw-r--r--data/items/consumables/pie_dish.yaml22
-rw-r--r--data/items/consumables/pizza_dough.yaml21
-rw-r--r--data/items/consumables/ranarr_potion_unf.yaml24
-rw-r--r--data/items/consumables/restoration_compound.yaml26
-rw-r--r--data/items/consumables/salmon.yaml26
-rw-r--r--data/items/consumables/sardine.yaml26
-rw-r--r--data/items/consumables/science_serum.yaml26
-rw-r--r--data/items/consumables/shield_potion.yaml26
-rw-r--r--data/items/consumables/shrimps.yaml26
-rw-r--r--data/items/consumables/snapdragon_potion_unf.yaml24
-rw-r--r--data/items/consumables/stim_potion.yaml26
-rw-r--r--data/items/consumables/super_amp.yaml26
-rw-r--r--data/items/consumables/super_bio_serum.yaml26
-rw-r--r--data/items/consumables/super_shield.yaml26
-rw-r--r--data/items/consumables/super_stim.yaml26
-rw-r--r--data/items/consumables/super_stim_cell.yaml26
-rw-r--r--data/items/consumables/targeting_serum.yaml26
-rw-r--r--data/items/consumables/tarromin_potion_unf.yaml24
-rw-r--r--data/items/consumables/tech_serum.yaml26
-rw-r--r--data/items/consumables/toadflax_potion_unf.yaml24
-rw-r--r--data/items/consumables/torstol_potion_unf.yaml24
-rw-r--r--data/items/consumables/trout.yaml26
-rw-r--r--data/items/equipment/adamant_dagger.yaml38
-rw-r--r--data/items/equipment/adamant_full_helm.yaml34
-rw-r--r--data/items/equipment/adamant_med_helm.yaml34
-rw-r--r--data/items/equipment/adamant_platebody.yaml34
-rw-r--r--data/items/equipment/adamant_sword.yaml38
-rw-r--r--data/items/equipment/bronze_dagger.yaml34
-rw-r--r--data/items/equipment/bronze_full_helm.yaml30
-rw-r--r--data/items/equipment/bronze_med_helm.yaml30
-rw-r--r--data/items/equipment/bronze_platebody.yaml30
-rw-r--r--data/items/equipment/bronze_sword.yaml34
-rw-r--r--data/items/equipment/coif.yaml41
-rw-r--r--data/items/equipment/diamond_amulet_u.yaml25
-rw-r--r--data/items/equipment/diamond_bracelet.yaml27
-rw-r--r--data/items/equipment/diamond_necklace.yaml27
-rw-r--r--data/items/equipment/diamond_ring.yaml27
-rw-r--r--data/items/equipment/emerald_amulet_u.yaml25
-rw-r--r--data/items/equipment/emerald_bracelet.yaml27
-rw-r--r--data/items/equipment/emerald_necklace.yaml27
-rw-r--r--data/items/equipment/emerald_ring.yaml27
-rw-r--r--data/items/equipment/gold_amulet_u.yaml22
-rw-r--r--data/items/equipment/gold_bracelet.yaml24
-rw-r--r--data/items/equipment/gold_necklace.yaml24
-rw-r--r--data/items/equipment/gold_ring.yaml24
-rw-r--r--data/items/equipment/hard_leather_body.yaml37
-rw-r--r--data/items/equipment/hard_leather_shield.yaml37
-rw-r--r--data/items/equipment/iron_dagger.yaml34
-rw-r--r--data/items/equipment/iron_full_helm.yaml30
-rw-r--r--data/items/equipment/iron_med_helm.yaml30
-rw-r--r--data/items/equipment/iron_platebody.yaml30
-rw-r--r--data/items/equipment/iron_sword.yaml34
-rw-r--r--data/items/equipment/leather_body.yaml33
-rw-r--r--data/items/equipment/leather_boots.yaml33
-rw-r--r--data/items/equipment/leather_chaps.yaml33
-rw-r--r--data/items/equipment/leather_cowl.yaml33
-rw-r--r--data/items/equipment/leather_gloves.yaml33
-rw-r--r--data/items/equipment/leather_vambraces.yaml35
-rw-r--r--data/items/equipment/longbow.yaml27
-rw-r--r--data/items/equipment/longbow_u.yaml15
-rw-r--r--data/items/equipment/magic_longbow.yaml31
-rw-r--r--data/items/equipment/magic_longbow_u.yaml15
-rw-r--r--data/items/equipment/magic_shortbow.yaml31
-rw-r--r--data/items/equipment/magic_shortbow_u.yaml15
-rw-r--r--data/items/equipment/maple_longbow.yaml31
-rw-r--r--data/items/equipment/maple_longbow_u.yaml15
-rw-r--r--data/items/equipment/maple_shortbow.yaml31
-rw-r--r--data/items/equipment/maple_shortbow_u.yaml15
-rw-r--r--data/items/equipment/mithril_dagger.yaml38
-rw-r--r--data/items/equipment/mithril_full_helm.yaml34
-rw-r--r--data/items/equipment/mithril_med_helm.yaml34
-rw-r--r--data/items/equipment/mithril_platebody.yaml34
-rw-r--r--data/items/equipment/mithril_sword.yaml38
-rw-r--r--data/items/equipment/oak_longbow.yaml31
-rw-r--r--data/items/equipment/oak_longbow_u.yaml15
-rw-r--r--data/items/equipment/oak_shortbow.yaml31
-rw-r--r--data/items/equipment/oak_shortbow_u.yaml15
-rw-r--r--data/items/equipment/ruby_amulet_u.yaml25
-rw-r--r--data/items/equipment/ruby_bracelet.yaml27
-rw-r--r--data/items/equipment/ruby_necklace.yaml27
-rw-r--r--data/items/equipment/ruby_ring.yaml27
-rw-r--r--data/items/equipment/rune_dagger.yaml38
-rw-r--r--data/items/equipment/rune_full_helm.yaml34
-rw-r--r--data/items/equipment/rune_med_helm.yaml34
-rw-r--r--data/items/equipment/rune_platebody.yaml34
-rw-r--r--data/items/equipment/rune_sword.yaml38
-rw-r--r--data/items/equipment/sapphire_amulet_u.yaml25
-rw-r--r--data/items/equipment/sapphire_bracelet.yaml27
-rw-r--r--data/items/equipment/sapphire_necklace.yaml27
-rw-r--r--data/items/equipment/sapphire_ring.yaml27
-rw-r--r--data/items/equipment/shortbow.yaml27
-rw-r--r--data/items/equipment/shortbow_u.yaml15
-rw-r--r--data/items/equipment/steel_dagger.yaml38
-rw-r--r--data/items/equipment/steel_full_helm.yaml34
-rw-r--r--data/items/equipment/steel_med_helm.yaml34
-rw-r--r--data/items/equipment/steel_platebody.yaml34
-rw-r--r--data/items/equipment/steel_sword.yaml38
-rw-r--r--data/items/equipment/willow_longbow.yaml31
-rw-r--r--data/items/equipment/willow_longbow_u.yaml15
-rw-r--r--data/items/equipment/willow_shortbow.yaml31
-rw-r--r--data/items/equipment/willow_shortbow_u.yaml15
-rw-r--r--data/items/equipment/yew_longbow.yaml31
-rw-r--r--data/items/equipment/yew_longbow_u.yaml15
-rw-r--r--data/items/equipment/yew_shortbow.yaml31
-rw-r--r--data/items/equipment/yew_shortbow_u.yaml15
-rw-r--r--data/items/materials/adamant_nails.yaml19
-rw-r--r--data/items/materials/adamantite_bar.yaml23
-rw-r--r--data/items/materials/avantoe.yaml19
-rw-r--r--data/items/materials/ball_of_wool.yaml19
-rw-r--r--data/items/materials/bowstring.yaml17
-rw-r--r--data/items/materials/bronze_bar.yaml23
-rw-r--r--data/items/materials/bronze_nails.yaml19
-rw-r--r--data/items/materials/cadantine.yaml19
-rw-r--r--data/items/materials/diamond.yaml18
-rw-r--r--data/items/materials/dwarf_weed.yaml19
-rw-r--r--data/items/materials/emerald.yaml18
-rw-r--r--data/items/materials/gold_bar.yaml20
-rw-r--r--data/items/materials/guam.yaml19
-rw-r--r--data/items/materials/harralander.yaml19
-rw-r--r--data/items/materials/irit.yaml19
-rw-r--r--data/items/materials/iron_bar.yaml24
-rw-r--r--data/items/materials/iron_nails.yaml19
-rw-r--r--data/items/materials/kwuarm.yaml19
-rw-r--r--data/items/materials/lantadyme.yaml19
-rw-r--r--data/items/materials/mahogany_planks.yaml20
-rw-r--r--data/items/materials/marrentill.yaml19
-rw-r--r--data/items/materials/mithril_bar.yaml23
-rw-r--r--data/items/materials/mithril_nails.yaml19
-rw-r--r--data/items/materials/oak_planks.yaml20
-rw-r--r--data/items/materials/planks.yaml20
-rw-r--r--data/items/materials/ranarr.yaml19
-rw-r--r--data/items/materials/ruby.yaml18
-rw-r--r--data/items/materials/rune_nails.yaml19
-rw-r--r--data/items/materials/runite_bar.yaml23
-rw-r--r--data/items/materials/sapphire.yaml18
-rw-r--r--data/items/materials/silver_bar.yaml20
-rw-r--r--data/items/materials/snapdragon.yaml19
-rw-r--r--data/items/materials/soft_clay.yaml27
-rw-r--r--data/items/materials/steel_bar.yaml23
-rw-r--r--data/items/materials/steel_nails.yaml19
-rw-r--r--data/items/materials/stim_cell.yaml26
-rw-r--r--data/items/materials/tarromin.yaml19
-rw-r--r--data/items/materials/teak_planks.yaml20
-rw-r--r--data/items/materials/toadflax.yaml19
-rw-r--r--data/items/materials/torstol.yaml19
-rw-r--r--data/items/misc/mahogany_table.yaml19
-rw-r--r--data/items/misc/oak_shelf.yaml19
-rw-r--r--data/items/misc/oak_table.yaml19
-rw-r--r--data/items/misc/teak_table.yaml19
-rw-r--r--data/items/misc/wooden_chair.yaml19
-rw-r--r--data/items/misc/wooden_shelf.yaml19
-rw-r--r--data/items/misc/wooden_table.yaml19
-rw-r--r--data/items/tools/empty_pot.yaml24
-rw-r--r--data/items/tools/plant_pot.yaml22
-rw-r--r--data/items/tools/unfired_pie_dish.yaml17
-rw-r--r--data/items/tools/unfired_plant_pot.yaml17
-rw-r--r--data/items/tools/unfired_pot.yaml17
-rw-r--r--data/recipes/clean/avantoe.yaml11
-rw-r--r--data/recipes/clean/cadantine.yaml11
-rw-r--r--data/recipes/clean/dwarf_weed.yaml11
-rw-r--r--data/recipes/clean/guam.yaml11
-rw-r--r--data/recipes/clean/harralander.yaml11
-rw-r--r--data/recipes/clean/irit.yaml11
-rw-r--r--data/recipes/clean/kwuarm.yaml11
-rw-r--r--data/recipes/clean/lantadyme.yaml11
-rw-r--r--data/recipes/clean/marrentill.yaml11
-rw-r--r--data/recipes/clean/ranarr.yaml11
-rw-r--r--data/recipes/clean/snapdragon.yaml11
-rw-r--r--data/recipes/clean/tarromin.yaml11
-rw-r--r--data/recipes/clean/toadflax.yaml11
-rw-r--r--data/recipes/clean/torstol.yaml11
-rw-r--r--data/recipes/construction/mahogany_planks.yaml12
-rw-r--r--data/recipes/construction/mahogany_table.yaml11
-rw-r--r--data/recipes/construction/oak_planks.yaml12
-rw-r--r--data/recipes/construction/oak_shelf.yaml11
-rw-r--r--data/recipes/construction/oak_table.yaml11
-rw-r--r--data/recipes/construction/planks.yaml12
-rw-r--r--data/recipes/construction/teak_planks.yaml12
-rw-r--r--data/recipes/construction/teak_table.yaml11
-rw-r--r--data/recipes/construction/wooden_chair.yaml11
-rw-r--r--data/recipes/construction/wooden_shelf.yaml11
-rw-r--r--data/recipes/construction/wooden_table.yaml11
-rw-r--r--data/recipes/cooking/anchovies.yaml13
-rw-r--r--data/recipes/cooking/bread.yaml13
-rw-r--r--data/recipes/cooking/herring.yaml13
-rw-r--r--data/recipes/cooking/salmon.yaml13
-rw-r--r--data/recipes/cooking/sardine.yaml13
-rw-r--r--data/recipes/cooking/shrimps.yaml13
-rw-r--r--data/recipes/cooking/trout.yaml13
-rw-r--r--data/recipes/crafting/ball_of_wool.yaml11
-rw-r--r--data/recipes/crafting/bowstring.yaml11
-rw-r--r--data/recipes/crafting/coif.yaml13
-rw-r--r--data/recipes/crafting/cut_diamond.yaml11
-rw-r--r--data/recipes/crafting/cut_emerald.yaml11
-rw-r--r--data/recipes/crafting/cut_ruby.yaml11
-rw-r--r--data/recipes/crafting/cut_sapphire.yaml11
-rw-r--r--data/recipes/crafting/diamond_amulet_u.yaml16
-rw-r--r--data/recipes/crafting/diamond_bracelet.yaml16
-rw-r--r--data/recipes/crafting/diamond_necklace.yaml16
-rw-r--r--data/recipes/crafting/diamond_ring.yaml16
-rw-r--r--data/recipes/crafting/emerald_amulet_u.yaml16
-rw-r--r--data/recipes/crafting/emerald_bracelet.yaml16
-rw-r--r--data/recipes/crafting/emerald_necklace.yaml16
-rw-r--r--data/recipes/crafting/emerald_ring.yaml16
-rw-r--r--data/recipes/crafting/gold_amulet_u.yaml14
-rw-r--r--data/recipes/crafting/gold_bracelet.yaml14
-rw-r--r--data/recipes/crafting/gold_necklace.yaml14
-rw-r--r--data/recipes/crafting/gold_ring.yaml14
-rw-r--r--data/recipes/crafting/hard_leather_body.yaml13
-rw-r--r--data/recipes/crafting/hard_leather_shield.yaml13
-rw-r--r--data/recipes/crafting/leather_body.yaml13
-rw-r--r--data/recipes/crafting/leather_boots.yaml13
-rw-r--r--data/recipes/crafting/leather_chaps.yaml13
-rw-r--r--data/recipes/crafting/leather_cowl.yaml13
-rw-r--r--data/recipes/crafting/leather_gloves.yaml13
-rw-r--r--data/recipes/crafting/leather_vambraces.yaml13
-rw-r--r--data/recipes/crafting/pie_dish.yaml16
-rw-r--r--data/recipes/crafting/plant_pot.yaml16
-rw-r--r--data/recipes/crafting/pot.yaml16
-rw-r--r--data/recipes/crafting/ruby_amulet_u.yaml16
-rw-r--r--data/recipes/crafting/ruby_bracelet.yaml16
-rw-r--r--data/recipes/crafting/ruby_necklace.yaml16
-rw-r--r--data/recipes/crafting/ruby_ring.yaml16
-rw-r--r--data/recipes/crafting/sapphire_amulet_u.yaml16
-rw-r--r--data/recipes/crafting/sapphire_bracelet.yaml16
-rw-r--r--data/recipes/crafting/sapphire_necklace.yaml16
-rw-r--r--data/recipes/crafting/sapphire_ring.yaml16
-rw-r--r--data/recipes/crafting/unfired_pie_dish.yaml11
-rw-r--r--data/recipes/crafting/unfired_plant_pot.yaml11
-rw-r--r--data/recipes/crafting/unfired_pot.yaml11
-rw-r--r--data/recipes/fletching/arrow_shafts.yaml52
-rw-r--r--data/recipes/fletching/bolt_feathering.yaml63
-rw-r--r--data/recipes/fletching/gem_bolt_tips.yaml36
-rw-r--r--data/recipes/fletching/gem_tipped_bolts.yaml43
-rw-r--r--data/recipes/fletching/headless_arrow.yaml13
-rw-r--r--data/recipes/fletching/strung_bows.yaml111
-rw-r--r--data/recipes/fletching/tipped_arrows.yaml63
-rw-r--r--data/recipes/fletching/unstrung_bows.yaml88
-rw-r--r--data/recipes/mixing/amp_potion.yaml13
-rw-r--r--data/recipes/mixing/bio_serum.yaml13
-rw-r--r--data/recipes/mixing/full_restore.yaml13
-rw-r--r--data/recipes/mixing/nano_restore.yaml13
-rw-r--r--data/recipes/mixing/restoration_compound.yaml13
-rw-r--r--data/recipes/mixing/science_serum.yaml13
-rw-r--r--data/recipes/mixing/shield_potion.yaml13
-rw-r--r--data/recipes/mixing/stim_cell.yaml13
-rw-r--r--data/recipes/mixing/stim_potion.yaml13
-rw-r--r--data/recipes/mixing/super_amp.yaml13
-rw-r--r--data/recipes/mixing/super_bio_serum.yaml13
-rw-r--r--data/recipes/mixing/super_shield.yaml13
-rw-r--r--data/recipes/mixing/super_stim.yaml13
-rw-r--r--data/recipes/mixing/super_stim_cell.yaml13
-rw-r--r--data/recipes/mixing/targeting_serum.yaml13
-rw-r--r--data/recipes/mixing/tech_serum.yaml13
-rw-r--r--data/recipes/smelting/adamantite.yaml15
-rw-r--r--data/recipes/smelting/gold.yaml13
-rw-r--r--data/recipes/smelting/runite.yaml15
-rw-r--r--data/recipes/smelting/silver.yaml13
-rw-r--r--data/recipes/smelting/smelt_bronze.yaml15
-rw-r--r--data/recipes/smelting/smelt_iron.yaml17
-rw-r--r--data/recipes/smelting/smelt_mithril.yaml15
-rw-r--r--data/recipes/smelting/smelt_steel.yaml15
-rw-r--r--data/recipes/smithing/adamant.yaml48
-rw-r--r--data/recipes/smithing/bronze.yaml48
-rw-r--r--data/recipes/smithing/iron.yaml48
-rw-r--r--data/recipes/smithing/mithril.yaml48
-rw-r--r--data/recipes/smithing/rune.yaml48
-rw-r--r--data/recipes/smithing/steel.yaml48
-rw-r--r--internal/action/recipe.go319
-rw-r--r--internal/game/action_clean.go60
-rw-r--r--internal/game/action_farm.go4
-rw-r--r--internal/game/action_fletch.go110
-rw-r--r--internal/game/action_steal.go2
-rw-r--r--internal/game/cmd_bank.go4
-rw-r--r--internal/game/cmd_clean.go42
-rw-r--r--internal/game/cmd_cook.go42
-rw-r--r--internal/game/cmd_craft.go185
-rw-r--r--internal/game/cmd_fletch.go181
-rw-r--r--internal/game/cmd_shop.go2
-rw-r--r--internal/game/cmd_smelt.go36
-rw-r--r--internal/game/cmd_smith.go59
-rw-r--r--internal/game/cmd_trigger_utility.go44
-rw-r--r--internal/game/cmd_use.go264
-rw-r--r--internal/game/cmd_verbs.go4
-rw-r--r--internal/game/core_production.go406
-rw-r--r--internal/game/core_startup.go95
-rw-r--r--internal/game/craft_index.go234
-rw-r--r--internal/game/game.go14
-rw-r--r--internal/net/server.go2
-rw-r--r--internal/object/item.go109
-rw-r--r--internal/world/mob.go3
-rw-r--r--internal/world/objects.go4
-rw-r--r--worldbuilding_guide/recipes.md328
346 files changed, 5232 insertions, 4623 deletions
diff --git a/AGENTS.md b/AGENTS.md
index 461c235..d3196cd 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -26,7 +26,7 @@ internal/
net/ Conn interface (tcpConn, wsConn), Hub, Session, session states, IAC echo
game/ Game struct, HandleSession state machine, command registry, all game logic
hacking/ Minigame interface + 3 implementations (Wumpus, Mastermind, Liar's Dice)
- action/ Behavior structs (GatherConfig, TalkConfig, UseConfig), RecipeStore, SuccessChance
+ action/ Behavior structs (GatherConfig, TalkConfig, UseConfig), SuccessChance
player/ Player struct, skills, inventory, bank, equipment, accounts, XP, validation
world/ World, RoomDef, MobDef, MobInstance, ObjState, ground items, wandering
combat/ Combat state tracking, OSRS-style formulas (HitChance, MaxHit, AttackRoll)
@@ -52,7 +52,7 @@ internal/
## Key Conventions
-**YAML-driven.** Rooms, items, objects, mobs, drops, recipes, modules, courses are YAML files under `data/`. Read from disk on every access — edit and it takes effect immediately, no restart. Recipes are walked from disk on first access then cached in-memory with an `O(1)` ID index (see GetByID below). Items, rooms, and recipes are organized into subdirectories by category (items: `equipment/`, `tools/`, etc.; rooms: `town/`, `wilderness/`, etc.; recipes: `smithing/`, `crafting/`, etc.). All IDs remain globally unique across subdirectories via path-indexed loading.
+**YAML-driven.** Rooms, items, objects, mobs, drops, modules, courses are YAML files under `data/`. Read from disk on every access — edit and it takes effect immediately, no restart. Crafting information lives in `craft:` blocks on item YAML files — no separate recipe directory. Items are organized into subdirectories by category (items: `equipment/`, `tools/`, etc.; rooms: `town/`, `wilderness/`, etc.). All IDs remain globally unique across subdirectories via path-indexed loading.
**Live state.** Player data (characters, accounts) is written to YAML on every state change. Ground items, mob instances, and object states live in memory only and are lost on restart.
@@ -86,12 +86,11 @@ The pattern is: lock, snapshot/unmarshal, unlock, then process. The engine creat
```
data/rooms/<id>.yaml Room definitions (exits, objects, mobs, item_spawns, on_enter)
data/rooms/player_housing/ Player house rooms
-data/items/<id>.yaml Item definitions (stats, equip_slot, tool_type, burn_ticks, search_table, etc.)
+data/items/<id>.yaml Item definitions (stats, equip_slot, tool_type, craft, burn_ticks, search_table, etc.)
data/mobs/<id>.yaml Mob definitions (combat stats, drops, behavior, steal config)
data/objects/<id>.yaml Object definitions (name, behavior, hidden, inroom_description, removal_item)
data/drops/<id>.yaml Shared drop tables (weighted item lists, sub-table references)
data/help/<id>.yaml Help topics
-data/recipes/<id>.yaml Recipe definitions — individual or consolidated format (see worldbuilding_guide/recipes.md)
data/modules/<id>.yaml Science module definitions (id, level, max_hit, base_xp, junk_cost, category)
data/courses/<id>.yaml Agility course definitions (name, required_level, start_room, obstacles)
data/players/accounts/ Account YAML (gitignored)
@@ -106,7 +105,7 @@ See `worldbuilding_guide/` for full YAML format reference with examples.
At startup, the server runs comprehensive integrity checks on all data files. Errors cause the server to exit; warnings are informational.
-**Duplicate ID checks** across all data directories (items, rooms, mobs, objects, drops, recipes, courses, modules, help). Detects collision from two files with the same ID in different subdirectories.
+**Duplicate ID checks** across all data directories (items, rooms, mobs, objects, drops, courses, modules, help). Detects collision from two files with the same ID in different subdirectories.
**Referential integrity checks:**
- Room exits → target rooms exist
@@ -115,8 +114,8 @@ At startup, the server runs comprehensive integrity checks on all data files. Er
- Object removal items, steal tables, guard mobs, use_interaction items → exist
- Object gather config drops → items/tables exist; bait items exist
- Object talk config node actions (give_item, take_item, teleport, shop items) → exist
-- Item search tables, made_from items/byproducts, farm_product → exist
-- Recipe consume items, output, fail product → exist
+- Item search tables, farm_product → exist
+- Item craft blocks → consume items, fail products, station objects, tools → exist
- Drop table sub-references → exist
- Course start_room and obstacle rooms → exist
@@ -481,8 +480,7 @@ Prompt variables: `%h` (HP), `%H` (max HP), `%b` (battery), `%B` (max battery),
| `search_table` / `search_misc_table` | Drop tables for searching this item |
| `search_ticks` | Ticks per search action |
| `search_message` | Custom search message |
-| `made_from` | MadeFrom entries for item-on-item combinations (with byproducts) |
-| `ticks` | Ticks per production cycle for MadeFrom combines (default 2) |
+| `craft` | Craft block — type, skill, level, XP, station, tool, consume, output_qty, fail, message, success, steps |
| `farming` | Farming-related fields (seed type, patch, yield, level, xp) |
| `potion` | Potion properties (buff levels for attack/strength/defense/agility) |
@@ -516,47 +514,28 @@ Prompt variables: `%h` (HP), `%H` (max HP), `%b` (battery), `%B` (max battery),
Production skills share a unified system in `core_production.go`:
-1. Create recipe YAMLs in `data/recipes/<type>/` — either individual (one per product) or consolidated (one per material group, with shared fields at top level and a `products:` list). See `worldbuilding_guide/recipes.md`.
+1. Create the output item YAML in `data/items/` with a `craft:` block. See `worldbuilding_guide/recipes.md`.
2. Create a station object in `data/objects/` if needed
3. Add a `cmd_<skill>.go` command handler following existing patterns
4. Add the command to `commandRegistry` in `cmd_registry.go` with the appropriate class
5. Wire up the `auto_start` option if desired
-The unified production cycle handles: HowMany prompt, start message, timed loops, success/fail rolls, XP, output quantity, stackable stacking, byproducts, and end message. Tool requirements are checked in the command handler before calling `promptHowMany`.
+The unified production cycle handles: HowMany prompt, start message, timed loops, success/fail rolls, XP, output quantity, stackable stacking, byproducts, multi-step messages, and end message. Tool requirements are checked in the command handler before calling `promptHowMany`.
-## Recipe Loading and Lookup
+## CraftIndex
-Recipes have two YAML formats — **individual** (one file per recipe) and **consolidated** (shared `type`/`station`/`wait`/`consume` at top level, products listed under `products:`). The loader expands consolidated files into individual `RecipeDef` structs transparently. The production engine (`core_production.go`) sees only `RecipeDef` — no code changes needed for format.
+`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:
-**RecipeStore** (`internal/action/recipe.go`) manages recipe access:
-
-### GetByID — Step by Step
-
-`GetByID(recipeID)` is an `O(1)` direct recipe lookup. Here's the full chain:
-
-1. **First call anywhere in the game** — a command like `cook`, `smith`, or the tick advancing an active production action calls `GetByID("smith_bronze_dagger")`.
-
-2. **Index check.** `GetByID` checks the `byID` map (`map[string]*RecipeDef`). On first call, it's empty, so the method calls `ensureLoaded()`.
-
-3. **`ensureLoaded()`** checks if the full recipe slice is already cached under key `"_all"` in `s.cache`. If not (cold cache), it calls `loadAllRaw()`.
-
-4. **`loadAllRaw()`** walks `data/recipes/` recursively. For each YAML file:
- - First attempts to unmarshal as a plain `RecipeDef` (individual format). If `type` is non-empty, the recipe is appended.
- - If that fails, attempts to unmarshal as `consolidatedRecipeDef`. If `type` is set and `products` is non-empty, it expands each product entry via `expandProduct()` into a full `RecipeDef` (inheriting top-level `type`, `station`, `wait`, `consume`; overriding with product-level fields where specified).
- - **YAML parse errors are logged to stderr** — no more silent failures.
-
-5. **Index building.** `ensureLoaded()` caches the full recipe slice as `s.cache["_all"]` (permanent backing store), then populates `s.byID` with pointers into that slice: `byID["smith_bronze_dagger"] = &all[i]`. The cached slice keeps the backing array alive, so byID pointers never dangle.
-
-6. **GetByID returns.** The `*RecipeDef` pointer is returned and used by the caller (e.g., `loadProductionRecipe()` or `menuEntryName()`).
-
-7. **All subsequent calls** to `GetByID`, `LoadAll()`, or `LoadByType()` hit the in-memory cache — no filesystem walk. `LoadByType("smithing")` filters the cached `"_all"` slice and caches the filtered result.
+- **`ByType("pharmacy")`** — all pharmacy-craftable items (used by `mix` command)
+- **`ByStation("anvil")`** — all items craftable at that station
+- **`ByInput("guam")`** — all items that consume that ingredient
+- **`FindByTwoInputs(a, b)`** — items consuming both inputs in different consume entries (used by `use` command)
**Key properties:**
-- `GetByID` — `O(1)` map lookup, zero allocations
-- `LoadByType` — `O(n)` filter pass on first call per type, `O(1)` copy on cache hits
-- `LoadAll` — same as `LoadByType("")`, returns copy of `"_all"` cache
-- All public methods return copies — callers can't mutate cached data
-- No cache invalidation — recipes are static after startup. Server restart picks up changes.
+- Built once at startup — `O(n)` one-pass scan over all items
+- All indexes are `O(1)` lookups with zero allocations
+- The CraftIndex stores `*ItemDef` pointers — no RecipeDef copies
+- No cache invalidation — craft data is static after startup
## Adding a New Command
diff --git a/data/items/ammo/adamant_arrow.yaml b/data/items/ammo/adamant_arrow.yaml
index bbcdcab..cbed73f 100644
--- a/data/items/ammo/adamant_arrow.yaml
+++ b/data/items/ammo/adamant_arrow.yaml
@@ -1,9 +1,23 @@
+color: "120"
+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
+description: An adamant-tipped arrow.
+equip_slot: ammo
id: adamant_arrow
name: adamant arrow
-color: "120"
-description: "An adamant-tipped arrow."
-value: 20
stackable: true
-equip_slot: ammo
stats:
- ranged_strength: 31
+ ranged_strength: 31
+value: 20
diff --git a/data/items/ammo/adamant_arrowtips.yaml b/data/items/ammo/adamant_arrowtips.yaml
index 49ddb57..989a954 100644
--- a/data/items/ammo/adamant_arrowtips.yaml
+++ b/data/items/ammo/adamant_arrowtips.yaml
@@ -1,6 +1,19 @@
+color: "120"
+craft:
+ consume:
+ - items:
+ - adamantite_bar
+ quantity: 1
+ level: 75
+ output_qty: 15
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 62
+description: A set of adamant arrowtips.
id: adamant_arrowtips
name: adamant arrowtips
-color: "120"
-description: "A set of adamant arrowtips."
-value: 16
stackable: true
+value: 16
diff --git a/data/items/ammo/adamant_bolts.yaml b/data/items/ammo/adamant_bolts.yaml
index 47d088f..d1404d1 100644
--- a/data/items/ammo/adamant_bolts.yaml
+++ b/data/items/ammo/adamant_bolts.yaml
@@ -1,9 +1,23 @@
+color: "120"
+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
+description: Adamant crossbow bolts.
+equip_slot: ammo
id: adamant_bolts
name: adamant bolts
-color: "120"
-description: "Adamant crossbow bolts."
-value: 24
stackable: true
-equip_slot: ammo
stats:
- ranged_strength: 43
+ ranged_strength: 43
+value: 24
diff --git a/data/items/ammo/adamant_bolts_unf.yaml b/data/items/ammo/adamant_bolts_unf.yaml
index db709db..9173679 100644
--- a/data/items/ammo/adamant_bolts_unf.yaml
+++ b/data/items/ammo/adamant_bolts_unf.yaml
@@ -1,6 +1,19 @@
+color: "120"
+craft:
+ consume:
+ - items:
+ - adamantite_bar
+ quantity: 1
+ level: 76
+ output_qty: 10
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 62
+description: Unfinished adamant crossbow bolts.
id: adamant_bolts_unf
name: adamant bolts (unf)
-color: "120"
-description: "Unfinished adamant crossbow bolts."
-value: 20
stackable: true
+value: 20
diff --git a/data/items/ammo/arrow_shaft.yaml b/data/items/ammo/arrow_shaft.yaml
index 715a3e6..ec2d625 100644
--- a/data/items/ammo/arrow_shaft.yaml
+++ b/data/items/ammo/arrow_shaft.yaml
@@ -1,6 +1,73 @@
+color: "137"
+craft:
+ - consume:
+ - items:
+ - logs
+ quantity: 1
+ level: 1
+ output_qty: 15
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 5
+ xp: 5
+ - consume:
+ - items:
+ - oak_logs
+ quantity: 1
+ level: 15
+ output_qty: 20
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 5
+ xp: 10
+ - consume:
+ - items:
+ - willow_logs
+ quantity: 1
+ level: 30
+ output_qty: 25
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 5
+ xp: 15
+ - consume:
+ - items:
+ - maple_logs
+ quantity: 1
+ level: 45
+ output_qty: 30
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 5
+ xp: 20
+ - consume:
+ - items:
+ - yew_logs
+ quantity: 1
+ level: 60
+ output_qty: 35
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 5
+ xp: 25
+ - consume:
+ - items:
+ - magic_logs
+ quantity: 1
+ level: 75
+ output_qty: 40
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 5
+ xp: 30
+description: A wooden arrow shaft. Needs feathers and a head.
id: arrow_shaft
name: arrow shaft
-color: "137"
-description: "A wooden arrow shaft. Needs feathers and a head."
-value: 1
stackable: true
+value: 1
diff --git a/data/items/ammo/bronze_arrow.yaml b/data/items/ammo/bronze_arrow.yaml
index eb73a48..bc26583 100644
--- a/data/items/ammo/bronze_arrow.yaml
+++ b/data/items/ammo/bronze_arrow.yaml
@@ -1,9 +1,23 @@
+color: "178"
+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
+description: A bronze-tipped arrow.
+equip_slot: ammo
id: bronze_arrow
name: bronze arrow
-color: "178"
-description: "A bronze-tipped arrow."
-value: 1
stackable: true
-equip_slot: ammo
stats:
- ranged_strength: 7
+ ranged_strength: 7
+value: 1
diff --git a/data/items/ammo/bronze_arrowtips.yaml b/data/items/ammo/bronze_arrowtips.yaml
index df42e59..2a453ce 100644
--- a/data/items/ammo/bronze_arrowtips.yaml
+++ b/data/items/ammo/bronze_arrowtips.yaml
@@ -1,6 +1,19 @@
+color: "178"
+craft:
+ consume:
+ - items:
+ - bronze_bar
+ quantity: 1
+ level: 5
+ output_qty: 15
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 12
+description: A set of bronze arrowtips.
id: bronze_arrowtips
name: bronze arrowtips
-color: "178"
-description: "A set of bronze arrowtips."
-value: 1
stackable: true
+value: 1
diff --git a/data/items/ammo/bronze_bolts.yaml b/data/items/ammo/bronze_bolts.yaml
index 0d3afae..7cf0776 100644
--- a/data/items/ammo/bronze_bolts.yaml
+++ b/data/items/ammo/bronze_bolts.yaml
@@ -1,9 +1,23 @@
+color: "178"
+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
+description: Bronze crossbow bolts.
+equip_slot: ammo
id: bronze_bolts
name: bronze bolts
-color: "178"
-description: "Bronze crossbow bolts."
-value: 1
stackable: true
-equip_slot: ammo
stats:
- ranged_strength: 10
+ ranged_strength: 10
+value: 1
diff --git a/data/items/ammo/bronze_bolts_unf.yaml b/data/items/ammo/bronze_bolts_unf.yaml
index 538d8ab..169b19a 100644
--- a/data/items/ammo/bronze_bolts_unf.yaml
+++ b/data/items/ammo/bronze_bolts_unf.yaml
@@ -1,6 +1,19 @@
+color: "178"
+craft:
+ consume:
+ - items:
+ - bronze_bar
+ quantity: 1
+ level: 6
+ output_qty: 10
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 12
+description: Unfinished bronze crossbow bolts.
id: bronze_bolts_unf
name: bronze bolts (unf)
-color: "178"
-description: "Unfinished bronze crossbow bolts."
-value: 1
stackable: true
+value: 1
diff --git a/data/items/ammo/diamond_bolt_tips.yaml b/data/items/ammo/diamond_bolt_tips.yaml
index 1529b11..1c925d6 100644
--- a/data/items/ammo/diamond_bolt_tips.yaml
+++ b/data/items/ammo/diamond_bolt_tips.yaml
@@ -1,6 +1,18 @@
+color: "255"
+craft:
+ consume:
+ - items:
+ - uncut_diamond
+ quantity: 1
+ level: 65
+ output_qty: 12
+ skill: fletching
+ tool: chisel
+ type: fletching
+ wait: 5
+ xp: 7
+description: Bolt tips cut from a diamond.
id: diamond_bolt_tips
name: diamond bolt tips
-color: "255"
-description: "Bolt tips cut from a diamond."
-value: 200
stackable: true
+value: 200
diff --git a/data/items/ammo/diamond_bolts.yaml b/data/items/ammo/diamond_bolts.yaml
index efe8013..e20349c 100644
--- a/data/items/ammo/diamond_bolts.yaml
+++ b/data/items/ammo/diamond_bolts.yaml
@@ -1,9 +1,23 @@
+color: "255"
+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
+description: Bolts tipped with diamond. Can be enchanted via science.
+equip_slot: ammo
id: diamond_bolts
name: diamond bolts
-color: "255"
-description: "Bolts tipped with diamond. Can be enchanted via science."
-value: 130
stackable: true
-equip_slot: ammo
stats:
- ranged_attack: 9
+ ranged_attack: 9
+value: 130
diff --git a/data/items/ammo/emerald_bolt_tips.yaml b/data/items/ammo/emerald_bolt_tips.yaml
index afd285b..068f3b6 100644
--- a/data/items/ammo/emerald_bolt_tips.yaml
+++ b/data/items/ammo/emerald_bolt_tips.yaml
@@ -1,6 +1,18 @@
+color: "83"
+craft:
+ consume:
+ - items:
+ - uncut_emerald
+ quantity: 1
+ level: 66
+ output_qty: 12
+ skill: fletching
+ tool: chisel
+ type: fletching
+ wait: 5
+ xp: 5
+description: Bolt tips cut from an emerald.
id: emerald_bolt_tips
name: emerald bolt tips
-color: "83"
-description: "Bolt tips cut from an emerald."
-value: 50
stackable: true
+value: 50
diff --git a/data/items/ammo/emerald_bolts.yaml b/data/items/ammo/emerald_bolts.yaml
index 1afdcd0..e90a562 100644
--- a/data/items/ammo/emerald_bolts.yaml
+++ b/data/items/ammo/emerald_bolts.yaml
@@ -1,9 +1,23 @@
+color: "34"
+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
+description: Bolts tipped with emerald. Can be enchanted via science.
+equip_slot: ammo
id: emerald_bolts
name: emerald bolts
-color: "34"
-description: "Bolts tipped with emerald. Can be enchanted via science."
-value: 40
stackable: true
-equip_slot: ammo
stats:
- ranged_attack: 5
+ ranged_attack: 5
+value: 40
diff --git a/data/items/ammo/headless_arrow.yaml b/data/items/ammo/headless_arrow.yaml
index acf1384..6f1c939 100644
--- a/data/items/ammo/headless_arrow.yaml
+++ b/data/items/ammo/headless_arrow.yaml
@@ -1,6 +1,21 @@
+color: "137"
+craft:
+ consume:
+ - items:
+ - arrow_shaft
+ quantity: 15
+ - items:
+ - feather
+ quantity: 15
+ level: 1
+ message: You attach feathers to the arrow shafts.
+ output_qty: 15
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 1
+description: An arrow shaft with feathers attached. Needs a head.
id: headless_arrow
name: headless arrow
-color: "137"
-description: "An arrow shaft with feathers attached. Needs a head."
-value: 1
stackable: true
+value: 1
diff --git a/data/items/ammo/iron_arrow.yaml b/data/items/ammo/iron_arrow.yaml
index ebadaec..4cd5bea 100644
--- a/data/items/ammo/iron_arrow.yaml
+++ b/data/items/ammo/iron_arrow.yaml
@@ -1,9 +1,23 @@
+color: "250"
+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
+description: An iron-tipped arrow.
+equip_slot: ammo
id: iron_arrow
name: iron arrow
-color: "250"
-description: "An iron-tipped arrow."
-value: 2
stackable: true
-equip_slot: ammo
stats:
- ranged_strength: 10
+ ranged_strength: 10
+value: 2
diff --git a/data/items/ammo/iron_arrowtips.yaml b/data/items/ammo/iron_arrowtips.yaml
index 54ffd11..dd7e6d5 100644
--- a/data/items/ammo/iron_arrowtips.yaml
+++ b/data/items/ammo/iron_arrowtips.yaml
@@ -1,6 +1,19 @@
+color: "250"
+craft:
+ consume:
+ - items:
+ - iron_bar
+ quantity: 1
+ level: 25
+ output_qty: 15
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 25
+description: A set of iron arrowtips.
id: iron_arrowtips
name: iron arrowtips
-color: "250"
-description: "A set of iron arrowtips."
-value: 2
stackable: true
+value: 2
diff --git a/data/items/ammo/iron_bolts.yaml b/data/items/ammo/iron_bolts.yaml
index ed81578..d2aeb62 100644
--- a/data/items/ammo/iron_bolts.yaml
+++ b/data/items/ammo/iron_bolts.yaml
@@ -1,9 +1,23 @@
+color: "250"
+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
+description: Iron crossbow bolts.
+equip_slot: ammo
id: iron_bolts
name: iron bolts
-color: "250"
-description: "Iron crossbow bolts."
-value: 3
stackable: true
-equip_slot: ammo
stats:
- ranged_strength: 17
+ ranged_strength: 17
+value: 3
diff --git a/data/items/ammo/iron_bolts_unf.yaml b/data/items/ammo/iron_bolts_unf.yaml
index 0a51ca0..78746a8 100644
--- a/data/items/ammo/iron_bolts_unf.yaml
+++ b/data/items/ammo/iron_bolts_unf.yaml
@@ -1,6 +1,19 @@
+color: "250"
+craft:
+ consume:
+ - items:
+ - iron_bar
+ quantity: 1
+ level: 26
+ output_qty: 10
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 25
+description: Unfinished iron crossbow bolts.
id: iron_bolts_unf
name: iron bolts (unf)
-color: "250"
-description: "Unfinished iron crossbow bolts."
-value: 2
stackable: true
+value: 2
diff --git a/data/items/ammo/mithril_arrow.yaml b/data/items/ammo/mithril_arrow.yaml
index 23464e9..3f07c09 100644
--- a/data/items/ammo/mithril_arrow.yaml
+++ b/data/items/ammo/mithril_arrow.yaml
@@ -1,9 +1,23 @@
+color: "75"
+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
+description: A mithril-tipped arrow.
+equip_slot: ammo
id: mithril_arrow
name: mithril arrow
-color: "75"
-description: "A mithril-tipped arrow."
-value: 10
stackable: true
-equip_slot: ammo
stats:
- ranged_strength: 22
+ ranged_strength: 22
+value: 10
diff --git a/data/items/ammo/mithril_arrowtips.yaml b/data/items/ammo/mithril_arrowtips.yaml
index 8f68a34..8dca92c 100644
--- a/data/items/ammo/mithril_arrowtips.yaml
+++ b/data/items/ammo/mithril_arrowtips.yaml
@@ -1,6 +1,19 @@
+color: "75"
+craft:
+ consume:
+ - items:
+ - mithril_bar
+ quantity: 1
+ level: 55
+ output_qty: 15
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 50
+description: A set of mithril arrowtips.
id: mithril_arrowtips
name: mithril arrowtips
-color: "75"
-description: "A set of mithril arrowtips."
-value: 8
stackable: true
+value: 8
diff --git a/data/items/ammo/mithril_bolts.yaml b/data/items/ammo/mithril_bolts.yaml
index e160065..d9b4183 100644
--- a/data/items/ammo/mithril_bolts.yaml
+++ b/data/items/ammo/mithril_bolts.yaml
@@ -1,9 +1,23 @@
+color: "75"
+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
+description: Mithril crossbow bolts.
+equip_slot: ammo
id: mithril_bolts
name: mithril bolts
-color: "75"
-description: "Mithril crossbow bolts."
-value: 12
stackable: true
-equip_slot: ammo
stats:
- ranged_strength: 32
+ ranged_strength: 32
+value: 12
diff --git a/data/items/ammo/mithril_bolts_unf.yaml b/data/items/ammo/mithril_bolts_unf.yaml
index 8015f1a..93a5358 100644
--- a/data/items/ammo/mithril_bolts_unf.yaml
+++ b/data/items/ammo/mithril_bolts_unf.yaml
@@ -1,6 +1,19 @@
+color: "75"
+craft:
+ consume:
+ - items:
+ - mithril_bar
+ quantity: 1
+ level: 56
+ output_qty: 10
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 50
+description: Unfinished mithril crossbow bolts.
id: mithril_bolts_unf
name: mithril bolts (unf)
-color: "75"
-description: "Unfinished mithril crossbow bolts."
-value: 10
stackable: true
+value: 10
diff --git a/data/items/ammo/ruby_bolt_tips.yaml b/data/items/ammo/ruby_bolt_tips.yaml
index 6ebe1a0..c5bc88c 100644
--- a/data/items/ammo/ruby_bolt_tips.yaml
+++ b/data/items/ammo/ruby_bolt_tips.yaml
@@ -1,6 +1,18 @@
+color: "196"
+craft:
+ consume:
+ - items:
+ - uncut_ruby
+ quantity: 1
+ level: 73
+ output_qty: 12
+ skill: fletching
+ tool: chisel
+ type: fletching
+ wait: 5
+ xp: 6
+description: Bolt tips cut from a ruby.
id: ruby_bolt_tips
name: ruby bolt tips
-color: "196"
-description: "Bolt tips cut from a ruby."
-value: 100
stackable: true
+value: 100
diff --git a/data/items/ammo/ruby_bolts.yaml b/data/items/ammo/ruby_bolts.yaml
index b83bec2..f10be7e 100644
--- a/data/items/ammo/ruby_bolts.yaml
+++ b/data/items/ammo/ruby_bolts.yaml
@@ -1,9 +1,23 @@
+color: "196"
+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
+description: Bolts tipped with ruby. Can be enchanted via science.
+equip_slot: ammo
id: ruby_bolts
name: ruby bolts
-color: "196"
-description: "Bolts tipped with ruby. Can be enchanted via science."
-value: 75
stackable: true
-equip_slot: ammo
stats:
- ranged_attack: 7
+ ranged_attack: 7
+value: 75
diff --git a/data/items/ammo/rune_arrow.yaml b/data/items/ammo/rune_arrow.yaml
index b00e922..0b497ff 100644
--- a/data/items/ammo/rune_arrow.yaml
+++ b/data/items/ammo/rune_arrow.yaml
@@ -1,9 +1,23 @@
+color: "87"
+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
+description: A rune-tipped arrow.
+equip_slot: ammo
id: rune_arrow
name: rune arrow
-color: "87"
-description: "A rune-tipped arrow."
-value: 40
stackable: true
-equip_slot: ammo
stats:
- ranged_strength: 49
+ ranged_strength: 49
+value: 40
diff --git a/data/items/ammo/rune_arrowtips.yaml b/data/items/ammo/rune_arrowtips.yaml
index c2d80e0..e1cd229 100644
--- a/data/items/ammo/rune_arrowtips.yaml
+++ b/data/items/ammo/rune_arrowtips.yaml
@@ -1,6 +1,19 @@
+color: "87"
+craft:
+ consume:
+ - items:
+ - runite_bar
+ quantity: 1
+ level: 90
+ output_qty: 15
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 75
+description: A set of rune arrowtips.
id: rune_arrowtips
name: rune arrowtips
-color: "87"
-description: "A set of rune arrowtips."
-value: 30
stackable: true
+value: 30
diff --git a/data/items/ammo/rune_bolts.yaml b/data/items/ammo/rune_bolts.yaml
index 82ac597..f92a49e 100644
--- a/data/items/ammo/rune_bolts.yaml
+++ b/data/items/ammo/rune_bolts.yaml
@@ -1,9 +1,23 @@
+color: "87"
+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
+description: Rune crossbow bolts.
+equip_slot: ammo
id: rune_bolts
name: rune bolts
-color: "87"
-description: "Rune crossbow bolts."
-value: 48
stackable: true
-equip_slot: ammo
stats:
- ranged_strength: 55
+ ranged_strength: 55
+value: 48
diff --git a/data/items/ammo/rune_bolts_unf.yaml b/data/items/ammo/rune_bolts_unf.yaml
index ecaf494..20f0e1a 100644
--- a/data/items/ammo/rune_bolts_unf.yaml
+++ b/data/items/ammo/rune_bolts_unf.yaml
@@ -1,6 +1,19 @@
+color: "87"
+craft:
+ consume:
+ - items:
+ - runite_bar
+ quantity: 1
+ level: 91
+ output_qty: 10
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 75
+description: Unfinished rune crossbow bolts.
id: rune_bolts_unf
name: rune bolts (unf)
-color: "87"
-description: "Unfinished rune crossbow bolts."
-value: 40
stackable: true
+value: 40
diff --git a/data/items/ammo/sapphire_bolt_tips.yaml b/data/items/ammo/sapphire_bolt_tips.yaml
index 7513fd3..0469bb0 100644
--- a/data/items/ammo/sapphire_bolt_tips.yaml
+++ b/data/items/ammo/sapphire_bolt_tips.yaml
@@ -1,6 +1,18 @@
+color: "69"
+craft:
+ consume:
+ - items:
+ - uncut_sapphire
+ quantity: 1
+ level: 63
+ output_qty: 12
+ skill: fletching
+ tool: chisel
+ type: fletching
+ wait: 5
+ xp: 4
+description: Bolt tips cut from a sapphire.
id: sapphire_bolt_tips
name: sapphire bolt tips
-color: "69"
-description: "Bolt tips cut from a sapphire."
-value: 25
stackable: true
+value: 25
diff --git a/data/items/ammo/sapphire_bolts.yaml b/data/items/ammo/sapphire_bolts.yaml
index a7102c9..5443347 100644
--- a/data/items/ammo/sapphire_bolts.yaml
+++ b/data/items/ammo/sapphire_bolts.yaml
@@ -1,9 +1,23 @@
+color: "39"
+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
+description: Bolts tipped with sapphire. Can be enchanted via science.
+equip_slot: ammo
id: sapphire_bolts
name: sapphire bolts
-color: "39"
-description: "Bolts tipped with sapphire. Can be enchanted via science."
-value: 20
stackable: true
-equip_slot: ammo
stats:
- ranged_attack: 3
+ ranged_attack: 3
+value: 20
diff --git a/data/items/ammo/steel_arrow.yaml b/data/items/ammo/steel_arrow.yaml
index e14171f..fb85c0d 100644
--- a/data/items/ammo/steel_arrow.yaml
+++ b/data/items/ammo/steel_arrow.yaml
@@ -1,9 +1,23 @@
+color: "253"
+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
+description: A steel-tipped arrow.
+equip_slot: ammo
id: steel_arrow
name: steel arrow
-color: "253"
-description: "A steel-tipped arrow."
-value: 5
stackable: true
-equip_slot: ammo
stats:
- ranged_strength: 16
+ ranged_strength: 16
+value: 5
diff --git a/data/items/ammo/steel_arrowtips.yaml b/data/items/ammo/steel_arrowtips.yaml
index 49000d2..140fb9a 100644
--- a/data/items/ammo/steel_arrowtips.yaml
+++ b/data/items/ammo/steel_arrowtips.yaml
@@ -1,6 +1,19 @@
+color: "253"
+craft:
+ consume:
+ - items:
+ - steel_bar
+ quantity: 1
+ level: 35
+ output_qty: 15
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 37
+description: A set of steel arrowtips.
id: steel_arrowtips
name: steel arrowtips
-color: "253"
-description: "A set of steel arrowtips."
-value: 4
stackable: true
+value: 4
diff --git a/data/items/ammo/steel_bolts.yaml b/data/items/ammo/steel_bolts.yaml
index 7dfadf1..1e19e64 100644
--- a/data/items/ammo/steel_bolts.yaml
+++ b/data/items/ammo/steel_bolts.yaml
@@ -1,9 +1,23 @@
+color: "253"
+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
+description: Steel crossbow bolts.
+equip_slot: ammo
id: steel_bolts
name: steel bolts
-color: "253"
-description: "Steel crossbow bolts."
-value: 6
stackable: true
-equip_slot: ammo
stats:
- ranged_strength: 25
+ ranged_strength: 25
+value: 6
diff --git a/data/items/ammo/steel_bolts_unf.yaml b/data/items/ammo/steel_bolts_unf.yaml
index b65d5fa..2404129 100644
--- a/data/items/ammo/steel_bolts_unf.yaml
+++ b/data/items/ammo/steel_bolts_unf.yaml
@@ -1,6 +1,19 @@
+color: "253"
+craft:
+ consume:
+ - items:
+ - steel_bar
+ quantity: 1
+ level: 36
+ output_qty: 10
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 37
+description: Unfinished steel crossbow bolts.
id: steel_bolts_unf
name: steel bolts (unf)
-color: "253"
-description: "Unfinished steel crossbow bolts."
-value: 5
stackable: true
+value: 5
diff --git a/data/items/consumables/amp_potion.yaml b/data/items/consumables/amp_potion.yaml
index 8167932..ac98966 100644
--- a/data/items/consumables/amp_potion.yaml
+++ b/data/items/consumables/amp_potion.yaml
@@ -1,9 +1,23 @@
-id: amp_potion
-name: "amp potion"
color: "220"
-description: "A glowing gold potion that amplifies physical strength."
-value: 55
-stackable: false
-potion_effect: "strength"
+craft:
+ consume:
+ - items:
+ - tarromin_potion_unf
+ quantity: 1
+ - items:
+ - limpwurt_root
+ quantity: 1
+ level: 12
+ message: You mix an amp potion.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 50
+description: A glowing gold potion that amplifies physical strength.
+id: amp_potion
+name: amp potion
potion_bonus: 10
potion_duration: 200
+potion_effect: strength
+stackable: false
+value: 55
diff --git a/data/items/consumables/anchovies.yaml b/data/items/consumables/anchovies.yaml
index 0abd846..535585a 100644
--- a/data/items/consumables/anchovies.yaml
+++ b/data/items/consumables/anchovies.yaml
@@ -1,8 +1,24 @@
+color: "209"
+craft:
+ consume:
+ - items:
+ - raw_anchovies
+ quantity: 1
+ fail: burnt_fish
+ fail_message: You accidentally burn the anchovies.
+ level: 1
+ message: Cooked to perfection. It looks great!
+ skill: cooking
+ station:
+ - fire
+ - cooking_range
+ type: cooking
+ wait: 6
+ xp: 30
+description: Some nicely cooked anchovies.
+eat_message: You eat the anchovies.
+heal_value: 3
id: anchovies
name: anchovies
-color: "209"
-description: "Some nicely cooked anchovies."
-value: 5
stackable: false
-heal_value: 3
-eat_message: "You eat the anchovies."
+value: 5
diff --git a/data/items/consumables/avantoe_potion_unf.yaml b/data/items/consumables/avantoe_potion_unf.yaml
index 62f2218..bd2ebb1 100644
--- a/data/items/consumables/avantoe_potion_unf.yaml
+++ b/data/items/consumables/avantoe_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: avantoe_potion_unf
-name: "avantoe potion (unf)"
color: "48"
-description: "An unfinished potion with avantoe floating in water."
-value: 50
+craft:
+ type: combine
+ consume:
+ - items:
+ - avantoe
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with avantoe floating in water.
+id: avantoe_potion_unf
+name: avantoe potion (unf)
stackable: false
-made_from:
- - items: [avantoe]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 50
diff --git a/data/items/consumables/bio_serum.yaml b/data/items/consumables/bio_serum.yaml
index b397e46..6a7e181 100644
--- a/data/items/consumables/bio_serum.yaml
+++ b/data/items/consumables/bio_serum.yaml
@@ -1,9 +1,23 @@
-id: bio_serum
-name: "bio serum"
color: "48"
-description: "A green serum that neutralizes biological toxins and restores health."
-value: 45
-stackable: false
-potion_effect: "heal"
+craft:
+ consume:
+ - items:
+ - marrentill_potion_unf
+ quantity: 1
+ - items:
+ - antitoxin_powder
+ quantity: 1
+ level: 5
+ message: You mix a bio serum.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 38
+description: A green serum that neutralizes biological toxins and restores health.
+id: bio_serum
+name: bio serum
potion_bonus: 60
potion_duration: 0
+potion_effect: heal
+stackable: false
+value: 45
diff --git a/data/items/consumables/bread.yaml b/data/items/consumables/bread.yaml
index 68cefdc..a00a3ef 100644
--- a/data/items/consumables/bread.yaml
+++ b/data/items/consumables/bread.yaml
@@ -1,8 +1,23 @@
+color: "222"
+craft:
+ consume:
+ - items:
+ - bread_dough
+ quantity: 1
+ fail: burnt_meat
+ fail_message: You burn the bread to a crisp.
+ level: 1
+ message: You bake the dough into a fresh loaf of bread.
+ skill: cooking
+ station:
+ - cooking_range
+ type: cooking
+ wait: 6
+ xp: 40
+description: A fresh loaf of bread, still warm from the oven.
+eat_message: You eat the bread. Warm and satisfying.
+heal_value: 5
id: bread
name: bread
-color: "222"
-description: "A fresh loaf of bread, still warm from the oven."
-value: 5
stackable: false
-heal_value: 5
-eat_message: "You eat the bread. Warm and satisfying."
+value: 5
diff --git a/data/items/consumables/bread_dough.yaml b/data/items/consumables/bread_dough.yaml
index 73ac998..7e09f3d 100644
--- a/data/items/consumables/bread_dough.yaml
+++ b/data/items/consumables/bread_dough.yaml
@@ -1,14 +1,22 @@
+color: "222"
+craft:
+ 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
+description: A ball of uncooked bread dough. It needs to be baked.
id: bread_dough
name: bread dough
-color: "222"
-description: "A ball of uncooked bread dough. It needs to be baked."
-value: 3
stackable: false
-ticks: 2
-made_from:
- - items: [pot_of_flour]
- quantity: 1
- byproducts: [empty_pot]
- - items: [bucket_of_water, jug_of_water]
- quantity: 1
- byproducts: [empty_bucket, empty_jug]
+value: 3
diff --git a/data/items/consumables/cadantine_potion_unf.yaml b/data/items/consumables/cadantine_potion_unf.yaml
index d1f13c6..5d5db0b 100644
--- a/data/items/consumables/cadantine_potion_unf.yaml
+++ b/data/items/consumables/cadantine_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: cadantine_potion_unf
-name: "cadantine potion (unf)"
color: "48"
-description: "An unfinished potion with cadantine floating in water."
-value: 72
+craft:
+ type: combine
+ consume:
+ - items:
+ - cadantine
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with cadantine floating in water.
+id: cadantine_potion_unf
+name: cadantine potion (unf)
stackable: false
-made_from:
- - items: [cadantine]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 72
diff --git a/data/items/consumables/dwarf_weed_potion_unf.yaml b/data/items/consumables/dwarf_weed_potion_unf.yaml
index 64f7faa..44f2eab 100644
--- a/data/items/consumables/dwarf_weed_potion_unf.yaml
+++ b/data/items/consumables/dwarf_weed_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: dwarf_weed_potion_unf
-name: "dwarf weed potion (unf)"
color: "48"
-description: "An unfinished potion with dwarf weed floating in water."
-value: 88
+craft:
+ type: combine
+ consume:
+ - items:
+ - dwarf_weed
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with dwarf weed floating in water.
+id: dwarf_weed_potion_unf
+name: dwarf weed potion (unf)
stackable: false
-made_from:
- - items: [dwarf_weed]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 88
diff --git a/data/items/consumables/full_restore.yaml b/data/items/consumables/full_restore.yaml
index 96f6036..28204e8 100644
--- a/data/items/consumables/full_restore.yaml
+++ b/data/items/consumables/full_restore.yaml
@@ -1,9 +1,23 @@
-id: full_restore
-name: "full restore"
color: "208"
-description: "A powerful orange potion that completely restores health."
-value: 200
-stackable: false
-potion_effect: "heal"
+craft:
+ consume:
+ - items:
+ - snapdragon_potion_unf
+ quantity: 1
+ - items:
+ - arachnid_enzyme
+ quantity: 1
+ level: 63
+ message: You mix a full restore.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 143
+description: A powerful orange potion that completely restores health.
+id: full_restore
+name: full restore
potion_bonus: 500
potion_duration: 0
+potion_effect: heal
+stackable: false
+value: 200
diff --git a/data/items/consumables/guam_potion_unf.yaml b/data/items/consumables/guam_potion_unf.yaml
index ba81e93..caddd74 100644
--- a/data/items/consumables/guam_potion_unf.yaml
+++ b/data/items/consumables/guam_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: guam_potion_unf
-name: "guam potion (unf)"
color: "48"
-description: "An unfinished potion with guam floating in water."
-value: 5
+craft:
+ type: combine
+ consume:
+ - items:
+ - guam
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with guam floating in water.
+id: guam_potion_unf
+name: guam potion (unf)
stackable: false
-made_from:
- - items: [guam]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 5
diff --git a/data/items/consumables/harralander_potion_unf.yaml b/data/items/consumables/harralander_potion_unf.yaml
index 522f8ea..22a7611 100644
--- a/data/items/consumables/harralander_potion_unf.yaml
+++ b/data/items/consumables/harralander_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: harralander_potion_unf
-name: "harralander potion (unf)"
color: "48"
-description: "An unfinished potion with harralander floating in water."
-value: 18
+craft:
+ type: combine
+ consume:
+ - items:
+ - harralander
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with harralander floating in water.
+id: harralander_potion_unf
+name: harralander potion (unf)
stackable: false
-made_from:
- - items: [harralander]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 18
diff --git a/data/items/consumables/herring.yaml b/data/items/consumables/herring.yaml
index adbbd95..1e20697 100644
--- a/data/items/consumables/herring.yaml
+++ b/data/items/consumables/herring.yaml
@@ -1,8 +1,24 @@
+color: "209"
+craft:
+ consume:
+ - items:
+ - raw_herring
+ quantity: 1
+ fail: burnt_fish
+ fail_message: You accidentally burn the herring.
+ level: 5
+ message: Cooked to perfection. It looks great!
+ skill: cooking
+ station:
+ - fire
+ - cooking_range
+ type: cooking
+ wait: 6
+ xp: 50
+description: A nicely cooked herring.
+eat_message: You eat the herring.
+heal_value: 5
id: herring
name: herring
-color: "209"
-description: "A nicely cooked herring."
-value: 7
stackable: false
-heal_value: 5
-eat_message: "You eat the herring."
+value: 7
diff --git a/data/items/consumables/irit_potion_unf.yaml b/data/items/consumables/irit_potion_unf.yaml
index 76a8577..841487e 100644
--- a/data/items/consumables/irit_potion_unf.yaml
+++ b/data/items/consumables/irit_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: irit_potion_unf
-name: "irit potion (unf)"
color: "48"
-description: "An unfinished potion with irit floating in water."
-value: 42
+craft:
+ type: combine
+ consume:
+ - items:
+ - irit
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with irit floating in water.
+id: irit_potion_unf
+name: irit potion (unf)
stackable: false
-made_from:
- - items: [irit]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 42
diff --git a/data/items/consumables/kwuarm_potion_unf.yaml b/data/items/consumables/kwuarm_potion_unf.yaml
index 0448145..9ab2f67 100644
--- a/data/items/consumables/kwuarm_potion_unf.yaml
+++ b/data/items/consumables/kwuarm_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: kwuarm_potion_unf
-name: "kwuarm potion (unf)"
color: "48"
-description: "An unfinished potion with kwuarm floating in water."
-value: 56
+craft:
+ type: combine
+ consume:
+ - items:
+ - kwuarm
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with kwuarm floating in water.
+id: kwuarm_potion_unf
+name: kwuarm potion (unf)
stackable: false
-made_from:
- - items: [kwuarm]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 56
diff --git a/data/items/consumables/lantadyme_potion_unf.yaml b/data/items/consumables/lantadyme_potion_unf.yaml
index 3460a18..52ff04f 100644
--- a/data/items/consumables/lantadyme_potion_unf.yaml
+++ b/data/items/consumables/lantadyme_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: lantadyme_potion_unf
-name: "lantadyme potion (unf)"
color: "48"
-description: "An unfinished potion with lantadyme floating in water."
-value: 80
+craft:
+ type: combine
+ consume:
+ - items:
+ - lantadyme
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with lantadyme floating in water.
+id: lantadyme_potion_unf
+name: lantadyme potion (unf)
stackable: false
-made_from:
- - items: [lantadyme]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 80
diff --git a/data/items/consumables/marrentill_potion_unf.yaml b/data/items/consumables/marrentill_potion_unf.yaml
index 4424e5e..8a01b4a 100644
--- a/data/items/consumables/marrentill_potion_unf.yaml
+++ b/data/items/consumables/marrentill_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: marrentill_potion_unf
-name: "marrentill potion (unf)"
color: "48"
-description: "An unfinished potion with marrentill floating in water."
-value: 8
+craft:
+ type: combine
+ consume:
+ - items:
+ - marrentill
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with marrentill floating in water.
+id: marrentill_potion_unf
+name: marrentill potion (unf)
stackable: false
-made_from:
- - items: [marrentill]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 8
diff --git a/data/items/consumables/nano_restore.yaml b/data/items/consumables/nano_restore.yaml
index e430b49..dc55b1b 100644
--- a/data/items/consumables/nano_restore.yaml
+++ b/data/items/consumables/nano_restore.yaml
@@ -1,9 +1,23 @@
-id: nano_restore
-name: "nano restore"
color: "208"
-description: "An orange potion containing nanobots that restore diminished attributes."
-value: 70
-stackable: false
-potion_effect: "heal"
+craft:
+ consume:
+ - items:
+ - harralander_potion_unf
+ quantity: 1
+ - items:
+ - arachnid_enzyme
+ quantity: 1
+ level: 22
+ message: You mix a nano restore.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 63
+description: An orange potion containing nanobots that restore diminished attributes.
+id: nano_restore
+name: nano restore
potion_bonus: 100
potion_duration: 0
+potion_effect: heal
+stackable: false
+value: 70
diff --git a/data/items/consumables/pastry_dough.yaml b/data/items/consumables/pastry_dough.yaml
index b607c83..0bacf84 100644
--- a/data/items/consumables/pastry_dough.yaml
+++ b/data/items/consumables/pastry_dough.yaml
@@ -1,11 +1,16 @@
+color: "222"
+craft:
+ type: combine
+ consume:
+ - items:
+ - pot_of_flour
+ quantity: 1
+ - items:
+ - bucket_of_water
+ quantity: 1
+ wait: 2
+description: A ball of uncooked pastry dough. It needs to be baked.
id: pastry_dough
name: pastry dough
-color: "222"
-description: "A ball of uncooked pastry dough. It needs to be baked."
-value: 3
stackable: false
-made_from:
- - items: [pot_of_flour]
- quantity: 1
- - items: [bucket_of_water]
- quantity: 1
+value: 3
diff --git a/data/items/consumables/pie_dish.yaml b/data/items/consumables/pie_dish.yaml
index bd6100b..a562b9e 100644
--- a/data/items/consumables/pie_dish.yaml
+++ b/data/items/consumables/pie_dish.yaml
@@ -1,5 +1,23 @@
+color: "130"
+craft:
+ consume:
+ - items:
+ - unfired_pie_dish
+ quantity: 1
+ fail_message: The pie dish cracks in the oven and is ruined.
+ level: 7
+ message: You fire the pie dish in the oven successfully.
+ skill: crafting
+ station:
+ - pottery_oven
+ success:
+ base: 0.4
+ cap: 0.95
+ per_level: 0.02
+ type: crafting
+ wait: 4
+ xp: 10
+description: A clay pie dish for baking pies.
id: pie_dish
name: pie dish
-color: "130"
-description: "A clay pie dish for baking pies."
value: 5
diff --git a/data/items/consumables/pizza_dough.yaml b/data/items/consumables/pizza_dough.yaml
index 12d8b14..bb69aca 100644
--- a/data/items/consumables/pizza_dough.yaml
+++ b/data/items/consumables/pizza_dough.yaml
@@ -1,11 +1,16 @@
+color: "222"
+craft:
+ type: combine
+ consume:
+ - items:
+ - pot_of_flour
+ quantity: 1
+ - items:
+ - bucket_of_water
+ quantity: 1
+ wait: 2
+description: A ball of uncooked pizza dough. It needs to be baked.
id: pizza_dough
name: pizza dough
-color: "222"
-description: "A ball of uncooked pizza dough. It needs to be baked."
-value: 3
stackable: false
-made_from:
- - items: [pot_of_flour]
- quantity: 1
- - items: [bucket_of_water]
- quantity: 1
+value: 3
diff --git a/data/items/consumables/ranarr_potion_unf.yaml b/data/items/consumables/ranarr_potion_unf.yaml
index d2c475d..941f5da 100644
--- a/data/items/consumables/ranarr_potion_unf.yaml
+++ b/data/items/consumables/ranarr_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: ranarr_potion_unf
-name: "ranarr potion (unf)"
color: "48"
-description: "An unfinished potion with ranarr floating in water."
-value: 38
+craft:
+ type: combine
+ consume:
+ - items:
+ - ranarr
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with ranarr floating in water.
+id: ranarr_potion_unf
+name: ranarr potion (unf)
stackable: false
-made_from:
- - items: [ranarr]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 38
diff --git a/data/items/consumables/restoration_compound.yaml b/data/items/consumables/restoration_compound.yaml
index 913153f..1bb2a14 100644
--- a/data/items/consumables/restoration_compound.yaml
+++ b/data/items/consumables/restoration_compound.yaml
@@ -1,9 +1,23 @@
-id: restoration_compound
-name: "restoration compound"
color: "51"
-description: "A teal compound that enhances all combat capabilities."
-value: 250
-stackable: false
-potion_effect: "all_combat"
+craft:
+ consume:
+ - items:
+ - toadflax_potion_unf
+ quantity: 1
+ - items:
+ - crushed_nest
+ quantity: 1
+ level: 81
+ message: You mix a restoration compound.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 180
+description: A teal compound that enhances all combat capabilities.
+id: restoration_compound
+name: restoration compound
potion_bonus: 15
potion_duration: 150
+potion_effect: all_combat
+stackable: false
+value: 250
diff --git a/data/items/consumables/salmon.yaml b/data/items/consumables/salmon.yaml
index 59b8237..e7988ed 100644
--- a/data/items/consumables/salmon.yaml
+++ b/data/items/consumables/salmon.yaml
@@ -1,8 +1,24 @@
+color: "209"
+craft:
+ consume:
+ - items:
+ - raw_salmon
+ quantity: 1
+ fail: burnt_fish
+ fail_message: You accidentally burn the salmon.
+ level: 25
+ message: Cooked to perfection. It looks great!
+ skill: cooking
+ station:
+ - fire
+ - cooking_range
+ type: cooking
+ wait: 6
+ xp: 90
+description: A nicely cooked salmon.
+eat_message: You eat the salmon. Savory and satisfying!
+heal_value: 9
id: salmon
name: salmon
-color: "209"
-description: "A nicely cooked salmon."
-value: 10
stackable: false
-heal_value: 9
-eat_message: "You eat the salmon. Savory and satisfying!"
+value: 10
diff --git a/data/items/consumables/sardine.yaml b/data/items/consumables/sardine.yaml
index c11b42b..c094185 100644
--- a/data/items/consumables/sardine.yaml
+++ b/data/items/consumables/sardine.yaml
@@ -1,8 +1,24 @@
+color: "209"
+craft:
+ consume:
+ - items:
+ - raw_sardine
+ quantity: 1
+ fail: burnt_fish
+ fail_message: You accidentally burn the sardine.
+ level: 1
+ message: Cooked to perfection. It looks great!
+ skill: cooking
+ station:
+ - fire
+ - cooking_range
+ type: cooking
+ wait: 6
+ xp: 40
+description: A nicely cooked sardine.
+eat_message: You eat the sardine.
+heal_value: 4
id: sardine
name: sardine
-color: "209"
-description: "A nicely cooked sardine."
-value: 5
stackable: false
-heal_value: 4
-eat_message: "You eat the sardine."
+value: 5
diff --git a/data/items/consumables/science_serum.yaml b/data/items/consumables/science_serum.yaml
index e78f915..b77cf58 100644
--- a/data/items/consumables/science_serum.yaml
+++ b/data/items/consumables/science_serum.yaml
@@ -1,9 +1,23 @@
-id: science_serum
-name: "science serum"
color: "141"
-description: "A violet serum that heightens scientific cognition."
-value: 200
-stackable: false
-potion_effect: "science"
+craft:
+ consume:
+ - items:
+ - lantadyme_potion_unf
+ quantity: 1
+ - items:
+ - potato_cactus
+ quantity: 1
+ level: 76
+ message: You mix a science serum.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 173
+description: A violet serum that heightens scientific cognition.
+id: science_serum
+name: science serum
potion_bonus: 10
potion_duration: 200
+potion_effect: science
+stackable: false
+value: 200
diff --git a/data/items/consumables/shield_potion.yaml b/data/items/consumables/shield_potion.yaml
index 4cd91e8..a0b65c2 100644
--- a/data/items/consumables/shield_potion.yaml
+++ b/data/items/consumables/shield_potion.yaml
@@ -1,9 +1,23 @@
-id: shield_potion
-name: "shield potion"
color: "21"
-description: "A deep blue potion that reinforces defensive capability."
-value: 85
-stackable: false
-potion_effect: "defense"
+craft:
+ consume:
+ - items:
+ - ranarr_potion_unf
+ quantity: 1
+ - items:
+ - white_berries
+ quantity: 1
+ level: 30
+ message: You mix a shield potion.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 75
+description: A deep blue potion that reinforces defensive capability.
+id: shield_potion
+name: shield potion
potion_bonus: 10
potion_duration: 200
+potion_effect: defense
+stackable: false
+value: 85
diff --git a/data/items/consumables/shrimps.yaml b/data/items/consumables/shrimps.yaml
index 9c280c8..03a7edb 100644
--- a/data/items/consumables/shrimps.yaml
+++ b/data/items/consumables/shrimps.yaml
@@ -1,8 +1,24 @@
+color: "209"
+craft:
+ consume:
+ - items:
+ - raw_shrimps
+ quantity: 1
+ fail: burnt_fish
+ fail_message: You accidentally burn the shrimps.
+ level: 1
+ message: Cooked to perfection. It looks great!
+ skill: cooking
+ station:
+ - fire
+ - cooking_range
+ type: cooking
+ wait: 6
+ xp: 30
+description: Some nicely cooked shrimps.
+eat_message: You eat the shrimps. Tasty!
+heal_value: 3
id: shrimps
name: shrimps
-color: "209"
-description: "Some nicely cooked shrimps."
-value: 5
stackable: false
-heal_value: 3
-eat_message: "You eat the shrimps. Tasty!"
+value: 5
diff --git a/data/items/consumables/snapdragon_potion_unf.yaml b/data/items/consumables/snapdragon_potion_unf.yaml
index ee8e366..fc91a84 100644
--- a/data/items/consumables/snapdragon_potion_unf.yaml
+++ b/data/items/consumables/snapdragon_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: snapdragon_potion_unf
-name: "snapdragon potion (unf)"
color: "48"
-description: "An unfinished potion with snapdragon floating in water."
-value: 68
+craft:
+ type: combine
+ consume:
+ - items:
+ - snapdragon
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with snapdragon floating in water.
+id: snapdragon_potion_unf
+name: snapdragon potion (unf)
stackable: false
-made_from:
- - items: [snapdragon]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 68
diff --git a/data/items/consumables/stim_potion.yaml b/data/items/consumables/stim_potion.yaml
index de51291..e9ac25f 100644
--- a/data/items/consumables/stim_potion.yaml
+++ b/data/items/consumables/stim_potion.yaml
@@ -1,9 +1,23 @@
-id: stim_potion
-name: "stim potion"
color: "196"
-description: "A bubbling red potion that temporarily boosts attack capability."
-value: 30
-stackable: false
-potion_effect: "attack"
+craft:
+ consume:
+ - items:
+ - guam_potion_unf
+ quantity: 1
+ - items:
+ - eye_of_newt
+ quantity: 1
+ level: 1
+ message: You mix a stim potion.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 25
+description: A bubbling red potion that temporarily boosts attack capability.
+id: stim_potion
+name: stim potion
potion_bonus: 10
potion_duration: 200
+potion_effect: attack
+stackable: false
+value: 30
diff --git a/data/items/consumables/super_amp.yaml b/data/items/consumables/super_amp.yaml
index 3bea59b..361c99b 100644
--- a/data/items/consumables/super_amp.yaml
+++ b/data/items/consumables/super_amp.yaml
@@ -1,9 +1,23 @@
-id: super_amp
-name: "super amp"
color: "220"
-description: "A concentrated gold potion that significantly amplifies physical strength."
-value: 155
-stackable: false
-potion_effect: "strength"
+craft:
+ consume:
+ - items:
+ - kwuarm_potion_unf
+ quantity: 1
+ - items:
+ - limpwurt_root
+ quantity: 1
+ level: 55
+ message: You mix a super amp.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 125
+description: A concentrated gold potion that significantly amplifies physical strength.
+id: super_amp
+name: super amp
potion_bonus: 20
potion_duration: 200
+potion_effect: strength
+stackable: false
+value: 155
diff --git a/data/items/consumables/super_bio_serum.yaml b/data/items/consumables/super_bio_serum.yaml
index fbc5ebe..9f5796e 100644
--- a/data/items/consumables/super_bio_serum.yaml
+++ b/data/items/consumables/super_bio_serum.yaml
@@ -1,9 +1,23 @@
-id: super_bio_serum
-name: "super bio serum"
color: "48"
-description: "A concentrated green serum that powerfully restores health."
-value: 130
-stackable: false
-potion_effect: "heal"
+craft:
+ consume:
+ - items:
+ - irit_potion_unf
+ quantity: 1
+ - items:
+ - antitoxin_powder
+ quantity: 1
+ level: 48
+ message: You mix a super bio serum.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 106
+description: A concentrated green serum that powerfully restores health.
+id: super_bio_serum
+name: super bio serum
potion_bonus: 120
potion_duration: 0
+potion_effect: heal
+stackable: false
+value: 130
diff --git a/data/items/consumables/super_shield.yaml b/data/items/consumables/super_shield.yaml
index 7545b2e..9dc6430 100644
--- a/data/items/consumables/super_shield.yaml
+++ b/data/items/consumables/super_shield.yaml
@@ -1,9 +1,23 @@
-id: super_shield
-name: "super shield"
color: "21"
-description: "A concentrated blue potion that provides significant defensive enhancement."
-value: 175
-stackable: false
-potion_effect: "defense"
+craft:
+ consume:
+ - items:
+ - cadantine_potion_unf
+ quantity: 1
+ - items:
+ - white_berries
+ quantity: 1
+ level: 66
+ message: You mix a super shield.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 150
+description: A concentrated blue potion that provides significant defensive enhancement.
+id: super_shield
+name: super shield
potion_bonus: 20
potion_duration: 200
+potion_effect: defense
+stackable: false
+value: 175
diff --git a/data/items/consumables/super_stim.yaml b/data/items/consumables/super_stim.yaml
index c290c5f..38275ac 100644
--- a/data/items/consumables/super_stim.yaml
+++ b/data/items/consumables/super_stim.yaml
@@ -1,9 +1,23 @@
-id: super_stim
-name: "super stim"
color: "196"
-description: "A potent red potion that dramatically boosts attack capability."
-value: 120
-stackable: false
-potion_effect: "attack"
+craft:
+ consume:
+ - items:
+ - irit_potion_unf
+ quantity: 1
+ - items:
+ - eye_of_newt
+ quantity: 1
+ level: 45
+ message: You mix a super stim.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 100
+description: A potent red potion that dramatically boosts attack capability.
+id: super_stim
+name: super stim
potion_bonus: 20
potion_duration: 200
+potion_effect: attack
+stackable: false
+value: 120
diff --git a/data/items/consumables/super_stim_cell.yaml b/data/items/consumables/super_stim_cell.yaml
index 1f25597..3fb5761 100644
--- a/data/items/consumables/super_stim_cell.yaml
+++ b/data/items/consumables/super_stim_cell.yaml
@@ -1,9 +1,23 @@
-id: super_stim_cell
-name: "super stim cell"
color: "226"
-description: "A concentrated yellow energy supplement that fully restores stamina."
-value: 140
-stackable: false
-potion_effect: "battery"
+craft:
+ consume:
+ - items:
+ - avantoe_potion_unf
+ quantity: 1
+ - items:
+ - blight_spore
+ quantity: 1
+ level: 52
+ message: You mix a super stim cell.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 118
+description: A concentrated yellow energy supplement that fully restores stamina.
+id: super_stim_cell
+name: super stim cell
potion_bonus: 50
potion_duration: 0
+potion_effect: battery
+stackable: false
+value: 140
diff --git a/data/items/consumables/targeting_serum.yaml b/data/items/consumables/targeting_serum.yaml
index b10ebc5..5c94040 100644
--- a/data/items/consumables/targeting_serum.yaml
+++ b/data/items/consumables/targeting_serum.yaml
@@ -1,9 +1,23 @@
-id: targeting_serum
-name: "targeting serum"
color: "202"
-description: "A pink serum that sharpens ranged targeting."
-value: 190
-stackable: false
-potion_effect: "ranged"
+craft:
+ consume:
+ - items:
+ - dwarf_weed_potion_unf
+ quantity: 1
+ - items:
+ - catalyst_wine
+ quantity: 1
+ level: 72
+ message: You mix a targeting serum.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 163
+description: A pink serum that sharpens ranged targeting.
+id: targeting_serum
+name: targeting serum
potion_bonus: 10
potion_duration: 200
+potion_effect: ranged
+stackable: false
+value: 190
diff --git a/data/items/consumables/tarromin_potion_unf.yaml b/data/items/consumables/tarromin_potion_unf.yaml
index abc6438..c7d7dd3 100644
--- a/data/items/consumables/tarromin_potion_unf.yaml
+++ b/data/items/consumables/tarromin_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: tarromin_potion_unf
-name: "tarromin potion (unf)"
color: "48"
-description: "An unfinished potion with tarromin floating in water."
-value: 12
+craft:
+ type: combine
+ consume:
+ - items:
+ - tarromin
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with tarromin floating in water.
+id: tarromin_potion_unf
+name: tarromin potion (unf)
stackable: false
-made_from:
- - items: [tarromin]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 12
diff --git a/data/items/consumables/tech_serum.yaml b/data/items/consumables/tech_serum.yaml
index aac3fdc..c763216 100644
--- a/data/items/consumables/tech_serum.yaml
+++ b/data/items/consumables/tech_serum.yaml
@@ -1,9 +1,23 @@
-id: tech_serum
-name: "tech serum"
color: "75"
-description: "A cyan serum that enhances technology interface."
-value: 100
-stackable: false
-potion_effect: "technology"
+craft:
+ consume:
+ - items:
+ - ranarr_potion_unf
+ quantity: 1
+ - items:
+ - snape_grass
+ quantity: 1
+ level: 38
+ message: You mix a tech serum.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 88
+description: A cyan serum that enhances technology interface.
+id: tech_serum
+name: tech serum
potion_bonus: 10
potion_duration: 200
+potion_effect: technology
+stackable: false
+value: 100
diff --git a/data/items/consumables/toadflax_potion_unf.yaml b/data/items/consumables/toadflax_potion_unf.yaml
index f05b406..305807d 100644
--- a/data/items/consumables/toadflax_potion_unf.yaml
+++ b/data/items/consumables/toadflax_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: toadflax_potion_unf
-name: "toadflax potion (unf)"
color: "48"
-description: "An unfinished potion with toadflax floating in water."
-value: 28
+craft:
+ type: combine
+ consume:
+ - items:
+ - toadflax
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with toadflax floating in water.
+id: toadflax_potion_unf
+name: toadflax potion (unf)
stackable: false
-made_from:
- - items: [toadflax]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 28
diff --git a/data/items/consumables/torstol_potion_unf.yaml b/data/items/consumables/torstol_potion_unf.yaml
index cf551df..a25fdee 100644
--- a/data/items/consumables/torstol_potion_unf.yaml
+++ b/data/items/consumables/torstol_potion_unf.yaml
@@ -1,12 +1,16 @@
-id: torstol_potion_unf
-name: "torstol potion (unf)"
color: "48"
-description: "An unfinished potion with torstol floating in water."
-value: 102
+craft:
+ type: combine
+ consume:
+ - items:
+ - torstol
+ quantity: 1
+ - items:
+ - vial_of_water
+ quantity: 1
+ wait: 0
+description: An unfinished potion with torstol floating in water.
+id: torstol_potion_unf
+name: torstol potion (unf)
stackable: false
-made_from:
- - items: [torstol]
- quantity: 1
- - items: [vial_of_water]
- quantity: 1
-ticks: 0
+value: 102
diff --git a/data/items/consumables/trout.yaml b/data/items/consumables/trout.yaml
index df6cdcc..bb2b69c 100644
--- a/data/items/consumables/trout.yaml
+++ b/data/items/consumables/trout.yaml
@@ -1,8 +1,24 @@
+color: "209"
+craft:
+ consume:
+ - items:
+ - raw_trout
+ quantity: 1
+ fail: burnt_fish
+ fail_message: You accidentally burn the trout.
+ level: 15
+ message: Cooked to perfection. It looks great!
+ skill: cooking
+ station:
+ - fire
+ - cooking_range
+ type: cooking
+ wait: 6
+ xp: 70
+description: A nicely cooked trout.
+eat_message: You eat the trout. Delicious!
+heal_value: 7
id: trout
name: trout
-color: "209"
-description: "A nicely cooked trout."
-value: 8
stackable: false
-heal_value: 7
-eat_message: "You eat the trout. Delicious!"
+value: 8
diff --git a/data/items/equipment/adamant_dagger.yaml b/data/items/equipment/adamant_dagger.yaml
index d78ecdc..a483667 100644
--- a/data/items/equipment/adamant_dagger.yaml
+++ b/data/items/equipment/adamant_dagger.yaml
@@ -1,17 +1,29 @@
+attack_type: stab
+color: "120"
+craft:
+ consume:
+ - items:
+ - adamantite_bar
+ quantity: 1
+ level: 70
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 62
+description: A small adamant dagger.
+equip_slot: main_hand
id: adamant_dagger
name: adamant dagger
-color: "120"
-description: "A small adamant dagger."
-value: 400
+requirements:
+ attack: 30
+speed: 3
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
stats:
- stab_attack: 23
- slash_attack: 14
- crush_attack: -1
- strength_bonus: 19
-speed: 3
-requirements:
- attack: 30
+ 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 3c36c62..0b0f367 100644
--- a/data/items/equipment/adamant_full_helm.yaml
+++ b/data/items/equipment/adamant_full_helm.yaml
@@ -1,15 +1,27 @@
+color: "120"
+craft:
+ consume:
+ - items:
+ - adamantite_bar
+ quantity: 2
+ level: 77
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 125
+description: An adamant full helmet.
+equip_slot: head
id: adamant_full_helm
name: adamant full helm
-color: "120"
-description: "An adamant full helmet."
-value: 1000
+requirements:
+ defense: 30
stackable: false
-equip_slot: head
stats:
- stab_defense: 22
- slash_defense: 24
- crush_defense: 16
- science_defense: -8
- ranged_defense: 22
-requirements:
- defense: 30
+ 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 1fb6785..5058f37 100644
--- a/data/items/equipment/adamant_med_helm.yaml
+++ b/data/items/equipment/adamant_med_helm.yaml
@@ -1,15 +1,27 @@
+color: "120"
+craft:
+ consume:
+ - items:
+ - adamantite_bar
+ quantity: 1
+ level: 73
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 62
+description: An adamant medium helmet.
+equip_slot: head
id: adamant_med_helm
name: adamant med helm
-color: "120"
-description: "An adamant medium helmet."
-value: 400
+requirements:
+ defense: 30
stackable: false
-equip_slot: head
stats:
- stab_defense: 14
- slash_defense: 16
- crush_defense: 10
- science_defense: -3
- ranged_defense: 14
-requirements:
- defense: 30
+ 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 a37b781..d2bf9cb 100644
--- a/data/items/equipment/adamant_platebody.yaml
+++ b/data/items/equipment/adamant_platebody.yaml
@@ -1,15 +1,27 @@
+color: "120"
+craft:
+ consume:
+ - items:
+ - adamantite_bar
+ quantity: 5
+ level: 88
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 312
+description: An adamant platebody.
+equip_slot: torso
id: adamant_platebody
name: adamant platebody
-color: "120"
-description: "An adamant platebody."
-value: 5000
+requirements:
+ defense: 30
stackable: false
-equip_slot: torso
stats:
- stab_defense: 49
- slash_defense: 55
- crush_defense: 40
- science_defense: -25
- ranged_defense: 49
-requirements:
- defense: 30
+ 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 035b615..cce058c 100644
--- a/data/items/equipment/adamant_sword.yaml
+++ b/data/items/equipment/adamant_sword.yaml
@@ -1,17 +1,29 @@
+attack_type: slash
+color: "120"
+craft:
+ consume:
+ - items:
+ - adamantite_bar
+ quantity: 1
+ level: 74
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 62
+description: An adamant sword.
+equip_slot: main_hand
id: adamant_sword
name: adamant sword
-color: "120"
-description: "An adamant sword."
-value: 700
+requirements:
+ attack: 30
+speed: 4
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
stats:
- stab_attack: 20
- slash_attack: 40
- crush_attack: -2
- strength_bonus: 27
-speed: 4
-requirements:
- attack: 30
+ crush_attack: -2
+ slash_attack: 40
+ stab_attack: 20
+ strength_bonus: 27
+value: 700
+weapon_type: melee
diff --git a/data/items/equipment/bronze_dagger.yaml b/data/items/equipment/bronze_dagger.yaml
index 95ec968..8a3d2ec 100644
--- a/data/items/equipment/bronze_dagger.yaml
+++ b/data/items/equipment/bronze_dagger.yaml
@@ -1,15 +1,27 @@
+attack_type: stab
+color: "178"
+craft:
+ consume:
+ - items:
+ - bronze_bar
+ quantity: 1
+ level: 1
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 12
+description: A small bronze dagger.
+equip_slot: main_hand
id: bronze_dagger
name: bronze dagger
-color: "178"
-description: "A small bronze dagger."
-value: 8
+speed: 3
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
stats:
- stab_attack: 5
- slash_attack: 3
- crush_attack: -1
- strength_bonus: 4
-speed: 3
+ 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 ed006a8..1d82567 100644
--- a/data/items/equipment/bronze_full_helm.yaml
+++ b/data/items/equipment/bronze_full_helm.yaml
@@ -1,13 +1,25 @@
+color: "178"
+craft:
+ consume:
+ - items:
+ - bronze_bar
+ quantity: 2
+ level: 7
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 25
+description: A bronze full helmet.
+equip_slot: head
id: bronze_full_helm
name: bronze full helm
-color: "178"
-description: "A bronze full helmet."
-value: 20
stackable: false
-equip_slot: head
stats:
- stab_defense: 5
- slash_defense: 6
- crush_defense: 4
- science_defense: -3
- ranged_defense: 5
+ 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 c9635cf..b1cb0db 100644
--- a/data/items/equipment/bronze_med_helm.yaml
+++ b/data/items/equipment/bronze_med_helm.yaml
@@ -1,13 +1,25 @@
+color: "178"
+craft:
+ consume:
+ - items:
+ - bronze_bar
+ quantity: 1
+ level: 3
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 12
+description: A bronze medium helmet.
+equip_slot: head
id: bronze_med_helm
name: bronze med helm
-color: "178"
-description: "A bronze medium helmet."
-value: 8
stackable: false
-equip_slot: head
stats:
- stab_defense: 3
- slash_defense: 4
- crush_defense: 2
- science_defense: -1
- ranged_defense: 3
+ 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 8e6dd2c..95bc528 100644
--- a/data/items/equipment/bronze_platebody.yaml
+++ b/data/items/equipment/bronze_platebody.yaml
@@ -1,13 +1,25 @@
+color: "178"
+craft:
+ consume:
+ - items:
+ - bronze_bar
+ quantity: 5
+ level: 18
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 62
+description: A bronze platebody.
+equip_slot: torso
id: bronze_platebody
name: bronze platebody
-color: "178"
-description: "A bronze platebody."
-value: 60
stackable: false
-equip_slot: torso
stats:
- stab_defense: 12
- slash_defense: 15
- crush_defense: 10
- science_defense: -10
- ranged_defense: 12
+ 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 0cfdd0e..7645076 100644
--- a/data/items/equipment/bronze_sword.yaml
+++ b/data/items/equipment/bronze_sword.yaml
@@ -1,15 +1,27 @@
+attack_type: slash
+color: "178"
+craft:
+ consume:
+ - items:
+ - bronze_bar
+ quantity: 1
+ level: 4
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 12
+description: A basic bronze sword.
+equip_slot: main_hand
id: bronze_sword
name: bronze sword
-color: "178"
-description: "A basic bronze sword."
-value: 12
+speed: 4
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
stats:
- stab_attack: 4
- slash_attack: 10
- crush_attack: -2
- strength_bonus: 7
-speed: 4
+ crush_attack: -2
+ slash_attack: 10
+ stab_attack: 4
+ strength_bonus: 7
+value: 12
+weapon_type: melee
diff --git a/data/items/equipment/coif.yaml b/data/items/equipment/coif.yaml
index 8d910d7..3ba300e 100644
--- a/data/items/equipment/coif.yaml
+++ b/data/items/equipment/coif.yaml
@@ -1,16 +1,31 @@
-id: coif
-name: coif
color: "94"
-description: "A hard leather coif."
-value: 45
+craft:
+ consume:
+ - items:
+ - hard_leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 38
+ message: You craft a coif.
+ skill: crafting
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 37
+description: A hard leather coif.
equip_slot: head
-stats:
- stab_defense: 2
- slash_defense: 4
- crush_defense: 2
- science_defense: 4
- ranged_defense: 6
- ranged_attack: 2
+id: coif
+name: coif
requirements:
- defense: 20
- ranged: 20
+ defense: 20
+ ranged: 20
+stats:
+ crush_defense: 2
+ ranged_attack: 2
+ ranged_defense: 6
+ science_defense: 4
+ slash_defense: 4
+ stab_defense: 2
+value: 45
diff --git a/data/items/equipment/diamond_amulet_u.yaml b/data/items/equipment/diamond_amulet_u.yaml
index 55eaf45..f46fb8b 100644
--- a/data/items/equipment/diamond_amulet_u.yaml
+++ b/data/items/equipment/diamond_amulet_u.yaml
@@ -1,5 +1,26 @@
+color: "255"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - diamond
+ quantity: 1
+ - byproducts:
+ - amulet_mould
+ items:
+ - amulet_mould
+ quantity: 1
+ level: 70
+ message: You cast a diamond amulet.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 100
+description: An unstrung gold amulet set with a diamond.
id: diamond_amulet_u
name: diamond amulet (u)
-color: "255"
-description: "An unstrung gold amulet set with a diamond."
value: 200
diff --git a/data/items/equipment/diamond_bracelet.yaml b/data/items/equipment/diamond_bracelet.yaml
index 5ba5778..c409d4a 100644
--- a/data/items/equipment/diamond_bracelet.yaml
+++ b/data/items/equipment/diamond_bracelet.yaml
@@ -1,6 +1,27 @@
+color: "255"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - diamond
+ quantity: 1
+ - byproducts:
+ - bracelet_mould
+ items:
+ - bracelet_mould
+ quantity: 1
+ level: 58
+ message: You cast a diamond bracelet.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 95
+description: A bracelet set with a diamond. Can be enchanted.
+equip_slot: hands
id: diamond_bracelet
name: diamond bracelet
-color: "255"
-description: "A bracelet set with a diamond. Can be enchanted."
value: 1500
-equip_slot: hands
diff --git a/data/items/equipment/diamond_necklace.yaml b/data/items/equipment/diamond_necklace.yaml
index 5629b8c..c53d1cc 100644
--- a/data/items/equipment/diamond_necklace.yaml
+++ b/data/items/equipment/diamond_necklace.yaml
@@ -1,6 +1,27 @@
+color: "255"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - diamond
+ quantity: 1
+ - byproducts:
+ - necklace_mould
+ items:
+ - necklace_mould
+ quantity: 1
+ level: 56
+ message: You cast a diamond necklace.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 90
+description: A necklace set with a diamond. Can be enchanted.
+equip_slot: neck
id: diamond_necklace
name: diamond necklace
-color: "255"
-description: "A necklace set with a diamond. Can be enchanted."
value: 1600
-equip_slot: neck
diff --git a/data/items/equipment/diamond_ring.yaml b/data/items/equipment/diamond_ring.yaml
index 54023c3..36d13d8 100644
--- a/data/items/equipment/diamond_ring.yaml
+++ b/data/items/equipment/diamond_ring.yaml
@@ -1,6 +1,27 @@
+color: "255"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - diamond
+ quantity: 1
+ - byproducts:
+ - ring_mould
+ items:
+ - ring_mould
+ quantity: 1
+ level: 43
+ message: You cast a diamond ring.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 85
+description: A ring set with a diamond. Can be enchanted.
+equip_slot: ring
id: diamond_ring
name: diamond ring
-color: "255"
-description: "A ring set with a diamond. Can be enchanted."
value: 1500
-equip_slot: ring
diff --git a/data/items/equipment/emerald_amulet_u.yaml b/data/items/equipment/emerald_amulet_u.yaml
index 87261d4..69a76d5 100644
--- a/data/items/equipment/emerald_amulet_u.yaml
+++ b/data/items/equipment/emerald_amulet_u.yaml
@@ -1,5 +1,26 @@
+color: "83"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - emerald
+ quantity: 1
+ - byproducts:
+ - amulet_mould
+ items:
+ - amulet_mould
+ quantity: 1
+ level: 31
+ message: You cast a emerald amulet.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 70
+description: An unstrung gold amulet set with a emerald.
id: emerald_amulet_u
name: emerald amulet (u)
-color: "83"
-description: "An unstrung gold amulet set with a emerald."
value: 110
diff --git a/data/items/equipment/emerald_bracelet.yaml b/data/items/equipment/emerald_bracelet.yaml
index 04e4979..a809c50 100644
--- a/data/items/equipment/emerald_bracelet.yaml
+++ b/data/items/equipment/emerald_bracelet.yaml
@@ -1,6 +1,27 @@
+color: "34"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - emerald
+ quantity: 1
+ - byproducts:
+ - bracelet_mould
+ items:
+ - bracelet_mould
+ quantity: 1
+ level: 30
+ message: You cast a emerald bracelet.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 65
+description: A bracelet set with an emerald. Can be enchanted.
+equip_slot: hands
id: emerald_bracelet
name: emerald bracelet
-color: "34"
-description: "A bracelet set with an emerald. Can be enchanted."
value: 400
-equip_slot: hands
diff --git a/data/items/equipment/emerald_necklace.yaml b/data/items/equipment/emerald_necklace.yaml
index 2107ca4..44d7528 100644
--- a/data/items/equipment/emerald_necklace.yaml
+++ b/data/items/equipment/emerald_necklace.yaml
@@ -1,6 +1,27 @@
+color: "34"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - emerald
+ quantity: 1
+ - byproducts:
+ - necklace_mould
+ items:
+ - necklace_mould
+ quantity: 1
+ level: 29
+ message: You cast a emerald necklace.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 60
+description: A necklace set with an emerald. Can be enchanted.
+equip_slot: neck
id: emerald_necklace
name: emerald necklace
-color: "34"
-description: "A necklace set with an emerald. Can be enchanted."
value: 450
-equip_slot: neck
diff --git a/data/items/equipment/emerald_ring.yaml b/data/items/equipment/emerald_ring.yaml
index 253f872..8ef1d4a 100644
--- a/data/items/equipment/emerald_ring.yaml
+++ b/data/items/equipment/emerald_ring.yaml
@@ -1,6 +1,27 @@
+color: "34"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - emerald
+ quantity: 1
+ - byproducts:
+ - ring_mould
+ items:
+ - ring_mould
+ quantity: 1
+ level: 27
+ message: You cast a emerald ring.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 55
+description: A ring set with an emerald. Can be enchanted.
+equip_slot: ring
id: emerald_ring
name: emerald ring
-color: "34"
-description: "A ring set with an emerald. Can be enchanted."
value: 400
-equip_slot: ring
diff --git a/data/items/equipment/gold_amulet_u.yaml b/data/items/equipment/gold_amulet_u.yaml
index 8671941..2aebbe5 100644
--- a/data/items/equipment/gold_amulet_u.yaml
+++ b/data/items/equipment/gold_amulet_u.yaml
@@ -1,5 +1,23 @@
+color: "220"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - byproducts:
+ - amulet_mould
+ items:
+ - amulet_mould
+ quantity: 1
+ level: 8
+ message: You cast a gold amulet.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 30
+description: An unstrung gold amulet.
id: gold_amulet_u
name: gold amulet (u)
-color: "220"
-description: "An unstrung gold amulet."
value: 70
diff --git a/data/items/equipment/gold_bracelet.yaml b/data/items/equipment/gold_bracelet.yaml
index 2c4534f..8c3f2af 100644
--- a/data/items/equipment/gold_bracelet.yaml
+++ b/data/items/equipment/gold_bracelet.yaml
@@ -1,6 +1,24 @@
+color: "220"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - byproducts:
+ - bracelet_mould
+ items:
+ - bracelet_mould
+ quantity: 1
+ level: 7
+ message: You cast a gold bracelet.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 25
+description: A gold bracelet.
+equip_slot: hands
id: gold_bracelet
name: gold bracelet
-color: "220"
-description: "A gold bracelet."
value: 65
-equip_slot: hands
diff --git a/data/items/equipment/gold_necklace.yaml b/data/items/equipment/gold_necklace.yaml
index 4a22ac1..569e9b6 100644
--- a/data/items/equipment/gold_necklace.yaml
+++ b/data/items/equipment/gold_necklace.yaml
@@ -1,6 +1,24 @@
+color: "220"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - byproducts:
+ - necklace_mould
+ items:
+ - necklace_mould
+ quantity: 1
+ level: 6
+ message: You cast a gold necklace.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 20
+description: A gold necklace.
+equip_slot: neck
id: gold_necklace
name: gold necklace
-color: "220"
-description: "A gold necklace."
value: 60
-equip_slot: neck
diff --git a/data/items/equipment/gold_ring.yaml b/data/items/equipment/gold_ring.yaml
index 1a39165..7a71889 100644
--- a/data/items/equipment/gold_ring.yaml
+++ b/data/items/equipment/gold_ring.yaml
@@ -1,6 +1,24 @@
+color: "220"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - byproducts:
+ - ring_mould
+ items:
+ - ring_mould
+ quantity: 1
+ level: 5
+ message: You cast a gold ring.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 15
+description: A simple gold ring.
+equip_slot: ring
id: gold_ring
name: gold ring
-color: "220"
-description: "A simple gold ring."
value: 50
-equip_slot: ring
diff --git a/data/items/equipment/hard_leather_body.yaml b/data/items/equipment/hard_leather_body.yaml
index a5fe30f..6c48cee 100644
--- a/data/items/equipment/hard_leather_body.yaml
+++ b/data/items/equipment/hard_leather_body.yaml
@@ -1,14 +1,29 @@
-id: hard_leather_body
-name: hard leather body
color: "94"
-description: "A hardened leather body armour."
-value: 60
+craft:
+ consume:
+ - items:
+ - hard_leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 28
+ message: You craft a hard leather body.
+ skill: crafting
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 35
+description: A hardened leather body armour.
equip_slot: torso
-stats:
- stab_defense: 8
- slash_defense: 12
- crush_defense: 8
- science_defense: 6
- ranged_defense: 14
+id: hard_leather_body
+name: hard leather body
requirements:
- defense: 10
+ defense: 10
+stats:
+ crush_defense: 8
+ ranged_defense: 14
+ science_defense: 6
+ slash_defense: 12
+ stab_defense: 8
+value: 60
diff --git a/data/items/equipment/hard_leather_shield.yaml b/data/items/equipment/hard_leather_shield.yaml
index fd7dabf..4d8e16d 100644
--- a/data/items/equipment/hard_leather_shield.yaml
+++ b/data/items/equipment/hard_leather_shield.yaml
@@ -1,14 +1,29 @@
-id: hard_leather_shield
-name: hard leather shield
color: "94"
-description: "A shield made of hardened leather."
-value: 55
+craft:
+ consume:
+ - items:
+ - hard_leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 41
+ message: You craft a hard leather shield.
+ skill: crafting
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 40
+description: A shield made of hardened leather.
equip_slot: off_hand
-stats:
- stab_defense: 5
- slash_defense: 7
- crush_defense: 5
- science_defense: 4
- ranged_defense: 8
+id: hard_leather_shield
+name: hard leather shield
requirements:
- defense: 10
+ defense: 10
+stats:
+ crush_defense: 5
+ ranged_defense: 8
+ science_defense: 4
+ slash_defense: 7
+ stab_defense: 5
+value: 55
diff --git a/data/items/equipment/iron_dagger.yaml b/data/items/equipment/iron_dagger.yaml
index 8fa645c..5c332e2 100644
--- a/data/items/equipment/iron_dagger.yaml
+++ b/data/items/equipment/iron_dagger.yaml
@@ -1,15 +1,27 @@
+attack_type: stab
+color: "250"
+craft:
+ consume:
+ - items:
+ - iron_bar
+ quantity: 1
+ level: 20
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 25
+description: A small iron dagger.
+equip_slot: main_hand
id: iron_dagger
name: iron dagger
-color: "250"
-description: "A small iron dagger."
-value: 20
+speed: 3
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
stats:
- stab_attack: 8
- slash_attack: 5
- crush_attack: -1
- strength_bonus: 7
-speed: 3
+ 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 3a2fc0c..6f96338 100644
--- a/data/items/equipment/iron_full_helm.yaml
+++ b/data/items/equipment/iron_full_helm.yaml
@@ -1,13 +1,25 @@
+color: "250"
+craft:
+ consume:
+ - items:
+ - iron_bar
+ quantity: 2
+ level: 27
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 50
+description: An iron full helmet.
+equip_slot: head
id: iron_full_helm
name: iron full helm
-color: "250"
-description: "An iron full helmet."
-value: 50
stackable: false
-equip_slot: head
stats:
- stab_defense: 8
- slash_defense: 9
- crush_defense: 6
- science_defense: -3
- ranged_defense: 8
+ 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 6956283..7f6aef5 100644
--- a/data/items/equipment/iron_med_helm.yaml
+++ b/data/items/equipment/iron_med_helm.yaml
@@ -1,13 +1,25 @@
+color: "250"
+craft:
+ consume:
+ - items:
+ - iron_bar
+ quantity: 1
+ level: 22
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 25
+description: An iron medium helmet.
+equip_slot: head
id: iron_med_helm
name: iron med helm
-color: "250"
-description: "An iron medium helmet."
-value: 20
stackable: false
-equip_slot: head
stats:
- stab_defense: 5
- slash_defense: 6
- crush_defense: 3
- science_defense: -1
- ranged_defense: 5
+ 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 3c0be80..8d5d300 100644
--- a/data/items/equipment/iron_platebody.yaml
+++ b/data/items/equipment/iron_platebody.yaml
@@ -1,13 +1,25 @@
+color: "250"
+craft:
+ consume:
+ - items:
+ - iron_bar
+ quantity: 5
+ level: 33
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 125
+description: An iron platebody.
+equip_slot: torso
id: iron_platebody
name: iron platebody
-color: "250"
-description: "An iron platebody."
-value: 150
stackable: false
-equip_slot: torso
stats:
- stab_defense: 18
- slash_defense: 22
- crush_defense: 14
- science_defense: -10
- ranged_defense: 18
+ 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 a5711fc..a975cc4 100644
--- a/data/items/equipment/iron_sword.yaml
+++ b/data/items/equipment/iron_sword.yaml
@@ -1,15 +1,27 @@
+attack_type: slash
+color: "250"
+craft:
+ consume:
+ - items:
+ - iron_bar
+ quantity: 1
+ level: 24
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 25
+description: An iron sword.
+equip_slot: main_hand
id: iron_sword
name: iron sword
-color: "250"
-description: "An iron sword."
-value: 30
+speed: 4
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
stats:
- stab_attack: 7
- slash_attack: 15
- crush_attack: -2
- strength_bonus: 10
-speed: 4
+ 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 e69342c..0e5b864 100644
--- a/data/items/equipment/leather_body.yaml
+++ b/data/items/equipment/leather_body.yaml
@@ -1,12 +1,27 @@
-id: leather_body
-name: leather body
color: "130"
-description: "A leather body armour."
-value: 25
+craft:
+ consume:
+ - items:
+ - leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 14
+ message: You craft a leather body.
+ skill: crafting
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 25
+description: A leather body armour.
equip_slot: torso
+id: leather_body
+name: leather body
stats:
- stab_defense: 4
- slash_defense: 7
- crush_defense: 5
- science_defense: 4
- ranged_defense: 10
+ 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 7716e95..7cea340 100644
--- a/data/items/equipment/leather_boots.yaml
+++ b/data/items/equipment/leather_boots.yaml
@@ -1,12 +1,27 @@
-id: leather_boots
-name: leather boots
color: "130"
-description: "A pair of leather boots."
-value: 12
+craft:
+ consume:
+ - items:
+ - leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 7
+ message: You craft a pair of leather boots.
+ skill: crafting
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 16
+description: A pair of leather boots.
equip_slot: feet
+id: leather_boots
+name: leather boots
stats:
- stab_defense: 1
- slash_defense: 1
- crush_defense: 1
- science_defense: 1
- ranged_defense: 2
+ 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 1a17c16..0c2dd44 100644
--- a/data/items/equipment/leather_chaps.yaml
+++ b/data/items/equipment/leather_chaps.yaml
@@ -1,12 +1,27 @@
-id: leather_chaps
-name: leather chaps
color: "130"
-description: "A pair of leather chaps."
-value: 20
+craft:
+ consume:
+ - items:
+ - leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 18
+ message: You craft a pair of leather chaps.
+ skill: crafting
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 27
+description: A pair of leather chaps.
equip_slot: legs
+id: leather_chaps
+name: leather chaps
stats:
- stab_defense: 2
- slash_defense: 4
- crush_defense: 3
- science_defense: 3
- ranged_defense: 6
+ 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 aa5965a..0c1ab0d 100644
--- a/data/items/equipment/leather_cowl.yaml
+++ b/data/items/equipment/leather_cowl.yaml
@@ -1,12 +1,27 @@
-id: leather_cowl
-name: leather cowl
color: "130"
-description: "A leather cowl."
-value: 15
+craft:
+ consume:
+ - items:
+ - leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 9
+ message: You craft a leather cowl.
+ skill: crafting
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 19
+description: A leather cowl.
equip_slot: head
+id: leather_cowl
+name: leather cowl
stats:
- stab_defense: 1
- slash_defense: 2
- crush_defense: 1
- science_defense: 2
- ranged_defense: 4
+ 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 7590fee..a526ab4 100644
--- a/data/items/equipment/leather_gloves.yaml
+++ b/data/items/equipment/leather_gloves.yaml
@@ -1,12 +1,27 @@
-id: leather_gloves
-name: leather gloves
color: "130"
-description: "A pair of leather gloves."
-value: 10
+craft:
+ consume:
+ - items:
+ - leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 1
+ message: You craft a pair of leather gloves.
+ skill: crafting
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 14
+description: A pair of leather gloves.
equip_slot: hands
+id: leather_gloves
+name: leather gloves
stats:
- stab_defense: 1
- slash_defense: 1
- crush_defense: 1
- science_defense: 1
- ranged_defense: 2
+ 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 fa59aa9..b23e190 100644
--- a/data/items/equipment/leather_vambraces.yaml
+++ b/data/items/equipment/leather_vambraces.yaml
@@ -1,13 +1,28 @@
-id: leather_vambraces
-name: leather vambraces
color: "130"
-description: "A pair of leather vambraces."
-value: 18
+craft:
+ consume:
+ - items:
+ - leather
+ quantity: 1
+ - items:
+ - thread
+ quantity: 1
+ level: 11
+ message: You craft a pair of leather vambraces.
+ skill: crafting
+ tool: needle
+ type: crafting
+ wait: 4
+ xp: 22
+description: A pair of leather vambraces.
equip_slot: hands
+id: leather_vambraces
+name: leather vambraces
stats:
- stab_defense: 2
- slash_defense: 2
- crush_defense: 2
- science_defense: 2
- ranged_defense: 4
- ranged_attack: 4
+ 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 f40c96d..ee6f68b 100644
--- a/data/items/equipment/longbow.yaml
+++ b/data/items/equipment/longbow.yaml
@@ -1,11 +1,24 @@
-id: longbow
-name: longbow
+attack_type: ranged
color: "137"
-description: "A longbow."
-value: 20
+craft:
+ consume:
+ - items:
+ - longbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 10
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 10
+description: A longbow.
equip_slot: main_hand
-weapon_type: ranged
-attack_type: ranged
+id: longbow
+name: longbow
speed: 6
stats:
- ranged_attack: 8
+ 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 93e5ff0..d368141 100644
--- a/data/items/equipment/longbow_u.yaml
+++ b/data/items/equipment/longbow_u.yaml
@@ -1,5 +1,16 @@
+color: "137"
+craft:
+ consume:
+ - items:
+ - logs
+ quantity: 1
+ level: 10
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 3
+ xp: 10
+description: An unstrung longbow.
id: longbow_u
name: longbow (u)
-color: "137"
-description: "An unstrung longbow."
value: 10
diff --git a/data/items/equipment/magic_longbow.yaml b/data/items/equipment/magic_longbow.yaml
index 07797fb..ebc6adc 100644
--- a/data/items/equipment/magic_longbow.yaml
+++ b/data/items/equipment/magic_longbow.yaml
@@ -1,13 +1,26 @@
-id: magic_longbow
-name: magic longbow
+attack_type: ranged
color: "63"
-description: "A magic longbow."
-value: 6000
+craft:
+ consume:
+ - items:
+ - magic_longbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 85
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 91
+description: A magic longbow.
equip_slot: main_hand
-weapon_type: ranged
-attack_type: ranged
+id: magic_longbow
+name: magic longbow
+requirements:
+ ranged: 50
speed: 6
stats:
- ranged_attack: 69
-requirements:
- ranged: 50
+ ranged_attack: 69
+value: 6000
+weapon_type: ranged
diff --git a/data/items/equipment/magic_longbow_u.yaml b/data/items/equipment/magic_longbow_u.yaml
index 65682ed..45a211b 100644
--- a/data/items/equipment/magic_longbow_u.yaml
+++ b/data/items/equipment/magic_longbow_u.yaml
@@ -1,5 +1,16 @@
+color: "63"
+craft:
+ consume:
+ - items:
+ - magic_logs
+ quantity: 1
+ level: 85
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 3
+ xp: 91
+description: An unstrung magic longbow.
id: magic_longbow_u
name: magic longbow (u)
-color: "63"
-description: "An unstrung magic longbow."
value: 3000
diff --git a/data/items/equipment/magic_shortbow.yaml b/data/items/equipment/magic_shortbow.yaml
index 893b287..ad28f22 100644
--- a/data/items/equipment/magic_shortbow.yaml
+++ b/data/items/equipment/magic_shortbow.yaml
@@ -1,13 +1,26 @@
-id: magic_shortbow
-name: magic shortbow
+attack_type: ranged
color: "63"
-description: "A magic shortbow."
-value: 4000
+craft:
+ consume:
+ - items:
+ - magic_shortbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 80
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 83
+description: A magic shortbow.
equip_slot: main_hand
-weapon_type: ranged
-attack_type: ranged
+id: magic_shortbow
+name: magic shortbow
+requirements:
+ ranged: 50
speed: 4
stats:
- ranged_attack: 69
-requirements:
- ranged: 50
+ ranged_attack: 69
+value: 4000
+weapon_type: ranged
diff --git a/data/items/equipment/magic_shortbow_u.yaml b/data/items/equipment/magic_shortbow_u.yaml
index 2075bd1..fa301b2 100644
--- a/data/items/equipment/magic_shortbow_u.yaml
+++ b/data/items/equipment/magic_shortbow_u.yaml
@@ -1,5 +1,16 @@
+color: "63"
+craft:
+ consume:
+ - items:
+ - magic_logs
+ quantity: 1
+ level: 80
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 3
+ xp: 83
+description: An unstrung magic shortbow.
id: magic_shortbow_u
name: magic shortbow (u)
-color: "63"
-description: "An unstrung magic shortbow."
value: 2000
diff --git a/data/items/equipment/maple_longbow.yaml b/data/items/equipment/maple_longbow.yaml
index 1c86128..5ceeab6 100644
--- a/data/items/equipment/maple_longbow.yaml
+++ b/data/items/equipment/maple_longbow.yaml
@@ -1,13 +1,26 @@
-id: maple_longbow
-name: maple longbow
+attack_type: ranged
color: "172"
-description: "A maple longbow."
-value: 1280
+craft:
+ consume:
+ - items:
+ - maple_longbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 55
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 58
+description: A maple longbow.
equip_slot: main_hand
-weapon_type: ranged
-attack_type: ranged
+id: maple_longbow
+name: maple longbow
+requirements:
+ ranged: 30
speed: 6
stats:
- ranged_attack: 29
-requirements:
- ranged: 30
+ ranged_attack: 29
+value: 1280
+weapon_type: ranged
diff --git a/data/items/equipment/maple_longbow_u.yaml b/data/items/equipment/maple_longbow_u.yaml
index 120bf1d..3eb4f4d 100644
--- a/data/items/equipment/maple_longbow_u.yaml
+++ b/data/items/equipment/maple_longbow_u.yaml
@@ -1,5 +1,16 @@
+color: "172"
+craft:
+ consume:
+ - items:
+ - maple_logs
+ quantity: 1
+ level: 55
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 3
+ xp: 58
+description: An unstrung maple longbow.
id: maple_longbow_u
name: maple longbow (u)
-color: "172"
-description: "An unstrung maple longbow."
value: 640
diff --git a/data/items/equipment/maple_shortbow.yaml b/data/items/equipment/maple_shortbow.yaml
index 38c27b5..0d02157 100644
--- a/data/items/equipment/maple_shortbow.yaml
+++ b/data/items/equipment/maple_shortbow.yaml
@@ -1,13 +1,26 @@
-id: maple_shortbow
-name: maple shortbow
+attack_type: ranged
color: "172"
-description: "A maple shortbow."
-value: 640
+craft:
+ consume:
+ - items:
+ - maple_shortbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 50
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 50
+description: A maple shortbow.
equip_slot: main_hand
-weapon_type: ranged
-attack_type: ranged
+id: maple_shortbow
+name: maple shortbow
+requirements:
+ ranged: 30
speed: 4
stats:
- ranged_attack: 29
-requirements:
- ranged: 30
+ ranged_attack: 29
+value: 640
+weapon_type: ranged
diff --git a/data/items/equipment/maple_shortbow_u.yaml b/data/items/equipment/maple_shortbow_u.yaml
index 6fc6b84..c734aad 100644
--- a/data/items/equipment/maple_shortbow_u.yaml
+++ b/data/items/equipment/maple_shortbow_u.yaml
@@ -1,5 +1,16 @@
+color: "172"
+craft:
+ consume:
+ - items:
+ - maple_logs
+ quantity: 1
+ level: 50
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 3
+ xp: 50
+description: An unstrung maple shortbow.
id: maple_shortbow_u
name: maple shortbow (u)
-color: "172"
-description: "An unstrung maple shortbow."
value: 320
diff --git a/data/items/equipment/mithril_dagger.yaml b/data/items/equipment/mithril_dagger.yaml
index 8ffe595..0d531e1 100644
--- a/data/items/equipment/mithril_dagger.yaml
+++ b/data/items/equipment/mithril_dagger.yaml
@@ -1,17 +1,29 @@
+attack_type: stab
+color: "75"
+craft:
+ consume:
+ - items:
+ - mithril_bar
+ quantity: 1
+ level: 50
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 50
+description: A small mithril dagger.
+equip_slot: main_hand
id: mithril_dagger
name: mithril dagger
-color: "75"
-description: "A small mithril dagger."
-value: 150
+requirements:
+ attack: 20
+speed: 3
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
stats:
- stab_attack: 17
- slash_attack: 10
- crush_attack: -1
- strength_bonus: 14
-speed: 3
-requirements:
- attack: 20
+ 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 d317705..bf69b5f 100644
--- a/data/items/equipment/mithril_full_helm.yaml
+++ b/data/items/equipment/mithril_full_helm.yaml
@@ -1,15 +1,27 @@
+color: "75"
+craft:
+ consume:
+ - items:
+ - mithril_bar
+ quantity: 2
+ level: 57
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 100
+description: A mithril full helmet.
+equip_slot: head
id: mithril_full_helm
name: mithril full helm
-color: "75"
-description: "A mithril full helmet."
-value: 400
+requirements:
+ defense: 20
stackable: false
-equip_slot: head
stats:
- stab_defense: 16
- slash_defense: 18
- crush_defense: 12
- science_defense: -6
- ranged_defense: 16
-requirements:
- defense: 20
+ 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 f244a7c..c680ff4 100644
--- a/data/items/equipment/mithril_med_helm.yaml
+++ b/data/items/equipment/mithril_med_helm.yaml
@@ -1,15 +1,27 @@
+color: "75"
+craft:
+ consume:
+ - items:
+ - mithril_bar
+ quantity: 1
+ level: 53
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 50
+description: A mithril medium helmet.
+equip_slot: head
id: mithril_med_helm
name: mithril med helm
-color: "75"
-description: "A mithril medium helmet."
-value: 150
+requirements:
+ defense: 20
stackable: false
-equip_slot: head
stats:
- stab_defense: 10
- slash_defense: 12
- crush_defense: 7
- science_defense: -3
- ranged_defense: 10
-requirements:
- defense: 20
+ 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 50e8ea0..1ff3d03 100644
--- a/data/items/equipment/mithril_platebody.yaml
+++ b/data/items/equipment/mithril_platebody.yaml
@@ -1,15 +1,27 @@
+color: "75"
+craft:
+ consume:
+ - items:
+ - mithril_bar
+ quantity: 5
+ level: 68
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 250
+description: A mithril platebody.
+equip_slot: torso
id: mithril_platebody
name: mithril platebody
-color: "75"
-description: "A mithril platebody."
-value: 1500
+requirements:
+ defense: 20
stackable: false
-equip_slot: torso
stats:
- stab_defense: 36
- slash_defense: 42
- crush_defense: 30
- science_defense: -20
- ranged_defense: 36
-requirements:
- defense: 20
+ 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 1824f5b..53dcd25 100644
--- a/data/items/equipment/mithril_sword.yaml
+++ b/data/items/equipment/mithril_sword.yaml
@@ -1,17 +1,29 @@
+attack_type: slash
+color: "75"
+craft:
+ consume:
+ - items:
+ - mithril_bar
+ quantity: 1
+ level: 54
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 50
+description: A mithril sword.
+equip_slot: main_hand
id: mithril_sword
name: mithril sword
-color: "75"
-description: "A mithril sword."
-value: 250
+requirements:
+ attack: 20
+speed: 4
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
stats:
- stab_attack: 14
- slash_attack: 30
- crush_attack: -2
- strength_bonus: 20
-speed: 4
-requirements:
- attack: 20
+ crush_attack: -2
+ slash_attack: 30
+ stab_attack: 14
+ strength_bonus: 20
+value: 250
+weapon_type: melee
diff --git a/data/items/equipment/oak_longbow.yaml b/data/items/equipment/oak_longbow.yaml
index 71e7c7c..07848d0 100644
--- a/data/items/equipment/oak_longbow.yaml
+++ b/data/items/equipment/oak_longbow.yaml
@@ -1,13 +1,26 @@
-id: oak_longbow
-name: oak longbow
+attack_type: ranged
color: "136"
-description: "An oak longbow."
-value: 80
+craft:
+ consume:
+ - items:
+ - oak_longbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 25
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 25
+description: An oak longbow.
equip_slot: main_hand
-weapon_type: ranged
-attack_type: ranged
+id: oak_longbow
+name: oak longbow
+requirements:
+ ranged: 5
speed: 6
stats:
- ranged_attack: 14
-requirements:
- ranged: 5
+ ranged_attack: 14
+value: 80
+weapon_type: ranged
diff --git a/data/items/equipment/oak_longbow_u.yaml b/data/items/equipment/oak_longbow_u.yaml
index 6df0dab..5ed7742 100644
--- a/data/items/equipment/oak_longbow_u.yaml
+++ b/data/items/equipment/oak_longbow_u.yaml
@@ -1,5 +1,16 @@
+color: "136"
+craft:
+ consume:
+ - items:
+ - oak_logs
+ quantity: 1
+ level: 25
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 3
+ xp: 25
+description: An unstrung oak longbow.
id: oak_longbow_u
name: oak longbow (u)
-color: "136"
-description: "An unstrung oak longbow."
value: 40
diff --git a/data/items/equipment/oak_shortbow.yaml b/data/items/equipment/oak_shortbow.yaml
index 30ebbce..187970f 100644
--- a/data/items/equipment/oak_shortbow.yaml
+++ b/data/items/equipment/oak_shortbow.yaml
@@ -1,13 +1,26 @@
-id: oak_shortbow
-name: oak shortbow
+attack_type: ranged
color: "136"
-description: "An oak shortbow."
-value: 40
+craft:
+ consume:
+ - items:
+ - oak_shortbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 20
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 16
+description: An oak shortbow.
equip_slot: main_hand
-weapon_type: ranged
-attack_type: ranged
+id: oak_shortbow
+name: oak shortbow
+requirements:
+ ranged: 5
speed: 4
stats:
- ranged_attack: 14
-requirements:
- ranged: 5
+ ranged_attack: 14
+value: 40
+weapon_type: ranged
diff --git a/data/items/equipment/oak_shortbow_u.yaml b/data/items/equipment/oak_shortbow_u.yaml
index 5bebc38..1d6d8ee 100644
--- a/data/items/equipment/oak_shortbow_u.yaml
+++ b/data/items/equipment/oak_shortbow_u.yaml
@@ -1,5 +1,16 @@
+color: "136"
+craft:
+ consume:
+ - items:
+ - oak_logs
+ quantity: 1
+ level: 20
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 3
+ xp: 16
+description: An unstrung oak shortbow.
id: oak_shortbow_u
name: oak shortbow (u)
-color: "136"
-description: "An unstrung oak shortbow."
value: 20
diff --git a/data/items/equipment/ruby_amulet_u.yaml b/data/items/equipment/ruby_amulet_u.yaml
index 031bf8b..e4123ea 100644
--- a/data/items/equipment/ruby_amulet_u.yaml
+++ b/data/items/equipment/ruby_amulet_u.yaml
@@ -1,5 +1,26 @@
+color: "196"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - ruby
+ quantity: 1
+ - byproducts:
+ - amulet_mould
+ items:
+ - amulet_mould
+ quantity: 1
+ level: 50
+ message: You cast a ruby amulet.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 85
+description: An unstrung gold amulet set with a ruby.
id: ruby_amulet_u
name: ruby amulet (u)
-color: "196"
-description: "An unstrung gold amulet set with a ruby."
value: 145
diff --git a/data/items/equipment/ruby_bracelet.yaml b/data/items/equipment/ruby_bracelet.yaml
index e5f3f70..edc9658 100644
--- a/data/items/equipment/ruby_bracelet.yaml
+++ b/data/items/equipment/ruby_bracelet.yaml
@@ -1,6 +1,27 @@
+color: "196"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - ruby
+ quantity: 1
+ - byproducts:
+ - bracelet_mould
+ items:
+ - bracelet_mould
+ quantity: 1
+ level: 42
+ message: You cast a ruby bracelet.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 80
+description: A bracelet set with a ruby. Can be enchanted.
+equip_slot: hands
id: ruby_bracelet
name: ruby bracelet
-color: "196"
-description: "A bracelet set with a ruby. Can be enchanted."
value: 800
-equip_slot: hands
diff --git a/data/items/equipment/ruby_necklace.yaml b/data/items/equipment/ruby_necklace.yaml
index e181777..8496eb6 100644
--- a/data/items/equipment/ruby_necklace.yaml
+++ b/data/items/equipment/ruby_necklace.yaml
@@ -1,6 +1,27 @@
+color: "196"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - ruby
+ quantity: 1
+ - byproducts:
+ - necklace_mould
+ items:
+ - necklace_mould
+ quantity: 1
+ level: 40
+ message: You cast a ruby necklace.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 75
+description: A necklace set with a ruby. Can be enchanted.
+equip_slot: neck
id: ruby_necklace
name: ruby necklace
-color: "196"
-description: "A necklace set with a ruby. Can be enchanted."
value: 850
-equip_slot: neck
diff --git a/data/items/equipment/ruby_ring.yaml b/data/items/equipment/ruby_ring.yaml
index cb0cfde..335fbb0 100644
--- a/data/items/equipment/ruby_ring.yaml
+++ b/data/items/equipment/ruby_ring.yaml
@@ -1,6 +1,27 @@
+color: "196"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - ruby
+ quantity: 1
+ - byproducts:
+ - ring_mould
+ items:
+ - ring_mould
+ quantity: 1
+ level: 63
+ message: You cast a ruby ring.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 70
+description: A ring set with a ruby. Can be enchanted.
+equip_slot: ring
id: ruby_ring
name: ruby ring
-color: "196"
-description: "A ring set with a ruby. Can be enchanted."
value: 800
-equip_slot: ring
diff --git a/data/items/equipment/rune_dagger.yaml b/data/items/equipment/rune_dagger.yaml
index 9e8507b..f1f53f6 100644
--- a/data/items/equipment/rune_dagger.yaml
+++ b/data/items/equipment/rune_dagger.yaml
@@ -1,17 +1,29 @@
+attack_type: stab
+color: "87"
+craft:
+ consume:
+ - items:
+ - runite_bar
+ quantity: 1
+ level: 85
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 75
+description: A small rune dagger.
+equip_slot: main_hand
id: rune_dagger
name: rune dagger
-color: "87"
-description: "A small rune dagger."
-value: 5000
+requirements:
+ attack: 40
+speed: 3
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
stats:
- stab_attack: 30
- slash_attack: 18
- crush_attack: -1
- strength_bonus: 24
-speed: 3
-requirements:
- attack: 40
+ 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 87d6973..273bfab 100644
--- a/data/items/equipment/rune_full_helm.yaml
+++ b/data/items/equipment/rune_full_helm.yaml
@@ -1,15 +1,27 @@
+color: "87"
+craft:
+ consume:
+ - items:
+ - runite_bar
+ quantity: 2
+ level: 92
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 150
+description: A rune full helmet.
+equip_slot: head
id: rune_full_helm
name: rune full helm
-color: "87"
-description: "A rune full helmet."
-value: 12000
+requirements:
+ defense: 40
stackable: false
-equip_slot: head
stats:
- stab_defense: 30
- slash_defense: 33
- crush_defense: 22
- science_defense: -11
- ranged_defense: 30
-requirements:
- defense: 40
+ 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 895d257..1b70577 100644
--- a/data/items/equipment/rune_med_helm.yaml
+++ b/data/items/equipment/rune_med_helm.yaml
@@ -1,15 +1,27 @@
+color: "87"
+craft:
+ consume:
+ - items:
+ - runite_bar
+ quantity: 1
+ level: 88
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 75
+description: A rune medium helmet.
+equip_slot: head
id: rune_med_helm
name: rune med helm
-color: "87"
-description: "A rune medium helmet."
-value: 5000
+requirements:
+ defense: 40
stackable: false
-equip_slot: head
stats:
- stab_defense: 19
- slash_defense: 22
- crush_defense: 13
- science_defense: -4
- ranged_defense: 19
-requirements:
- defense: 40
+ 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 d1e5fb1..ce63d76 100644
--- a/data/items/equipment/rune_platebody.yaml
+++ b/data/items/equipment/rune_platebody.yaml
@@ -1,15 +1,27 @@
+color: "87"
+craft:
+ consume:
+ - items:
+ - runite_bar
+ quantity: 5
+ level: 99
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 375
+description: A rune platebody.
+equip_slot: torso
id: rune_platebody
name: rune platebody
-color: "87"
-description: "A rune platebody."
-value: 40000
+requirements:
+ defense: 40
stackable: false
-equip_slot: torso
stats:
- stab_defense: 65
- slash_defense: 72
- crush_defense: 52
- science_defense: -30
- ranged_defense: 65
-requirements:
- defense: 40
+ 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 e0bfb74..3451450 100644
--- a/data/items/equipment/rune_sword.yaml
+++ b/data/items/equipment/rune_sword.yaml
@@ -1,17 +1,29 @@
+attack_type: slash
+color: "87"
+craft:
+ consume:
+ - items:
+ - runite_bar
+ quantity: 1
+ level: 89
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 75
+description: A rune sword.
+equip_slot: main_hand
id: rune_sword
name: rune sword
-color: "87"
-description: "A rune sword."
-value: 10000
+requirements:
+ attack: 40
+speed: 4
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
stats:
- stab_attack: 27
- slash_attack: 52
- crush_attack: -2
- strength_bonus: 36
-speed: 4
-requirements:
- attack: 40
+ 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 22ef39c..b342ae4 100644
--- a/data/items/equipment/sapphire_amulet_u.yaml
+++ b/data/items/equipment/sapphire_amulet_u.yaml
@@ -1,5 +1,26 @@
+color: "69"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - sapphire
+ quantity: 1
+ - byproducts:
+ - amulet_mould
+ items:
+ - amulet_mould
+ quantity: 1
+ level: 24
+ message: You cast a sapphire amulet.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 65
+description: An unstrung gold amulet set with a sapphire.
id: sapphire_amulet_u
name: sapphire amulet (u)
-color: "69"
-description: "An unstrung gold amulet set with a sapphire."
value: 100
diff --git a/data/items/equipment/sapphire_bracelet.yaml b/data/items/equipment/sapphire_bracelet.yaml
index a2996eb..7d19018 100644
--- a/data/items/equipment/sapphire_bracelet.yaml
+++ b/data/items/equipment/sapphire_bracelet.yaml
@@ -1,6 +1,27 @@
+color: "39"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - sapphire
+ quantity: 1
+ - byproducts:
+ - bracelet_mould
+ items:
+ - bracelet_mould
+ quantity: 1
+ level: 23
+ message: You cast a sapphire bracelet.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 60
+description: A bracelet set with a sapphire. Can be enchanted.
+equip_slot: hands
id: sapphire_bracelet
name: sapphire bracelet
-color: "39"
-description: "A bracelet set with a sapphire. Can be enchanted."
value: 200
-equip_slot: hands
diff --git a/data/items/equipment/sapphire_necklace.yaml b/data/items/equipment/sapphire_necklace.yaml
index e553973..e51a47f 100644
--- a/data/items/equipment/sapphire_necklace.yaml
+++ b/data/items/equipment/sapphire_necklace.yaml
@@ -1,6 +1,27 @@
+color: "39"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - sapphire
+ quantity: 1
+ - byproducts:
+ - necklace_mould
+ items:
+ - necklace_mould
+ quantity: 1
+ level: 22
+ message: You cast a sapphire necklace.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 55
+description: A necklace set with a sapphire. Can be enchanted.
+equip_slot: neck
id: sapphire_necklace
name: sapphire necklace
-color: "39"
-description: "A necklace set with a sapphire. Can be enchanted."
value: 250
-equip_slot: neck
diff --git a/data/items/equipment/sapphire_ring.yaml b/data/items/equipment/sapphire_ring.yaml
index 40e8cc5..d880c75 100644
--- a/data/items/equipment/sapphire_ring.yaml
+++ b/data/items/equipment/sapphire_ring.yaml
@@ -1,6 +1,27 @@
+color: "39"
+craft:
+ consume:
+ - items:
+ - gold_bar
+ quantity: 1
+ - items:
+ - sapphire
+ quantity: 1
+ - byproducts:
+ - ring_mould
+ items:
+ - ring_mould
+ quantity: 1
+ level: 20
+ message: You cast a sapphire ring.
+ skill: crafting
+ station:
+ - furnace
+ type: crafting
+ wait: 4
+ xp: 40
+description: A ring set with a sapphire. Can be enchanted.
+equip_slot: ring
id: sapphire_ring
name: sapphire ring
-color: "39"
-description: "A ring set with a sapphire. Can be enchanted."
value: 200
-equip_slot: ring
diff --git a/data/items/equipment/shortbow.yaml b/data/items/equipment/shortbow.yaml
index ecd3630..50c77fe 100644
--- a/data/items/equipment/shortbow.yaml
+++ b/data/items/equipment/shortbow.yaml
@@ -1,11 +1,24 @@
-id: shortbow
-name: shortbow
+attack_type: ranged
color: "137"
-description: "A shortbow."
-value: 10
+craft:
+ consume:
+ - items:
+ - shortbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 5
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 5
+description: A shortbow.
equip_slot: main_hand
-weapon_type: ranged
-attack_type: ranged
+id: shortbow
+name: shortbow
speed: 4
stats:
- ranged_attack: 8
+ 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 6ffcafc..2d4e615 100644
--- a/data/items/equipment/shortbow_u.yaml
+++ b/data/items/equipment/shortbow_u.yaml
@@ -1,5 +1,16 @@
+color: "137"
+craft:
+ consume:
+ - items:
+ - logs
+ quantity: 1
+ level: 5
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 3
+ xp: 5
+description: An unstrung shortbow.
id: shortbow_u
name: shortbow (u)
-color: "137"
-description: "An unstrung shortbow."
value: 5
diff --git a/data/items/equipment/steel_dagger.yaml b/data/items/equipment/steel_dagger.yaml
index e52b882..9df7d14 100644
--- a/data/items/equipment/steel_dagger.yaml
+++ b/data/items/equipment/steel_dagger.yaml
@@ -1,17 +1,29 @@
+attack_type: stab
+color: "253"
+craft:
+ consume:
+ - items:
+ - steel_bar
+ quantity: 1
+ level: 30
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 37
+description: A small steel dagger.
+equip_slot: main_hand
id: steel_dagger
name: steel dagger
-color: "253"
-description: "A small steel dagger."
-value: 60
+requirements:
+ attack: 5
+speed: 3
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: stab
stats:
- stab_attack: 12
- slash_attack: 7
- crush_attack: -1
- strength_bonus: 10
-speed: 3
-requirements:
- attack: 5
+ 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 7152ded..af6a113 100644
--- a/data/items/equipment/steel_full_helm.yaml
+++ b/data/items/equipment/steel_full_helm.yaml
@@ -1,15 +1,27 @@
+color: "253"
+craft:
+ consume:
+ - items:
+ - steel_bar
+ quantity: 2
+ level: 37
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 75
+description: A steel full helmet.
+equip_slot: head
id: steel_full_helm
name: steel full helm
-color: "253"
-description: "A steel full helmet."
-value: 150
+requirements:
+ defense: 5
stackable: false
-equip_slot: head
stats:
- stab_defense: 12
- slash_defense: 13
- crush_defense: 9
- science_defense: -5
- ranged_defense: 12
-requirements:
- defense: 5
+ 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 9e15fdd..1a6a768 100644
--- a/data/items/equipment/steel_med_helm.yaml
+++ b/data/items/equipment/steel_med_helm.yaml
@@ -1,15 +1,27 @@
+color: "253"
+craft:
+ consume:
+ - items:
+ - steel_bar
+ quantity: 1
+ level: 33
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 37
+description: A steel medium helmet.
+equip_slot: head
id: steel_med_helm
name: steel med helm
-color: "253"
-description: "A steel medium helmet."
-value: 60
+requirements:
+ defense: 5
stackable: false
-equip_slot: head
stats:
- stab_defense: 7
- slash_defense: 9
- crush_defense: 5
- science_defense: -2
- ranged_defense: 7
-requirements:
- defense: 5
+ 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 f44dfa3..0769c55 100644
--- a/data/items/equipment/steel_platebody.yaml
+++ b/data/items/equipment/steel_platebody.yaml
@@ -1,15 +1,27 @@
+color: "253"
+craft:
+ consume:
+ - items:
+ - steel_bar
+ quantity: 5
+ level: 48
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 187
+description: A steel platebody.
+equip_slot: torso
id: steel_platebody
name: steel platebody
-color: "253"
-description: "A steel platebody."
-value: 500
+requirements:
+ defense: 5
stackable: false
-equip_slot: torso
stats:
- stab_defense: 27
- slash_defense: 32
- crush_defense: 22
- science_defense: -15
- ranged_defense: 27
-requirements:
- defense: 5
+ 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 e096dc9..b0ca858 100644
--- a/data/items/equipment/steel_sword.yaml
+++ b/data/items/equipment/steel_sword.yaml
@@ -1,17 +1,29 @@
+attack_type: slash
+color: "253"
+craft:
+ consume:
+ - items:
+ - steel_bar
+ quantity: 1
+ level: 34
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 37
+description: A steel sword.
+equip_slot: main_hand
id: steel_sword
name: steel sword
-color: "253"
-description: "A steel sword."
-value: 100
+requirements:
+ attack: 5
+speed: 4
stackable: false
-equip_slot: main_hand
-weapon_type: melee
-attack_type: slash
stats:
- stab_attack: 10
- slash_attack: 22
- crush_attack: -2
- strength_bonus: 14
-speed: 4
-requirements:
- attack: 5
+ 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 e30528c..6b9c4c0 100644
--- a/data/items/equipment/willow_longbow.yaml
+++ b/data/items/equipment/willow_longbow.yaml
@@ -1,13 +1,26 @@
-id: willow_longbow
-name: willow longbow
+attack_type: ranged
color: "107"
-description: "A willow longbow."
-value: 320
+craft:
+ consume:
+ - items:
+ - willow_longbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 40
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 41
+description: A willow longbow.
equip_slot: main_hand
-weapon_type: ranged
-attack_type: ranged
+id: willow_longbow
+name: willow longbow
+requirements:
+ ranged: 20
speed: 6
stats:
- ranged_attack: 20
-requirements:
- ranged: 20
+ ranged_attack: 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 5e4e9da..3812dfa 100644
--- a/data/items/equipment/willow_longbow_u.yaml
+++ b/data/items/equipment/willow_longbow_u.yaml
@@ -1,5 +1,16 @@
+color: "107"
+craft:
+ consume:
+ - items:
+ - willow_logs
+ quantity: 1
+ level: 40
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 3
+ xp: 41
+description: An unstrung willow longbow.
id: willow_longbow_u
name: willow longbow (u)
-color: "107"
-description: "An unstrung willow longbow."
value: 160
diff --git a/data/items/equipment/willow_shortbow.yaml b/data/items/equipment/willow_shortbow.yaml
index f75cfe9..8289742 100644
--- a/data/items/equipment/willow_shortbow.yaml
+++ b/data/items/equipment/willow_shortbow.yaml
@@ -1,13 +1,26 @@
-id: willow_shortbow
-name: willow shortbow
+attack_type: ranged
color: "107"
-description: "A willow shortbow."
-value: 160
+craft:
+ consume:
+ - items:
+ - willow_shortbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 35
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 33
+description: A willow shortbow.
equip_slot: main_hand
-weapon_type: ranged
-attack_type: ranged
+id: willow_shortbow
+name: willow shortbow
+requirements:
+ ranged: 20
speed: 4
stats:
- ranged_attack: 20
-requirements:
- ranged: 20
+ ranged_attack: 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 b467455..642665a 100644
--- a/data/items/equipment/willow_shortbow_u.yaml
+++ b/data/items/equipment/willow_shortbow_u.yaml
@@ -1,5 +1,16 @@
+color: "107"
+craft:
+ consume:
+ - items:
+ - willow_logs
+ quantity: 1
+ level: 35
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 3
+ xp: 33
+description: An unstrung willow shortbow.
id: willow_shortbow_u
name: willow shortbow (u)
-color: "107"
-description: "An unstrung willow shortbow."
value: 80
diff --git a/data/items/equipment/yew_longbow.yaml b/data/items/equipment/yew_longbow.yaml
index 1e7c0f5..b550e1f 100644
--- a/data/items/equipment/yew_longbow.yaml
+++ b/data/items/equipment/yew_longbow.yaml
@@ -1,13 +1,26 @@
-id: yew_longbow
-name: yew longbow
+attack_type: ranged
color: "94"
-description: "A yew longbow."
-value: 2000
+craft:
+ consume:
+ - items:
+ - yew_longbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 65
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 75
+description: A yew longbow.
equip_slot: main_hand
-weapon_type: ranged
-attack_type: ranged
+id: yew_longbow
+name: yew longbow
+requirements:
+ ranged: 40
speed: 6
stats:
- ranged_attack: 47
-requirements:
- ranged: 40
+ ranged_attack: 47
+value: 2000
+weapon_type: ranged
diff --git a/data/items/equipment/yew_longbow_u.yaml b/data/items/equipment/yew_longbow_u.yaml
index ac4fdb2..53f9f79 100644
--- a/data/items/equipment/yew_longbow_u.yaml
+++ b/data/items/equipment/yew_longbow_u.yaml
@@ -1,5 +1,16 @@
+color: "94"
+craft:
+ consume:
+ - items:
+ - yew_logs
+ quantity: 1
+ level: 65
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 3
+ xp: 75
+description: An unstrung yew longbow.
id: yew_longbow_u
name: yew longbow (u)
-color: "94"
-description: "An unstrung yew longbow."
value: 1000
diff --git a/data/items/equipment/yew_shortbow.yaml b/data/items/equipment/yew_shortbow.yaml
index c302a38..8d41739 100644
--- a/data/items/equipment/yew_shortbow.yaml
+++ b/data/items/equipment/yew_shortbow.yaml
@@ -1,13 +1,26 @@
-id: yew_shortbow
-name: yew shortbow
+attack_type: ranged
color: "94"
-description: "A yew shortbow."
-value: 1600
+craft:
+ consume:
+ - items:
+ - yew_shortbow_u
+ quantity: 1
+ - items:
+ - bowstring
+ quantity: 1
+ level: 60
+ skill: fletching
+ type: fletching
+ wait: 2
+ xp: 67
+description: A yew shortbow.
equip_slot: main_hand
-weapon_type: ranged
-attack_type: ranged
+id: yew_shortbow
+name: yew shortbow
+requirements:
+ ranged: 40
speed: 4
stats:
- ranged_attack: 47
-requirements:
- ranged: 40
+ ranged_attack: 47
+value: 1600
+weapon_type: ranged
diff --git a/data/items/equipment/yew_shortbow_u.yaml b/data/items/equipment/yew_shortbow_u.yaml
index 4c2124b..2096ae9 100644
--- a/data/items/equipment/yew_shortbow_u.yaml
+++ b/data/items/equipment/yew_shortbow_u.yaml
@@ -1,5 +1,16 @@
+color: "94"
+craft:
+ consume:
+ - items:
+ - yew_logs
+ quantity: 1
+ level: 60
+ skill: fletching
+ tool: knife
+ type: fletching
+ wait: 3
+ xp: 67
+description: An unstrung yew shortbow.
id: yew_shortbow_u
name: yew shortbow (u)
-color: "94"
-description: "An unstrung yew shortbow."
value: 800
diff --git a/data/items/materials/adamant_nails.yaml b/data/items/materials/adamant_nails.yaml
index c40d228..4c16f8d 100644
--- a/data/items/materials/adamant_nails.yaml
+++ b/data/items/materials/adamant_nails.yaml
@@ -1,6 +1,19 @@
+color: "120"
+craft:
+ consume:
+ - items:
+ - adamantite_bar
+ quantity: 1
+ level: 74
+ output_qty: 15
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 62
+description: A set of adamant nails.
id: adamant_nails
name: adamant nails
-color: "120"
-description: "A set of adamant nails."
-value: 16
stackable: true
+value: 16
diff --git a/data/items/materials/adamantite_bar.yaml b/data/items/materials/adamantite_bar.yaml
index 5a9b827..1367d4e 100644
--- a/data/items/materials/adamantite_bar.yaml
+++ b/data/items/materials/adamantite_bar.yaml
@@ -1,6 +1,23 @@
+color: "71"
+craft:
+ consume:
+ - items:
+ - adamantite_ore
+ quantity: 1
+ - items:
+ - coal
+ quantity: 6
+ fail_message: You fail to smelt a usable bar.
+ level: 65
+ message: You remove a white hot adamantite bar!
+ skill: smithing
+ station:
+ - furnace
+ type: smelting
+ wait: 4
+ xp: 37
+description: A bar of adamantite metal, extremely durable.
id: adamantite_bar
name: adamantite bar
-color: "71"
-description: "A bar of adamantite metal, extremely durable."
-value: 200
stackable: false
+value: 200
diff --git a/data/items/materials/avantoe.yaml b/data/items/materials/avantoe.yaml
index d596096..47fc405 100644
--- a/data/items/materials/avantoe.yaml
+++ b/data/items/materials/avantoe.yaml
@@ -1,6 +1,17 @@
-id: avantoe
-name: "avantoe"
color: "48"
-description: "A clean avantoe leaf, ready for use in pharmacy."
-value: 48
+craft:
+ consume:
+ - items:
+ - grimy_avantoe
+ quantity: 1
+ level: 48
+ message: You clean the grimy avantoe leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 10
+description: A clean avantoe leaf, ready for use in pharmacy.
+id: avantoe
+name: avantoe
stackable: true
+value: 48
diff --git a/data/items/materials/ball_of_wool.yaml b/data/items/materials/ball_of_wool.yaml
index 1a680a4..0f2c000 100644
--- a/data/items/materials/ball_of_wool.yaml
+++ b/data/items/materials/ball_of_wool.yaml
@@ -1,6 +1,19 @@
+color: "255"
+craft:
+ consume:
+ - items:
+ - wool
+ quantity: 1
+ level: 1
+ message: You spin the wool into a ball.
+ skill: crafting
+ station:
+ - spinning_wheel
+ type: crafting
+ wait: 3
+ xp: 3
+description: A ball of spun wool.
id: ball_of_wool
name: ball of wool
-color: "255"
-description: "A ball of spun wool."
-value: 5
stackable: false
+value: 5
diff --git a/data/items/materials/bowstring.yaml b/data/items/materials/bowstring.yaml
index a180081..ef17ce5 100644
--- a/data/items/materials/bowstring.yaml
+++ b/data/items/materials/bowstring.yaml
@@ -1,5 +1,18 @@
+color: "230"
+craft:
+ consume:
+ - items:
+ - flax
+ quantity: 1
+ level: 1
+ message: You spin the flax into a bowstring.
+ skill: crafting
+ station:
+ - spinning_wheel
+ type: crafting
+ wait: 3
+ xp: 15
+description: A string used for stringing bows.
id: bowstring
name: bowstring
-color: "230"
-description: "A string used for stringing bows."
value: 10
diff --git a/data/items/materials/bronze_bar.yaml b/data/items/materials/bronze_bar.yaml
index 053cfe8..7590114 100644
--- a/data/items/materials/bronze_bar.yaml
+++ b/data/items/materials/bronze_bar.yaml
@@ -1,6 +1,23 @@
+color: "178"
+craft:
+ consume:
+ - items:
+ - copper_ore
+ quantity: 1
+ - items:
+ - tin_ore
+ quantity: 1
+ fail_message: You fail to smelt a usable bar.
+ level: 1
+ message: You remove a white hot bronze bar!
+ skill: smithing
+ station:
+ - furnace
+ type: smelting
+ wait: 4
+ xp: 6
+description: A bar of bronze metal, smelted from copper and tin ore.
id: bronze_bar
name: bronze bar
-color: "178"
-description: "A bar of bronze metal, smelted from copper and tin ore."
-value: 4
stackable: false
+value: 4
diff --git a/data/items/materials/bronze_nails.yaml b/data/items/materials/bronze_nails.yaml
index 513e9c4..634c658 100644
--- a/data/items/materials/bronze_nails.yaml
+++ b/data/items/materials/bronze_nails.yaml
@@ -1,6 +1,19 @@
+color: "178"
+craft:
+ consume:
+ - items:
+ - bronze_bar
+ quantity: 1
+ level: 4
+ output_qty: 15
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 12
+description: A set of bronze nails.
id: bronze_nails
name: bronze nails
-color: "178"
-description: "A set of bronze nails."
-value: 1
stackable: true
+value: 1
diff --git a/data/items/materials/cadantine.yaml b/data/items/materials/cadantine.yaml
index f684f0d..24da88a 100644
--- a/data/items/materials/cadantine.yaml
+++ b/data/items/materials/cadantine.yaml
@@ -1,6 +1,17 @@
-id: cadantine
-name: "cadantine"
color: "48"
-description: "A clean cadantine leaf, ready for use in pharmacy."
-value: 70
+craft:
+ consume:
+ - items:
+ - grimy_cadantine
+ quantity: 1
+ level: 65
+ message: You clean the grimy cadantine leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 13
+description: A clean cadantine leaf, ready for use in pharmacy.
+id: cadantine
+name: cadantine
stackable: true
+value: 70
diff --git a/data/items/materials/diamond.yaml b/data/items/materials/diamond.yaml
index 2ec703f..aecdade 100644
--- a/data/items/materials/diamond.yaml
+++ b/data/items/materials/diamond.yaml
@@ -1,6 +1,18 @@
+color: "255"
+craft:
+ consume:
+ - items:
+ - uncut_diamond
+ quantity: 1
+ level: 43
+ message: You cut the diamond into a beautiful gem.
+ skill: crafting
+ tool: chisel
+ type: crafting
+ wait: 3
+ xp: 108
+description: A beautifully cut diamond.
id: diamond
name: diamond
-color: "255"
-description: "A beautifully cut diamond."
-value: 400
stackable: false
+value: 400
diff --git a/data/items/materials/dwarf_weed.yaml b/data/items/materials/dwarf_weed.yaml
index 559edf3..13b54ec 100644
--- a/data/items/materials/dwarf_weed.yaml
+++ b/data/items/materials/dwarf_weed.yaml
@@ -1,6 +1,17 @@
-id: dwarf_weed
-name: "dwarf weed"
color: "48"
-description: "A clean dwarf weed leaf, ready for use in pharmacy."
-value: 85
+craft:
+ consume:
+ - items:
+ - grimy_dwarf_weed
+ quantity: 1
+ level: 70
+ message: You clean the grimy dwarf weed leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 14
+description: A clean dwarf weed leaf, ready for use in pharmacy.
+id: dwarf_weed
+name: dwarf weed
stackable: true
+value: 85
diff --git a/data/items/materials/emerald.yaml b/data/items/materials/emerald.yaml
index 9a0c71d..8b3a888 100644
--- a/data/items/materials/emerald.yaml
+++ b/data/items/materials/emerald.yaml
@@ -1,6 +1,18 @@
+color: "83"
+craft:
+ consume:
+ - items:
+ - uncut_emerald
+ quantity: 1
+ level: 27
+ message: You cut the emerald into a beautiful gem.
+ skill: crafting
+ tool: chisel
+ type: crafting
+ wait: 3
+ xp: 68
+description: A beautifully cut emerald.
id: emerald
name: emerald
-color: "83"
-description: "A beautifully cut emerald."
-value: 100
stackable: false
+value: 100
diff --git a/data/items/materials/gold_bar.yaml b/data/items/materials/gold_bar.yaml
index 3272b04..e46bb1b 100644
--- a/data/items/materials/gold_bar.yaml
+++ b/data/items/materials/gold_bar.yaml
@@ -1,6 +1,20 @@
+color: "220"
+craft:
+ consume:
+ - items:
+ - gold_ore
+ quantity: 1
+ fail_message: You fail to smelt a usable bar.
+ level: 45
+ message: You remove a white hot gold bar!
+ skill: smithing
+ station:
+ - furnace
+ type: smelting
+ wait: 4
+ xp: 22
+description: A bar of gold metal.
id: gold_bar
name: gold bar
-color: "220"
-description: "A bar of gold metal."
-value: 50
stackable: false
+value: 50
diff --git a/data/items/materials/guam.yaml b/data/items/materials/guam.yaml
index 8fe7972..191252a 100644
--- a/data/items/materials/guam.yaml
+++ b/data/items/materials/guam.yaml
@@ -1,6 +1,17 @@
-id: guam
-name: "guam"
color: "48"
-description: "A clean guam leaf, ready for use in pharmacy."
-value: 3
+craft:
+ consume:
+ - items:
+ - grimy_guam
+ quantity: 1
+ level: 1
+ message: You clean the grimy guam leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 3
+description: A clean guam leaf, ready for use in pharmacy.
+id: guam
+name: guam
stackable: true
+value: 3
diff --git a/data/items/materials/harralander.yaml b/data/items/materials/harralander.yaml
index 08e66ed..c8b42e7 100644
--- a/data/items/materials/harralander.yaml
+++ b/data/items/materials/harralander.yaml
@@ -1,6 +1,17 @@
-id: harralander
-name: "harralander"
color: "48"
-description: "A clean harralander leaf, ready for use in pharmacy."
-value: 16
+craft:
+ consume:
+ - items:
+ - grimy_harralander
+ quantity: 1
+ level: 20
+ message: You clean the grimy harralander leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 6
+description: A clean harralander leaf, ready for use in pharmacy.
+id: harralander
+name: harralander
stackable: true
+value: 16
diff --git a/data/items/materials/irit.yaml b/data/items/materials/irit.yaml
index 40f1956..3a6498e 100644
--- a/data/items/materials/irit.yaml
+++ b/data/items/materials/irit.yaml
@@ -1,6 +1,17 @@
-id: irit
-name: "irit"
color: "48"
-description: "A clean irit leaf, ready for use in pharmacy."
-value: 40
+craft:
+ consume:
+ - items:
+ - grimy_irit
+ quantity: 1
+ level: 40
+ message: You clean the grimy irit leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 9
+description: A clean irit leaf, ready for use in pharmacy.
+id: irit
+name: irit
stackable: true
+value: 40
diff --git a/data/items/materials/iron_bar.yaml b/data/items/materials/iron_bar.yaml
index 26c8352..ae474cd 100644
--- a/data/items/materials/iron_bar.yaml
+++ b/data/items/materials/iron_bar.yaml
@@ -1,6 +1,24 @@
+color: "250"
+craft:
+ consume:
+ - items:
+ - iron_ore
+ quantity: 1
+ fail_message: The iron is too impure to make a workable bar.
+ level: 20
+ message: You remove a white hot iron bar!
+ skill: smithing
+ station:
+ - furnace
+ success:
+ base: 0.5
+ cap: 0.5
+ per_level: 0
+ type: smelting
+ wait: 4
+ xp: 12
+description: A bar of iron metal.
id: iron_bar
name: iron bar
-color: "250"
-description: "A bar of iron metal."
-value: 12
stackable: false
+value: 12
diff --git a/data/items/materials/iron_nails.yaml b/data/items/materials/iron_nails.yaml
index 77323c9..3a15192 100644
--- a/data/items/materials/iron_nails.yaml
+++ b/data/items/materials/iron_nails.yaml
@@ -1,6 +1,19 @@
+color: "250"
+craft:
+ consume:
+ - items:
+ - iron_bar
+ quantity: 1
+ level: 24
+ output_qty: 15
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 25
+description: A set of iron nails.
id: iron_nails
name: iron nails
-color: "250"
-description: "A set of iron nails."
-value: 2
stackable: true
+value: 2
diff --git a/data/items/materials/kwuarm.yaml b/data/items/materials/kwuarm.yaml
index ab0d7ff..bcb9af4 100644
--- a/data/items/materials/kwuarm.yaml
+++ b/data/items/materials/kwuarm.yaml
@@ -1,6 +1,17 @@
-id: kwuarm
-name: "kwuarm"
color: "48"
-description: "A clean kwuarm leaf, ready for use in pharmacy."
-value: 54
+craft:
+ consume:
+ - items:
+ - grimy_kwuarm
+ quantity: 1
+ level: 54
+ message: You clean the grimy kwuarm leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 11
+description: A clean kwuarm leaf, ready for use in pharmacy.
+id: kwuarm
+name: kwuarm
stackable: true
+value: 54
diff --git a/data/items/materials/lantadyme.yaml b/data/items/materials/lantadyme.yaml
index d38b7da..ef55555 100644
--- a/data/items/materials/lantadyme.yaml
+++ b/data/items/materials/lantadyme.yaml
@@ -1,6 +1,17 @@
-id: lantadyme
-name: "lantadyme"
color: "48"
-description: "A clean lantadyme leaf, ready for use in pharmacy."
-value: 78
+craft:
+ consume:
+ - items:
+ - grimy_lantadyme
+ quantity: 1
+ level: 67
+ message: You clean the grimy lantadyme leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 13
+description: A clean lantadyme leaf, ready for use in pharmacy.
+id: lantadyme
+name: lantadyme
stackable: true
+value: 78
diff --git a/data/items/materials/mahogany_planks.yaml b/data/items/materials/mahogany_planks.yaml
index d551321..da50f67 100644
--- a/data/items/materials/mahogany_planks.yaml
+++ b/data/items/materials/mahogany_planks.yaml
@@ -1,6 +1,20 @@
+color: "96"
+craft:
+ consume:
+ - items:
+ - mahogany_logs
+ quantity: 1
+ level: 50
+ message: You saw the mahogany logs into luxurious mahogany planks.
+ skill: construction
+ station:
+ - workbench
+ tool: saw
+ type: construction
+ wait: 20
+ xp: 30
+description: Luxurious mahogany planks with a deep reddish hue.
id: mahogany_planks
name: mahogany planks
-color: "96"
-description: "Luxurious mahogany planks with a deep reddish hue."
-value: 120
stackable: false
+value: 120
diff --git a/data/items/materials/marrentill.yaml b/data/items/materials/marrentill.yaml
index 4bd492d..1da0427 100644
--- a/data/items/materials/marrentill.yaml
+++ b/data/items/materials/marrentill.yaml
@@ -1,6 +1,17 @@
-id: marrentill
-name: "marrentill"
color: "48"
-description: "A clean marrentill leaf, ready for use in pharmacy."
-value: 6
+craft:
+ consume:
+ - items:
+ - grimy_marrentill
+ quantity: 1
+ level: 5
+ message: You clean the grimy marrentill leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 4
+description: A clean marrentill leaf, ready for use in pharmacy.
+id: marrentill
+name: marrentill
stackable: true
+value: 6
diff --git a/data/items/materials/mithril_bar.yaml b/data/items/materials/mithril_bar.yaml
index 00e97a4..aee681a 100644
--- a/data/items/materials/mithril_bar.yaml
+++ b/data/items/materials/mithril_bar.yaml
@@ -1,6 +1,23 @@
+color: "75"
+craft:
+ consume:
+ - items:
+ - mithril_ore
+ quantity: 1
+ - items:
+ - coal
+ quantity: 4
+ fail_message: You fail to smelt a usable bar.
+ level: 50
+ message: You remove a white hot mithril bar!
+ skill: smithing
+ station:
+ - furnace
+ type: smelting
+ wait: 4
+ xp: 30
+description: A bar of mithril metal, light and strong.
id: mithril_bar
name: mithril bar
-color: "75"
-description: "A bar of mithril metal, light and strong."
-value: 100
stackable: false
+value: 100
diff --git a/data/items/materials/mithril_nails.yaml b/data/items/materials/mithril_nails.yaml
index aeb0544..4fef9d0 100644
--- a/data/items/materials/mithril_nails.yaml
+++ b/data/items/materials/mithril_nails.yaml
@@ -1,6 +1,19 @@
+color: "75"
+craft:
+ consume:
+ - items:
+ - mithril_bar
+ quantity: 1
+ level: 54
+ output_qty: 15
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 50
+description: A set of mithril nails.
id: mithril_nails
name: mithril nails
-color: "75"
-description: "A set of mithril nails."
-value: 8
stackable: true
+value: 8
diff --git a/data/items/materials/oak_planks.yaml b/data/items/materials/oak_planks.yaml
index 29376c6..2975555 100644
--- a/data/items/materials/oak_planks.yaml
+++ b/data/items/materials/oak_planks.yaml
@@ -1,6 +1,20 @@
+color: "102"
+craft:
+ consume:
+ - items:
+ - oak_logs
+ quantity: 1
+ level: 15
+ message: You saw the oak logs into sturdy oak planks.
+ skill: construction
+ station:
+ - workbench
+ tool: saw
+ type: construction
+ wait: 20
+ xp: 10
+description: Sturdy oak planks, ideal for furniture.
id: oak_planks
name: oak planks
-color: "102"
-description: "Sturdy oak planks, ideal for furniture."
-value: 40
stackable: false
+value: 40
diff --git a/data/items/materials/planks.yaml b/data/items/materials/planks.yaml
index 67de72e..ce0e68f 100644
--- a/data/items/materials/planks.yaml
+++ b/data/items/materials/planks.yaml
@@ -1,6 +1,20 @@
+color: "137"
+craft:
+ consume:
+ - items:
+ - logs
+ quantity: 1
+ level: 1
+ message: You saw the logs into some regular planks.
+ skill: construction
+ station:
+ - workbench
+ tool: saw
+ type: construction
+ wait: 20
+ xp: 5
+description: Some regular wooden planks, ready for construction.
id: planks
name: planks
-color: "137"
-description: "Some regular wooden planks, ready for construction."
-value: 20
stackable: false
+value: 20
diff --git a/data/items/materials/ranarr.yaml b/data/items/materials/ranarr.yaml
index 683cce6..c34cbb7 100644
--- a/data/items/materials/ranarr.yaml
+++ b/data/items/materials/ranarr.yaml
@@ -1,6 +1,17 @@
-id: ranarr
-name: "ranarr"
color: "48"
-description: "A clean ranarr leaf, ready for use in pharmacy."
-value: 35
+craft:
+ consume:
+ - items:
+ - grimy_ranarr
+ quantity: 1
+ level: 25
+ message: You clean the grimy ranarr leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 8
+description: A clean ranarr leaf, ready for use in pharmacy.
+id: ranarr
+name: ranarr
stackable: true
+value: 35
diff --git a/data/items/materials/ruby.yaml b/data/items/materials/ruby.yaml
index 644e1bf..301bdd3 100644
--- a/data/items/materials/ruby.yaml
+++ b/data/items/materials/ruby.yaml
@@ -1,6 +1,18 @@
+color: "196"
+craft:
+ consume:
+ - items:
+ - uncut_ruby
+ quantity: 1
+ level: 63
+ message: You cut the ruby into a beautiful gem.
+ skill: crafting
+ tool: chisel
+ type: crafting
+ wait: 3
+ xp: 85
+description: A beautifully cut ruby.
id: ruby
name: ruby
-color: "196"
-description: "A beautifully cut ruby."
-value: 200
stackable: false
+value: 200
diff --git a/data/items/materials/rune_nails.yaml b/data/items/materials/rune_nails.yaml
index 5d2dfc5..ce12da1 100644
--- a/data/items/materials/rune_nails.yaml
+++ b/data/items/materials/rune_nails.yaml
@@ -1,6 +1,19 @@
+color: "87"
+craft:
+ consume:
+ - items:
+ - runite_bar
+ quantity: 1
+ level: 89
+ output_qty: 15
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 75
+description: A set of rune nails.
id: rune_nails
name: rune nails
-color: "87"
-description: "A set of rune nails."
-value: 30
stackable: true
+value: 30
diff --git a/data/items/materials/runite_bar.yaml b/data/items/materials/runite_bar.yaml
index 1faff69..65f5209 100644
--- a/data/items/materials/runite_bar.yaml
+++ b/data/items/materials/runite_bar.yaml
@@ -1,6 +1,23 @@
+color: "39"
+craft:
+ consume:
+ - items:
+ - runite_ore
+ quantity: 1
+ - items:
+ - coal
+ quantity: 8
+ fail_message: You fail to smelt a usable bar.
+ level: 80
+ message: You remove a white hot runite bar!
+ skill: smithing
+ station:
+ - furnace
+ type: smelting
+ wait: 4
+ xp: 50
+description: A bar of runite metal, the finest alloy known.
id: runite_bar
name: runite bar
-color: "39"
-description: "A bar of runite metal, the finest alloy known."
-value: 500
stackable: false
+value: 500
diff --git a/data/items/materials/sapphire.yaml b/data/items/materials/sapphire.yaml
index 9710d45..e2a2bb3 100644
--- a/data/items/materials/sapphire.yaml
+++ b/data/items/materials/sapphire.yaml
@@ -1,6 +1,18 @@
+color: "69"
+craft:
+ consume:
+ - items:
+ - uncut_sapphire
+ quantity: 1
+ level: 20
+ message: You cut the sapphire into a beautiful gem.
+ skill: crafting
+ tool: chisel
+ type: crafting
+ wait: 3
+ xp: 50
+description: A beautifully cut sapphire.
id: sapphire
name: sapphire
-color: "69"
-description: "A beautifully cut sapphire."
-value: 50
stackable: false
+value: 50
diff --git a/data/items/materials/silver_bar.yaml b/data/items/materials/silver_bar.yaml
index eccb25f..58c2056 100644
--- a/data/items/materials/silver_bar.yaml
+++ b/data/items/materials/silver_bar.yaml
@@ -1,6 +1,20 @@
+color: "255"
+craft:
+ consume:
+ - items:
+ - silver_ore
+ quantity: 1
+ fail_message: You fail to smelt a usable bar.
+ level: 25
+ message: You remove a white hot silver bar!
+ skill: smithing
+ station:
+ - furnace
+ type: smelting
+ wait: 4
+ xp: 14
+description: A bar of silver metal.
id: silver_bar
name: silver bar
-color: "255"
-description: "A bar of silver metal."
-value: 20
stackable: false
+value: 20
diff --git a/data/items/materials/snapdragon.yaml b/data/items/materials/snapdragon.yaml
index 0c3ba2f..95ac4bf 100644
--- a/data/items/materials/snapdragon.yaml
+++ b/data/items/materials/snapdragon.yaml
@@ -1,6 +1,17 @@
-id: snapdragon
-name: "snapdragon"
color: "48"
-description: "A clean snapdragon leaf, ready for use in pharmacy."
-value: 65
+craft:
+ consume:
+ - items:
+ - grimy_snapdragon
+ quantity: 1
+ level: 59
+ message: You clean the grimy snapdragon leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 12
+description: A clean snapdragon leaf, ready for use in pharmacy.
+id: snapdragon
+name: snapdragon
stackable: true
+value: 65
diff --git a/data/items/materials/soft_clay.yaml b/data/items/materials/soft_clay.yaml
index 018e5ef..9a4dbe5 100644
--- a/data/items/materials/soft_clay.yaml
+++ b/data/items/materials/soft_clay.yaml
@@ -1,13 +1,20 @@
+color: "173"
+craft:
+ type: combine
+ consume:
+ - items:
+ - clay
+ quantity: 1
+ - byproducts:
+ - empty_bucket
+ - empty_jug
+ items:
+ - bucket_of_water
+ - jug_of_water
+ quantity: 1
+ wait: 2
+description: Soft, workable clay ready for shaping on a pottery wheel.
id: soft_clay
name: soft clay
-color: "173"
-description: "Soft, workable clay ready for shaping on a pottery wheel."
-value: 2
stackable: false
-ticks: 2
-made_from:
- - items: [clay]
- quantity: 1
- - items: [bucket_of_water, jug_of_water]
- quantity: 1
- byproducts: [empty_bucket, empty_jug]
+value: 2
diff --git a/data/items/materials/steel_bar.yaml b/data/items/materials/steel_bar.yaml
index 450fd5c..27ed65c 100644
--- a/data/items/materials/steel_bar.yaml
+++ b/data/items/materials/steel_bar.yaml
@@ -1,6 +1,23 @@
+color: "254"
+craft:
+ consume:
+ - items:
+ - iron_ore
+ quantity: 1
+ - items:
+ - coal
+ quantity: 2
+ fail_message: You fail to smelt a usable bar.
+ level: 40
+ message: You remove a white hot steel bar!
+ skill: smithing
+ station:
+ - furnace
+ type: smelting
+ wait: 4
+ xp: 17
+description: A bar of steel, forged from iron ore and coal.
id: steel_bar
name: steel bar
-color: "254"
-description: "A bar of steel, forged from iron ore and coal."
-value: 30
stackable: false
+value: 30
diff --git a/data/items/materials/steel_nails.yaml b/data/items/materials/steel_nails.yaml
index 4890d18..e9f9813 100644
--- a/data/items/materials/steel_nails.yaml
+++ b/data/items/materials/steel_nails.yaml
@@ -1,6 +1,19 @@
+color: "253"
+craft:
+ consume:
+ - items:
+ - steel_bar
+ quantity: 1
+ level: 34
+ output_qty: 15
+ skill: smithing
+ station:
+ - anvil
+ type: smithing
+ wait: 4
+ xp: 37
+description: A set of steel nails.
id: steel_nails
name: steel nails
-color: "253"
-description: "A set of steel nails."
-value: 4
stackable: true
+value: 4
diff --git a/data/items/materials/stim_cell.yaml b/data/items/materials/stim_cell.yaml
index b431532..c89191c 100644
--- a/data/items/materials/stim_cell.yaml
+++ b/data/items/materials/stim_cell.yaml
@@ -1,9 +1,23 @@
-id: stim_cell
-name: "stim cell"
color: "226"
-description: "A bright yellow energy supplement that restores stamina."
-value: 65
-stackable: false
-potion_effect: "battery"
+craft:
+ consume:
+ - items:
+ - harralander_potion_unf
+ quantity: 1
+ - items:
+ - chocolate_dust
+ quantity: 1
+ level: 26
+ message: You mix a stim cell.
+ skill: pharmacy
+ type: pharmacy
+ wait: 4
+ xp: 68
+description: A bright yellow energy supplement that restores stamina.
+id: stim_cell
+name: stim cell
potion_bonus: 25
potion_duration: 0
+potion_effect: battery
+stackable: false
+value: 65
diff --git a/data/items/materials/tarromin.yaml b/data/items/materials/tarromin.yaml
index 5af2573..3451e89 100644
--- a/data/items/materials/tarromin.yaml
+++ b/data/items/materials/tarromin.yaml
@@ -1,6 +1,17 @@
-id: tarromin
-name: "tarromin"
color: "48"
-description: "A clean tarromin leaf, ready for use in pharmacy."
-value: 10
+craft:
+ consume:
+ - items:
+ - grimy_tarromin
+ quantity: 1
+ level: 11
+ message: You clean the grimy tarromin leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 5
+description: A clean tarromin leaf, ready for use in pharmacy.
+id: tarromin
+name: tarromin
stackable: true
+value: 10
diff --git a/data/items/materials/teak_planks.yaml b/data/items/materials/teak_planks.yaml
index f0a0642..8a9effc 100644
--- a/data/items/materials/teak_planks.yaml
+++ b/data/items/materials/teak_planks.yaml
@@ -1,6 +1,20 @@
+color: "100"
+craft:
+ consume:
+ - items:
+ - teak_logs
+ quantity: 1
+ level: 35
+ message: You saw the teak logs into fine teak planks.
+ skill: construction
+ station:
+ - workbench
+ tool: saw
+ type: construction
+ wait: 20
+ xp: 20
+description: Fine teak planks with a rich grain.
id: teak_planks
name: teak planks
-color: "100"
-description: "Fine teak planks with a rich grain."
-value: 80
stackable: false
+value: 80
diff --git a/data/items/materials/toadflax.yaml b/data/items/materials/toadflax.yaml
index 0de8f01..d3fa236 100644
--- a/data/items/materials/toadflax.yaml
+++ b/data/items/materials/toadflax.yaml
@@ -1,6 +1,17 @@
-id: toadflax
-name: "toadflax"
color: "48"
-description: "A clean toadflax leaf, ready for use in pharmacy."
-value: 25
+craft:
+ consume:
+ - items:
+ - grimy_toadflax
+ quantity: 1
+ level: 30
+ message: You clean the grimy toadflax leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 8
+description: A clean toadflax leaf, ready for use in pharmacy.
+id: toadflax
+name: toadflax
stackable: true
+value: 25
diff --git a/data/items/materials/torstol.yaml b/data/items/materials/torstol.yaml
index a8b8fbb..97cc20e 100644
--- a/data/items/materials/torstol.yaml
+++ b/data/items/materials/torstol.yaml
@@ -1,6 +1,17 @@
-id: torstol
-name: "torstol"
color: "48"
-description: "A clean torstol leaf, ready for use in pharmacy."
-value: 100
+craft:
+ consume:
+ - items:
+ - grimy_torstol
+ quantity: 1
+ level: 75
+ message: You clean the grimy torstol leaf.
+ skill: pharmacy
+ type: clean
+ wait: 2
+ xp: 15
+description: A clean torstol leaf, ready for use in pharmacy.
+id: torstol
+name: torstol
stackable: true
+value: 100
diff --git a/data/items/misc/mahogany_table.yaml b/data/items/misc/mahogany_table.yaml
index 245b929..f932fbe 100644
--- a/data/items/misc/mahogany_table.yaml
+++ b/data/items/misc/mahogany_table.yaml
@@ -1,6 +1,19 @@
+color: "96"
+craft:
+ consume:
+ - items:
+ - mahogany_planks
+ quantity: 3
+ level: 52
+ message: You build a luxurious mahogany table.
+ skill: construction
+ station:
+ - workbench
+ type: construction
+ wait: 6
+ xp: 120
+description: A luxurious mahogany table, the centerpiece of any room.
id: mahogany_table
name: mahogany table
-color: "96"
-description: "A luxurious mahogany table, the centerpiece of any room."
-value: 240
stackable: false
+value: 240
diff --git a/data/items/misc/oak_shelf.yaml b/data/items/misc/oak_shelf.yaml
index 0f08fa5..b797d70 100644
--- a/data/items/misc/oak_shelf.yaml
+++ b/data/items/misc/oak_shelf.yaml
@@ -1,6 +1,19 @@
+color: "94"
+craft:
+ consume:
+ - items:
+ - oak_planks
+ quantity: 2
+ level: 18
+ message: You assemble the oak planks into a sturdy shelf.
+ skill: construction
+ station:
+ - workbench
+ type: construction
+ wait: 6
+ xp: 40
+description: A solid oak shelf, built to last.
id: oak_shelf
name: oak shelf
-color: "94"
-description: "A solid oak shelf, built to last."
-value: 80
stackable: false
+value: 80
diff --git a/data/items/misc/oak_table.yaml b/data/items/misc/oak_table.yaml
index e94b372..407380f 100644
--- a/data/items/misc/oak_table.yaml
+++ b/data/items/misc/oak_table.yaml
@@ -1,6 +1,19 @@
+color: "94"
+craft:
+ consume:
+ - items:
+ - oak_planks
+ quantity: 3
+ level: 20
+ message: You build a fine oak table.
+ skill: construction
+ station:
+ - workbench
+ type: construction
+ wait: 6
+ xp: 50
+description: A fine oak table with carved legs.
id: oak_table
name: oak table
-color: "94"
-description: "A fine oak table with carved legs."
-value: 100
stackable: false
+value: 100
diff --git a/data/items/misc/teak_table.yaml b/data/items/misc/teak_table.yaml
index 5c2c5c5..36c1be6 100644
--- a/data/items/misc/teak_table.yaml
+++ b/data/items/misc/teak_table.yaml
@@ -1,6 +1,19 @@
+color: "100"
+craft:
+ consume:
+ - items:
+ - teak_planks
+ quantity: 3
+ level: 38
+ message: You build an elegant teak table.
+ skill: construction
+ station:
+ - workbench
+ type: construction
+ wait: 6
+ xp: 80
+description: An elegant teak table with a polished surface.
id: teak_table
name: teak table
-color: "100"
-description: "An elegant teak table with a polished surface."
-value: 160
stackable: false
+value: 160
diff --git a/data/items/misc/wooden_chair.yaml b/data/items/misc/wooden_chair.yaml
index 16220ef..786a818 100644
--- a/data/items/misc/wooden_chair.yaml
+++ b/data/items/misc/wooden_chair.yaml
@@ -1,6 +1,19 @@
+color: "130"
+craft:
+ consume:
+ - items:
+ - planks
+ quantity: 2
+ level: 8
+ message: You craft a comfortable wooden chair.
+ skill: construction
+ station:
+ - workbench
+ type: construction
+ wait: 6
+ xp: 30
+description: A comfortable wooden chair with a smooth finish.
id: wooden_chair
name: wooden chair
-color: "130"
-description: "A comfortable wooden chair with a smooth finish."
-value: 35
stackable: false
+value: 35
diff --git a/data/items/misc/wooden_shelf.yaml b/data/items/misc/wooden_shelf.yaml
index 3b0d97d..a99c0a5 100644
--- a/data/items/misc/wooden_shelf.yaml
+++ b/data/items/misc/wooden_shelf.yaml
@@ -1,6 +1,19 @@
+color: "130"
+craft:
+ consume:
+ - items:
+ - planks
+ quantity: 2
+ level: 4
+ message: You assemble the planks into a wooden shelf.
+ skill: construction
+ station:
+ - workbench
+ type: construction
+ wait: 6
+ xp: 20
+description: A simple wooden shelf, useful for storing small items.
id: wooden_shelf
name: wooden shelf
-color: "130"
-description: "A simple wooden shelf, useful for storing small items."
-value: 40
stackable: false
+value: 40
diff --git a/data/items/misc/wooden_table.yaml b/data/items/misc/wooden_table.yaml
index 171a9bb..c59c997 100644
--- a/data/items/misc/wooden_table.yaml
+++ b/data/items/misc/wooden_table.yaml
@@ -1,6 +1,19 @@
+color: "130"
+craft:
+ consume:
+ - items:
+ - planks
+ quantity: 3
+ level: 6
+ message: You build a sturdy wooden table.
+ skill: construction
+ station:
+ - workbench
+ type: construction
+ wait: 6
+ xp: 25
+description: A sturdy wooden table, perfect for dining or displaying objects.
id: wooden_table
name: wooden table
-color: "130"
-description: "A sturdy wooden table, perfect for dining or displaying objects."
-value: 50
stackable: false
+value: 50
diff --git a/data/items/tools/empty_pot.yaml b/data/items/tools/empty_pot.yaml
index a57b441..0e1669a 100644
--- a/data/items/tools/empty_pot.yaml
+++ b/data/items/tools/empty_pot.yaml
@@ -1,6 +1,24 @@
+color: "130"
+craft:
+ consume:
+ - items:
+ - unfired_pot
+ quantity: 1
+ fail_message: The pot cracks in the oven and is ruined.
+ level: 1
+ message: You fire the pot in the oven successfully.
+ skill: crafting
+ station:
+ - pottery_oven
+ success:
+ base: 0.5
+ cap: 0.95
+ per_level: 0.02
+ type: crafting
+ wait: 4
+ xp: 6
+description: An empty clay pot.
id: empty_pot
name: empty pot
-color: "130"
-description: "An empty clay pot."
-value: 1
stackable: false
+value: 1
diff --git a/data/items/tools/plant_pot.yaml b/data/items/tools/plant_pot.yaml
index 2f81e08..bafd007 100644
--- a/data/items/tools/plant_pot.yaml
+++ b/data/items/tools/plant_pot.yaml
@@ -1,5 +1,23 @@
+color: "130"
+craft:
+ consume:
+ - items:
+ - unfired_plant_pot
+ quantity: 1
+ fail_message: The plant pot cracks in the oven and is ruined.
+ level: 19
+ message: You fire the plant pot in the oven successfully.
+ skill: crafting
+ station:
+ - pottery_oven
+ success:
+ base: 0.3
+ cap: 0.95
+ per_level: 0.02
+ type: crafting
+ wait: 4
+ xp: 18
+description: A clay plant pot for growing plants.
id: plant_pot
name: plant pot
-color: "130"
-description: "A clay plant pot for growing plants."
value: 5
diff --git a/data/items/tools/unfired_pie_dish.yaml b/data/items/tools/unfired_pie_dish.yaml
index fddc4d1..ee3fc6b 100644
--- a/data/items/tools/unfired_pie_dish.yaml
+++ b/data/items/tools/unfired_pie_dish.yaml
@@ -1,5 +1,18 @@
+color: "173"
+craft:
+ consume:
+ - items:
+ - soft_clay
+ quantity: 1
+ level: 7
+ message: You shape the clay into a pie dish.
+ skill: crafting
+ station:
+ - pottery_wheel
+ type: crafting
+ wait: 4
+ xp: 15
+description: An unfired clay pie dish. It needs to be fired in a pottery oven.
id: unfired_pie_dish
name: unfired pie dish
-color: "173"
-description: "An unfired clay pie dish. It needs to be fired in a pottery oven."
value: 1
diff --git a/data/items/tools/unfired_plant_pot.yaml b/data/items/tools/unfired_plant_pot.yaml
index c088242..960112e 100644
--- a/data/items/tools/unfired_plant_pot.yaml
+++ b/data/items/tools/unfired_plant_pot.yaml
@@ -1,5 +1,18 @@
+color: "173"
+craft:
+ consume:
+ - items:
+ - soft_clay
+ quantity: 1
+ level: 19
+ message: You shape the clay into a plant pot.
+ skill: crafting
+ station:
+ - pottery_wheel
+ type: crafting
+ wait: 4
+ xp: 18
+description: An unfired clay plant pot. It needs to be fired in a pottery oven.
id: unfired_plant_pot
name: unfired plant pot
-color: "173"
-description: "An unfired clay plant pot. It needs to be fired in a pottery oven."
value: 1
diff --git a/data/items/tools/unfired_pot.yaml b/data/items/tools/unfired_pot.yaml
index 265a19f..654cd77 100644
--- a/data/items/tools/unfired_pot.yaml
+++ b/data/items/tools/unfired_pot.yaml
@@ -1,5 +1,18 @@
+color: "173"
+craft:
+ consume:
+ - items:
+ - soft_clay
+ quantity: 1
+ level: 1
+ message: You shape the clay into a pot.
+ skill: crafting
+ station:
+ - pottery_wheel
+ type: crafting
+ wait: 4
+ xp: 6
+description: An unfired clay pot. It needs to be fired in a pottery oven.
id: unfired_pot
name: unfired pot
-color: "173"
-description: "An unfired clay pot. It needs to be fired in a pottery oven."
value: 1
diff --git a/data/recipes/clean/avantoe.yaml b/data/recipes/clean/avantoe.yaml
deleted file mode 100644
index fe00db5..0000000
--- a/data/recipes/clean/avantoe.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: avantoe
-type: clean
-skill: pharmacy
-level: 48
-xp: 10
-wait: 2
-consume:
- - items: [grimy_avantoe]
- quantity: 1
-output: avantoe
-message: "You clean the grimy avantoe leaf."
diff --git a/data/recipes/clean/cadantine.yaml b/data/recipes/clean/cadantine.yaml
deleted file mode 100644
index 8acb2c5..0000000
--- a/data/recipes/clean/cadantine.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: cadantine
-type: clean
-skill: pharmacy
-level: 65
-xp: 13
-wait: 2
-consume:
- - items: [grimy_cadantine]
- quantity: 1
-output: cadantine
-message: "You clean the grimy cadantine leaf."
diff --git a/data/recipes/clean/dwarf_weed.yaml b/data/recipes/clean/dwarf_weed.yaml
deleted file mode 100644
index 70c6c42..0000000
--- a/data/recipes/clean/dwarf_weed.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: dwarf_weed
-type: clean
-skill: pharmacy
-level: 70
-xp: 14
-wait: 2
-consume:
- - items: [grimy_dwarf_weed]
- quantity: 1
-output: dwarf_weed
-message: "You clean the grimy dwarf weed leaf."
diff --git a/data/recipes/clean/guam.yaml b/data/recipes/clean/guam.yaml
deleted file mode 100644
index 410e309..0000000
--- a/data/recipes/clean/guam.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: guam
-type: clean
-skill: pharmacy
-level: 3
-xp: 3
-wait: 2
-consume:
- - items: [grimy_guam]
- quantity: 1
-output: guam
-message: "You clean the grimy guam leaf."
diff --git a/data/recipes/clean/harralander.yaml b/data/recipes/clean/harralander.yaml
deleted file mode 100644
index f60a737..0000000
--- a/data/recipes/clean/harralander.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: harralander
-type: clean
-skill: pharmacy
-level: 20
-xp: 6
-wait: 2
-consume:
- - items: [grimy_harralander]
- quantity: 1
-output: harralander
-message: "You clean the grimy harralander leaf."
diff --git a/data/recipes/clean/irit.yaml b/data/recipes/clean/irit.yaml
deleted file mode 100644
index 5853b89..0000000
--- a/data/recipes/clean/irit.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: irit
-type: clean
-skill: pharmacy
-level: 40
-xp: 9
-wait: 2
-consume:
- - items: [grimy_irit]
- quantity: 1
-output: irit
-message: "You clean the grimy irit leaf."
diff --git a/data/recipes/clean/kwuarm.yaml b/data/recipes/clean/kwuarm.yaml
deleted file mode 100644
index 04e50ad..0000000
--- a/data/recipes/clean/kwuarm.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: kwuarm
-type: clean
-skill: pharmacy
-level: 54
-xp: 11
-wait: 2
-consume:
- - items: [grimy_kwuarm]
- quantity: 1
-output: kwuarm
-message: "You clean the grimy kwuarm leaf."
diff --git a/data/recipes/clean/lantadyme.yaml b/data/recipes/clean/lantadyme.yaml
deleted file mode 100644
index 499dee9..0000000
--- a/data/recipes/clean/lantadyme.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: lantadyme
-type: clean
-skill: pharmacy
-level: 67
-xp: 13
-wait: 2
-consume:
- - items: [grimy_lantadyme]
- quantity: 1
-output: lantadyme
-message: "You clean the grimy lantadyme leaf."
diff --git a/data/recipes/clean/marrentill.yaml b/data/recipes/clean/marrentill.yaml
deleted file mode 100644
index 291ea21..0000000
--- a/data/recipes/clean/marrentill.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: marrentill
-type: clean
-skill: pharmacy
-level: 5
-xp: 4
-wait: 2
-consume:
- - items: [grimy_marrentill]
- quantity: 1
-output: marrentill
-message: "You clean the grimy marrentill leaf."
diff --git a/data/recipes/clean/ranarr.yaml b/data/recipes/clean/ranarr.yaml
deleted file mode 100644
index 989de1e..0000000
--- a/data/recipes/clean/ranarr.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: ranarr
-type: clean
-skill: pharmacy
-level: 25
-xp: 8
-wait: 2
-consume:
- - items: [grimy_ranarr]
- quantity: 1
-output: ranarr
-message: "You clean the grimy ranarr leaf."
diff --git a/data/recipes/clean/snapdragon.yaml b/data/recipes/clean/snapdragon.yaml
deleted file mode 100644
index 8819630..0000000
--- a/data/recipes/clean/snapdragon.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: snapdragon
-type: clean
-skill: pharmacy
-level: 59
-xp: 12
-wait: 2
-consume:
- - items: [grimy_snapdragon]
- quantity: 1
-output: snapdragon
-message: "You clean the grimy snapdragon leaf."
diff --git a/data/recipes/clean/tarromin.yaml b/data/recipes/clean/tarromin.yaml
deleted file mode 100644
index 89bdce3..0000000
--- a/data/recipes/clean/tarromin.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: tarromin
-type: clean
-skill: pharmacy
-level: 11
-xp: 5
-wait: 2
-consume:
- - items: [grimy_tarromin]
- quantity: 1
-output: tarromin
-message: "You clean the grimy tarromin leaf."
diff --git a/data/recipes/clean/toadflax.yaml b/data/recipes/clean/toadflax.yaml
deleted file mode 100644
index 77fb955..0000000
--- a/data/recipes/clean/toadflax.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: toadflax
-type: clean
-skill: pharmacy
-level: 30
-xp: 8
-wait: 2
-consume:
- - items: [grimy_toadflax]
- quantity: 1
-output: toadflax
-message: "You clean the grimy toadflax leaf."
diff --git a/data/recipes/clean/torstol.yaml b/data/recipes/clean/torstol.yaml
deleted file mode 100644
index 8833eb4..0000000
--- a/data/recipes/clean/torstol.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: torstol
-type: clean
-skill: pharmacy
-level: 75
-xp: 15
-wait: 2
-consume:
- - items: [grimy_torstol]
- quantity: 1
-output: torstol
-message: "You clean the grimy torstol leaf."
diff --git a/data/recipes/construction/mahogany_planks.yaml b/data/recipes/construction/mahogany_planks.yaml
deleted file mode 100644
index 3008a07..0000000
--- a/data/recipes/construction/mahogany_planks.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-id: mahogany_planks
-type: construction
-level: 50
-xp: 30
-wait: 20
-station: [workbench]
-tool: saw
-consume:
- - items: [mahogany_logs]
- quantity: 1
-output: mahogany_planks
-message: "You saw the mahogany logs into luxurious mahogany planks."
diff --git a/data/recipes/construction/mahogany_table.yaml b/data/recipes/construction/mahogany_table.yaml
deleted file mode 100644
index 4b08c06..0000000
--- a/data/recipes/construction/mahogany_table.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: mahogany_table
-type: construction
-level: 52
-xp: 120
-wait: 6
-station: [workbench]
-consume:
- - items: [mahogany_planks]
- quantity: 3
-output: mahogany_table
-message: "You build a luxurious mahogany table."
diff --git a/data/recipes/construction/oak_planks.yaml b/data/recipes/construction/oak_planks.yaml
deleted file mode 100644
index da50f88..0000000
--- a/data/recipes/construction/oak_planks.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-id: oak_planks
-type: construction
-level: 15
-xp: 10
-wait: 20
-station: [workbench]
-tool: saw
-consume:
- - items: [oak_logs]
- quantity: 1
-output: oak_planks
-message: "You saw the oak logs into sturdy oak planks."
diff --git a/data/recipes/construction/oak_shelf.yaml b/data/recipes/construction/oak_shelf.yaml
deleted file mode 100644
index c9f6242..0000000
--- a/data/recipes/construction/oak_shelf.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: oak_shelf
-type: construction
-level: 18
-xp: 40
-wait: 6
-station: [workbench]
-consume:
- - items: [oak_planks]
- quantity: 2
-output: oak_shelf
-message: "You assemble the oak planks into a sturdy shelf."
diff --git a/data/recipes/construction/oak_table.yaml b/data/recipes/construction/oak_table.yaml
deleted file mode 100644
index 6428200..0000000
--- a/data/recipes/construction/oak_table.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: oak_table
-type: construction
-level: 20
-xp: 50
-wait: 6
-station: [workbench]
-consume:
- - items: [oak_planks]
- quantity: 3
-output: oak_table
-message: "You build a fine oak table."
diff --git a/data/recipes/construction/planks.yaml b/data/recipes/construction/planks.yaml
deleted file mode 100644
index 9d202ff..0000000
--- a/data/recipes/construction/planks.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-id: planks
-type: construction
-level: 1
-xp: 5
-wait: 20
-station: [workbench]
-tool: saw
-consume:
- - items: [logs]
- quantity: 1
-output: planks
-message: "You saw the logs into some regular planks."
diff --git a/data/recipes/construction/teak_planks.yaml b/data/recipes/construction/teak_planks.yaml
deleted file mode 100644
index 1780d8b..0000000
--- a/data/recipes/construction/teak_planks.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-id: teak_planks
-type: construction
-level: 35
-xp: 20
-wait: 20
-station: [workbench]
-tool: saw
-consume:
- - items: [teak_logs]
- quantity: 1
-output: teak_planks
-message: "You saw the teak logs into fine teak planks."
diff --git a/data/recipes/construction/teak_table.yaml b/data/recipes/construction/teak_table.yaml
deleted file mode 100644
index ce579e1..0000000
--- a/data/recipes/construction/teak_table.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: teak_table
-type: construction
-level: 38
-xp: 80
-wait: 6
-station: [workbench]
-consume:
- - items: [teak_planks]
- quantity: 3
-output: teak_table
-message: "You build an elegant teak table."
diff --git a/data/recipes/construction/wooden_chair.yaml b/data/recipes/construction/wooden_chair.yaml
deleted file mode 100644
index a1db252..0000000
--- a/data/recipes/construction/wooden_chair.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: wooden_chair
-type: construction
-level: 8
-xp: 30
-wait: 6
-station: [workbench]
-consume:
- - items: [planks]
- quantity: 2
-output: wooden_chair
-message: "You craft a comfortable wooden chair."
diff --git a/data/recipes/construction/wooden_shelf.yaml b/data/recipes/construction/wooden_shelf.yaml
deleted file mode 100644
index 3ea97cc..0000000
--- a/data/recipes/construction/wooden_shelf.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: wooden_shelf
-type: construction
-level: 4
-xp: 20
-wait: 6
-station: [workbench]
-consume:
- - items: [planks]
- quantity: 2
-output: wooden_shelf
-message: "You assemble the planks into a wooden shelf."
diff --git a/data/recipes/construction/wooden_table.yaml b/data/recipes/construction/wooden_table.yaml
deleted file mode 100644
index 73050a7..0000000
--- a/data/recipes/construction/wooden_table.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: wooden_table
-type: construction
-level: 6
-xp: 25
-wait: 6
-station: [workbench]
-consume:
- - items: [planks]
- quantity: 3
-output: wooden_table
-message: "You build a sturdy wooden table."
diff --git a/data/recipes/cooking/anchovies.yaml b/data/recipes/cooking/anchovies.yaml
deleted file mode 100644
index 26bcc39..0000000
--- a/data/recipes/cooking/anchovies.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: anchovies
-type: cooking
-level: 1
-xp: 30
-wait: 6
-station: [fire, cooking_range]
-consume:
- - items: [raw_anchovies]
- quantity: 1
-output: anchovies
-fail: burnt_fish
-message: "Cooked to perfection. It looks great!"
-fail_message: "You accidentally burn the anchovies."
diff --git a/data/recipes/cooking/bread.yaml b/data/recipes/cooking/bread.yaml
deleted file mode 100644
index 0a27439..0000000
--- a/data/recipes/cooking/bread.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: bread
-type: cooking
-level: 1
-xp: 40
-wait: 6
-station: [cooking_range]
-consume:
- - items: [bread_dough]
- quantity: 1
-output: bread
-fail: burnt_meat
-message: "You bake the dough into a fresh loaf of bread."
-fail_message: "You burn the bread to a crisp."
diff --git a/data/recipes/cooking/herring.yaml b/data/recipes/cooking/herring.yaml
deleted file mode 100644
index 486476c..0000000
--- a/data/recipes/cooking/herring.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: herring
-type: cooking
-level: 5
-xp: 50
-wait: 6
-station: [fire, cooking_range]
-consume:
- - items: [raw_herring]
- quantity: 1
-output: herring
-fail: burnt_fish
-message: "Cooked to perfection. It looks great!"
-fail_message: "You accidentally burn the herring."
diff --git a/data/recipes/cooking/salmon.yaml b/data/recipes/cooking/salmon.yaml
deleted file mode 100644
index c3362c0..0000000
--- a/data/recipes/cooking/salmon.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: salmon
-type: cooking
-level: 25
-xp: 90
-wait: 6
-station: [fire, cooking_range]
-consume:
- - items: [raw_salmon]
- quantity: 1
-output: salmon
-fail: burnt_fish
-message: "Cooked to perfection. It looks great!"
-fail_message: "You accidentally burn the salmon."
diff --git a/data/recipes/cooking/sardine.yaml b/data/recipes/cooking/sardine.yaml
deleted file mode 100644
index c03b7f0..0000000
--- a/data/recipes/cooking/sardine.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: sardine
-type: cooking
-level: 1
-xp: 40
-wait: 6
-station: [fire, cooking_range]
-consume:
- - items: [raw_sardine]
- quantity: 1
-output: sardine
-fail: burnt_fish
-message: "Cooked to perfection. It looks great!"
-fail_message: "You accidentally burn the sardine."
diff --git a/data/recipes/cooking/shrimps.yaml b/data/recipes/cooking/shrimps.yaml
deleted file mode 100644
index 19ae396..0000000
--- a/data/recipes/cooking/shrimps.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: shrimps
-type: cooking
-level: 1
-xp: 30
-wait: 6
-station: [fire, cooking_range]
-consume:
- - items: [raw_shrimps]
- quantity: 1
-output: shrimps
-fail: burnt_fish
-message: "Cooked to perfection. It looks great!"
-fail_message: "You accidentally burn the shrimps."
diff --git a/data/recipes/cooking/trout.yaml b/data/recipes/cooking/trout.yaml
deleted file mode 100644
index 72587d5..0000000
--- a/data/recipes/cooking/trout.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: trout
-type: cooking
-level: 15
-xp: 70
-wait: 6
-station: [fire, cooking_range]
-consume:
- - items: [raw_trout]
- quantity: 1
-output: trout
-fail: burnt_fish
-message: "Cooked to perfection. It looks great!"
-fail_message: "You accidentally burn the trout."
diff --git a/data/recipes/crafting/ball_of_wool.yaml b/data/recipes/crafting/ball_of_wool.yaml
deleted file mode 100644
index 09fca8b..0000000
--- a/data/recipes/crafting/ball_of_wool.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: ball_of_wool
-type: crafting
-level: 1
-xp: 3
-wait: 3
-station: [spinning_wheel]
-consume:
- - items: [wool]
- quantity: 1
-output: ball_of_wool
-message: "You spin the wool into a ball."
diff --git a/data/recipes/crafting/bowstring.yaml b/data/recipes/crafting/bowstring.yaml
deleted file mode 100644
index dfb4ea7..0000000
--- a/data/recipes/crafting/bowstring.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: bowstring
-type: crafting
-level: 1
-xp: 15
-wait: 3
-station: [spinning_wheel]
-consume:
- - items: [flax]
- quantity: 1
-output: bowstring
-message: "You spin the flax into a bowstring."
diff --git a/data/recipes/crafting/coif.yaml b/data/recipes/crafting/coif.yaml
deleted file mode 100644
index 8e6cac9..0000000
--- a/data/recipes/crafting/coif.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: coif
-type: crafting
-level: 38
-xp: 37
-wait: 4
-tool: needle
-consume:
- - items: [hard_leather]
- quantity: 1
- - items: [thread]
- quantity: 1
-output: coif
-message: "You craft a coif."
diff --git a/data/recipes/crafting/cut_diamond.yaml b/data/recipes/crafting/cut_diamond.yaml
deleted file mode 100644
index e1e7343..0000000
--- a/data/recipes/crafting/cut_diamond.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: cut_diamond
-type: crafting
-level: 43
-xp: 108
-wait: 3
-tool: chisel
-consume:
- - items: [uncut_diamond]
- quantity: 1
-output: diamond
-message: "You cut the diamond into a beautiful gem."
diff --git a/data/recipes/crafting/cut_emerald.yaml b/data/recipes/crafting/cut_emerald.yaml
deleted file mode 100644
index 405fb53..0000000
--- a/data/recipes/crafting/cut_emerald.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: cut_emerald
-type: crafting
-level: 27
-xp: 68
-wait: 3
-tool: chisel
-consume:
- - items: [uncut_emerald]
- quantity: 1
-output: emerald
-message: "You cut the emerald into a beautiful gem."
diff --git a/data/recipes/crafting/cut_ruby.yaml b/data/recipes/crafting/cut_ruby.yaml
deleted file mode 100644
index 02ab139..0000000
--- a/data/recipes/crafting/cut_ruby.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: cut_ruby
-type: crafting
-level: 63
-xp: 85
-wait: 3
-tool: chisel
-consume:
- - items: [uncut_ruby]
- quantity: 1
-output: ruby
-message: "You cut the ruby into a beautiful gem."
diff --git a/data/recipes/crafting/cut_sapphire.yaml b/data/recipes/crafting/cut_sapphire.yaml
deleted file mode 100644
index 330834f..0000000
--- a/data/recipes/crafting/cut_sapphire.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: cut_sapphire
-type: crafting
-level: 20
-xp: 50
-wait: 3
-tool: chisel
-consume:
- - items: [uncut_sapphire]
- quantity: 1
-output: sapphire
-message: "You cut the sapphire into a beautiful gem."
diff --git a/data/recipes/crafting/diamond_amulet_u.yaml b/data/recipes/crafting/diamond_amulet_u.yaml
deleted file mode 100644
index 9c90b8c..0000000
--- a/data/recipes/crafting/diamond_amulet_u.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: diamond_amulet_u
-type: crafting
-level: 70
-xp: 100
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [diamond]
- quantity: 1
- - items: [amulet_mould]
- quantity: 1
- byproducts: [amulet_mould]
-output: diamond_amulet_u
-message: "You cast a diamond amulet."
diff --git a/data/recipes/crafting/diamond_bracelet.yaml b/data/recipes/crafting/diamond_bracelet.yaml
deleted file mode 100644
index 0a15546..0000000
--- a/data/recipes/crafting/diamond_bracelet.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: diamond_bracelet
-type: crafting
-level: 58
-xp: 95
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [diamond]
- quantity: 1
- - items: [bracelet_mould]
- quantity: 1
- byproducts: [bracelet_mould]
-output: diamond_bracelet
-message: "You cast a diamond bracelet."
diff --git a/data/recipes/crafting/diamond_necklace.yaml b/data/recipes/crafting/diamond_necklace.yaml
deleted file mode 100644
index 9c15245..0000000
--- a/data/recipes/crafting/diamond_necklace.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: diamond_necklace
-type: crafting
-level: 56
-xp: 90
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [diamond]
- quantity: 1
- - items: [necklace_mould]
- quantity: 1
- byproducts: [necklace_mould]
-output: diamond_necklace
-message: "You cast a diamond necklace."
diff --git a/data/recipes/crafting/diamond_ring.yaml b/data/recipes/crafting/diamond_ring.yaml
deleted file mode 100644
index 3045b74..0000000
--- a/data/recipes/crafting/diamond_ring.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: diamond_ring
-type: crafting
-level: 43
-xp: 85
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [diamond]
- quantity: 1
- - items: [ring_mould]
- quantity: 1
- byproducts: [ring_mould]
-output: diamond_ring
-message: "You cast a diamond ring."
diff --git a/data/recipes/crafting/emerald_amulet_u.yaml b/data/recipes/crafting/emerald_amulet_u.yaml
deleted file mode 100644
index 4530fa2..0000000
--- a/data/recipes/crafting/emerald_amulet_u.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: emerald_amulet_u
-type: crafting
-level: 31
-xp: 70
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [emerald]
- quantity: 1
- - items: [amulet_mould]
- quantity: 1
- byproducts: [amulet_mould]
-output: emerald_amulet_u
-message: "You cast a emerald amulet."
diff --git a/data/recipes/crafting/emerald_bracelet.yaml b/data/recipes/crafting/emerald_bracelet.yaml
deleted file mode 100644
index 498b1ce..0000000
--- a/data/recipes/crafting/emerald_bracelet.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: emerald_bracelet
-type: crafting
-level: 30
-xp: 65
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [emerald]
- quantity: 1
- - items: [bracelet_mould]
- quantity: 1
- byproducts: [bracelet_mould]
-output: emerald_bracelet
-message: "You cast a emerald bracelet."
diff --git a/data/recipes/crafting/emerald_necklace.yaml b/data/recipes/crafting/emerald_necklace.yaml
deleted file mode 100644
index 2d0596e..0000000
--- a/data/recipes/crafting/emerald_necklace.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: emerald_necklace
-type: crafting
-level: 29
-xp: 60
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [emerald]
- quantity: 1
- - items: [necklace_mould]
- quantity: 1
- byproducts: [necklace_mould]
-output: emerald_necklace
-message: "You cast a emerald necklace."
diff --git a/data/recipes/crafting/emerald_ring.yaml b/data/recipes/crafting/emerald_ring.yaml
deleted file mode 100644
index c5cee62..0000000
--- a/data/recipes/crafting/emerald_ring.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: emerald_ring
-type: crafting
-level: 27
-xp: 55
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [emerald]
- quantity: 1
- - items: [ring_mould]
- quantity: 1
- byproducts: [ring_mould]
-output: emerald_ring
-message: "You cast a emerald ring."
diff --git a/data/recipes/crafting/gold_amulet_u.yaml b/data/recipes/crafting/gold_amulet_u.yaml
deleted file mode 100644
index d960be6..0000000
--- a/data/recipes/crafting/gold_amulet_u.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-id: gold_amulet_u
-type: crafting
-level: 8
-xp: 30
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [amulet_mould]
- quantity: 1
- byproducts: [amulet_mould]
-output: gold_amulet_u
-message: "You cast a gold amulet."
diff --git a/data/recipes/crafting/gold_bracelet.yaml b/data/recipes/crafting/gold_bracelet.yaml
deleted file mode 100644
index 9669943..0000000
--- a/data/recipes/crafting/gold_bracelet.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-id: gold_bracelet
-type: crafting
-level: 7
-xp: 25
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [bracelet_mould]
- quantity: 1
- byproducts: [bracelet_mould]
-output: gold_bracelet
-message: "You cast a gold bracelet."
diff --git a/data/recipes/crafting/gold_necklace.yaml b/data/recipes/crafting/gold_necklace.yaml
deleted file mode 100644
index e2a96c4..0000000
--- a/data/recipes/crafting/gold_necklace.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-id: gold_necklace
-type: crafting
-level: 6
-xp: 20
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [necklace_mould]
- quantity: 1
- byproducts: [necklace_mould]
-output: gold_necklace
-message: "You cast a gold necklace."
diff --git a/data/recipes/crafting/gold_ring.yaml b/data/recipes/crafting/gold_ring.yaml
deleted file mode 100644
index dcb3c09..0000000
--- a/data/recipes/crafting/gold_ring.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-id: gold_ring
-type: crafting
-level: 5
-xp: 15
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [ring_mould]
- quantity: 1
- byproducts: [ring_mould]
-output: gold_ring
-message: "You cast a gold ring."
diff --git a/data/recipes/crafting/hard_leather_body.yaml b/data/recipes/crafting/hard_leather_body.yaml
deleted file mode 100644
index 869867e..0000000
--- a/data/recipes/crafting/hard_leather_body.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: hard_leather_body
-type: crafting
-level: 28
-xp: 35
-wait: 4
-tool: needle
-consume:
- - items: [hard_leather]
- quantity: 1
- - items: [thread]
- quantity: 1
-output: hard_leather_body
-message: "You craft a hard leather body."
diff --git a/data/recipes/crafting/hard_leather_shield.yaml b/data/recipes/crafting/hard_leather_shield.yaml
deleted file mode 100644
index b46222f..0000000
--- a/data/recipes/crafting/hard_leather_shield.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: hard_leather_shield
-type: crafting
-level: 41
-xp: 40
-wait: 4
-tool: needle
-consume:
- - items: [hard_leather]
- quantity: 1
- - items: [thread]
- quantity: 1
-output: hard_leather_shield
-message: "You craft a hard leather shield."
diff --git a/data/recipes/crafting/leather_body.yaml b/data/recipes/crafting/leather_body.yaml
deleted file mode 100644
index 4f87c8f..0000000
--- a/data/recipes/crafting/leather_body.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: leather_body
-type: crafting
-level: 14
-xp: 25
-wait: 4
-tool: needle
-consume:
- - items: [leather]
- quantity: 1
- - items: [thread]
- quantity: 1
-output: leather_body
-message: "You craft a leather body."
diff --git a/data/recipes/crafting/leather_boots.yaml b/data/recipes/crafting/leather_boots.yaml
deleted file mode 100644
index 569fb81..0000000
--- a/data/recipes/crafting/leather_boots.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: leather_boots
-type: crafting
-level: 7
-xp: 16
-wait: 4
-tool: needle
-consume:
- - items: [leather]
- quantity: 1
- - items: [thread]
- quantity: 1
-output: leather_boots
-message: "You craft a pair of leather boots."
diff --git a/data/recipes/crafting/leather_chaps.yaml b/data/recipes/crafting/leather_chaps.yaml
deleted file mode 100644
index 82fdac9..0000000
--- a/data/recipes/crafting/leather_chaps.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: leather_chaps
-type: crafting
-level: 18
-xp: 27
-wait: 4
-tool: needle
-consume:
- - items: [leather]
- quantity: 1
- - items: [thread]
- quantity: 1
-output: leather_chaps
-message: "You craft a pair of leather chaps."
diff --git a/data/recipes/crafting/leather_cowl.yaml b/data/recipes/crafting/leather_cowl.yaml
deleted file mode 100644
index 70fc500..0000000
--- a/data/recipes/crafting/leather_cowl.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: leather_cowl
-type: crafting
-level: 9
-xp: 19
-wait: 4
-tool: needle
-consume:
- - items: [leather]
- quantity: 1
- - items: [thread]
- quantity: 1
-output: leather_cowl
-message: "You craft a leather cowl."
diff --git a/data/recipes/crafting/leather_gloves.yaml b/data/recipes/crafting/leather_gloves.yaml
deleted file mode 100644
index fa4cd37..0000000
--- a/data/recipes/crafting/leather_gloves.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: leather_gloves
-type: crafting
-level: 1
-xp: 14
-wait: 4
-tool: needle
-consume:
- - items: [leather]
- quantity: 1
- - items: [thread]
- quantity: 1
-output: leather_gloves
-message: "You craft a pair of leather gloves."
diff --git a/data/recipes/crafting/leather_vambraces.yaml b/data/recipes/crafting/leather_vambraces.yaml
deleted file mode 100644
index 6167294..0000000
--- a/data/recipes/crafting/leather_vambraces.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: leather_vambraces
-type: crafting
-level: 11
-xp: 22
-wait: 4
-tool: needle
-consume:
- - items: [leather]
- quantity: 1
- - items: [thread]
- quantity: 1
-output: leather_vambraces
-message: "You craft a pair of leather vambraces."
diff --git a/data/recipes/crafting/pie_dish.yaml b/data/recipes/crafting/pie_dish.yaml
deleted file mode 100644
index 5f49d62..0000000
--- a/data/recipes/crafting/pie_dish.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: pie_dish
-type: crafting
-level: 7
-xp: 10
-wait: 4
-station: [pottery_oven]
-consume:
- - items: [unfired_pie_dish]
- quantity: 1
-output: pie_dish
-message: "You fire the pie dish in the oven successfully."
-fail_message: "The pie dish cracks in the oven and is ruined."
-success:
- base: 0.40
- per_level: 0.02
- cap: 0.95
diff --git a/data/recipes/crafting/plant_pot.yaml b/data/recipes/crafting/plant_pot.yaml
deleted file mode 100644
index 2c4b228..0000000
--- a/data/recipes/crafting/plant_pot.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: plant_pot
-type: crafting
-level: 19
-xp: 18
-wait: 4
-station: [pottery_oven]
-consume:
- - items: [unfired_plant_pot]
- quantity: 1
-output: plant_pot
-message: "You fire the plant pot in the oven successfully."
-fail_message: "The plant pot cracks in the oven and is ruined."
-success:
- base: 0.30
- per_level: 0.02
- cap: 0.95
diff --git a/data/recipes/crafting/pot.yaml b/data/recipes/crafting/pot.yaml
deleted file mode 100644
index 3215036..0000000
--- a/data/recipes/crafting/pot.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: pot
-type: crafting
-level: 1
-xp: 6
-wait: 4
-station: [pottery_oven]
-consume:
- - items: [unfired_pot]
- quantity: 1
-output: empty_pot
-message: "You fire the pot in the oven successfully."
-fail_message: "The pot cracks in the oven and is ruined."
-success:
- base: 0.50
- per_level: 0.02
- cap: 0.95
diff --git a/data/recipes/crafting/ruby_amulet_u.yaml b/data/recipes/crafting/ruby_amulet_u.yaml
deleted file mode 100644
index 2958d77..0000000
--- a/data/recipes/crafting/ruby_amulet_u.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: ruby_amulet_u
-type: crafting
-level: 50
-xp: 85
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [ruby]
- quantity: 1
- - items: [amulet_mould]
- quantity: 1
- byproducts: [amulet_mould]
-output: ruby_amulet_u
-message: "You cast a ruby amulet."
diff --git a/data/recipes/crafting/ruby_bracelet.yaml b/data/recipes/crafting/ruby_bracelet.yaml
deleted file mode 100644
index 7f36bce..0000000
--- a/data/recipes/crafting/ruby_bracelet.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: ruby_bracelet
-type: crafting
-level: 42
-xp: 80
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [ruby]
- quantity: 1
- - items: [bracelet_mould]
- quantity: 1
- byproducts: [bracelet_mould]
-output: ruby_bracelet
-message: "You cast a ruby bracelet."
diff --git a/data/recipes/crafting/ruby_necklace.yaml b/data/recipes/crafting/ruby_necklace.yaml
deleted file mode 100644
index 7be562e..0000000
--- a/data/recipes/crafting/ruby_necklace.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: ruby_necklace
-type: crafting
-level: 40
-xp: 75
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [ruby]
- quantity: 1
- - items: [necklace_mould]
- quantity: 1
- byproducts: [necklace_mould]
-output: ruby_necklace
-message: "You cast a ruby necklace."
diff --git a/data/recipes/crafting/ruby_ring.yaml b/data/recipes/crafting/ruby_ring.yaml
deleted file mode 100644
index 78a6339..0000000
--- a/data/recipes/crafting/ruby_ring.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: ruby_ring
-type: crafting
-level: 63
-xp: 70
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [ruby]
- quantity: 1
- - items: [ring_mould]
- quantity: 1
- byproducts: [ring_mould]
-output: ruby_ring
-message: "You cast a ruby ring."
diff --git a/data/recipes/crafting/sapphire_amulet_u.yaml b/data/recipes/crafting/sapphire_amulet_u.yaml
deleted file mode 100644
index 36302d8..0000000
--- a/data/recipes/crafting/sapphire_amulet_u.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: sapphire_amulet_u
-type: crafting
-level: 24
-xp: 65
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [sapphire]
- quantity: 1
- - items: [amulet_mould]
- quantity: 1
- byproducts: [amulet_mould]
-output: sapphire_amulet_u
-message: "You cast a sapphire amulet."
diff --git a/data/recipes/crafting/sapphire_bracelet.yaml b/data/recipes/crafting/sapphire_bracelet.yaml
deleted file mode 100644
index 2e1031e..0000000
--- a/data/recipes/crafting/sapphire_bracelet.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: sapphire_bracelet
-type: crafting
-level: 23
-xp: 60
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [sapphire]
- quantity: 1
- - items: [bracelet_mould]
- quantity: 1
- byproducts: [bracelet_mould]
-output: sapphire_bracelet
-message: "You cast a sapphire bracelet."
diff --git a/data/recipes/crafting/sapphire_necklace.yaml b/data/recipes/crafting/sapphire_necklace.yaml
deleted file mode 100644
index 2671e6b..0000000
--- a/data/recipes/crafting/sapphire_necklace.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: sapphire_necklace
-type: crafting
-level: 22
-xp: 55
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [sapphire]
- quantity: 1
- - items: [necklace_mould]
- quantity: 1
- byproducts: [necklace_mould]
-output: sapphire_necklace
-message: "You cast a sapphire necklace."
diff --git a/data/recipes/crafting/sapphire_ring.yaml b/data/recipes/crafting/sapphire_ring.yaml
deleted file mode 100644
index f755b6f..0000000
--- a/data/recipes/crafting/sapphire_ring.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-id: sapphire_ring
-type: crafting
-level: 20
-xp: 40
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_bar]
- quantity: 1
- - items: [sapphire]
- quantity: 1
- - items: [ring_mould]
- quantity: 1
- byproducts: [ring_mould]
-output: sapphire_ring
-message: "You cast a sapphire ring."
diff --git a/data/recipes/crafting/unfired_pie_dish.yaml b/data/recipes/crafting/unfired_pie_dish.yaml
deleted file mode 100644
index d698cf4..0000000
--- a/data/recipes/crafting/unfired_pie_dish.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: unfired_pie_dish
-type: crafting
-level: 7
-xp: 15
-wait: 4
-station: [pottery_wheel]
-consume:
- - items: [soft_clay]
- quantity: 1
-output: unfired_pie_dish
-message: "You shape the clay into a pie dish."
diff --git a/data/recipes/crafting/unfired_plant_pot.yaml b/data/recipes/crafting/unfired_plant_pot.yaml
deleted file mode 100644
index e0675db..0000000
--- a/data/recipes/crafting/unfired_plant_pot.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: unfired_plant_pot
-type: crafting
-level: 19
-xp: 18
-wait: 4
-station: [pottery_wheel]
-consume:
- - items: [soft_clay]
- quantity: 1
-output: unfired_plant_pot
-message: "You shape the clay into a plant pot."
diff --git a/data/recipes/crafting/unfired_pot.yaml b/data/recipes/crafting/unfired_pot.yaml
deleted file mode 100644
index 04604e3..0000000
--- a/data/recipes/crafting/unfired_pot.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-id: unfired_pot
-type: crafting
-level: 1
-xp: 6
-wait: 4
-station: [pottery_wheel]
-consume:
- - items: [soft_clay]
- quantity: 1
-output: unfired_pot
-message: "You shape the clay into a pot."
diff --git a/data/recipes/fletching/arrow_shafts.yaml b/data/recipes/fletching/arrow_shafts.yaml
deleted file mode 100644
index c382340..0000000
--- a/data/recipes/fletching/arrow_shafts.yaml
+++ /dev/null
@@ -1,52 +0,0 @@
-type: fletching
-tool: knife
-wait: 5
-products:
- - id: arrow_shaft
- output: arrow_shaft
- level: 1
- xp: 5
- output_qty: 15
- consume:
- - items: [logs]
- quantity: 1
- - id: arrow_shaft_oak
- output: arrow_shaft
- level: 15
- xp: 10
- output_qty: 20
- consume:
- - items: [oak_logs]
- quantity: 1
- - id: arrow_shaft_willow
- output: arrow_shaft
- level: 30
- xp: 15
- output_qty: 25
- consume:
- - items: [willow_logs]
- quantity: 1
- - id: arrow_shaft_maple
- output: arrow_shaft
- level: 45
- xp: 20
- output_qty: 30
- consume:
- - items: [maple_logs]
- quantity: 1
- - id: arrow_shaft_yew
- output: arrow_shaft
- level: 60
- xp: 25
- output_qty: 35
- consume:
- - items: [yew_logs]
- quantity: 1
- - id: arrow_shaft_magic
- output: arrow_shaft
- level: 75
- xp: 30
- output_qty: 40
- consume:
- - items: [magic_logs]
- quantity: 1
diff --git a/data/recipes/fletching/bolt_feathering.yaml b/data/recipes/fletching/bolt_feathering.yaml
deleted file mode 100644
index 2197de1..0000000
--- a/data/recipes/fletching/bolt_feathering.yaml
+++ /dev/null
@@ -1,63 +0,0 @@
-type: fletching
-wait: 0
-products:
- - id: bronze_bolts
- output: bronze_bolts
- level: 9
- xp: 5
- output_qty: 10
- consume:
- - items: [bronze_bolts_unf]
- quantity: 10
- - items: [feather]
- quantity: 10
- - id: iron_bolts
- output: iron_bolts
- level: 39
- xp: 15
- output_qty: 10
- consume:
- - items: [iron_bolts_unf]
- quantity: 10
- - items: [feather]
- quantity: 10
- - id: steel_bolts
- output: steel_bolts
- level: 46
- xp: 35
- output_qty: 10
- consume:
- - items: [steel_bolts_unf]
- quantity: 10
- - items: [feather]
- quantity: 10
- - id: mithril_bolts
- output: mithril_bolts
- level: 54
- xp: 50
- output_qty: 10
- consume:
- - items: [mithril_bolts_unf]
- quantity: 10
- - items: [feather]
- quantity: 10
- - id: adamant_bolts
- output: adamant_bolts
- level: 61
- xp: 70
- output_qty: 10
- consume:
- - items: [adamant_bolts_unf]
- quantity: 10
- - items: [feather]
- quantity: 10
- - id: rune_bolts
- output: rune_bolts
- level: 69
- xp: 100
- output_qty: 10
- consume:
- - items: [rune_bolts_unf]
- quantity: 10
- - items: [feather]
- quantity: 10
diff --git a/data/recipes/fletching/gem_bolt_tips.yaml b/data/recipes/fletching/gem_bolt_tips.yaml
deleted file mode 100644
index 5482140..0000000
--- a/data/recipes/fletching/gem_bolt_tips.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-type: fletching
-tool: chisel
-wait: 5
-products:
- - id: sapphire_bolt_tips
- output: sapphire_bolt_tips
- level: 63
- xp: 4
- output_qty: 12
- consume:
- - items: [uncut_sapphire]
- quantity: 1
- - id: diamond_bolt_tips
- output: diamond_bolt_tips
- level: 65
- xp: 7
- output_qty: 12
- consume:
- - items: [uncut_diamond]
- quantity: 1
- - id: emerald_bolt_tips
- output: emerald_bolt_tips
- level: 66
- xp: 5
- output_qty: 12
- consume:
- - items: [uncut_emerald]
- quantity: 1
- - id: ruby_bolt_tips
- output: ruby_bolt_tips
- level: 73
- xp: 6
- output_qty: 12
- consume:
- - items: [uncut_ruby]
- quantity: 1
diff --git a/data/recipes/fletching/gem_tipped_bolts.yaml b/data/recipes/fletching/gem_tipped_bolts.yaml
deleted file mode 100644
index 7ed5adc..0000000
--- a/data/recipes/fletching/gem_tipped_bolts.yaml
+++ /dev/null
@@ -1,43 +0,0 @@
-type: fletching
-wait: 2
-products:
- - id: sapphire_bolts
- output: sapphire_bolts
- level: 63
- xp: 4
- output_qty: 10
- consume:
- - items: [mithril_bolts]
- quantity: 10
- - items: [sapphire_bolt_tips]
- quantity: 10
- - id: diamond_bolts
- output: diamond_bolts
- level: 65
- xp: 7
- output_qty: 10
- consume:
- - items: [adamant_bolts]
- quantity: 10
- - items: [diamond_bolt_tips]
- quantity: 10
- - id: emerald_bolts
- output: emerald_bolts
- level: 66
- xp: 5
- output_qty: 10
- consume:
- - items: [mithril_bolts]
- quantity: 10
- - items: [emerald_bolt_tips]
- quantity: 10
- - id: ruby_bolts
- output: ruby_bolts
- level: 73
- xp: 6
- output_qty: 10
- consume:
- - items: [adamant_bolts]
- quantity: 10
- - items: [ruby_bolt_tips]
- quantity: 10
diff --git a/data/recipes/fletching/headless_arrow.yaml b/data/recipes/fletching/headless_arrow.yaml
deleted file mode 100644
index 0c1e363..0000000
--- a/data/recipes/fletching/headless_arrow.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: headless_arrow
-type: fletching
-level: 1
-xp: 1
-wait: 2
-consume:
- - items: [arrow_shaft]
- quantity: 15
- - items: [feather]
- quantity: 15
-output: headless_arrow
-output_qty: 15
-message: "You attach feathers to the arrow shafts."
diff --git a/data/recipes/fletching/strung_bows.yaml b/data/recipes/fletching/strung_bows.yaml
deleted file mode 100644
index c7e2c3d..0000000
--- a/data/recipes/fletching/strung_bows.yaml
+++ /dev/null
@@ -1,111 +0,0 @@
-type: fletching
-wait: 2
-products:
- - id: shortbow
- output: shortbow
- level: 5
- xp: 5
- consume:
- - items: [shortbow_u]
- quantity: 1
- - items: [bowstring]
- quantity: 1
- - id: longbow
- output: longbow
- level: 10
- xp: 10
- consume:
- - items: [longbow_u]
- quantity: 1
- - items: [bowstring]
- quantity: 1
- - id: oak_shortbow
- output: oak_shortbow
- level: 20
- xp: 16
- consume:
- - items: [oak_shortbow_u]
- quantity: 1
- - items: [bowstring]
- quantity: 1
- - id: oak_longbow
- output: oak_longbow
- level: 25
- xp: 25
- consume:
- - items: [oak_longbow_u]
- quantity: 1
- - items: [bowstring]
- quantity: 1
- - id: willow_shortbow
- output: willow_shortbow
- level: 35
- xp: 33
- consume:
- - items: [willow_shortbow_u]
- quantity: 1
- - items: [bowstring]
- quantity: 1
- - id: willow_longbow
- output: willow_longbow
- level: 40
- xp: 41
- consume:
- - items: [willow_longbow_u]
- quantity: 1
- - items: [bowstring]
- quantity: 1
- - id: maple_shortbow
- output: maple_shortbow
- level: 50
- xp: 50
- consume:
- - items: [maple_shortbow_u]
- quantity: 1
- - items: [bowstring]
- quantity: 1
- - id: maple_longbow
- output: maple_longbow
- level: 55
- xp: 58
- consume:
- - items: [maple_longbow_u]
- quantity: 1
- - items: [bowstring]
- quantity: 1
- - id: yew_shortbow
- output: yew_shortbow
- level: 60
- xp: 67
- consume:
- - items: [yew_shortbow_u]
- quantity: 1
- - items: [bowstring]
- quantity: 1
- - id: yew_longbow
- output: yew_longbow
- level: 65
- xp: 75
- consume:
- - items: [yew_longbow_u]
- quantity: 1
- - items: [bowstring]
- quantity: 1
- - id: magic_shortbow
- output: magic_shortbow
- level: 80
- xp: 83
- consume:
- - items: [magic_shortbow_u]
- quantity: 1
- - items: [bowstring]
- quantity: 1
- - id: magic_longbow
- output: magic_longbow
- level: 85
- xp: 91
- consume:
- - items: [magic_longbow_u]
- quantity: 1
- - items: [bowstring]
- quantity: 1
diff --git a/data/recipes/fletching/tipped_arrows.yaml b/data/recipes/fletching/tipped_arrows.yaml
deleted file mode 100644
index 8e4530b..0000000
--- a/data/recipes/fletching/tipped_arrows.yaml
+++ /dev/null
@@ -1,63 +0,0 @@
-type: fletching
-wait: 2
-products:
- - id: bronze_arrow
- output: bronze_arrow
- level: 1
- xp: 1
- output_qty: 15
- consume:
- - items: [headless_arrow]
- quantity: 15
- - items: [bronze_arrowtips]
- quantity: 15
- - id: iron_arrow
- output: iron_arrow
- level: 15
- xp: 2
- output_qty: 15
- consume:
- - items: [headless_arrow]
- quantity: 15
- - items: [iron_arrowtips]
- quantity: 15
- - id: steel_arrow
- output: steel_arrow
- level: 30
- xp: 5
- output_qty: 15
- consume:
- - items: [headless_arrow]
- quantity: 15
- - items: [steel_arrowtips]
- quantity: 15
- - id: mithril_arrow
- output: mithril_arrow
- level: 45
- xp: 7
- output_qty: 15
- consume:
- - items: [headless_arrow]
- quantity: 15
- - items: [mithril_arrowtips]
- quantity: 15
- - id: adamant_arrow
- output: adamant_arrow
- level: 60
- xp: 10
- output_qty: 15
- consume:
- - items: [headless_arrow]
- quantity: 15
- - items: [adamant_arrowtips]
- quantity: 15
- - id: rune_arrow
- output: rune_arrow
- level: 75
- xp: 12
- output_qty: 15
- consume:
- - items: [headless_arrow]
- quantity: 15
- - items: [rune_arrowtips]
- quantity: 15
diff --git a/data/recipes/fletching/unstrung_bows.yaml b/data/recipes/fletching/unstrung_bows.yaml
deleted file mode 100644
index 3195c8d..0000000
--- a/data/recipes/fletching/unstrung_bows.yaml
+++ /dev/null
@@ -1,88 +0,0 @@
-type: fletching
-tool: knife
-wait: 3
-products:
- - id: shortbow_u
- output: shortbow_u
- level: 5
- xp: 5
- consume:
- - items: [logs]
- quantity: 1
- - id: longbow_u
- output: longbow_u
- level: 10
- xp: 10
- consume:
- - items: [logs]
- quantity: 1
- - id: oak_shortbow_u
- output: oak_shortbow_u
- level: 20
- xp: 16
- consume:
- - items: [oak_logs]
- quantity: 1
- - id: oak_longbow_u
- output: oak_longbow_u
- level: 25
- xp: 25
- consume:
- - items: [oak_logs]
- quantity: 1
- - id: willow_shortbow_u
- output: willow_shortbow_u
- level: 35
- xp: 33
- consume:
- - items: [willow_logs]
- quantity: 1
- - id: willow_longbow_u
- output: willow_longbow_u
- level: 40
- xp: 41
- consume:
- - items: [willow_logs]
- quantity: 1
- - id: maple_shortbow_u
- output: maple_shortbow_u
- level: 50
- xp: 50
- consume:
- - items: [maple_logs]
- quantity: 1
- - id: maple_longbow_u
- output: maple_longbow_u
- level: 55
- xp: 58
- consume:
- - items: [maple_logs]
- quantity: 1
- - id: yew_shortbow_u
- output: yew_shortbow_u
- level: 60
- xp: 67
- consume:
- - items: [yew_logs]
- quantity: 1
- - id: yew_longbow_u
- output: yew_longbow_u
- level: 65
- xp: 75
- consume:
- - items: [yew_logs]
- quantity: 1
- - id: magic_shortbow_u
- output: magic_shortbow_u
- level: 80
- xp: 83
- consume:
- - items: [magic_logs]
- quantity: 1
- - id: magic_longbow_u
- output: magic_longbow_u
- level: 85
- xp: 91
- consume:
- - items: [magic_logs]
- quantity: 1
diff --git a/data/recipes/mixing/amp_potion.yaml b/data/recipes/mixing/amp_potion.yaml
deleted file mode 100644
index 40aeb84..0000000
--- a/data/recipes/mixing/amp_potion.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: amp_potion
-type: pharmacy
-skill: pharmacy
-level: 12
-xp: 50
-wait: 4
-consume:
- - items: [tarromin_potion_unf]
- quantity: 1
- - items: [limpwurt_root]
- quantity: 1
-output: amp_potion
-message: "You mix an amp potion."
diff --git a/data/recipes/mixing/bio_serum.yaml b/data/recipes/mixing/bio_serum.yaml
deleted file mode 100644
index f7f728a..0000000
--- a/data/recipes/mixing/bio_serum.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: bio_serum
-type: pharmacy
-skill: pharmacy
-level: 5
-xp: 38
-wait: 4
-consume:
- - items: [marrentill_potion_unf]
- quantity: 1
- - items: [antitoxin_powder]
- quantity: 1
-output: bio_serum
-message: "You mix a bio serum."
diff --git a/data/recipes/mixing/full_restore.yaml b/data/recipes/mixing/full_restore.yaml
deleted file mode 100644
index c5ed30b..0000000
--- a/data/recipes/mixing/full_restore.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: full_restore
-type: pharmacy
-skill: pharmacy
-level: 63
-xp: 143
-wait: 4
-consume:
- - items: [snapdragon_potion_unf]
- quantity: 1
- - items: [arachnid_enzyme]
- quantity: 1
-output: full_restore
-message: "You mix a full restore."
diff --git a/data/recipes/mixing/nano_restore.yaml b/data/recipes/mixing/nano_restore.yaml
deleted file mode 100644
index c14f151..0000000
--- a/data/recipes/mixing/nano_restore.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: nano_restore
-type: pharmacy
-skill: pharmacy
-level: 22
-xp: 63
-wait: 4
-consume:
- - items: [harralander_potion_unf]
- quantity: 1
- - items: [arachnid_enzyme]
- quantity: 1
-output: nano_restore
-message: "You mix a nano restore."
diff --git a/data/recipes/mixing/restoration_compound.yaml b/data/recipes/mixing/restoration_compound.yaml
deleted file mode 100644
index 0613d47..0000000
--- a/data/recipes/mixing/restoration_compound.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: restoration_compound
-type: pharmacy
-skill: pharmacy
-level: 81
-xp: 180
-wait: 4
-consume:
- - items: [toadflax_potion_unf]
- quantity: 1
- - items: [crushed_nest]
- quantity: 1
-output: restoration_compound
-message: "You mix a restoration compound."
diff --git a/data/recipes/mixing/science_serum.yaml b/data/recipes/mixing/science_serum.yaml
deleted file mode 100644
index 311f3c4..0000000
--- a/data/recipes/mixing/science_serum.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: science_serum
-type: pharmacy
-skill: pharmacy
-level: 76
-xp: 173
-wait: 4
-consume:
- - items: [lantadyme_potion_unf]
- quantity: 1
- - items: [potato_cactus]
- quantity: 1
-output: science_serum
-message: "You mix a science serum."
diff --git a/data/recipes/mixing/shield_potion.yaml b/data/recipes/mixing/shield_potion.yaml
deleted file mode 100644
index f334b5a..0000000
--- a/data/recipes/mixing/shield_potion.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: shield_potion
-type: pharmacy
-skill: pharmacy
-level: 30
-xp: 75
-wait: 4
-consume:
- - items: [ranarr_potion_unf]
- quantity: 1
- - items: [white_berries]
- quantity: 1
-output: shield_potion
-message: "You mix a shield potion."
diff --git a/data/recipes/mixing/stim_cell.yaml b/data/recipes/mixing/stim_cell.yaml
deleted file mode 100644
index ddf37f8..0000000
--- a/data/recipes/mixing/stim_cell.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: stim_cell
-type: pharmacy
-skill: pharmacy
-level: 26
-xp: 68
-wait: 4
-consume:
- - items: [harralander_potion_unf]
- quantity: 1
- - items: [chocolate_dust]
- quantity: 1
-output: stim_cell
-message: "You mix a stim cell."
diff --git a/data/recipes/mixing/stim_potion.yaml b/data/recipes/mixing/stim_potion.yaml
deleted file mode 100644
index ad6abd3..0000000
--- a/data/recipes/mixing/stim_potion.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: stim_potion
-type: pharmacy
-skill: pharmacy
-level: 3
-xp: 25
-wait: 4
-consume:
- - items: [guam_potion_unf]
- quantity: 1
- - items: [eye_of_newt]
- quantity: 1
-output: stim_potion
-message: "You mix a stim potion."
diff --git a/data/recipes/mixing/super_amp.yaml b/data/recipes/mixing/super_amp.yaml
deleted file mode 100644
index dc4ef81..0000000
--- a/data/recipes/mixing/super_amp.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: super_amp
-type: pharmacy
-skill: pharmacy
-level: 55
-xp: 125
-wait: 4
-consume:
- - items: [kwuarm_potion_unf]
- quantity: 1
- - items: [limpwurt_root]
- quantity: 1
-output: super_amp
-message: "You mix a super amp."
diff --git a/data/recipes/mixing/super_bio_serum.yaml b/data/recipes/mixing/super_bio_serum.yaml
deleted file mode 100644
index 3c763a6..0000000
--- a/data/recipes/mixing/super_bio_serum.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: super_bio_serum
-type: pharmacy
-skill: pharmacy
-level: 48
-xp: 106
-wait: 4
-consume:
- - items: [irit_potion_unf]
- quantity: 1
- - items: [antitoxin_powder]
- quantity: 1
-output: super_bio_serum
-message: "You mix a super bio serum."
diff --git a/data/recipes/mixing/super_shield.yaml b/data/recipes/mixing/super_shield.yaml
deleted file mode 100644
index 706d402..0000000
--- a/data/recipes/mixing/super_shield.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: super_shield
-type: pharmacy
-skill: pharmacy
-level: 66
-xp: 150
-wait: 4
-consume:
- - items: [cadantine_potion_unf]
- quantity: 1
- - items: [white_berries]
- quantity: 1
-output: super_shield
-message: "You mix a super shield."
diff --git a/data/recipes/mixing/super_stim.yaml b/data/recipes/mixing/super_stim.yaml
deleted file mode 100644
index 8ef6819..0000000
--- a/data/recipes/mixing/super_stim.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: super_stim
-type: pharmacy
-skill: pharmacy
-level: 45
-xp: 100
-wait: 4
-consume:
- - items: [irit_potion_unf]
- quantity: 1
- - items: [eye_of_newt]
- quantity: 1
-output: super_stim
-message: "You mix a super stim."
diff --git a/data/recipes/mixing/super_stim_cell.yaml b/data/recipes/mixing/super_stim_cell.yaml
deleted file mode 100644
index ec4a515..0000000
--- a/data/recipes/mixing/super_stim_cell.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: super_stim_cell
-type: pharmacy
-skill: pharmacy
-level: 52
-xp: 118
-wait: 4
-consume:
- - items: [avantoe_potion_unf]
- quantity: 1
- - items: [blight_spore]
- quantity: 1
-output: super_stim_cell
-message: "You mix a super stim cell."
diff --git a/data/recipes/mixing/targeting_serum.yaml b/data/recipes/mixing/targeting_serum.yaml
deleted file mode 100644
index e91e20e..0000000
--- a/data/recipes/mixing/targeting_serum.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: targeting_serum
-type: pharmacy
-skill: pharmacy
-level: 72
-xp: 163
-wait: 4
-consume:
- - items: [dwarf_weed_potion_unf]
- quantity: 1
- - items: [catalyst_wine]
- quantity: 1
-output: targeting_serum
-message: "You mix a targeting serum."
diff --git a/data/recipes/mixing/tech_serum.yaml b/data/recipes/mixing/tech_serum.yaml
deleted file mode 100644
index 3a21619..0000000
--- a/data/recipes/mixing/tech_serum.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: tech_serum
-type: pharmacy
-skill: pharmacy
-level: 38
-xp: 88
-wait: 4
-consume:
- - items: [ranarr_potion_unf]
- quantity: 1
- - items: [snape_grass]
- quantity: 1
-output: tech_serum
-message: "You mix a tech serum."
diff --git a/data/recipes/smelting/adamantite.yaml b/data/recipes/smelting/adamantite.yaml
deleted file mode 100644
index af9283d..0000000
--- a/data/recipes/smelting/adamantite.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-id: adamantite
-type: smelting
-skill: smithing
-level: 65
-xp: 37
-wait: 4
-station: [furnace]
-consume:
- - items: [adamantite_ore]
- quantity: 1
- - items: [coal]
- quantity: 6
-output: adamantite_bar
-message: "You remove a white hot adamantite bar!"
-fail_message: "You fail to smelt a usable bar."
diff --git a/data/recipes/smelting/gold.yaml b/data/recipes/smelting/gold.yaml
deleted file mode 100644
index 9ebb96b..0000000
--- a/data/recipes/smelting/gold.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: gold
-type: smelting
-skill: smithing
-level: 45
-xp: 22
-wait: 4
-station: [furnace]
-consume:
- - items: [gold_ore]
- quantity: 1
-output: gold_bar
-message: "You remove a white hot gold bar!"
-fail_message: "You fail to smelt a usable bar."
diff --git a/data/recipes/smelting/runite.yaml b/data/recipes/smelting/runite.yaml
deleted file mode 100644
index 36d44a4..0000000
--- a/data/recipes/smelting/runite.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-id: runite
-type: smelting
-skill: smithing
-level: 80
-xp: 50
-wait: 4
-station: [furnace]
-consume:
- - items: [runite_ore]
- quantity: 1
- - items: [coal]
- quantity: 8
-output: runite_bar
-message: "You remove a white hot runite bar!"
-fail_message: "You fail to smelt a usable bar."
diff --git a/data/recipes/smelting/silver.yaml b/data/recipes/smelting/silver.yaml
deleted file mode 100644
index 05a76ef..0000000
--- a/data/recipes/smelting/silver.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: silver
-type: smelting
-skill: smithing
-level: 25
-xp: 14
-wait: 4
-station: [furnace]
-consume:
- - items: [silver_ore]
- quantity: 1
-output: silver_bar
-message: "You remove a white hot silver bar!"
-fail_message: "You fail to smelt a usable bar."
diff --git a/data/recipes/smelting/smelt_bronze.yaml b/data/recipes/smelting/smelt_bronze.yaml
deleted file mode 100644
index af28ca0..0000000
--- a/data/recipes/smelting/smelt_bronze.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-id: bronze
-type: smelting
-skill: smithing
-level: 1
-xp: 6
-wait: 4
-station: [furnace]
-consume:
- - items: [copper_ore]
- quantity: 1
- - items: [tin_ore]
- quantity: 1
-output: bronze_bar
-message: "You remove a white hot bronze bar!"
-fail_message: "You fail to smelt a usable bar."
diff --git a/data/recipes/smelting/smelt_iron.yaml b/data/recipes/smelting/smelt_iron.yaml
deleted file mode 100644
index fa210d6..0000000
--- a/data/recipes/smelting/smelt_iron.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-id: iron
-type: smelting
-skill: smithing
-level: 20
-xp: 12
-wait: 4
-station: [furnace]
-consume:
- - items: [iron_ore]
- quantity: 1
-output: iron_bar
-message: "You remove a white hot iron bar!"
-fail_message: "The iron is too impure to make a workable bar."
-success:
- base: 0.5
- per_level: 0
- cap: 0.5
diff --git a/data/recipes/smelting/smelt_mithril.yaml b/data/recipes/smelting/smelt_mithril.yaml
deleted file mode 100644
index 8e11e61..0000000
--- a/data/recipes/smelting/smelt_mithril.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-id: mithril
-type: smelting
-skill: smithing
-level: 50
-xp: 30
-wait: 4
-station: [furnace]
-consume:
- - items: [mithril_ore]
- quantity: 1
- - items: [coal]
- quantity: 4
-output: mithril_bar
-message: "You remove a white hot mithril bar!"
-fail_message: "You fail to smelt a usable bar."
diff --git a/data/recipes/smelting/smelt_steel.yaml b/data/recipes/smelting/smelt_steel.yaml
deleted file mode 100644
index 86ee366..0000000
--- a/data/recipes/smelting/smelt_steel.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-id: steel
-type: smelting
-skill: smithing
-level: 40
-xp: 17
-wait: 4
-station: [furnace]
-consume:
- - items: [iron_ore]
- quantity: 1
- - items: [coal]
- quantity: 2
-output: steel_bar
-message: "You remove a white hot steel bar!"
-fail_message: "You fail to smelt a usable bar."
diff --git a/data/recipes/smithing/adamant.yaml b/data/recipes/smithing/adamant.yaml
deleted file mode 100644
index bd9debe..0000000
--- a/data/recipes/smithing/adamant.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-type: smithing
-station: [anvil]
-wait: 4
-consume:
- - items: [adamantite_bar]
- quantity: 1
-products:
- - id: adamant_dagger
- output: adamant_dagger
- level: 70
- xp: 62
- - id: adamant_med_helm
- output: adamant_med_helm
- level: 73
- xp: 62
- - id: adamant_sword
- output: adamant_sword
- level: 74
- xp: 62
- - id: adamant_nails
- output: adamant_nails
- level: 74
- xp: 62
- output_qty: 15
- - id: adamant_arrowtips
- output: adamant_arrowtips
- level: 75
- xp: 62
- output_qty: 15
- - id: adamant_bolts_unf
- output: adamant_bolts_unf
- level: 76
- xp: 62
- output_qty: 10
- - id: adamant_full_helm
- output: adamant_full_helm
- level: 77
- xp: 125
- consume:
- - items: [adamantite_bar]
- quantity: 2
- - id: adamant_platebody
- output: adamant_platebody
- level: 88
- xp: 312
- consume:
- - items: [adamantite_bar]
- quantity: 5
diff --git a/data/recipes/smithing/bronze.yaml b/data/recipes/smithing/bronze.yaml
deleted file mode 100644
index da1b55f..0000000
--- a/data/recipes/smithing/bronze.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-type: smithing
-station: [anvil]
-wait: 4
-consume:
- - items: [bronze_bar]
- quantity: 1
-products:
- - id: bronze_dagger
- output: bronze_dagger
- level: 1
- xp: 12
- - id: bronze_med_helm
- output: bronze_med_helm
- level: 3
- xp: 12
- - id: bronze_sword
- output: bronze_sword
- level: 4
- xp: 12
- - id: bronze_nails
- output: bronze_nails
- level: 4
- xp: 12
- output_qty: 15
- - id: bronze_arrowtips
- output: bronze_arrowtips
- level: 5
- xp: 12
- output_qty: 15
- - id: bronze_bolts_unf
- output: bronze_bolts_unf
- level: 6
- xp: 12
- output_qty: 10
- - id: bronze_full_helm
- output: bronze_full_helm
- level: 7
- xp: 25
- consume:
- - items: [bronze_bar]
- quantity: 2
- - id: bronze_platebody
- output: bronze_platebody
- level: 18
- xp: 62
- consume:
- - items: [bronze_bar]
- quantity: 5
diff --git a/data/recipes/smithing/iron.yaml b/data/recipes/smithing/iron.yaml
deleted file mode 100644
index e39facd..0000000
--- a/data/recipes/smithing/iron.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-type: smithing
-station: [anvil]
-wait: 4
-consume:
- - items: [iron_bar]
- quantity: 1
-products:
- - id: iron_dagger
- output: iron_dagger
- level: 20
- xp: 25
- - id: iron_med_helm
- output: iron_med_helm
- level: 22
- xp: 25
- - id: iron_sword
- output: iron_sword
- level: 24
- xp: 25
- - id: iron_nails
- output: iron_nails
- level: 24
- xp: 25
- output_qty: 15
- - id: iron_arrowtips
- output: iron_arrowtips
- level: 25
- xp: 25
- output_qty: 15
- - id: iron_bolts_unf
- output: iron_bolts_unf
- level: 26
- xp: 25
- output_qty: 10
- - id: iron_full_helm
- output: iron_full_helm
- level: 27
- xp: 50
- consume:
- - items: [iron_bar]
- quantity: 2
- - id: iron_platebody
- output: iron_platebody
- level: 33
- xp: 125
- consume:
- - items: [iron_bar]
- quantity: 5
diff --git a/data/recipes/smithing/mithril.yaml b/data/recipes/smithing/mithril.yaml
deleted file mode 100644
index dfa4a6d..0000000
--- a/data/recipes/smithing/mithril.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-type: smithing
-station: [anvil]
-wait: 4
-consume:
- - items: [mithril_bar]
- quantity: 1
-products:
- - id: mithril_dagger
- output: mithril_dagger
- level: 50
- xp: 50
- - id: mithril_med_helm
- output: mithril_med_helm
- level: 53
- xp: 50
- - id: mithril_sword
- output: mithril_sword
- level: 54
- xp: 50
- - id: mithril_nails
- output: mithril_nails
- level: 54
- xp: 50
- output_qty: 15
- - id: mithril_arrowtips
- output: mithril_arrowtips
- level: 55
- xp: 50
- output_qty: 15
- - id: mithril_bolts_unf
- output: mithril_bolts_unf
- level: 56
- xp: 50
- output_qty: 10
- - id: mithril_full_helm
- output: mithril_full_helm
- level: 57
- xp: 100
- consume:
- - items: [mithril_bar]
- quantity: 2
- - id: mithril_platebody
- output: mithril_platebody
- level: 68
- xp: 250
- consume:
- - items: [mithril_bar]
- quantity: 5
diff --git a/data/recipes/smithing/rune.yaml b/data/recipes/smithing/rune.yaml
deleted file mode 100644
index fdffafc..0000000
--- a/data/recipes/smithing/rune.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-type: smithing
-station: [anvil]
-wait: 4
-consume:
- - items: [runite_bar]
- quantity: 1
-products:
- - id: rune_dagger
- output: rune_dagger
- level: 85
- xp: 75
- - id: rune_med_helm
- output: rune_med_helm
- level: 88
- xp: 75
- - id: rune_sword
- output: rune_sword
- level: 89
- xp: 75
- - id: rune_nails
- output: rune_nails
- level: 89
- xp: 75
- output_qty: 15
- - id: rune_arrowtips
- output: rune_arrowtips
- level: 90
- xp: 75
- output_qty: 15
- - id: rune_bolts_unf
- output: rune_bolts_unf
- level: 91
- xp: 75
- output_qty: 10
- - id: rune_full_helm
- output: rune_full_helm
- level: 92
- xp: 150
- consume:
- - items: [runite_bar]
- quantity: 2
- - id: rune_platebody
- output: rune_platebody
- level: 99
- xp: 375
- consume:
- - items: [runite_bar]
- quantity: 5
diff --git a/data/recipes/smithing/steel.yaml b/data/recipes/smithing/steel.yaml
deleted file mode 100644
index 204b427..0000000
--- a/data/recipes/smithing/steel.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-type: smithing
-station: [anvil]
-wait: 4
-consume:
- - items: [steel_bar]
- quantity: 1
-products:
- - id: steel_dagger
- output: steel_dagger
- level: 30
- xp: 37
- - id: steel_med_helm
- output: steel_med_helm
- level: 33
- xp: 37
- - id: steel_sword
- output: steel_sword
- level: 34
- xp: 37
- - id: steel_nails
- output: steel_nails
- level: 34
- xp: 37
- output_qty: 15
- - id: steel_arrowtips
- output: steel_arrowtips
- level: 35
- xp: 37
- output_qty: 15
- - id: steel_bolts_unf
- output: steel_bolts_unf
- level: 36
- xp: 37
- output_qty: 10
- - id: steel_full_helm
- output: steel_full_helm
- level: 37
- xp: 75
- consume:
- - items: [steel_bar]
- quantity: 2
- - id: steel_platebody
- output: steel_platebody
- level: 48
- xp: 187
- consume:
- - items: [steel_bar]
- quantity: 5
diff --git a/internal/action/recipe.go b/internal/action/recipe.go
deleted file mode 100644
index 8779d53..0000000
--- a/internal/action/recipe.go
+++ /dev/null
@@ -1,319 +0,0 @@
-package action
-
-import (
- "fmt"
- "os"
- "path/filepath"
-
- "gopkg.in/yaml.v3"
-)
-
-type ConsumeEntry struct {
- Items []string `yaml:"items"`
- Quantity int `yaml:"quantity"`
- Byproducts []string `yaml:"byproducts"`
-}
-
-type RecipeDef struct {
- ID string `yaml:"id"`
- Type string `yaml:"type"`
- Skill string `yaml:"skill"`
- Level int `yaml:"level"`
- XP int `yaml:"xp"`
- Station []string `yaml:"station"`
- Tool string `yaml:"tool"`
- Consume []ConsumeEntry `yaml:"consume"`
- Output string `yaml:"output"`
- OutputQty int `yaml:"output_qty"`
- Fail string `yaml:"fail"`
- Message string `yaml:"message"`
- FailMessage string `yaml:"fail_message"`
- Wait float64 `yaml:"wait"`
- Success *SuccessFormula `yaml:"success"`
-}
-
-type consolidatedRecipeDef struct {
- Type string `yaml:"type"`
- Skill string `yaml:"skill"`
- Station []string `yaml:"station"`
- Tool string `yaml:"tool"`
- Wait float64 `yaml:"wait"`
- Consume []ConsumeEntry `yaml:"consume"`
- Products []recipeProduct `yaml:"products"`
-}
-
-type recipeProduct struct {
- ID string `yaml:"id"`
- Output string `yaml:"output"`
- OutputQty int `yaml:"output_qty"`
- Level int `yaml:"level"`
- XP int `yaml:"xp"`
- Consume []ConsumeEntry `yaml:"consume"`
- Message string `yaml:"message"`
- FailMessage string `yaml:"fail_message"`
- Fail string `yaml:"fail"`
- Success *SuccessFormula `yaml:"success"`
-}
-
-type RecipeStore struct {
- dataDir string
- cache map[string][]RecipeDef
- byID map[string]*RecipeDef
-}
-
-func NewRecipeStore(dataDir string) *RecipeStore {
- return &RecipeStore{
- dataDir: dataDir,
- cache: make(map[string][]RecipeDef),
- byID: make(map[string]*RecipeDef),
- }
-}
-
-func (s *RecipeStore) GetByID(recipeID string) *RecipeDef {
- if r, ok := s.byID[recipeID]; ok {
- return r
- }
- s.ensureLoaded()
- if r, ok := s.byID[recipeID]; ok {
- return r
- }
- return nil
-}
-
-func (s *RecipeStore) ensureLoaded() {
- if _, ok := s.cache["_all"]; ok {
- return
- }
- all, err := s.loadAllRaw()
- if err != nil {
- return
- }
- s.cache["_all"] = all
- for i := range all {
- if all[i].ID != "" {
- s.byID[all[i].ID] = &all[i]
- }
- }
-}
-
-func (s *RecipeStore) LoadAll() ([]RecipeDef, error) {
- return s.LoadByType("")
-}
-
-func (s *RecipeStore) LoadByType(recipeType string) ([]RecipeDef, error) {
- if recipeType == "" {
- recipeType = "_all"
- }
- if cached, ok := s.cache[recipeType]; ok {
- result := make([]RecipeDef, len(cached))
- copy(result, cached)
- return result, nil
- }
-
- s.ensureLoaded()
- all := s.cache["_all"]
-
- if recipeType == "_all" {
- result := make([]RecipeDef, len(all))
- copy(result, all)
- return result, nil
- }
-
- var filtered []RecipeDef
- for _, r := range all {
- if r.Type == recipeType {
- filtered = append(filtered, r)
- }
- }
- s.cache[recipeType] = filtered
- result := make([]RecipeDef, len(filtered))
- copy(result, filtered)
- return result, nil
-}
-
-func (s *RecipeStore) loadAllRaw() ([]RecipeDef, error) {
- dir := filepath.Join(s.dataDir, "recipes")
- var recipes []RecipeDef
- WalkYAMLDir(dir, func(path, id string, data []byte) error {
- var r RecipeDef
- if err := yaml.Unmarshal(data, &r); err == nil && r.Type != "" {
- recipes = append(recipes, r)
- return nil
- }
-
- var cr consolidatedRecipeDef
- if err := yaml.Unmarshal(data, &cr); err != nil {
- fmt.Fprintf(os.Stderr, "recipe parse error %s: %v\n", path, err)
- return nil
- }
- if cr.Type == "" || len(cr.Products) == 0 {
- return nil
- }
-
- for _, p := range cr.Products {
- recipes = append(recipes, s.expandProduct(cr, p))
- }
- return nil
- })
- return recipes, nil
-}
-
-func (s *RecipeStore) expandProduct(cr consolidatedRecipeDef, p recipeProduct) RecipeDef {
- r := RecipeDef{
- ID: p.ID,
- Output: p.Output,
- OutputQty: p.OutputQty,
- Type: cr.Type,
- Skill: cr.Skill,
- Level: p.Level,
- XP: p.XP,
- Station: cr.Station,
- Tool: cr.Tool,
- Wait: cr.Wait,
- Consume: p.Consume,
- Message: p.Message,
- FailMessage: p.FailMessage,
- Fail: p.Fail,
- Success: p.Success,
- }
- if len(r.Consume) == 0 {
- r.Consume = cr.Consume
- }
- return r
-}
-
-func (r *RecipeDef) MatchesEntry(itemID string) bool {
- for _, e := range r.Consume {
- for _, id := range e.Items {
- if id == itemID {
- return true
- }
- }
- }
- return false
-}
-
-func (s *RecipeStore) FindByInput(recipeType string, itemID string) *RecipeDef {
- all, err := s.LoadByType(recipeType)
- if err != nil {
- return nil
- }
- for i := range all {
- for _, e := range all[i].Consume {
- for _, id := range e.Items {
- if id == itemID {
- return &all[i]
- }
- }
- }
- }
- return nil
-}
-
-
-func (s *RecipeStore) FindByStation(itemID string, stationIDs []string, recipeType string) ([]RecipeDef, error) {
- all, err := s.LoadByType(recipeType)
- if err != nil {
- return nil, err
- }
- var matches []RecipeDef
- for _, r := range all {
- if recipeType != "" && r.Type != recipeType {
- continue
- }
- if !r.MatchesEntry(itemID) {
- continue
- }
- if len(r.Station) == 0 {
- continue
- }
- for _, station := range stationIDs {
- for _, rs := range r.Station {
- if rs == station {
- matches = append(matches, r)
- goto next
- }
- }
- }
- next:
- }
- return matches, nil
-}
-
-func (r *RecipeDef) DisplayName() string {
- return r.Output
-}
-
-func (r *RecipeDef) EffectiveSkill() string {
- if r.Skill != "" {
- return r.Skill
- }
- return r.Type
-}
-
-
-
-func (r *RecipeDef) FirstItemName(load func(string) (string, bool)) string {
- for _, e := range r.Consume {
- for _, id := range e.Items {
- if name, ok := load(id); ok {
- return name
- }
- return id
- }
- }
- return ""
-}
-
-func (r *RecipeDef) ConsumeAll(hasItem func(string) bool, removeItem func(string, int) bool) bool {
- for _, e := range r.Consume {
- found := false
- for _, id := range e.Items {
- if hasItem(id) {
- removeItem(id, e.Quantity)
- found = true
- break
- }
- }
- if !found {
- return false
- }
-}
-return true
-}
-
-func (r *RecipeDef) HasAllItemsQty(countItem func(string) int) bool {
- for _, e := range r.Consume {
- found := false
- for _, id := range e.Items {
- qty := e.Quantity
- if qty <= 0 {
- qty = 1
- }
- if countItem(id) >= qty {
- found = true
- break
- }
- }
- if !found {
- return false
- }
- }
- return true
-}
-
-func (r *RecipeDef) HasAllItems(hasItem func(string) bool) bool {
- for _, e := range r.Consume {
- found := false
- for _, id := range e.Items {
- if hasItem(id) {
- found = true
- break
- }
- }
- if !found {
- return false
- }
- }
- return true
-}
diff --git a/internal/game/action_clean.go b/internal/game/action_clean.go
index 7330883..b85a3e0 100644
--- a/internal/game/action_clean.go
+++ b/internal/game/action_clean.go
@@ -19,37 +19,31 @@ func (g *Game) advanceClean(sess *net.Session, p *player.Player) {
return
}
- allRecipes, err := g.RecipeStore.LoadAll()
- if err != nil {
- g.cancelBgAction(p)
- return
- }
+ items := g.CraftIndex.ByType("clean")
- var recipe *cleanMatch
- for _, r := range allRecipes {
- if r.Type != "clean" {
- continue
- }
+ var match *cleanMatch
+ for _, item := range items {
+ c := item.FirstCraft()
if filter != "" {
- if len(r.Consume) == 0 || len(r.Consume[0].Items) == 0 {
+ if len(c.Consume) == 0 || len(c.Consume[0].Items) == 0 {
continue
}
- if !strings.Contains(r.Consume[0].Items[0], filter) &&
- !strings.Contains(r.Output, filter) {
+ if !strings.Contains(c.Consume[0].Items[0], filter) &&
+ !strings.Contains(item.ID, filter) {
continue
}
}
- if p.Level(player.Pharmacy) < r.Level {
+ if p.Level(player.Pharmacy) < c.Level {
continue
}
- if !r.HasAllItems(p.HasItem) {
+ if !craftHasAllItems(c, p.HasItem) {
continue
}
- recipe = &cleanMatch{r.Consume[0].Items[0], r.Output, r.XP, r.Message}
+ match = &cleanMatch{c.Consume[0].Items[0], item.ID, c.XP, c.Message}
break
}
- if recipe == nil {
+ if match == nil {
sess.WriteLine("\nYou've finished cleaning herbs.")
g.cancelBgAction(p)
return
@@ -57,47 +51,45 @@ func (g *Game) advanceClean(sess *net.Session, p *player.Player) {
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
- if slot != nil && slot.ItemID == recipe.consumeID {
- slot.ItemID = recipe.outputID
+ if slot != nil && slot.ItemID == match.consumeID {
+ slot.ItemID = match.outputID
break
}
}
- if recipe.xp > 0 {
- g.awardSkillXP(sess, p, player.Pharmacy, recipe.xp)
+ if match.xp > 0 {
+ g.awardSkillXP(sess, p, player.Pharmacy, match.xp)
}
g.AccountStore.SaveCharacter(p)
- msg := recipe.message
+ msg := match.message
if msg == "" {
- outDef, _ := g.ItemStore.Load(recipe.outputID)
- outputName := recipe.outputID
+ outDef, _ := g.ItemStore.Load(match.outputID)
+ outputName := match.outputID
if outDef != nil {
outputName = outDef.Name
}
msg = fmt.Sprintf("You clean a %s.", outputName)
}
- if p.OptionBool("xp_drops") && recipe.xp > 0 {
+ if p.OptionBool("xp_drops") && match.xp > 0 {
abbr := player.SkillAbbr[player.Pharmacy]
- msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp %s)", recipe.xp, abbr))
+ msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp %s)", match.xp, abbr))
}
sess.WriteLine(msg)
hasMore := false
- for _, r := range allRecipes {
- if r.Type != "clean" {
- continue
- }
+ for _, item := range items {
+ c := item.FirstCraft()
if filter != "" {
- if len(r.Consume) == 0 || len(r.Consume[0].Items) == 0 {
+ if len(c.Consume) == 0 || len(c.Consume[0].Items) == 0 {
continue
}
- if !strings.Contains(r.Consume[0].Items[0], filter) &&
- !strings.Contains(r.Output, filter) {
+ if !strings.Contains(c.Consume[0].Items[0], filter) &&
+ !strings.Contains(item.ID, filter) {
continue
}
}
- if p.Level(player.Pharmacy) >= r.Level && r.HasAllItems(p.HasItem) {
+ if p.Level(player.Pharmacy) >= c.Level && craftHasAllItems(c, p.HasItem) {
hasMore = true
break
}
diff --git a/internal/game/action_farm.go b/internal/game/action_farm.go
index 21604aa..679af83 100644
--- a/internal/game/action_farm.go
+++ b/internal/game/action_farm.go
@@ -6,8 +6,8 @@ import (
"sort"
"strings"
- "thehouseoficarus/internal/action"
"thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
"thehouseoficarus/internal/world"
)
@@ -484,7 +484,7 @@ func (g *Game) farmMatchPrefix(p *player.Player, input string) (prefix string, d
lower := strings.ToLower(input)
for _, f := range farms {
- if strings.Contains(lower, strings.ToLower(f.name)) || action.WordPrefixMatch(lower, f.name) {
+ if strings.Contains(lower, strings.ToLower(f.name)) || object.WordPrefixMatch(lower, f.name) {
return f.prefix, f.defID, f.index
}
}
diff --git a/internal/game/action_fletch.go b/internal/game/action_fletch.go
index 3d8c30f..52de86b 100644
--- a/internal/game/action_fletch.go
+++ b/internal/game/action_fletch.go
@@ -6,43 +6,45 @@ import (
"thehouseoficarus/internal/action"
"thehouseoficarus/internal/engine"
"thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
)
-func (g *Game) startFletchAction(sess *net.Session, p *player.Player, recipe *action.RecipeDef, count int) {
+func (g *Game) startFletchAction(sess *net.Session, p *player.Player, item *object.ItemDef, count int) {
p.EnsureFlags()
- p.Flags["last_fletch"] = recipe.ID
+ p.Flags["last_fletch"] = item.ID
g.AccountStore.SaveCharacter(p)
- if recipe.Wait <= 0 {
- g.doInstantFletch(sess, p, recipe, count)
+ if item.FirstCraft().Wait <= 0 {
+ g.doInstantFletch(sess, p, item, count)
return
}
- g.startTimedFletch(sess, p, recipe, count)
+ g.startTimedFletch(sess, p, item, count)
}
-func (g *Game) doInstantFletch(sess *net.Session, p *player.Player, recipe *action.RecipeDef, count int) {
- outDef, _ := g.ItemStore.Load(recipe.Output)
- outputQty := recipe.OutputQty
+func (g *Game) doInstantFletch(sess *net.Session, p *player.Player, item *object.ItemDef, count int) {
+ c := item.FirstCraft()
+ outputQty := c.OutputQty
if outputQty <= 0 {
outputQty = 1
}
batches := 0
totalOutput := 0
- skill := player.SkillName(recipe.EffectiveSkill())
+ skill := player.SkillName(c.EffectiveSkill())
+ tmpItem := &object.ItemDef{Craft: object.CraftList{*c}}
for {
- if !g.canDoFletchRecipe(p, recipe) {
+ if !g.canDoFletchItem(p, tmpItem) {
break
}
placed := false
- if outDef != nil && outDef.Stackable {
+ if item.Stackable {
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
- if slot != nil && slot.ItemID == recipe.Output {
- recipe.ConsumeAll(p.HasItem, p.RemoveItem)
+ if slot != nil && slot.ItemID == item.ID {
+ craftConsumeAll(c, p.HasItem, p.RemoveItem)
slot.Quantity += outputQty
placed = true
break
@@ -50,16 +52,16 @@ func (g *Game) doInstantFletch(sess *net.Session, p *player.Player, recipe *acti
}
}
if !placed {
- recipe.ConsumeAll(p.HasItem, p.RemoveItem)
+ craftConsumeAll(c, p.HasItem, p.RemoveItem)
freeSlot := p.FirstFreeSlot()
if freeSlot == -1 {
break
}
- p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: recipe.Output, Quantity: outputQty})
+ p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: item.ID, Quantity: outputQty})
}
- if recipe.XP > 0 {
- g.awardSkillXP(sess, p, skill, recipe.XP)
+ if c.XP > 0 {
+ g.awardSkillXP(sess, p, skill, c.XP)
}
batches++
@@ -80,57 +82,47 @@ func (g *Game) doInstantFletch(sess *net.Session, p *player.Player, recipe *acti
g.AccountStore.SaveCharacter(p)
- outputName := recipe.Output
- if outDef != nil {
- outputName = outDef.Name
- }
-
- msg := fmt.Sprintf("You fletch %d %s.", totalOutput, outputName)
- if p.OptionBool("xp_drops") && recipe.XP > 0 {
- totalXP := recipe.XP * batches
+ msg := fmt.Sprintf("You fletch %d %s.", totalOutput, item.Name)
+ if p.OptionBool("xp_drops") && c.XP > 0 {
+ totalXP := c.XP * batches
abbr := player.SkillAbbr[skill]
msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp %s)", totalXP, abbr))
}
sess.WriteLine(msg)
}
-func (g *Game) startTimedFletch(sess *net.Session, p *player.Player, recipe *action.RecipeDef, count int) {
- outDef, _ := g.ItemStore.Load(recipe.Output)
- outputName := recipe.Output
- if outDef != nil {
- outputName = outDef.Name
- }
-
+func (g *Game) startTimedFletch(sess *net.Session, p *player.Player, item *object.ItemDef, count int) {
p.BackgroundAction = &action.Action{
Type: "fletch",
- TargetID: recipe.ID,
- TargetName: outputName,
+ TargetID: item.ID,
+ TargetName: item.Name,
Data: map[string]any{
- "recipe_id": recipe.ID,
+ "item_id": item.ID,
"phase": 0,
- "wait": recipe.Wait,
+ "wait": item.FirstCraft().Wait,
"remaining": count,
},
WaitLeft: engine.ToTicks(1),
}
- p.BackgroundActionState = &ActionState{Type: ActionFletching, TargetName: outputName}
+ p.BackgroundActionState = &ActionState{Type: ActionFletching, TargetName: item.Name}
g.broadcastAction(sess, "\n%s starts fletching.", p.Name)
- sess.WriteLine(fmt.Sprintf("\nYou begin fletching %s.", outputName))
+ sess.WriteLine(fmt.Sprintf("\nYou begin fletching %s.", item.Name))
}
func (g *Game) advanceFletch(sess *net.Session, p *player.Player) {
- recipeID, _ := p.BackgroundAction.Data["recipe_id"].(string)
+ itemID, _ := p.BackgroundAction.Data["item_id"].(string)
phase, _ := p.BackgroundAction.Data["phase"].(int)
wait, _ := p.BackgroundAction.Data["wait"].(float64)
remaining, _ := p.BackgroundAction.Data["remaining"].(int)
- recipe := g.loadRecipe(recipeID)
- if recipe == nil {
+ item := g.loadCraftItem(itemID)
+ if item == nil {
g.cancelBgAction(p)
return
}
+ c := item.FirstCraft()
if phase == 0 {
p.BackgroundAction.Data["phase"] = 1
@@ -138,26 +130,26 @@ func (g *Game) advanceFletch(sess *net.Session, p *player.Player) {
return
}
- if !g.canDoFletchRecipe(p, recipe) {
+ tmpItem := &object.ItemDef{Craft: object.CraftList{*c}}
+ if !g.canDoFletchItem(p, tmpItem) {
sess.WriteLine("\nYou've run out of fletching materials.")
g.cancelBgAction(p)
return
}
- outDef, _ := g.ItemStore.Load(recipe.Output)
- outputQty := recipe.OutputQty
+ outputQty := c.OutputQty
if outputQty <= 0 {
outputQty = 1
}
- skill := player.SkillName(recipe.EffectiveSkill())
+ skill := player.SkillName(c.EffectiveSkill())
placed := false
- if outDef != nil && outDef.Stackable {
+ if item.Stackable {
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
- if slot != nil && slot.ItemID == recipe.Output {
- recipe.ConsumeAll(p.HasItem, p.RemoveItem)
+ if slot != nil && slot.ItemID == item.ID {
+ craftConsumeAll(c, p.HasItem, p.RemoveItem)
slot.Quantity += outputQty
placed = true
break
@@ -165,32 +157,28 @@ func (g *Game) advanceFletch(sess *net.Session, p *player.Player) {
}
}
if !placed {
- recipe.ConsumeAll(p.HasItem, p.RemoveItem)
+ craftConsumeAll(c, p.HasItem, p.RemoveItem)
freeSlot := p.FirstFreeSlot()
if freeSlot == -1 {
sess.WriteLine("\nYour inventory is too full!")
g.cancelBgAction(p)
return
}
- p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: recipe.Output, Quantity: outputQty})
+ p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: item.ID, Quantity: outputQty})
}
- if recipe.XP > 0 {
- g.awardSkillXP(sess, p, skill, recipe.XP)
+ if c.XP > 0 {
+ g.awardSkillXP(sess, p, skill, c.XP)
}
g.AccountStore.SaveCharacter(p)
- outputName := recipe.Output
- if outDef != nil {
- outputName = outDef.Name
- }
- msg := recipe.Message
+ msg := c.Message
if msg == "" {
- msg = fmt.Sprintf("You fletch %s.", outputName)
+ msg = fmt.Sprintf("You fletch %s.", item.Name)
}
- if p.OptionBool("xp_drops") && recipe.XP > 0 {
+ if p.OptionBool("xp_drops") && c.XP > 0 {
abbr := player.SkillAbbr[skill]
- msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp %s)", recipe.XP, abbr))
+ msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp %s)", c.XP, abbr))
}
sess.WriteLine(msg)
@@ -204,7 +192,7 @@ func (g *Game) advanceFletch(sess *net.Session, p *player.Player) {
}
}
- if !g.canContinueProduction(p, recipe) {
+ if !g.canContinueProduction(p, item) {
sess.WriteLine("\nYou've run out of fletching materials.")
g.cancelBgAction(p)
return
diff --git a/internal/game/action_steal.go b/internal/game/action_steal.go
index a95be25..259c914 100644
--- a/internal/game/action_steal.go
+++ b/internal/game/action_steal.go
@@ -169,7 +169,7 @@ func (g *Game) resolveStealTarget(sess *net.Session, p *player.Player, input str
var matchingObjs []*object.ObjectDef
for _, o := range stealObjs {
- if action.WordPrefixMatch(searchName, o.Name) {
+ if object.WordPrefixMatch(searchName, o.Name) {
matchingObjs = append(matchingObjs, o)
}
}
diff --git a/internal/game/cmd_bank.go b/internal/game/cmd_bank.go
index d91e115..5f9067d 100644
--- a/internal/game/cmd_bank.go
+++ b/internal/game/cmd_bank.go
@@ -5,8 +5,8 @@ import (
"sort"
"strings"
- "thehouseoficarus/internal/action"
"thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
)
@@ -239,7 +239,7 @@ func (g *Game) doBankWithdraw(sess *net.Session, input string) {
}
lower := strings.ToLower(input)
for _, e := range entries {
- if action.WordPrefixMatch(e.name, lower) {
+ if object.WordPrefixMatch(e.name, lower) {
matches = append(matches, e)
}
}
diff --git a/internal/game/cmd_clean.go b/internal/game/cmd_clean.go
index 951b41d..28758b8 100644
--- a/internal/game/cmd_clean.go
+++ b/internal/game/cmd_clean.go
@@ -7,6 +7,7 @@ import (
"thehouseoficarus/internal/combat"
"thehouseoficarus/internal/engine"
"thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
)
@@ -22,26 +23,19 @@ func (g *Game) doClean(sess *net.Session, input string) {
return
}
- allRecipes, err := g.RecipeStore.LoadAll()
- if err != nil {
- sess.WriteLine("Error loading recipes.")
- return
- }
+ items := g.CraftIndex.ByType("clean")
filter := strings.TrimSpace(input)
found := false
- for _, r := range allRecipes {
- if r.Type != "clean" {
- continue
- }
- if filter != "" && !matchesCleanFilter(r, filter) {
+ for _, item := range items {
+ if filter != "" && !matchesCleanFilter(item, filter) {
continue
}
- if p.Level(player.Pharmacy) < r.Level {
+ if p.Level(player.Pharmacy) < item.FirstCraft().Level {
continue
}
- if !r.HasAllItems(p.HasItem) {
+ if !craftHasAllItems(item.FirstCraft(), p.HasItem) {
continue
}
found = true
@@ -49,7 +43,21 @@ func (g *Game) doClean(sess *net.Session, input string) {
}
if !found {
- sess.WriteLine("You don't have any herbs to clean.")
+ hasHerbs := false
+ for _, item := range items {
+ if filter != "" && !matchesCleanFilter(item, filter) {
+ continue
+ }
+ if craftHasAllItems(item.FirstCraft(), p.HasItem) {
+ hasHerbs = true
+ break
+ }
+ }
+ if hasHerbs {
+ sess.WriteLine("You don't have the Pharmacy level to clean any of your herbs.")
+ } else {
+ sess.WriteLine("You don't have any herbs to clean.")
+ }
return
}
@@ -71,13 +79,13 @@ func (g *Game) doClean(sess *net.Session, input string) {
sess.WriteLine("\nYou begin cleaning herbs.")
}
-func matchesCleanFilter(r action.RecipeDef, filter string) bool {
- if len(r.Consume) > 0 && len(r.Consume[0].Items) > 0 {
- if strings.Contains(r.Consume[0].Items[0], filter) {
+func matchesCleanFilter(item *object.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) {
return true
}
}
- if strings.Contains(r.Output, filter) {
+ if strings.Contains(item.ID, filter) {
return true
}
return false
diff --git a/internal/game/cmd_cook.go b/internal/game/cmd_cook.go
index 04c98bb..6dfbcce 100644
--- a/internal/game/cmd_cook.go
+++ b/internal/game/cmd_cook.go
@@ -4,8 +4,8 @@ import (
"fmt"
"strings"
- "thehouseoficarus/internal/action"
"thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
)
@@ -23,14 +23,10 @@ func (g *Game) doCook(sess *net.Session, input string) {
return
}
- allRecipes, err := g.RecipeStore.LoadAll()
- if err != nil {
- sess.WriteLine("Error loading recipes.")
- return
- }
+ items := g.CraftIndex.ByType("cooking")
if input == "" {
- g.showCookMenu(sess, p, allRecipes, stationDefID, stationName)
+ g.showCookMenu(sess, p, items, stationDefID, stationName)
return
}
@@ -51,13 +47,11 @@ func (g *Game) doCook(sess *net.Session, input string) {
itemID := matches[0].ID
- var recipes []action.RecipeDef
- for _, r := range allRecipes {
- if r.Type != "cooking" {
- continue
- }
- if stationMatch(stationDefID, r.Station) && r.MatchesEntry(itemID) && r.HasAllItems(p.HasItem) {
- recipes = append(recipes, r)
+ var recipes []*object.ItemDef
+ for _, item := range items {
+ c := item.FirstCraft()
+ if stationMatch(stationDefID, c.Station) && craftMatchesEntry(c, itemID) && craftHasAllItems(c, p.HasItem) {
+ recipes = append(recipes, item)
}
}
@@ -65,25 +59,23 @@ func (g *Game) doCook(sess *net.Session, input string) {
g.showRecipeChoice(sess, p, recipes, "You can't cook that.", title, "")
}
-func (g *Game) showCookMenu(sess *net.Session, p *player.Player, allRecipes []action.RecipeDef, stationDefID, stationName string) {
+func (g *Game) showCookMenu(sess *net.Session, p *player.Player, items []*object.ItemDef, stationDefID, stationName string) {
seen := make(map[string]bool)
- var recipes []action.RecipeDef
+ var recipes []*object.ItemDef
- for _, r := range allRecipes {
- if r.Type != "cooking" {
- continue
- }
- if !stationMatch(stationDefID, r.Station) {
+ for _, item := range items {
+ c := item.FirstCraft()
+ if !stationMatch(stationDefID, c.Station) {
continue
}
- if seen[r.ID] {
+ if seen[item.ID] {
continue
}
- if !r.HasAllItems(p.HasItem) {
+ if !craftHasAllItems(c, p.HasItem) {
continue
}
- seen[r.ID] = true
- recipes = append(recipes, r)
+ seen[item.ID] = true
+ recipes = append(recipes, item)
}
title := fmt.Sprintf("What would you like to cook on the %s?", stationName)
diff --git a/internal/game/cmd_craft.go b/internal/game/cmd_craft.go
index d061a9c..3290daa 100644
--- a/internal/game/cmd_craft.go
+++ b/internal/game/cmd_craft.go
@@ -3,8 +3,8 @@ package game
import (
"strings"
- "thehouseoficarus/internal/action"
"thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
)
@@ -28,35 +28,27 @@ func (g *Game) doStationSkill(sess *net.Session, input string, recipeType string
p := sess.Player
g.cancelAction(p)
- allRecipes, err := g.RecipeStore.LoadAll()
- if err != nil {
- sess.WriteLine("Error loading recipes.")
- return
- }
+ items := g.CraftIndex.ByType(recipeType)
- var recipes []action.RecipeDef
- for _, r := range allRecipes {
- if r.Type != recipeType {
- continue
- }
- if !g.recipeVisible(p, &r) {
+ var filtered []*object.ItemDef
+ for _, item := range items {
+ if !g.itemVisible(p, item) {
continue
}
- recipes = append(recipes, r)
+ filtered = append(filtered, item)
}
if input != "" {
- g.doWithInput(sess, p, recipes, input, skill, flagKey, label, verbCap)
+ g.doWithInput(sess, p, filtered, input, skill, flagKey, label, verbCap)
return
}
- lastRecipeID, _ := p.Flags[flagKey].(string)
- if lastRecipeID != "" {
- for i := range recipes {
- if recipes[i].ID == lastRecipeID {
- r := &recipes[i]
- if g.canDoRecipe(p, r, skill) {
- g.startRecipe(sess, p, r, 0, flagKey)
+ lastItemID, _ := p.Flags[flagKey].(string)
+ if lastItemID != "" {
+ for _, item := range filtered {
+ if item.ID == lastItemID {
+ if g.canDoItem(p, item, skill) {
+ g.startItem(sess, p, item, 0, flagKey)
return
}
break
@@ -64,34 +56,33 @@ func (g *Game) doStationSkill(sess *net.Session, input string, recipeType string
}
}
- available := g.availableRecipes(p, recipes)
+ available := g.availableItems(p, filtered)
if len(available) == 0 {
+ if recipeType == "pharmacy" && g.hasGrimyHerbs(p) {
+ sess.WriteLine("Mix what? Grimy herbs? Clean those first!")
+ return
+ }
sess.WriteLine("You don't have any materials to " + verbPast + ".")
return
}
optionKey := verbPast + "_all"
if p.OptionBool(optionKey) && len(available) == 1 {
- g.startRecipe(sess, p, &available[0], 0, flagKey)
+ g.startItem(sess, p, available[0], 0, flagKey)
return
}
g.showProductionTable(sess, p, available, label, recipeType, flagKey, verbCap, false)
}
-func (g *Game) doWithInput(sess *net.Session, p *player.Player, recipes []action.RecipeDef, input string, skill player.SkillName, flagKey, label, verbCap string) {
+func (g *Game) doWithInput(sess *net.Session, p *player.Player, items []*object.ItemDef, input string, skill player.SkillName, flagKey, label, verbCap string) {
qty, productName := parseQty(input)
productName = strings.TrimSpace(productName)
- var matched []action.RecipeDef
- for _, r := range recipes {
- outDef, _ := g.ItemStore.Load(r.Output)
- name := r.Output
- if outDef != nil {
- name = outDef.Name
- }
- if action.WordPrefixMatch(productName, name) {
- matched = append(matched, r)
+ var matched []*object.ItemDef
+ for _, item := range items {
+ if object.WordPrefixMatch(productName, item.Name) {
+ matched = append(matched, item)
}
}
@@ -100,10 +91,10 @@ func (g *Game) doWithInput(sess *net.Session, p *player.Player, recipes []action
return
}
- var available []action.RecipeDef
- for _, r := range matched {
- if g.canDoRecipe(p, &r, skill) {
- available = append(available, r)
+ var available []*object.ItemDef
+ for _, item := range matched {
+ if g.canDoItem(p, item, skill) {
+ available = append(available, item)
}
}
@@ -113,68 +104,85 @@ func (g *Game) doWithInput(sess *net.Session, p *player.Player, recipes []action
}
if len(available) == 1 {
- g.startRecipe(sess, p, &available[0], qty, flagKey)
+ g.startItem(sess, p, available[0], qty, flagKey)
return
}
g.showProductionTable(sess, p, available, label, label, flagKey, verbCap, false)
}
-func (g *Game) recipeVisible(p *player.Player, r *action.RecipeDef) bool {
- if len(r.Station) > 0 {
- stID, _ := g.findStation(p.RoomID, r.Station)
- if stID == "" {
+func (g *Game) itemVisible(p *player.Player, item *object.ItemDef) bool {
+ return item.FindCraft(func(c object.CraftDef) bool {
+ if len(c.Station) > 0 {
+ stID, _ := g.findStation(p.RoomID, c.Station)
+ if stID == "" {
+ return false
+ }
+ }
+ if c.Tool != "" && !g.hasToolType(p, c.Tool) {
return false
}
- }
- if r.Tool != "" && !g.hasToolType(p, r.Tool) {
- return false
- }
- return true
+ return true
+ }) != nil
}
-func (g *Game) canDoRecipe(p *player.Player, r *action.RecipeDef, skill player.SkillName) bool {
- if p.Level(skill) < r.Level {
- return false
- }
- if !r.HasAllItemsQty(p.CountItem) {
- return false
- }
- if len(r.Station) > 0 {
- stID, _ := g.findStation(p.RoomID, r.Station)
- if stID == "" {
+func (g *Game) canDoItem(p *player.Player, item *object.ItemDef, skill player.SkillName) bool {
+ m := item.FindCraft(func(c object.CraftDef) bool {
+ if p.Level(skill) < c.Level {
return false
}
- }
- if r.Tool != "" && !g.hasToolType(p, r.Tool) {
- return false
- }
- return true
+ if !craftHasAllItemsQty(&c, p.CountItem) {
+ return false
+ }
+ if len(c.Station) > 0 {
+ stID, _ := g.findStation(p.RoomID, c.Station)
+ if stID == "" {
+ return false
+ }
+ }
+ if c.Tool != "" && !g.hasToolType(p, c.Tool) {
+ return false
+ }
+ return true
+ })
+ return m != nil
}
-func (g *Game) availableRecipes(p *player.Player, allRecipes []action.RecipeDef) []action.RecipeDef {
- var result []action.RecipeDef
- for _, r := range allRecipes {
- if r.HasAllItemsQty(p.CountItem) {
- result = append(result, r)
+func (g *Game) availableItems(p *player.Player, allItems []*object.ItemDef) []*object.ItemDef {
+ var result []*object.ItemDef
+ for _, item := range allItems {
+ m := item.FindCraft(func(c object.CraftDef) bool {
+ return craftHasAllItemsQty(&c, p.CountItem)
+ })
+ if m != nil {
+ result = append(result, item)
}
}
return result
}
-func (g *Game) startRecipe(sess *net.Session, p *player.Player, recipe *action.RecipeDef, count int, flagKey string) {
+func (g *Game) startItem(sess *net.Session, p *player.Player, item *object.ItemDef, count int, flagKey string) {
p.EnsureFlags()
- p.Flags[flagKey] = recipe.ID
+ p.Flags[flagKey] = item.ID
g.AccountStore.SaveCharacter(p)
- g.startProductionFromRecipe(sess, p, recipe, count)
+ g.startProductionFromItem(sess, p, item, count)
}
-func (g *Game) findCraftRecipes(p *player.Player, itemAID, itemBID string) []action.RecipeDef {
- allRecipes, err := g.RecipeStore.LoadAll()
- if err != nil {
- return nil
+func (g *Game) hasGrimyHerbs(p *player.Player) bool {
+ items := g.CraftIndex.ByType("clean")
+ for _, item := range items {
+ if item.FindCraft(func(c object.CraftDef) bool {
+ return craftHasAllItems(&c, p.HasItem)
+ }) != nil {
+ return true
+ }
}
+ return false
+}
+
+func (g *Game) findCraftItems(p *player.Player, itemAID, itemBID string) []*object.ItemDef {
+ items := g.CraftIndex.ByType("crafting")
toolTypeA := ""
toolTypeB := ""
@@ -185,19 +193,22 @@ func (g *Game) findCraftRecipes(p *player.Player, itemAID, itemBID string) []act
toolTypeB = defB.ToolType
}
- var matched []action.RecipeDef
- for _, r := range allRecipes {
- if r.Type != "crafting" {
- continue
- }
- if r.Tool == "" {
- continue
- }
-
- if r.Tool == toolTypeA && r.MatchesEntry(itemBID) {
- matched = append(matched, r)
- } else if r.Tool == toolTypeB && r.MatchesEntry(itemAID) {
- matched = append(matched, r)
+ var matched []*object.ItemDef
+ for _, item := range items {
+ match := item.FindCraft(func(c object.CraftDef) bool {
+ if c.Tool == "" {
+ return false
+ }
+ if c.Tool == toolTypeA && craftMatchesEntry(&c, itemBID) {
+ return true
+ }
+ if c.Tool == toolTypeB && craftMatchesEntry(&c, itemAID) {
+ return true
+ }
+ return false
+ })
+ if match != nil {
+ matched = append(matched, item)
}
}
return matched
diff --git a/internal/game/cmd_fletch.go b/internal/game/cmd_fletch.go
index 5a4bc19..088ce63 100644
--- a/internal/game/cmd_fletch.go
+++ b/internal/game/cmd_fletch.go
@@ -3,9 +3,9 @@ package game
import (
"strings"
- "thehouseoficarus/internal/action"
"thehouseoficarus/internal/combat"
"thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
)
@@ -23,34 +23,38 @@ var fletchGemItems = map[string]bool{
"uncut_ruby": true, "uncut_diamond": true,
}
-func fletchNeedsKnife(r *action.RecipeDef) bool {
- if r.Tool == "knife" {
- return true
- }
- for _, e := range r.Consume {
- for _, id := range e.Items {
- if fletchLogItems[id] {
- if strings.Contains(r.Output, "shaft") || strings.Contains(r.Output, "bow") {
- return true
+func fletchNeedsKnife(item *object.ItemDef) bool {
+ return item.FindCraft(func(c object.CraftDef) bool {
+ if c.Tool == "knife" {
+ return true
+ }
+ for _, e := range c.Consume {
+ for _, id := range e.Items {
+ if fletchLogItems[id] {
+ if strings.Contains(item.ID, "shaft") || strings.Contains(item.ID, "bow") {
+ return true
+ }
}
}
}
- }
- return false
+ return false
+ }) != nil
}
-func fletchNeedsChisel(r *action.RecipeDef) bool {
- if r.Tool == "chisel" {
- return true
- }
- for _, e := range r.Consume {
- for _, id := range e.Items {
- if fletchGemItems[id] {
- return true
+func fletchNeedsChisel(item *object.ItemDef) bool {
+ return item.FindCraft(func(c object.CraftDef) bool {
+ if c.Tool == "chisel" {
+ return true
+ }
+ for _, e := range c.Consume {
+ for _, id := range e.Items {
+ if fletchGemItems[id] {
+ return true
+ }
}
}
- }
- return false
+ return false
+ }) != nil
}
func (g *Game) doFletch(sess *net.Session, input string) {
@@ -66,32 +70,19 @@ func (g *Game) doFletch(sess *net.Session, input string) {
p.ActionState = nil
}
- allRecipes, err := g.RecipeStore.LoadAll()
- if err != nil {
- sess.WriteLine("Error loading recipes.")
- return
- }
-
- var fletchRecipes []action.RecipeDef
- for _, r := range allRecipes {
- if r.Type != "fletching" {
- continue
- }
- fletchRecipes = append(fletchRecipes, r)
- }
+ items := g.CraftIndex.ByType("fletching")
if input != "" {
- g.doFletchWithInput(sess, p, fletchRecipes, input)
+ g.doFletchWithInput(sess, p, items, input)
return
}
- lastRecipeID, _ := p.Flags["last_fletch"].(string)
- if lastRecipeID != "" {
- for i := range fletchRecipes {
- if fletchRecipes[i].ID == lastRecipeID {
- r := &fletchRecipes[i]
- if g.canDoFletchRecipe(p, r) {
- g.startFletchAction(sess, p, r, 0)
+ lastItemID, _ := p.Flags["last_fletch"].(string)
+ if lastItemID != "" {
+ for _, item := range items {
+ if item.ID == lastItemID {
+ if g.canDoFletchItem(p, item) {
+ g.startFletchAction(sess, p, item, 0)
return
}
break
@@ -99,33 +90,28 @@ func (g *Game) doFletch(sess *net.Session, input string) {
}
}
- available := g.availableFletchRecipes(p, fletchRecipes)
+ available := g.availableFletchItems(p, items)
if len(available) == 0 {
sess.WriteLine("You don't have any materials to fletch.")
return
}
if p.OptionBool("fletch_all") && len(available) == 1 {
- g.startFletchAction(sess, p, &available[0], 0)
+ g.startFletchAction(sess, p, available[0], 0)
return
}
g.showProductionTable(sess, p, available, "Fletching", "fletching", "last_fletch", "Fletch", true)
}
-func (g *Game) doFletchWithInput(sess *net.Session, p *player.Player, fletchRecipes []action.RecipeDef, input string) {
+func (g *Game) doFletchWithInput(sess *net.Session, p *player.Player, items []*object.ItemDef, input string) {
qty, productName := parseQty(input)
productName = strings.TrimSpace(productName)
- var matched []action.RecipeDef
- for _, r := range fletchRecipes {
- outDef, _ := g.ItemStore.Load(r.Output)
- name := r.Output
- if outDef != nil {
- name = outDef.Name
- }
- if action.WordPrefixMatch(productName, name) {
- matched = append(matched, r)
+ var matched []*object.ItemDef
+ for _, item := range items {
+ if object.WordPrefixMatch(productName, item.Name) {
+ matched = append(matched, item)
}
}
@@ -134,10 +120,10 @@ func (g *Game) doFletchWithInput(sess *net.Session, p *player.Player, fletchReci
return
}
- var available []action.RecipeDef
- for _, r := range matched {
- if g.canDoFletchRecipe(p, &r) {
- available = append(available, r)
+ var available []*object.ItemDef
+ for _, item := range matched {
+ if g.canDoFletchItem(p, item) {
+ available = append(available, item)
}
}
@@ -147,48 +133,49 @@ func (g *Game) doFletchWithInput(sess *net.Session, p *player.Player, fletchReci
}
if len(available) == 1 {
- count := qty
- g.startFletchAction(sess, p, &available[0], count)
+ g.startFletchAction(sess, p, available[0], qty)
return
}
g.showProductionTable(sess, p, available, "Fletching", "fletching", "last_fletch", "Fletch", true)
}
-func (g *Game) hasFletchTools(p *player.Player, r *action.RecipeDef) bool {
- if r.Tool != "" {
- return g.hasToolType(p, r.Tool)
+func (g *Game) hasFletchTools(p *player.Player, item *object.ItemDef) bool {
+ c := item.FirstCraft()
+ if c.Tool != "" {
+ return g.hasToolType(p, c.Tool)
}
- if fletchNeedsKnife(r) && !g.hasToolType(p, "knife") {
+ if fletchNeedsKnife(item) && !g.hasToolType(p, "knife") {
return false
}
- if fletchNeedsChisel(r) && !g.hasToolType(p, "chisel") {
+ if fletchNeedsChisel(item) && !g.hasToolType(p, "chisel") {
return false
}
return true
}
-func (g *Game) canDoFletchRecipe(p *player.Player, r *action.RecipeDef) bool {
- return p.Level(player.Fletching) >= r.Level &&
- r.HasAllItemsQty(p.CountItem) &&
- g.hasFletchTools(p, r)
+func (g *Game) canDoFletchItem(p *player.Player, item *object.ItemDef) bool {
+ return item.FindCraft(func(c object.CraftDef) bool {
+ return p.Level(player.Fletching) >= c.Level &&
+ craftHasAllItemsQty(&c, p.CountItem)
+ }) != nil && g.hasFletchTools(p, item)
}
-func (g *Game) availableFletchRecipes(p *player.Player, allFletch []action.RecipeDef) []action.RecipeDef {
- var result []action.RecipeDef
- for _, r := range allFletch {
- if r.HasAllItemsQty(p.CountItem) && g.hasFletchTools(p, &r) {
- result = append(result, r)
+func (g *Game) availableFletchItems(p *player.Player, allItems []*object.ItemDef) []*object.ItemDef {
+ var result []*object.ItemDef
+ for _, item := range allItems {
+ match := item.FindCraft(func(c object.CraftDef) bool {
+ return craftHasAllItemsQty(&c, p.CountItem)
+ })
+ if match != nil && g.hasFletchTools(p, item) {
+ result = append(result, item)
}
}
return result
}
-func (g *Game) findFletchRecipes(p *player.Player, itemAID, itemBID string) []action.RecipeDef {
- allRecipes, err := g.RecipeStore.LoadAll()
- if err != nil {
- return nil
- }
+func (g *Game) findFletchItems(p *player.Player, itemAID, itemBID string) []*object.ItemDef {
+ items := g.CraftIndex.ByType("fletching")
toolTypeA := ""
toolTypeB := ""
@@ -201,26 +188,24 @@ func (g *Game) findFletchRecipes(p *player.Player, itemAID, itemBID string) []ac
isToolA := toolTypeA == "knife" || toolTypeA == "chisel"
isToolB := toolTypeB == "knife" || toolTypeB == "chisel"
- var matched []action.RecipeDef
- for _, r := range allRecipes {
- if r.Type != "fletching" {
- continue
- }
+ var matched []*object.ItemDef
+ for _, item := range items {
+ c := item.FirstCraft()
- if r.Tool != "" {
- if r.Tool == toolTypeA && r.MatchesEntry(itemBID) {
- matched = append(matched, r)
+ if c.Tool != "" {
+ if c.Tool == toolTypeA && craftMatchesEntry(c, itemBID) {
+ matched = append(matched, item)
continue
}
- if r.Tool == toolTypeB && r.MatchesEntry(itemAID) {
- matched = append(matched, r)
+ if c.Tool == toolTypeB && craftMatchesEntry(c, itemAID) {
+ matched = append(matched, item)
continue
}
}
aMatch := false
bMatch := false
- for _, e := range r.Consume {
+ for _, e := range c.Consume {
for _, id := range e.Items {
if id == itemAID {
aMatch = true
@@ -232,21 +217,21 @@ func (g *Game) findFletchRecipes(p *player.Player, itemAID, itemBID string) []ac
}
if aMatch && bMatch {
- matched = append(matched, r)
+ matched = append(matched, item)
continue
}
if isToolA && bMatch {
- if (fletchNeedsKnife(&r) && toolTypeA == "knife") ||
- (fletchNeedsChisel(&r) && toolTypeA == "chisel") {
- matched = append(matched, r)
+ if (fletchNeedsKnife(item) && toolTypeA == "knife") ||
+ (fletchNeedsChisel(item) && toolTypeA == "chisel") {
+ matched = append(matched, item)
continue
}
}
if isToolB && aMatch {
- if (fletchNeedsKnife(&r) && toolTypeB == "knife") ||
- (fletchNeedsChisel(&r) && toolTypeB == "chisel") {
- matched = append(matched, r)
+ if (fletchNeedsKnife(item) && toolTypeB == "knife") ||
+ (fletchNeedsChisel(item) && toolTypeB == "chisel") {
+ matched = append(matched, item)
continue
}
}
diff --git a/internal/game/cmd_shop.go b/internal/game/cmd_shop.go
index 2742151..c6b7831 100644
--- a/internal/game/cmd_shop.go
+++ b/internal/game/cmd_shop.go
@@ -158,7 +158,7 @@ func (g *Game) findShopMatches(input string, cfg *action.ShopConfig) []shopMatch
if def != nil {
itemName = def.Name
}
- if action.WordPrefixMatch(input, itemName) {
+ if object.WordPrefixMatch(input, itemName) {
matches = append(matches, shopMatch{si: si, name: itemName, def: def})
}
}
diff --git a/internal/game/cmd_smelt.go b/internal/game/cmd_smelt.go
index f1ff4e2..8ad2391 100644
--- a/internal/game/cmd_smelt.go
+++ b/internal/game/cmd_smelt.go
@@ -4,8 +4,8 @@ import (
"fmt"
"strings"
- "thehouseoficarus/internal/action"
"thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
)
@@ -23,14 +23,10 @@ func (g *Game) doSmelt(sess *net.Session, input string) {
return
}
- allRecipes, err := g.RecipeStore.LoadAll()
- if err != nil {
- sess.WriteLine("Error loading recipes.")
- return
- }
+ items := g.CraftIndex.ByType("smelting")
if input == "" {
- g.showSmeltMenu(sess, p, allRecipes, stationDefID, stationName)
+ g.showSmeltMenu(sess, p, items, stationDefID, stationName)
return
}
@@ -48,33 +44,35 @@ func (g *Game) doSmelt(sess *net.Session, input string) {
itemID := matches[0].ID
- var recipes []action.RecipeDef
- for _, r := range allRecipes {
- if r.Type != "smelting" || !stationMatch(stationDefID, r.Station) || !r.MatchesEntry(itemID) {
+ var recipes []*object.ItemDef
+ for _, item := range items {
+ c := item.FirstCraft()
+ if !stationMatch(stationDefID, c.Station) || !craftMatchesEntry(c, itemID) {
continue
}
- if !r.HasAllItemsQty(p.CountItem) || p.Level(player.SkillName(r.EffectiveSkill())) < r.Level {
+ if !craftHasAllItemsQty(c, p.CountItem) || p.Level(player.SkillName(c.EffectiveSkill())) < c.Level {
continue
}
- recipes = append(recipes, r)
+ recipes = append(recipes, item)
}
g.showRecipeChoice(sess, p, recipes, "You can't smelt that.", "What would you like to smelt?", "")
}
-func (g *Game) showSmeltMenu(sess *net.Session, p *player.Player, allRecipes []action.RecipeDef, stationDefID, stationName string) {
+func (g *Game) showSmeltMenu(sess *net.Session, p *player.Player, items []*object.ItemDef, stationDefID, stationName string) {
seen := make(map[string]bool)
- var recipes []action.RecipeDef
+ var recipes []*object.ItemDef
- for _, r := range allRecipes {
- if r.Type != "smelting" || !stationMatch(stationDefID, r.Station) {
+ for _, item := range items {
+ c := item.FirstCraft()
+ if !stationMatch(stationDefID, c.Station) {
continue
}
- if seen[r.ID] || !r.HasAllItemsQty(p.CountItem) || p.Level(player.SkillName(r.EffectiveSkill())) < r.Level {
+ if seen[item.ID] || !craftHasAllItemsQty(c, p.CountItem) || p.Level(player.SkillName(c.EffectiveSkill())) < c.Level {
continue
}
- seen[r.ID] = true
- recipes = append(recipes, r)
+ seen[item.ID] = true
+ recipes = append(recipes, item)
}
g.showRecipeChoice(sess, p, recipes, "You don't have anything you can smelt.", "What would you like to smelt?", "smelt_all")
diff --git a/internal/game/cmd_smith.go b/internal/game/cmd_smith.go
index c7a32cd..712c03c 100644
--- a/internal/game/cmd_smith.go
+++ b/internal/game/cmd_smith.go
@@ -5,8 +5,8 @@ import (
"sort"
"strings"
- "thehouseoficarus/internal/action"
"thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
)
@@ -29,11 +29,7 @@ func (g *Game) doSmith(sess *net.Session, input string) {
return
}
- allRecipes, err := g.RecipeStore.LoadAll()
- if err != nil {
- sess.WriteLine("Error loading recipes.")
- return
- }
+ items := g.CraftIndex.ByType("smithing")
if input != "" {
matches := g.findInventoryMatches(input, p)
@@ -47,15 +43,15 @@ func (g *Game) doSmith(sess *net.Session, input string) {
return
}
barID := matches[0].ID
- if !hasSmithingRecipes(allRecipes, barID) {
+ if !hasSmithingItems(items, barID) {
sess.WriteLine("You can't smith that.")
return
}
- g.showSmithTable(sess, p, barID, allRecipes)
+ g.showSmithTable(sess, p, barID)
return
}
- barTypes := findSmithableBars(allRecipes, p)
+ barTypes := findSmithableBars(items, p)
if len(barTypes) == 0 {
sess.WriteLine("You don't have any bars to smith.")
return
@@ -63,12 +59,12 @@ func (g *Game) doSmith(sess *net.Session, input string) {
if len(barTypes) == 1 {
if p.OptionBool("smith_all") {
- if recipe := g.findSmithRecipe(p, barTypes[0], allRecipes); recipe != nil {
- g.startProductionFromRecipe(sess, p, recipe, 0)
+ if item := g.findSmithItem(p, barTypes[0], items); item != nil {
+ g.startProductionFromItem(sess, p, item, 0)
return
}
}
- g.showSmithTable(sess, p, barTypes[0], allRecipes)
+ g.showSmithTable(sess, p, barTypes[0])
return
}
@@ -86,17 +82,18 @@ func (g *Game) doSmith(sess *net.Session, input string) {
g.showMenuTable(sess, "Which bars would you like to smith?", names)
}
-func (g *Game) showSmithTable(sess *net.Session, p *player.Player, barID string, allRecipes []action.RecipeDef) {
+func (g *Game) showSmithTable(sess *net.Session, p *player.Player, barID string) {
barDef, _ := g.ItemStore.Load(barID)
barName := barID
if barDef != nil {
barName = barDef.Name
}
- var recipes []action.RecipeDef
- for _, r := range allRecipes {
- if r.Type == "smithing" && r.MatchesEntry(barID) {
- recipes = append(recipes, r)
+ allItems := g.CraftIndex.ByType("smithing")
+ var recipes []*object.ItemDef
+ for _, item := range allItems {
+ if craftMatchesEntry(item.FirstCraft(), barID) {
+ recipes = append(recipes, item)
}
}
@@ -109,22 +106,19 @@ func (g *Game) showSmithTable(sess *net.Session, p *player.Player, barID string,
g.showProductionTable(sess, p, recipes, titleCase(barName)+" Smithing", "smithing", lastFlag, "Produce", false)
}
-func hasSmithingRecipes(allRecipes []action.RecipeDef, barID string) bool {
- for _, r := range allRecipes {
- if r.Type == "smithing" && r.MatchesEntry(barID) {
+func hasSmithingItems(items []*object.ItemDef, barID string) bool {
+ for _, item := range items {
+ if craftMatchesEntry(item.FirstCraft(), barID) {
return true
}
}
return false
}
-func findSmithableBars(allRecipes []action.RecipeDef, p *player.Player) []string {
+func findSmithableBars(items []*object.ItemDef, p *player.Player) []string {
barSet := make(map[string]bool)
- for _, r := range allRecipes {
- if r.Type != "smithing" {
- continue
- }
- for _, e := range r.Consume {
+ for _, item := range items {
+ for _, e := range item.FirstCraft().Consume {
for _, id := range e.Items {
if p.HasItem(id) {
barSet[id] = true
@@ -148,15 +142,16 @@ func barMenuData(barIDs []string) []map[string]string {
return out
}
-func (g *Game) findSmithRecipe(p *player.Player, barID string, allRecipes []action.RecipeDef) *action.RecipeDef {
- var makeable []*action.RecipeDef
+func (g *Game) findSmithItem(p *player.Player, barID string, items []*object.ItemDef) *object.ItemDef {
+ var makeable []*object.ItemDef
skillLevel := p.Level(player.Smithing)
- for i, r := range allRecipes {
- if r.Type != "smithing" || !r.MatchesEntry(barID) {
+ for _, item := range items {
+ c := item.FirstCraft()
+ if !craftMatchesEntry(c, barID) {
continue
}
- if skillLevel >= r.Level && r.HasAllItemsQty(p.CountItem) {
- makeable = append(makeable, &allRecipes[i])
+ if skillLevel >= c.Level && craftHasAllItemsQty(c, p.CountItem) {
+ makeable = append(makeable, item)
}
}
if len(makeable) == 1 {
diff --git a/internal/game/cmd_trigger_utility.go b/internal/game/cmd_trigger_utility.go
index ce59975..69abc34 100644
--- a/internal/game/cmd_trigger_utility.go
+++ b/internal/game/cmd_trigger_utility.go
@@ -6,6 +6,7 @@ import (
"thehouseoficarus/internal/combat"
"thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
)
@@ -169,18 +170,25 @@ func (g *Game) triggerSuperheat(sess *net.Session, p *player.Player, mod *ModDef
return
}
- recipe := g.RecipeStore.FindByInput("smelt", inv.ItemID)
- if recipe == nil {
+ var match *object.ItemDef
+ for _, item := range g.CraftIndex.ByType("smelting") {
+ if craftMatchesEntry(item.FirstCraft(), inv.ItemID) {
+ match = item
+ break
+ }
+ }
+ if match == nil {
sess.WriteLine("You can't superheat that.")
return
}
+ c := match.FirstCraft()
- if recipe.Level > 0 && p.Level(player.SkillName(recipe.Skill)) < recipe.Level {
- sess.WriteLine(fmt.Sprintf("You need level %d %s to smelt that.", recipe.Level, recipe.Skill))
+ 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))
return
}
- for _, e := range recipe.Consume {
+ for _, e := range c.Consume {
for _, itemID := range e.Items {
qty := e.Quantity
if qty <= 0 {
@@ -202,24 +210,16 @@ func (g *Game) triggerSuperheat(sess *net.Session, p *player.Player, mod *ModDef
g.cancelAction(p)
}
- for _, e := range recipe.Consume {
- for _, itemID := range e.Items {
- qty := e.Quantity
- if qty <= 0 {
- qty = 1
- }
- p.RemoveItem(itemID, qty)
- }
- }
+ craftConsumeAll(c, p.HasItem, p.RemoveItem)
- outputQty := recipe.OutputQty
+ outputQty := c.OutputQty
if outputQty <= 0 {
outputQty = 1
}
placed := false
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
- if slot != nil && slot.ItemID == recipe.Output {
+ if slot != nil && slot.ItemID == match.ID {
slot.Quantity += outputQty
placed = true
break
@@ -227,20 +227,16 @@ func (g *Game) triggerSuperheat(sess *net.Session, p *player.Player, mod *ModDef
}
if !placed {
freeSlot := p.FirstFreeSlot()
- p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: recipe.Output, Quantity: outputQty})
+ p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: match.ID, Quantity: outputQty})
}
var extraGains []xpGain
- if recipe.XP > 0 {
- extraGains = []xpGain{{recipe.Skill, recipe.XP}}
+ if c.XP > 0 {
+ extraGains = []xpGain{{c.EffectiveSkill(), c.XP}}
}
g.triggerModReward(sess, p, mod, 1.0, extraGains...)
- outputDef, _ := g.ItemStore.Load(recipe.Output)
- outputName := recipe.Output
- if outputDef != nil {
- outputName = outputDef.Name
- }
+ outputName := match.Name
inputDef, _ := g.ItemStore.Load(inv.ItemID)
inputName := inv.ItemID
if inputDef != nil {
diff --git a/internal/game/cmd_use.go b/internal/game/cmd_use.go
index e653c93..640e28b 100644
--- a/internal/game/cmd_use.go
+++ b/internal/game/cmd_use.go
@@ -4,7 +4,6 @@ import (
"fmt"
"strings"
- "thehouseoficarus/internal/action"
"thehouseoficarus/internal/net"
"thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
@@ -116,57 +115,42 @@ func (g *Game) useRoomObject(sess *net.Session, p *player.Player, input string)
return true
}
-func (g *Game) stationRecipes(p *player.Player, stationDefID string) []action.RecipeDef {
- allRecipes, err := g.RecipeStore.LoadAll()
- if err != nil {
- return nil
- }
- var results []action.RecipeDef
- for _, r := range allRecipes {
- if len(r.Station) == 0 {
- continue
- }
- stationMatch := false
- for _, sid := range r.Station {
- if sid == stationDefID {
- stationMatch = true
- break
- }
- }
- if !stationMatch {
+func (g *Game) stationRecipes(p *player.Player, stationDefID string) []*object.ItemDef {
+ items := g.CraftIndex.ByStation(stationDefID)
+ var results []*object.ItemDef
+ for _, item := range items {
+ c := item.FirstCraft()
+ if !craftHasAllItemsQty(c, p.CountItem) {
continue
}
- if !r.HasAllItemsQty(p.CountItem) {
+ skillName := player.SkillName(c.EffectiveSkill())
+ if p.Level(skillName) < c.Level {
continue
}
- skillName := player.SkillName(r.EffectiveSkill())
- if p.Level(skillName) < r.Level {
- continue
- }
- if r.Tool != "" && !g.hasToolType(p, r.Tool) {
+ if c.Tool != "" && !g.hasToolType(p, c.Tool) {
continue
}
if stationDefID == "anvil" && !g.hasToolType(p, "hammer") {
continue
}
- results = append(results, r)
+ results = append(results, item)
}
return results
}
-func (g *Game) showRecipeMenu(sess *net.Session, p *player.Player, recipes []action.RecipeDef, stationName string) {
- if len(recipes) == 0 {
+func (g *Game) showRecipeMenu(sess *net.Session, p *player.Player, items []*object.ItemDef, stationName string) {
+ if len(items) == 0 {
sess.WriteLine(fmt.Sprintf("You don't have anything you can make at the %s.", stationName))
return
}
- if len(recipes) == 1 {
- g.promptHowMany(sess, recipes[0].ID)
+ if len(items) == 1 {
+ g.promptHowMany(sess, items[0].ID)
return
}
needTypes := false
- for i := range recipes {
- for j := i + 1; j < len(recipes); j++ {
- if recipes[i].Type != recipes[j].Type {
+ 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 {
needTypes = true
break
}
@@ -175,17 +159,23 @@ func (g *Game) showRecipeMenu(sess *net.Session, p *player.Player, recipes []act
break
}
}
- menu := recipeMenuData(recipeDefIDs(recipes))
+ menu := itemMenuData(itemIDs(items))
sess.PendingMenu = menu
sess.State = net.StateRecipeChoice
var names []string
- for _, r := range recipes {
- name := g.recipeName(sess, &r)
+ 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:]
if needTypes {
- typeLabel := strings.ToUpper(r.Type[:1]) + r.Type[1:]
- names = append(names, fmt.Sprintf("%s (%s)", name, typeLabel))
+ names = append(names, fmt.Sprintf("%s (%s)", g.itemName(sess, item), typeLabel))
} else {
- names = append(names, name)
+ names = append(names, g.itemName(sess, item))
}
}
g.showMenuTable(sess, fmt.Sprintf("What would you like to make at the %s?", stationName), names)
@@ -217,23 +207,28 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName,
return
}
- stationIDs := []string{def.ID}
- recipes, err := g.RecipeStore.FindByStation(itemAID, stationIDs, "")
- if err == nil && len(recipes) > 0 {
- if recipes[0].Type == "smelting" {
- filtered := recipes[:0:0]
- for _, r := range recipes {
- if r.Type != "smelting" {
+ recipes := g.CraftIndex.FindByStationInput(def.ID, itemAID)
+
+ if len(recipes) > 0 {
+ cType := ""
+ if len(recipes[0].Craft) > 0 {
+ cType = recipes[0].FirstCraft().Type
+ }
+ if cType == "smelting" {
+ var filtered []*object.ItemDef
+ for _, item := range recipes {
+ c := item.FirstCraft()
+ if c.Type != "smelting" {
continue
}
- if !r.HasAllItemsQty(p.CountItem) {
+ if !craftHasAllItemsQty(c, p.CountItem) {
continue
}
- skillLevel := p.Level(player.SkillName(r.EffectiveSkill()))
- if skillLevel < r.Level {
+ skillLevel := p.Level(player.SkillName(c.EffectiveSkill()))
+ if skillLevel < c.Level {
continue
}
- filtered = append(filtered, r)
+ filtered = append(filtered, item)
}
if len(filtered) == 0 {
sess.WriteLine("You can't smelt that here.")
@@ -243,36 +238,35 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName,
g.promptHowMany(sess, filtered[0].ID)
return
}
- sess.PendingMenu = recipeMenuData(recipeDefIDs(filtered))
- sess.State = net.StateRecipeChoice
- var names []string
- for _, r := range filtered {
- names = append(names, g.recipeName(sess, &r))
- }
- g.showMenuTable(sess, "What would you like to smelt?", names)
- return
+ sess.PendingMenu = itemMenuData(itemIDs(filtered))
+ sess.State = net.StateRecipeChoice
+ var names []string
+ for _, item := range filtered {
+ names = append(names, g.itemName(sess, item))
+ }
+ g.showMenuTable(sess, "What would you like to smelt?", names)
+ return
}
- if recipes[0].Type == "smithing" {
+ if cType == "smithing" {
if !g.hasToolType(p, "hammer") {
sess.WriteLine("You need a hammer to smith.")
return
}
- allRecipes, _ := g.RecipeStore.LoadAll()
- g.showSmithTable(sess, p, itemAID, allRecipes)
+ g.showSmithTable(sess, p, itemAID)
return
}
if len(recipes) == 1 {
g.promptHowMany(sess, recipes[0].ID)
return
}
- sess.PendingMenu = recipeMenuData(recipeDefIDs(recipes))
- sess.State = net.StateRecipeChoice
- var names []string
- for _, r := range recipes {
- names = append(names, g.recipeName(sess, &r))
- }
- g.showMenuTable(sess, fmt.Sprintf("What would you like to make with %s on the %s?", itemAName, def.Name), names)
- return
+ sess.PendingMenu = itemMenuData(itemIDs(recipes))
+ sess.State = net.StateRecipeChoice
+ var names []string
+ for _, item := range recipes {
+ names = append(names, g.itemName(sess, item))
+ }
+ g.showMenuTable(sess, fmt.Sprintf("What would you like to make with %s on the %s?", itemAName, def.Name), names)
+ return
}
for _, ui := range def.UseInteractions {
@@ -314,29 +308,19 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName,
itemBID := matchesB[0].ID
- craftRecipes := g.findCraftRecipes(p, itemAID, itemBID)
- fletchRecipes := g.findFletchRecipes(p, itemAID, itemBID)
+ craftItems := g.findCraftItems(p, itemAID, itemBID)
+ fletchItems := g.findFletchItems(p, itemAID, itemBID)
- if len(craftRecipes) > 0 && len(fletchRecipes) > 0 {
+ if len(craftItems) > 0 && len(fletchItems) > 0 {
var menu []map[string]string
var names []string
- for _, r := range craftRecipes {
- menu = append(menu, map[string]string{"recipe_id": r.ID})
- outDef, _ := g.ItemStore.Load(r.Output)
- name := r.Output
- if outDef != nil {
- name = outDef.Name
- }
- names = append(names, fmt.Sprintf("%s (Crafting)", name))
+ for _, item := range craftItems {
+ menu = append(menu, map[string]string{"item_id": item.ID})
+ names = append(names, fmt.Sprintf("%s (Crafting)", item.Name))
}
- for _, r := range fletchRecipes {
- menu = append(menu, map[string]string{"fletch_recipe_id": r.ID})
- outDef, _ := g.ItemStore.Load(r.Output)
- name := r.Output
- if outDef != nil {
- name = outDef.Name
- }
- names = append(names, fmt.Sprintf("%s (Fletching)", name))
+ for _, item := range fletchItems {
+ menu = append(menu, map[string]string{"item_id": item.ID})
+ names = append(names, fmt.Sprintf("%s (Fletching)", item.Name))
}
sess.PendingMenu = menu
sess.State = net.StateRecipeChoice
@@ -344,15 +328,15 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName,
return
}
- if len(craftRecipes) > 0 {
- var available []action.RecipeDef
- for _, r := range craftRecipes {
- if g.canDoRecipe(p, &r, player.Crafting) {
- available = append(available, r)
+ if len(craftItems) > 0 {
+ var available []*object.ItemDef
+ for _, item := range craftItems {
+ if g.canDoItem(p, item, player.Crafting) {
+ available = append(available, item)
}
}
if len(available) == 1 {
- g.startRecipe(sess, p, &available[0], 0, "last_craft")
+ g.startItem(sess, p, available[0], 0, "last_craft")
return
}
if len(available) > 1 {
@@ -361,15 +345,15 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName,
}
}
- if len(fletchRecipes) > 0 {
- var available []action.RecipeDef
- for _, r := range fletchRecipes {
- if g.canDoFletchRecipe(p, &r) {
- available = append(available, r)
+ if len(fletchItems) > 0 {
+ var available []*object.ItemDef
+ for _, item := range fletchItems {
+ if g.canDoFletchItem(p, item) {
+ available = append(available, item)
}
}
if len(available) == 1 {
- g.startFletchAction(sess, p, &available[0], 0)
+ g.startFletchAction(sess, p, available[0], 0)
return
}
if len(available) > 1 {
@@ -378,22 +362,33 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName,
}
}
- matched := g.findCombineResults(sess, p, itemAID, itemBID)
- if len(matched) == 1 {
- g.promptHowMany(sess, "combine_"+matched[0].ID)
+ matched := g.CraftIndex.FindByTwoInputs(itemAID, itemBID)
+ var matchable []*object.ItemDef
+ for _, item := range matched {
+ if craftHasAllItemsQty(item.FirstCraft(), p.CountItem) {
+ matchable = append(matchable, item)
+ }
+ }
+ if len(matchable) == 1 {
+ g.promptHowMany(sess, matchable[0].ID)
return
}
- if len(matched) > 1 {
- sess.PendingMenu = combineMenuData(matched)
+ if len(matchable) > 1 {
+ sess.PendingMenu = itemMenuData(itemIDs(matchable))
sess.State = net.StateRecipeChoice
var names []string
- for _, def := range matched {
- names = append(names, g.itemColorize(sess, def, def.Name))
+ for _, item := range matchable {
+ names = append(names, g.itemName(sess, item))
}
g.showMenuTable(sess, "What would you like to make?", names)
return
}
+ if g.isGrimyHerb(itemAID) && itemBID == "vial_of_water" {
+ sess.WriteLine("Don't put a grimy herb in there! Clean it first!")
+ return
+ }
+
sess.WriteLine("You can't combine those items.")
return
}
@@ -401,55 +396,16 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName,
sess.WriteLine(fmt.Sprintf("There's no '%s' here to use that on.", itemBName))
}
-func (g *Game) findCombineResults(sess *net.Session, p *player.Player, itemAID, itemBID string) []*object.ItemDef {
- items, err := g.ItemStore.LoadAll()
- if err != nil {
- return nil
- }
- var matched []*object.ItemDef
- for _, def := range items {
- if len(def.MadeFrom) == 0 {
- continue
- }
- aEntry := -1
- bEntry := -1
- for ei, entry := range def.MadeFrom {
- for _, id := range entry.Items {
- if id == itemAID && aEntry < 0 {
- aEntry = ei
- }
- if id == itemBID && bEntry < 0 {
- bEntry = ei
+func (g *Game) isGrimyHerb(itemID string) bool {
+ items := g.CraftIndex.ByType("clean")
+ for _, item := range items {
+ for _, e := range item.FirstCraft().Consume {
+ for _, id := range e.Items {
+ if id == itemID {
+ return true
}
}
}
- if aEntry >= 0 && bEntry >= 0 && aEntry != bEntry && madeFromAvailable(p, def.MadeFrom) {
- matched = append(matched, def)
- }
}
- return matched
-}
-
-func combineMenuData(defs []*object.ItemDef) []map[string]string {
- out := make([]map[string]string, len(defs))
- for i, d := range defs {
- out[i] = map[string]string{"combine_item": d.ID}
- }
- return out
-}
-
-func madeFromAvailable(p *player.Player, madeFrom []object.MadeFromEntry) bool {
- for _, entry := range madeFrom {
- found := false
- for _, id := range entry.Items {
- if p.HasItem(id) {
- found = true
- break
- }
- }
- if !found {
- return false
- }
- }
- return true
+ return false
}
diff --git a/internal/game/cmd_verbs.go b/internal/game/cmd_verbs.go
index 7801903..f66dc74 100644
--- a/internal/game/cmd_verbs.go
+++ b/internal/game/cmd_verbs.go
@@ -88,12 +88,12 @@ 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.Type]; ok {
+ if info, ok := productionTypes[r.FirstCraft().Type]; ok {
if info.ActionType != "combine" && info.ActionType != "fletch" && info.ActionType != "mix" {
addUnique(&sectionObjs, &seen, info.ActionType)
}
}
- for _, c := range r.Consume {
+ for _, c := range r.FirstCraft().Consume {
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/core_production.go b/internal/game/core_production.go
index 59517d5..4aecc54 100644
--- a/internal/game/core_production.go
+++ b/internal/game/core_production.go
@@ -13,47 +13,61 @@ import (
"thehouseoficarus/internal/net"
"thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
-
)
-func (g *Game) startProduction(sess *net.Session, p *player.Player, recipe *action.RecipeDef, actionType, displayVerb, startMsg, endMsg string, count int) {
+func craftFirstItemName(c *object.CraftDef, load func(string) (string, bool)) string {
+ if c == nil {
+ return ""
+ }
+ for _, e := range c.Consume {
+ for _, id := range e.Items {
+ if name, ok := load(id); ok {
+ return name
+ }
+ return id
+ }
+ }
+ return ""
+}
+
+func (g *Game) startProduction(sess *net.Session, p *player.Player, item *object.ItemDef, craft *object.CraftDef, actionType, displayVerb, startMsg, endMsg string, count int) {
g.cancelAction(p)
g.cancelBgAction(p)
- skill := recipe.EffectiveSkill()
+ skill := craft.EffectiveSkill()
if skill != "" {
skillLevel := p.Level(player.SkillName(skill))
- if skillLevel < recipe.Level {
- sess.WriteLine(fmt.Sprintf("You need level %d %s to do that.", recipe.Level, skill))
+ if skillLevel < craft.Level {
+ sess.WriteLine(fmt.Sprintf("You need level %d %s to do that.", craft.Level, skill))
g.reprompt(sess)
return
}
}
- if !recipe.HasAllItemsQty(p.CountItem) {
+ if !craftHasAllItemsQty(craft, p.CountItem) {
sess.WriteLine("You don't have the required materials.")
g.reprompt(sess)
return
}
- outDef, _ := g.ItemStore.Load(recipe.Output)
+ outDef, _ := g.ItemStore.Load(item.ID)
- wait := recipe.Wait
+ wait := craft.Wait
if wait <= 0 {
wait = 4
}
- outputName := recipe.Output
+ outputName := item.ID
if outDef != nil {
outputName = outDef.Name
}
p.Action = &action.Action{
Type: actionType,
- TargetID: recipe.ID,
+ TargetID: item.ID,
TargetName: outputName,
Data: map[string]any{
- "recipe_id": recipe.ID,
+ "item_id": item.ID,
"phase": 0,
"wait": wait,
"start_msg": startMsg,
@@ -68,13 +82,18 @@ func (g *Game) startProduction(sess *net.Session, p *player.Player, recipe *acti
g.broadcastAction(sess, "\n%s starts %s.", p.Name, displayVerb)
}
-func (g *Game) startProductionFromRecipe(sess *net.Session, p *player.Player, recipe *action.RecipeDef, count int) {
- _, stationName := g.findStation(p.RoomID, recipe.Station)
+func (g *Game) startProductionFromItem(sess *net.Session, p *player.Player, item *object.ItemDef, count int) {
+ craft := item.FirstCraft()
+ if craft == nil {
+ return
+ }
+
+ _, stationName := g.findStation(p.RoomID, craft.Station)
if stationName == "" {
stationName = "inventory"
}
- firstItem := recipe.FirstItemName(func(id string) (string, bool) {
+ firstItem := craftFirstItemName(craft, func(id string) (string, bool) {
def, err := g.ItemStore.Load(id)
if err != nil {
return id, false
@@ -82,9 +101,9 @@ func (g *Game) startProductionFromRecipe(sess *net.Session, p *player.Player, re
return def.Name, true
})
- info, ok := productionTypes[recipe.Type]
+ info, ok := productionTypes[craft.Type]
if !ok {
- info = productionTypeInfo{recipe.Type, recipe.Type}
+ info = productionTypeInfo{craft.Type, craft.Type}
}
actionType := info.ActionType
displayVerb := info.DisplayVerb
@@ -97,67 +116,79 @@ func (g *Game) startProductionFromRecipe(sess *net.Session, p *player.Player, re
}
endMsg = fmt.Sprintf("You've finished %s.", displayVerb)
- g.startProduction(sess, p, recipe, actionType, displayVerb, startMsg, endMsg, count)
+ g.startProduction(sess, p, item, craft, actionType, displayVerb, startMsg, endMsg, count)
}
-func (g *Game) loadRecipe(recipeID string) *action.RecipeDef {
- if r := g.RecipeStore.GetByID(recipeID); r != nil {
- return r
+func (g *Game) loadCraftItem(itemID string) *object.ItemDef {
+ item, err := g.ItemStore.Load(itemID)
+ if err != nil || len(item.Craft) == 0 {
+ return nil
}
- if strings.HasPrefix(recipeID, "combine_") {
- itemID := strings.TrimPrefix(recipeID, "combine_")
- if itemDef, err := g.ItemStore.Load(itemID); err == nil && len(itemDef.MadeFrom) > 0 {
- return g.buildCombineRecipe(itemDef)
- }
- }
- return nil
+ return item
}
func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool {
- recipeID := p.Action.Data["recipe_id"].(string)
+ itemID := p.Action.Data["item_id"].(string)
phase := p.Action.Data["phase"].(int)
wait := p.Action.Data["wait"].(float64)
startMsg, _ := p.Action.Data["start_msg"].(string)
endMsg, _ := p.Action.Data["end_msg"].(string)
remaining, _ := p.Action.Data["remaining"].(int)
- recipe := g.loadRecipe(recipeID)
- if recipe == nil {
+ item := g.loadCraftItem(itemID)
+ if item == nil {
g.cancelAction(p)
return false
}
+ craft := item.FirstCraft()
if phase == 0 {
if startMsg != "" {
sess.WriteLine(fmt.Sprintf("\n%s", startMsg))
}
+ if len(craft.Steps) > 0 {
+ p.Action.Data["next_step_index"] = 0
+ p.Action.Data["steps_accumulated_ticks"] = float64(0)
+ }
p.Action.Data["phase"] = 1
p.Action.WaitLeft = engine.ToTicks(wait)
return true
}
- skill := recipe.EffectiveSkill()
+ if stepsIdx, ok := p.Action.Data["next_step_index"].(int); ok && stepsIdx < len(craft.Steps) {
+ accTicks, _ := p.Action.Data["steps_accumulated_ticks"].(float64)
+ accTicks += wait
+ p.Action.Data["steps_accumulated_ticks"] = accTicks
+ for i := stepsIdx; i < len(craft.Steps); i++ {
+ if accTicks >= craft.Steps[i].Tick {
+ sess.WriteLine(craft.Steps[i].Message)
+ p.Action.Data["next_step_index"] = i + 1
+ }
+ }
+ }
+
+ skill := craft.EffectiveSkill()
skillLevel := p.Level(player.SkillName(skill))
chance := 1.0
- if recipe.Success != nil {
- chance = action.SuccessChance(*recipe.Success, skillLevel, recipe.Level)
+ if craft.Success != nil {
+ chance = action.SuccessChance(action.SuccessFormula(*craft.Success), skillLevel, craft.Level)
}
if rand.Float64() < chance {
- outputQty := recipe.OutputQty
+ outputQty := craft.OutputQty
if outputQty <= 0 {
outputQty = 1
}
- byproducts := g.collectByproducts(p, recipe)
+ byproducts := g.collectByproducts(p, item)
placed := false
- outDef, _ := g.ItemStore.Load(recipe.Output)
+ outDef, _ := g.ItemStore.Load(item.ID)
if outDef != nil && outDef.Stackable {
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
- if slot != nil && slot.ItemID == recipe.Output {
- recipe.ConsumeAll(p.HasItem, p.RemoveItem)
+ if slot != nil && slot.ItemID == item.ID {
+ craftConsumeAll(craft, p.HasItem, p.RemoveItem)
slot.Quantity += outputQty
placed = true
break
@@ -165,14 +196,14 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool {
}
}
if !placed {
- recipe.ConsumeAll(p.HasItem, p.RemoveItem)
+ craftConsumeAll(craft, p.HasItem, p.RemoveItem)
freeSlot := p.FirstFreeSlot()
if freeSlot == -1 {
sess.WriteLine("Your inventory is too full!")
g.cancelAction(p)
return false
}
- p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: recipe.Output, Quantity: outputQty})
+ p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: item.ID, Quantity: outputQty})
}
for _, bp := range byproducts {
@@ -181,38 +212,38 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool {
}
}
- if recipe.XP > 0 {
- if newLevel := p.AddSkillXP(player.SkillName(skill), recipe.XP); newLevel > 0 {
+ if craft.XP > 0 {
+ if newLevel := p.AddSkillXP(player.SkillName(skill), craft.XP); newLevel > 0 {
sess.WriteLine(g.colorize(sess, "level_up", fmt.Sprintf("*** You are now level %d %s! ***", newLevel, skill)))
}
}
g.AccountStore.SaveCharacter(p)
- msg := recipe.Message
+ msg := craft.Message
if msg == "" {
- outputName := recipe.Output
+ outputName := item.ID
if outDef != nil {
outputName = outDef.Name
}
msg = fmt.Sprintf("You produce %s.", outputName)
}
- if p.OptionBool("xp_drops") && recipe.XP > 0 {
+ if p.OptionBool("xp_drops") && craft.XP > 0 {
abbr := player.SkillAbbr[player.SkillName(skill)]
- msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp %s)", recipe.XP, abbr))
+ msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp %s)", craft.XP, abbr))
}
sess.WriteLine(msg)
} else {
- recipe.ConsumeAll(p.HasItem, p.RemoveItem)
+ craftConsumeAll(craft, p.HasItem, p.RemoveItem)
- if recipe.Fail != "" {
+ if craft.Fail != "" {
freeSlot := p.FirstFreeSlot()
if freeSlot >= 0 {
- p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: recipe.Fail, Quantity: 1})
+ p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: craft.Fail, Quantity: 1})
}
}
g.AccountStore.SaveCharacter(p)
- msg := recipe.FailMessage
+ msg := craft.FailMessage
if msg == "" {
msg = "You fail and the materials are lost."
}
@@ -231,7 +262,7 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool {
}
}
- if g.canContinueProduction(p, recipe) {
+ if g.canContinueProduction(p, item) {
p.Action.WaitLeft = engine.ToTicks(wait)
return true
}
@@ -243,9 +274,13 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool {
return false
}
-func (g *Game) collectByproducts(p *player.Player, recipe *action.RecipeDef) []string {
+func (g *Game) collectByproducts(p *player.Player, item *object.ItemDef) []string {
+ craft := item.FirstCraft()
+ if craft == nil {
+ return nil
+ }
var byproducts []string
- for _, e := range recipe.Consume {
+ for _, e := range craft.Consume {
if len(e.Byproducts) == 0 {
continue
}
@@ -259,19 +294,23 @@ func (g *Game) collectByproducts(p *player.Player, recipe *action.RecipeDef) []s
return byproducts
}
-func (g *Game) canContinueProduction(p *player.Player, recipe *action.RecipeDef) bool {
- if !recipe.HasAllItemsQty(p.CountItem) {
+func (g *Game) canContinueProduction(p *player.Player, item *object.ItemDef) bool {
+ craft := item.FirstCraft()
+ if craft == nil {
return false
}
- outputQty := recipe.OutputQty
+ if !craftHasAllItemsQty(craft, p.CountItem) {
+ return false
+ }
+ outputQty := craft.OutputQty
if outputQty <= 0 {
outputQty = 1
}
- outDef, _ := g.ItemStore.Load(recipe.Output)
+ outDef, _ := g.ItemStore.Load(item.ID)
if outDef != nil && outDef.Stackable {
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
- if slot != nil && slot.ItemID == recipe.Output {
+ if slot != nil && slot.ItemID == item.ID {
return true
}
}
@@ -316,7 +355,7 @@ func (g *Game) handleRecipeChoice(sess *net.Session, input string) {
if name == "" {
continue
}
- if strings.ToLower(name) == lower || action.WordPrefixMatch(input, name) {
+ if strings.ToLower(name) == lower || object.WordPrefixMatch(input, name) {
if matchIdx >= 0 {
sess.WriteLine("That's ambiguous.")
return
@@ -338,21 +377,11 @@ func (g *Game) handleRecipeChoice(sess *net.Session, input string) {
}
func (g *Game) menuEntryName(entry map[string]string) string {
- if rid, ok := entry["recipe_id"]; ok {
- if r := g.RecipeStore.GetByID(rid); r != nil {
- if def, err := g.ItemStore.Load(r.Output); err == nil {
- return def.Name
- }
- return r.Output
- }
- }
- if rid, ok := entry["fletch_recipe_id"]; ok {
- if r := g.RecipeStore.GetByID(rid); r != nil {
- if def, err := g.ItemStore.Load(r.Output); err == nil {
- return def.Name
- }
- return r.Output
+ if iid, ok := entry["item_id"]; ok {
+ if def, _ := g.ItemStore.Load(iid); def != nil {
+ return def.Name
}
+ return iid
}
if barID, ok := entry["bar_id"]; ok {
if def, _ := g.ItemStore.Load(barID); def != nil {
@@ -360,51 +389,34 @@ func (g *Game) menuEntryName(entry map[string]string) string {
}
return barID
}
- if cid, ok := entry["combine_item"]; ok {
- if def, _ := g.ItemStore.Load(cid); def != nil {
- return def.Name
- }
- return cid
- }
return ""
}
func (g *Game) dispatchMenuEntry(sess *net.Session, entry map[string]string) {
- if cid, ok := entry["combine_item"]; ok {
- g.promptHowMany(sess, "combine_"+cid)
+ if iid, ok := entry["item_id"]; ok {
+ g.promptHowMany(sess, iid)
return
}
if barID, ok := entry["bar_id"]; ok {
- p := sess.Player
- allRecipes, _ := g.RecipeStore.LoadAll()
- g.showSmithTable(sess, p, barID, allRecipes)
- return
- }
- if rid, ok := entry["fletch_recipe_id"]; ok {
- p := sess.Player
- if r := g.RecipeStore.GetByID(rid); r != nil {
- g.startFletchAction(sess, p, r, 0)
- return
- }
- g.reprompt(sess)
- return
- }
- if rid, ok := entry["recipe_id"]; ok {
- g.promptHowMany(sess, rid)
+ g.showSmithTable(sess, sess.Player, barID)
return
}
g.reprompt(sess)
}
-func (g *Game) formatMaterials(r *action.RecipeDef) string {
+func (g *Game) formatMaterials(item *object.ItemDef) string {
+ craft := item.FirstCraft()
+ if craft == nil {
+ return ""
+ }
var parts []string
- for _, e := range r.Consume {
+ for _, e := range craft.Consume {
itemName := e.Items[0]
if def, err := g.ItemStore.Load(e.Items[0]); err == nil {
itemName = def.Name
}
- if e.Quantity > 1 {
- parts = append(parts, fmt.Sprintf("%s x%d", itemName, e.Quantity))
+ if e.Quantity > 1 {
+ parts = append(parts, fmt.Sprintf("%s x%d", itemName, e.Quantity))
} else {
parts = append(parts, itemName)
}
@@ -412,9 +424,9 @@ func (g *Game) formatMaterials(r *action.RecipeDef) string {
return strings.Join(parts, ", ")
}
-func (g *Game) showProductionTable(sess *net.Session, p *player.Player, recipes []action.RecipeDef, title, skill, lastFlagKey, promptVerb string, background bool) {
- sort.Slice(recipes, func(i, j int) bool {
- return recipes[i].Level < recipes[j].Level
+func (g *Game) showProductionTable(sess *net.Session, p *player.Player, items []*object.ItemDef, title, skill, lastFlagKey, promptVerb string, background bool) {
+ sort.Slice(items, func(i, j int) bool {
+ return items[i].FirstCraft().Level < items[j].FirstCraft().Level
})
mode := g.colorMode(sess)
@@ -426,14 +438,15 @@ func (g *Game) showProductionTable(sess *net.Session, p *player.Player, recipes
Columns: []string{"#", "Product", "Materials", "Level"},
}
- for i, r := range recipes {
- outDef, _ := g.ItemStore.Load(r.Output)
- productName := r.Output
+ for i, item := range items {
+ craft := item.FirstCraft()
+ outDef, _ := g.ItemStore.Load(item.ID)
+ productName := item.ID
if outDef != nil {
productName = outDef.Name
}
- outputQty := r.OutputQty
+ outputQty := craft.OutputQty
if outputQty <= 0 {
outputQty = 1
}
@@ -441,11 +454,11 @@ func (g *Game) showProductionTable(sess *net.Session, p *player.Player, recipes
productName = fmt.Sprintf("%s x%d", productName, outputQty)
}
- matStr := g.formatMaterials(&r)
- levelStr := fmt.Sprint(r.Level)
+ matStr := g.formatMaterials(item)
+ levelStr := fmt.Sprint(craft.Level)
numStr := fmt.Sprintf("%d", i+1)
- canMake := skillLevel >= r.Level && r.HasAllItemsQty(p.CountItem)
+ canMake := skillLevel >= craft.Level && craftHasAllItemsQty(craft, p.CountItem)
if canMake {
productName = g.itemColorize(sess, outDef, productName)
} else {
@@ -464,12 +477,12 @@ func (g *Game) showProductionTable(sess *net.Session, p *player.Player, recipes
sess.WriteLine(line)
}
- lastRecipeID, _ := p.Flags[lastFlagKey].(string)
+ lastItemID, _ := p.Flags[lastFlagKey].(string)
hint := ""
- if lastRecipeID != "" {
- for _, r := range recipes {
- if r.ID == lastRecipeID {
- if outDef, err := g.ItemStore.Load(r.Output); err == nil {
+ if lastItemID != "" {
+ for _, item := range items {
+ if item.ID == lastItemID {
+ if outDef, err := g.ItemStore.Load(item.ID); err == nil {
hint = outDef.Name
}
break
@@ -483,7 +496,7 @@ func (g *Game) showProductionTable(sess *net.Session, p *player.Player, recipes
sess.Write(fmt.Sprintf("%s what: ", promptVerb))
}
- sess.PendingMenu = recipeMenuData(recipeDefIDs(recipes))
+ sess.PendingMenu = itemMenuData(itemIDs(items))
sess.PendingSkill = skill
sess.PendingLastFlag = lastFlagKey
sess.PendingBackground = background
@@ -504,26 +517,26 @@ func (g *Game) handleProductChoice(sess *net.Session, input string) {
input = strings.TrimSpace(input)
- var recipes []action.RecipeDef
+ var items []*object.ItemDef
for _, entry := range menuData {
- rid := entry["recipe_id"]
- if r := g.RecipeStore.GetByID(rid); r != nil {
- recipes = append(recipes, *r)
+ iid := entry["item_id"]
+ if item := g.loadCraftItem(iid); item != nil {
+ items = append(items, item)
}
}
- if len(recipes) == 0 {
+ if len(items) == 0 {
g.reprompt(sess)
return
}
- sort.Slice(recipes, func(i, j int) bool {
- return recipes[i].Level < recipes[j].Level
+ sort.Slice(items, func(i, j int) bool {
+ return items[i].FirstCraft().Level < items[j].FirstCraft().Level
})
- lastRecipeID, _ := p.Flags[lastFlagKey].(string)
+ lastItemID, _ := p.Flags[lastFlagKey].(string)
- sel, errMsg := g.resolveRecipeByInput(input, recipes, lastRecipeID)
+ sel, errMsg := g.resolveCraftByInput(input, items, lastItemID)
switch errMsg {
case "ambiguous":
sess.WriteLine("That's ambiguous.")
@@ -535,26 +548,27 @@ func (g *Game) handleProductChoice(sess *net.Session, input string) {
return
}
+ craft := sel.Item.FirstCraft()
skillLevel := p.Level(player.SkillName(skill))
- if skillLevel < sel.Recipe.Level {
- sess.WriteLine(fmt.Sprintf("You need level %d %s to make that.", sel.Recipe.Level, skill))
+ if skillLevel < craft.Level {
+ sess.WriteLine(fmt.Sprintf("You need level %d %s to make that.", craft.Level, skill))
g.reprompt(sess)
return
}
- if !sel.Recipe.HasAllItemsQty(p.CountItem) {
+ if !craftHasAllItemsQty(craft, p.CountItem) {
sess.WriteLine("You don't have the materials for that.")
g.reprompt(sess)
return
}
p.EnsureFlags()
- p.Flags[lastFlagKey] = sel.Recipe.ID
+ p.Flags[lastFlagKey] = sel.Item.ID
g.AccountStore.SaveCharacter(p)
if background {
- g.startFletchAction(sess, p, sel.Recipe, sel.Count)
+ g.startFletchAction(sess, p, sel.Item, sel.Count)
} else {
- g.startProductionFromRecipe(sess, p, sel.Recipe, sel.Count)
+ g.startProductionFromItem(sess, p, sel.Item, sel.Count)
}
}
@@ -591,10 +605,6 @@ func (g *Game) hasToolType(p *player.Player, toolType string) bool {
_, _, found := g.findTool(p, []string{toolType})
return found
}
-type recipeEntry struct {
- ItemName string
- Recipe action.RecipeDef
-}
type productionTypeInfo struct {
ActionType string
@@ -621,15 +631,15 @@ func init() {
}
}
-func recipeMenuData(recipeIDs []string) []map[string]string {
- out := make([]map[string]string, len(recipeIDs))
- for i, id := range recipeIDs {
- out[i] = map[string]string{"recipe_id": id}
+func itemMenuData(itemIDs []string) []map[string]string {
+ out := make([]map[string]string, len(itemIDs))
+ for i, id := range itemIDs {
+ out[i] = map[string]string{"item_id": id}
}
return out
}
-func recipeDefIDs(defs []action.RecipeDef) []string {
+func itemIDs(defs []*object.ItemDef) []string {
ids := make([]string, len(defs))
for i, d := range defs {
ids[i] = d.ID
@@ -637,33 +647,25 @@ func recipeDefIDs(defs []action.RecipeDef) []string {
return ids
}
-func entryMenuData(entries []recipeEntry) []map[string]string {
- out := make([]map[string]string, len(entries))
- for i, e := range entries {
- out[i] = map[string]string{"recipe_id": e.Recipe.ID}
- }
- return out
-}
-
-func (g *Game) promptHowMany(sess *net.Session, recipeID string) {
- sess.PendingRecipeID = recipeID
+func (g *Game) promptHowMany(sess *net.Session, itemID string) {
+ sess.PendingItemID = itemID
sess.State = net.StateHowMany
sess.Write("How many (return for all)?: ")
}
-type recipeSelection struct {
- Recipe *action.RecipeDef
- Count int
+type craftSelection struct {
+ Item *object.ItemDef
+ Count int
}
-func (g *Game) resolveRecipeByInput(input string, recipes []action.RecipeDef, lastRecipeID string) (sel recipeSelection, errMsg string) {
+func (g *Game) resolveCraftByInput(input string, items []*object.ItemDef, lastItemID string) (sel craftSelection, errMsg string) {
if input == "" {
- if lastRecipeID == "" {
+ if lastItemID == "" {
return sel, "never_mind"
}
- for i := range recipes {
- if recipes[i].ID == lastRecipeID {
- sel.Recipe = &recipes[i]
+ for i := range items {
+ if items[i].ID == lastItemID {
+ sel.Item = items[i]
return sel, ""
}
}
@@ -673,22 +675,22 @@ func (g *Game) resolveRecipeByInput(input string, recipes []action.RecipeDef, la
qty, productName := parseQty(input)
productName = strings.TrimSpace(productName)
- if idx, err := strconv.Atoi(productName); err == nil && idx > 0 && idx <= len(recipes) {
- sel.Recipe = &recipes[idx-1]
+ if idx, err := strconv.Atoi(productName); err == nil && idx > 0 && idx <= len(items) {
+ sel.Item = items[idx-1]
sel.Count = qty
return sel, ""
}
matchCount := 0
- for i := range recipes {
- outDef, _ := g.ItemStore.Load(recipes[i].Output)
- name := recipes[i].Output
+ for i := range items {
+ outDef, _ := g.ItemStore.Load(items[i].ID)
+ name := items[i].ID
if outDef != nil {
name = outDef.Name
}
- if action.WordPrefixMatch(productName, name) {
+ if object.WordPrefixMatch(productName, name) {
if matchCount == 0 {
- sel.Recipe = &recipes[i]
+ sel.Item = items[i]
sel.Count = qty
}
matchCount++
@@ -697,7 +699,7 @@ func (g *Game) resolveRecipeByInput(input string, recipes []action.RecipeDef, la
if matchCount > 1 {
return sel, "ambiguous"
}
- if sel.Recipe == nil {
+ if sel.Item == nil {
return sel, "never_mind"
}
return sel, ""
@@ -716,41 +718,42 @@ func (g *Game) showMenuTable(sess *net.Session, title string, names []string) {
}
}
-func (g *Game) recipeName(sess *net.Session, r *action.RecipeDef) string {
- if r.Output != "" {
- if def, err := g.ItemStore.Load(r.Output); err == nil {
+func (g *Game) itemName(sess *net.Session, item *object.ItemDef) string {
+ if item != nil {
+ if def, err := g.ItemStore.Load(item.ID); err == nil {
return g.itemColorize(sess, def, def.Name)
}
+ return item.ID
}
- return r.DisplayName()
+ return ""
}
-func (g *Game) showRecipeChoice(sess *net.Session, p *player.Player, recipes []action.RecipeDef, emptyMsg, title string, autoOption string) {
- if len(recipes) == 0 {
+func (g *Game) showRecipeChoice(sess *net.Session, p *player.Player, items []*object.ItemDef, emptyMsg, title string, autoOption string) {
+ if len(items) == 0 {
sess.WriteLine(emptyMsg)
return
}
- if len(recipes) == 1 {
+ if len(items) == 1 {
if autoOption != "" && p.OptionBool(autoOption) {
- g.startProductionFromRecipe(sess, p, &recipes[0], 0)
+ g.startProductionFromItem(sess, p, items[0], 0)
return
}
- g.promptHowMany(sess, recipes[0].ID)
+ g.promptHowMany(sess, items[0].ID)
return
}
sess.State = net.StateRecipeChoice
- sess.PendingMenu = recipeMenuData(recipeDefIDs(recipes))
+ sess.PendingMenu = itemMenuData(itemIDs(items))
var names []string
- for i := range recipes {
- names = append(names, g.recipeName(sess, &recipes[i]))
+ for _, item := range items {
+ names = append(names, g.itemName(sess, item))
}
g.showMenuTable(sess, title, names)
}
func (g *Game) handleHowMany(sess *net.Session, input string) {
p := sess.Player
- recipeID := sess.PendingRecipeID
- sess.PendingRecipeID = ""
+ itemID := sess.PendingItemID
+ sess.PendingItemID = ""
sess.State = net.StateGame
input = strings.TrimSpace(input)
@@ -766,46 +769,11 @@ func (g *Game) handleHowMany(sess *net.Session, input string) {
return
}
- var recipe *action.RecipeDef
- if strings.HasPrefix(recipeID, "combine_") {
- itemID := strings.TrimPrefix(recipeID, "combine_")
- itemDef, err := g.ItemStore.Load(itemID)
- if err != nil || len(itemDef.MadeFrom) == 0 {
- g.reprompt(sess)
- return
- }
- recipe = g.buildCombineRecipe(itemDef)
- } else {
- recipe = g.RecipeStore.GetByID(recipeID)
- }
-
- if recipe == nil {
+ item := g.loadCraftItem(itemID)
+ if item == nil {
g.reprompt(sess)
return
}
- g.startProductionFromRecipe(sess, p, recipe, count)
-}
-
-func (g *Game) buildCombineRecipe(def *object.ItemDef) *action.RecipeDef {
- var consume []action.ConsumeEntry
- for _, mf := range def.MadeFrom {
- consume = append(consume, action.ConsumeEntry{
- Items: mf.Items,
- Quantity: mf.Quantity,
- Byproducts: mf.Byproducts,
- })
- }
- wait := def.Ticks
- if wait <= 0 {
- wait = 2
- }
- return &action.RecipeDef{
- ID: "combine_" + def.ID,
- Type: "combine",
- Wait: wait,
- Consume: consume,
- Output: def.ID,
- Message: fmt.Sprintf("You create %s.", def.Name),
- }
+ g.startProductionFromItem(sess, p, item, count)
}
diff --git a/internal/game/core_startup.go b/internal/game/core_startup.go
index ef33ff7..4bdb50d 100644
--- a/internal/game/core_startup.go
+++ b/internal/game/core_startup.go
@@ -25,7 +25,6 @@ func (g *Game) ValidateStartup() []StartupIssue {
issues = append(issues, validateIDs(g.DataDir, "mobs", "mob")...)
issues = append(issues, validateIDs(g.DataDir, "objects", "object")...)
issues = append(issues, validateIDs(g.DataDir, "drops", "drop table")...)
- issues = append(issues, validateIDs(g.DataDir, "recipes", "recipe")...)
issues = append(issues, validateIDs(g.DataDir, "courses", "course")...)
issues = append(issues, validateIDs(g.DataDir, "modules", "module")...)
issues = append(issues, validateIDs(g.DataDir, "help", "help topic")...)
@@ -34,7 +33,7 @@ func (g *Game) ValidateStartup() []StartupIssue {
issues = append(issues, validateMobs(g)...)
issues = append(issues, validateObjects(g)...)
issues = append(issues, validateItems(g)...)
- issues = append(issues, validateRecipes(g)...)
+ issues = append(issues, validateCraftBlocks(g)...)
issues = append(issues, validateDropTables(g)...)
issues = append(issues, validateCourses(g)...)
issues = append(issues, validateRoomWiring(g)...)
@@ -368,29 +367,6 @@ func validateItems(g *Game) []StartupIssue {
})
}
- for _, mf := range def.MadeFrom {
- for _, item := range mf.Items {
- if item != "" && !itemIDs[item] {
- issues = append(issues, StartupIssue{
- Level: "ERROR",
- Type: "reference",
- Message: fmt.Sprintf("Item %q: made_from item %q does not exist",
- id, item),
- })
- }
- }
- for _, bp := range mf.Byproducts {
- if bp != "" && !itemIDs[bp] {
- issues = append(issues, StartupIssue{
- Level: "ERROR",
- Type: "reference",
- Message: fmt.Sprintf("Item %q: made_from byproduct %q does not exist",
- id, bp),
- })
- }
- }
- }
-
if def.FarmProduct != "" && !itemIDs[def.FarmProduct] {
issues = append(issues, StartupIssue{
Level: "ERROR",
@@ -404,64 +380,61 @@ func validateItems(g *Game) []StartupIssue {
return issues
}
-func validateRecipes(g *Game) []StartupIssue {
+func validateCraftBlocks(g *Game) []StartupIssue {
var issues []StartupIssue
itemIDs := g.ItemStore.IDSet()
+ objIDs := g.ObjectStore.IDSet()
- allRecipes, err := g.RecipeStore.LoadAll()
+ allItems, err := g.ItemStore.LoadAll()
if err != nil {
issues = append(issues, StartupIssue{
Level: "ERROR",
Type: "reference",
- Message: fmt.Sprintf("Failed to load recipes: %v", err),
+ Message: fmt.Sprintf("Failed to load items: %v", err),
})
return issues
}
- recipeIDs := make(map[string]bool)
- for _, r := range allRecipes {
- if r.ID != "" {
- if recipeIDs[r.ID] {
+ for _, item := range allItems {
+ if len(item.Craft) == 0 {
+ continue
+ }
+ for ci := range item.Craft {
+ c := &item.Craft[ci]
+
+ for _, e := range c.Consume {
+ for _, consumeItem := range e.Items {
+ if consumeItem != "" && !itemIDs[consumeItem] {
+ issues = append(issues, StartupIssue{
+ Level: "ERROR",
+ Type: "reference",
+ Message: fmt.Sprintf("Item %q (craft: %s): consumes nonexistent item %q",
+ item.ID, c.Type, consumeItem),
+ })
+ }
+ }
+ }
+
+ if c.Fail != "" && !itemIDs[c.Fail] {
issues = append(issues, StartupIssue{
Level: "ERROR",
- Type: "duplicate",
- Message: fmt.Sprintf("Recipe %q: duplicate ID field (two recipe files with same ID value)",
- r.ID),
+ Type: "reference",
+ Message: fmt.Sprintf("Item %q (craft: %s): fail product %q does not exist",
+ item.ID, c.Type, c.Fail),
})
}
- recipeIDs[r.ID] = true
- }
- for _, e := range r.Consume {
- for _, item := range e.Items {
- if item != "" && !itemIDs[item] {
+ for _, sid := range c.Station {
+ if sid != "" && !objIDs[sid] {
issues = append(issues, StartupIssue{
Level: "ERROR",
Type: "reference",
- Message: fmt.Sprintf("Recipe %q (%s): consumes nonexistent item %q",
- r.ID, r.Type, item),
+ Message: fmt.Sprintf("Item %q (craft: %s): station object %q does not exist",
+ item.ID, c.Type, sid),
})
}
}
}
-
- if r.Output != "" && !itemIDs[r.Output] {
- issues = append(issues, StartupIssue{
- Level: "ERROR",
- Type: "reference",
- Message: fmt.Sprintf("Recipe %q (%s): output item %q does not exist",
- r.ID, r.Type, r.Output),
- })
- }
-
- if r.Fail != "" && !itemIDs[r.Fail] {
- issues = append(issues, StartupIssue{
- Level: "ERROR",
- Type: "reference",
- Message: fmt.Sprintf("Recipe %q (%s): fail product %q does not exist",
- r.ID, r.Type, r.Fail),
- })
- }
}
return issues
@@ -778,4 +751,4 @@ func LogStartupIssues(issues []StartupIssue) {
if errors > 0 {
fmt.Fprintf(os.Stderr, "*** %d startup errors detected. The server may behave unexpectedly. ***\n", errors)
}
-} \ No newline at end of file
+}
diff --git a/internal/game/craft_index.go b/internal/game/craft_index.go
new file mode 100644
index 0000000..078ec3c
--- /dev/null
+++ b/internal/game/craft_index.go
@@ -0,0 +1,234 @@
+package game
+
+import (
+ "sync"
+
+ "thehouseoficarus/internal/object"
+)
+
+type CraftIndex struct {
+ mu sync.RWMutex
+ byType map[string][]*object.ItemDef
+ byInput map[string][]*object.ItemDef
+ byStation map[string][]*object.ItemDef
+}
+
+func NewCraftIndex() *CraftIndex {
+ return &CraftIndex{
+ byType: make(map[string][]*object.ItemDef),
+ byInput: make(map[string][]*object.ItemDef),
+ byStation: make(map[string][]*object.ItemDef),
+ }
+}
+
+func (idx *CraftIndex) Build(items []*object.ItemDef) {
+ idx.mu.Lock()
+ defer idx.mu.Unlock()
+
+ idx.byType = make(map[string][]*object.ItemDef)
+ idx.byInput = make(map[string][]*object.ItemDef)
+ idx.byStation = make(map[string][]*object.ItemDef)
+
+ for _, def := range items {
+ if len(def.Craft) == 0 {
+ continue
+ }
+ for ci := range def.Craft {
+ craft := &def.Craft[ci]
+ t := craft.Type
+ if t == "" {
+ t = "combine"
+ }
+ idx.byType[t] = appendUnique(idx.byType[t], def)
+
+ for _, e := range craft.Consume {
+ for _, id := range e.Items {
+ idx.byInput[id] = appendUnique(idx.byInput[id], def)
+ }
+ }
+
+ for _, sid := range craft.Station {
+ idx.byStation[sid] = appendUnique(idx.byStation[sid], def)
+ }
+ }
+ }
+}
+
+func appendUnique(items []*object.ItemDef, item *object.ItemDef) []*object.ItemDef {
+ for _, existing := range items {
+ if existing == item {
+ return items
+ }
+ }
+ return append(items, item)
+}
+
+func (idx *CraftIndex) ByType(craftType string) []*object.ItemDef {
+ idx.mu.RLock()
+ defer idx.mu.RUnlock()
+ items := idx.byType[craftType]
+ out := make([]*object.ItemDef, len(items))
+ copy(out, items)
+ return out
+}
+
+func (idx *CraftIndex) ByStation(stationDefID string) []*object.ItemDef {
+ idx.mu.RLock()
+ defer idx.mu.RUnlock()
+ items := idx.byStation[stationDefID]
+ out := make([]*object.ItemDef, len(items))
+ copy(out, items)
+ return out
+}
+
+func (idx *CraftIndex) ByInput(itemID string) []*object.ItemDef {
+ idx.mu.RLock()
+ defer idx.mu.RUnlock()
+ items := idx.byInput[itemID]
+ out := make([]*object.ItemDef, len(items))
+ copy(out, items)
+ return out
+}
+
+func (idx *CraftIndex) FindByTwoInputs(itemA, itemB string) []*object.ItemDef {
+ idx.mu.RLock()
+ defer idx.mu.RUnlock()
+
+ candidatesA := idx.byInput[itemA]
+ if len(candidatesA) == 0 {
+ return nil
+ }
+
+ candidateSetB := make(map[string]bool)
+ for _, def := range idx.byInput[itemB] {
+ candidateSetB[def.ID] = true
+ }
+
+ var results []*object.ItemDef
+ for _, def := range candidatesA {
+ if !candidateSetB[def.ID] {
+ continue
+ }
+ for _, craft := range def.Craft {
+ entryA := craftEntry(craft, itemA)
+ entryB := craftEntry(craft, itemB)
+ if entryA >= 0 && entryB >= 0 && entryA != entryB {
+ results = append(results, def)
+ break
+ }
+ }
+ }
+ return results
+}
+
+func (idx *CraftIndex) FindByStationInput(stationDefID, itemID string) []*object.ItemDef {
+ idx.mu.RLock()
+ defer idx.mu.RUnlock()
+
+ var results []*object.ItemDef
+ for _, def := range idx.byStation[stationDefID] {
+ for _, craft := range def.Craft {
+ if craftMatchesEntry(&craft, itemID) {
+ results = append(results, def)
+ break
+ }
+ }
+ }
+ return results
+}
+
+func craftEntryFor(def *object.ItemDef, itemID string) int {
+ for _, craft := range def.Craft {
+ if ei := craftEntry(craft, itemID); ei >= 0 {
+ return ei
+ }
+ }
+ return -1
+}
+
+func craftEntry(craft object.CraftDef, itemID string) int {
+ for ei, e := range craft.Consume {
+ for _, id := range e.Items {
+ if id == itemID {
+ return ei
+ }
+ }
+ }
+ return -1
+}
+
+func craftMatchesEntry(c *object.CraftDef, itemID string) bool {
+ if c == nil {
+ return false
+ }
+ for _, e := range c.Consume {
+ for _, id := range e.Items {
+ if id == itemID {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func craftHasAllItems(c *object.CraftDef, hasItem func(string) bool) bool {
+ if c == nil {
+ return false
+ }
+ for _, e := range c.Consume {
+ found := false
+ for _, id := range e.Items {
+ if hasItem(id) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return false
+ }
+ }
+ return true
+}
+
+func craftHasAllItemsQty(c *object.CraftDef, countItem func(string) int) bool {
+ if c == nil {
+ return false
+ }
+ for _, e := range c.Consume {
+ found := false
+ for _, id := range e.Items {
+ qty := e.Quantity
+ if qty <= 0 {
+ qty = 1
+ }
+ if countItem(id) >= qty {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return false
+ }
+ }
+ return true
+}
+
+func craftConsumeAll(c *object.CraftDef, hasItem func(string) bool, removeItem func(string, int) bool) bool {
+ if c == nil {
+ return false
+ }
+ for _, e := range c.Consume {
+ found := false
+ for _, id := range e.Items {
+ if hasItem(id) {
+ removeItem(id, e.Quantity)
+ found = true
+ break
+ }
+ }
+ if !found {
+ return false
+ }
+ }
+ return true
+}
diff --git a/internal/game/game.go b/internal/game/game.go
index ac8622e..0dd5bdc 100644
--- a/internal/game/game.go
+++ b/internal/game/game.go
@@ -7,7 +7,6 @@ import (
"sync"
"time"
- "thehouseoficarus/internal/action"
"thehouseoficarus/internal/combat"
"thehouseoficarus/internal/config"
"thehouseoficarus/internal/engine"
@@ -40,7 +39,7 @@ type Game struct {
ItemStore *object.ItemStore
AccountStore *player.AccountStore
MobStore *world.MobStore
- RecipeStore *action.RecipeStore
+ CraftIndex *CraftIndex
CourseStore *CourseStore
Hub *net.Hub
Ticks *engine.Engine
@@ -67,7 +66,7 @@ func New(dataDir string, colorConfig *config.ColorsConfig, valConfig config.Vali
ItemStore: object.NewItemStore(dataDir),
AccountStore: player.NewAccountStore(dataDir),
MobStore: world.NewMobStore(dataDir),
- RecipeStore: action.NewRecipeStore(dataDir),
+ CraftIndex: NewCraftIndex(),
CourseStore: NewCourseStore(dataDir),
Ticks: engine.New(),
WorldFlags: make(map[string]any),
@@ -85,6 +84,7 @@ func New(dataDir string, colorConfig *config.ColorsConfig, valConfig config.Vali
}
g.CourseStore.LoadAll()
g.LoadMods()
+ g.buildCraftIndex()
g.ValidateAndLog()
return g
}
@@ -331,6 +331,14 @@ func (g *Game) ProcessQueuedCommands() {
g.flushPendingDepletions()
}
+func (g *Game) buildCraftIndex() {
+ items, err := g.ItemStore.LoadAll()
+ if err != nil {
+ return
+ }
+ g.CraftIndex.Build(items)
+}
+
type pendingDepletion struct {
instanceKey string
objDefID string
diff --git a/internal/net/server.go b/internal/net/server.go
index 6706a3a..eaa8f17 100644
--- a/internal/net/server.go
+++ b/internal/net/server.go
@@ -51,7 +51,7 @@ type Session struct {
PendingChar string
PendingPass string
PendingMenu []map[string]string
- PendingRecipeID string
+ PendingItemID string
PendingSkill string
PendingLastFlag string
PendingBackground bool
diff --git a/internal/object/item.go b/internal/object/item.go
index 8ec1565..241cc0b 100644
--- a/internal/object/item.go
+++ b/internal/object/item.go
@@ -1,11 +1,33 @@
package object
import (
+ "fmt"
"strings"
- "thehouseoficarus/internal/action"
+ "gopkg.in/yaml.v3"
)
+func WordPrefixMatch(input, name string) bool {
+ inputWords := strings.Fields(strings.ToLower(input))
+ if len(inputWords) == 0 {
+ return false
+ }
+ nameWords := strings.Fields(strings.ToLower(name))
+ for _, iw := range inputWords {
+ found := false
+ for _, nw := range nameWords {
+ if strings.HasPrefix(nw, iw) || strings.HasPrefix(iw, nw) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return false
+ }
+ }
+ return true
+}
+
type EquipSlot string
const (
@@ -56,8 +78,7 @@ type ItemDef struct {
SearchMessage string `yaml:"search_message"`
HealValue int `yaml:"heal_value"`
EatMessage string `yaml:"eat_message"`
- MadeFrom []MadeFromEntry `yaml:"made_from,omitempty"`
- Ticks float64 `yaml:"ticks"`
+ Craft CraftList `yaml:"craft,omitempty"`
ProvidesJunk string `yaml:"provides_junk,omitempty"`
FarmPatchType string `yaml:"farm_patch_type"`
FarmLevel int `yaml:"farm_level"`
@@ -72,12 +93,90 @@ type ItemDef struct {
PotionDuration float64 `yaml:"potion_duration"`
}
-type MadeFromEntry struct {
+type CraftList []CraftDef
+
+func (cl *CraftList) UnmarshalYAML(value *yaml.Node) error {
+ switch value.Kind {
+ case yaml.SequenceNode:
+ return value.Decode((*[]CraftDef)(cl))
+ case yaml.MappingNode:
+ var single CraftDef
+ if err := value.Decode(&single); err != nil {
+ return err
+ }
+ *cl = []CraftDef{single}
+ return nil
+ }
+ return fmt.Errorf("craft: expected mapping or sequence")
+}
+
+func (d *ItemDef) FirstCraft() *CraftDef {
+ if len(d.Craft) == 0 {
+ return nil
+ }
+ return &d.Craft[0]
+}
+
+func (d *ItemDef) FindCraft(fn func(CraftDef) bool) *CraftDef {
+ for i := range d.Craft {
+ if fn(d.Craft[i]) {
+ return &d.Craft[i]
+ }
+ }
+ return nil
+}
+
+func (d *ItemDef) MatchingCrafts(fn func(CraftDef) bool) []*CraftDef {
+ var result []*CraftDef
+ for i := range d.Craft {
+ if fn(d.Craft[i]) {
+ result = append(result, &d.Craft[i])
+ }
+ }
+ return result
+}
+
+type ConsumeEntry struct {
Items []string `yaml:"items"`
Quantity int `yaml:"quantity"`
Byproducts []string `yaml:"byproducts"`
}
+type CraftStep struct {
+ Tick float64 `yaml:"tick"`
+ Message string `yaml:"message"`
+}
+
+type SuccessFormula struct {
+ Base float64 `yaml:"base"`
+ PerLevel float64 `yaml:"per_level"`
+ Cap float64 `yaml:"cap"`
+}
+
+type CraftDef struct {
+ Type string `yaml:"type"`
+ Skill string `yaml:"skill"`
+ Level int `yaml:"level"`
+ XP int `yaml:"xp"`
+ Wait float64 `yaml:"wait"`
+ Station []string `yaml:"station"`
+ Tool string `yaml:"tool"`
+ Consume []ConsumeEntry `yaml:"consume"`
+ OutputQty int `yaml:"output_qty"`
+ Fail string `yaml:"fail"`
+ Message string `yaml:"message"`
+ FailMessage string `yaml:"fail_message"`
+ Steps []CraftStep `yaml:"steps"`
+ Success *SuccessFormula `yaml:"success"`
+}
+
+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"`
@@ -105,5 +204,5 @@ func (d *ItemDef) MatchesName(input string) bool {
if strings.ToLower(d.Name) == lower {
return true
}
- return action.WordPrefixMatch(lower, d.Name)
+ return WordPrefixMatch(lower, d.Name)
}
diff --git a/internal/world/mob.go b/internal/world/mob.go
index 9d07532..9ca1a45 100644
--- a/internal/world/mob.go
+++ b/internal/world/mob.go
@@ -9,6 +9,7 @@ import (
"sync"
"thehouseoficarus/internal/action"
+ "thehouseoficarus/internal/object"
"gopkg.in/yaml.v3"
)
@@ -122,7 +123,7 @@ func (m *MobInstance) MatchQuality(input string) int {
if strings.ToLower(m.Name) == lower {
return MatchExact
}
- if action.WordPrefixMatch(input, m.Name) {
+ if object.WordPrefixMatch(input, m.Name) {
return MatchPrefix
}
return MatchNone
diff --git a/internal/world/objects.go b/internal/world/objects.go
index 72e4a6d..ae2586a 100644
--- a/internal/world/objects.go
+++ b/internal/world/objects.go
@@ -6,7 +6,7 @@ import (
"sort"
"strings"
- "thehouseoficarus/internal/action"
+ "thehouseoficarus/internal/object"
)
type ObjState struct {
@@ -181,7 +181,7 @@ func (w *World) FindObjInstances(roomID int, name string) []ObjState {
}
func wordMatchesObj(lower, defID, objName string) bool {
- if action.WordPrefixMatch(lower, objName) {
+ if object.WordPrefixMatch(lower, objName) {
return true
}
inputWords := strings.Fields(strings.ToLower(lower))
diff --git a/worldbuilding_guide/recipes.md b/worldbuilding_guide/recipes.md
index 335a6cc..e442c8c 100644
--- a/worldbuilding_guide/recipes.md
+++ b/worldbuilding_guide/recipes.md
@@ -1,36 +1,52 @@
# The House of Icarus - World Building Guide
-## Recipes
+## Crafting
-Recipes define how items are processed on stations to produce new items. They are the foundation for cooking, smithing, crafting, and fletching.
+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.
-Recipe files live in `data/recipes/<type>/<id>.yaml`. Two formats are supported: **individual** (one recipe per file) and **consolidated** (multiple related recipes per file). Consolidated format is preferred when many recipes share the same type, station, and materials (e.g., all bronze smithing products in one file).
-
-### Recipe vs MadeFrom
-
-- **Recipes** (`data/recipes/`) use a station (fire, range, furnace, anvil, etc.). The player must be near the station object.
-- **MadeFrom** (on the item YAML directly) is for item-on-item combinations that need no station. The player combines ingredients from their inventory.
-
-### Recipe YAML Reference
+### Craft YAML Reference
| Field | Type | Description |
| -------------- | -------------- | ----------------------------------------------- |
-| `id` | string | Unique recipe identifier |
-| `type` | string | Recipe type (cooking, smithing, crafting, etc.) |
-| `skill` | string | Skill checked for success |
+| `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) |
| `level` | int | Required skill level |
| `xp` | int | XP awarded on success |
-| `wait` | float64 | Ticks per craft cycle (default 6) |
-| `station` | []string | Station object IDs required |
+| `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) |
-| `output` | string | ItemID produced on success |
| `output_qty` | int | Quantity produced (default 1, for stackables) |
| `fail` | string | ItemID produced on failure (optional) |
| `message` | string | Success message |
| `fail_message` | string | Failure message |
+| `steps` | []CraftStep | Multi-step messages at tick intervals (optional)|
| `success` | SuccessFormula | Optional skill check formula (see Behaviors) |
-The recipe's name in menus comes from the output item's `name` field, colored using the output item's `color`.
+The output is the item itself — no `output` field needed. The item ID IS the craft ID.
+
+### CraftStep — Multi-Step Messages
+
+Optional interim messages fired at specific tick offsets during the craft cycle. The `tick` is the number of ticks elapsed since the start of the cycle.
+
+```yaml
+craft:
+ type: ""
+ wait: 6
+ consume:
+ - items: [map_piece_1]
+ quantity: 1
+ - items: [map_piece_2]
+ quantity: 1
+ - items: [map_piece_3]
+ quantity: 1
+ steps:
+ - tick: 1
+ message: "You try to puzzle how the pieces fit together."
+ - tick: 3
+ message: "Aha, this edge lines up here!"
+ message: "You assemble the map!"
+```
### ConsumeEntry — Multi-Item Ingredient Slots
@@ -51,199 +67,153 @@ consume:
quantity: 1
byproducts: [empty_bucket, ""] # bucket returns empty, vial is consumed entirely
- items: [herb]
- quantity: 1 # no byproducts — herb is consumed entirely
+ quantity: 1 # no byproducts — herb is consumed entirely
```
-### Consolidated Format
-
-For recipe categories where many recipes share the same `type`, `station`, and `wait` (e.g., smithing all bronze items from bronze bars, fletching all arrow shafts from logs), use a **consolidated** file. Shared fields are set once at the top level; each product entry provides only the fields that differ.
-
-**Loader behavior:** At load time, the loader expands each `products` entry into an individual `RecipeDef` with the same shape as the individual format. The production engine sees no difference — all lookup, matching, and execution code works identically.
-
-**Inheritance:** Child fields override parent. `consume` at the top level provides a default ingredient list; any product with different `consume` (e.g., a platebody needing 5 bars instead of 1) specifies its own. `output_qty`, `message`, `fail`, and `level` are per-product.
+### Skill-Based Production
```yaml
-# data/recipes/smithing/bronze.yaml — consolidated: 8 products in one file
-
-type: smithing # shared by all products
-station: [anvil] # shared by all products
-wait: 4 # shared by all products
-consume: # default ingredient list — overridden per-product if needed
- - items: [bronze_bar]
- quantity: 1
-
-products:
- - id: smith_bronze_dagger
- output: bronze_dagger
- level: 1
- xp: 12
- # inherits consume: 1 bronze_bar from parent
-
- - id: smith_bronze_nails
- output: bronze_nails
- level: 4
- xp: 12
- output_qty: 15 # stackable output, 15 per cycle
-
- - id: smith_bronze_full_helm
- output: bronze_full_helm
- level: 7
- xp: 25
- consume: # overrides parent — 2 bars instead of 1
- - items: [bronze_bar]
- quantity: 2
-
- - id: smith_bronze_platebody
- output: bronze_platebody
- level: 18
- xp: 62
- consume: # overrides parent — 5 bars
- - items: [bronze_bar]
- quantity: 5
-
- # ... dagger, sword, med_helm, arrowtips, bolts_unf ...
+# data/items/consumables/stim_potion.yaml
+craft:
+ type: pharmacy
+ skill: pharmacy
+ level: 3
+ xp: 25
+ wait: 4
+ consume:
+ - items: [guam_potion_unf]
+ quantity: 1
+ - items: [eye_of_newt]
+ quantity: 1
+ message: "You mix a stim potion."
```
-**Use consolidated files when:**
-- Many recipes share the same `type`, `station`, and `material`
-- Adding a new tier means copy-pasting identical consume/station/wait fields
-- The relationship between recipes is easier to understand as a group
-
-**Use individual files when:**
-- The recipe is unique and doesn't share structure with others (e.g., compound smelting recipes with different ore ratios)
-- You want fine-grained version control on a single recipe
-- The recipe is complex enough that a consolidated file would be harder to read
-
-Individual and consolidated files coexist — the loader handles both transparently.
-
-### Station-based recipe (cooking on a fire)
+### Station-Based Production
```yaml
-id: cook_trout
-type: cooking
-skill: cooking
-level: 15
-xp: 70
-wait: 6
-station: [fire, cooking_range]
-consume:
- - items: [raw_trout]
- quantity: 1
-output: trout
-fail: burnt_fish
-message: "Cooked to perfection. It looks great!"
-fail_message: "You accidentally burn the trout."
+# data/items/materials/bronze_bar.yaml
+craft:
+ type: smelting
+ skill: smithing
+ level: 1
+ xp: 6
+ wait: 4
+ station: [furnace]
+ consume:
+ - items: [copper_ore]
+ quantity: 1
+ - items: [tin_ore]
+ quantity: 1
+ message: "You smelt a bronze bar."
```
-### Station-specific recipe (range only, not fire)
-
```yaml
-id: cook_bread
-type: cooking
-skill: cooking
-level: 1
-xp: 40
-wait: 6
-station: [cooking_range]
-consume:
- - items: [bread_dough]
- quantity: 1
-output: bread
-fail: burnt_meat
-message: "You bake the dough into a fresh loaf of bread."
-fail_message: "You burn the bread to a crisp."
+# data/items/equipment/bronze_dagger.yaml
+craft:
+ type: smithing
+ skill: smithing
+ level: 1
+ xp: 12
+ wait: 4
+ station: [anvil]
+ consume:
+ - items: [bronze_bar]
+ quantity: 1
+ message: "You hammer out a bronze dagger."
```
-### Smelting recipe (ore to bar at a furnace)
-
-Smelting recipes use `type: smelting` and `station: [furnace]`. The `smelt` command or `use <ore> on furnace` triggers them. Multi-ore recipes use `qty` on consume entries.
+For stackable outputs, use `output_qty`:
```yaml
-id: smelt_steel
-type: smelting
-skill: smithing
-level: 40
-xp: 17
-wait: 4
-station: [furnace]
-consume:
- - items: [iron_ore]
- quantity: 1
- - items: [coal]
- quantity: 2
-output: steel_bar
-message: "You remove a white hot steel bar!"
-fail_message: "You fail to smelt a usable bar."
+# data/items/ammo/bronze_nails.yaml
+craft:
+ type: smithing
+ skill: smithing
+ level: 4
+ xp: 12
+ wait: 4
+ station: [anvil]
+ consume:
+ - items: [bronze_bar]
+ quantity: 1
+ output_qty: 15
+ message: "You hammer out some bronze nails."
```
-Iron smelting uses a `success` formula for its 50% failure rate:
+### Tool-Based Production
```yaml
-success:
- base: 0.5
- per_level: 0
- cap: 0.5
+# data/items/ammo/arrow_shafts.yaml
+craft:
+ type: fletching
+ level: 1
+ xp: 5
+ wait: 3
+ tool: knife
+ consume:
+ - items: [logs, oak_logs, willow_logs]
+ quantity: 1
+ output_qty: 15
```
-### Smithing recipe (bar to item at an anvil)
-
-Smithing recipes use `type: smithing` and `station: [anvil]`. The `smith` command or `use <bar> on anvil` triggers them. Requires a hammer (`tool_type: hammer`) in inventory.
+The `tool` field requires the player to have an item with that `tool_type` equipped or in inventory. The tool is NOT consumed.
-**Recommended:** Use consolidated format (one file per metal type) since all products for a given metal share the same bar, station, and wait. See [Consolidated Format](#consolidated-format) above.
+### Skill-Less Combinations (Item-on-Item)
-The individual format is also supported:
+Omit `type` (or leave it empty) for combinations with no skill check, no XP, and 100% success:
```yaml
-id: smith_steel_platebody
-type: smithing
-skill: smithing
-level: 48
-xp: 187
-wait: 4
-station: [anvil]
-consume:
- - items: [steel_bar]
- quantity: 5
-output: steel_platebody
-message: "You hammer out a steel platebody."
+# data/items/consumables/bucket_of_water.yaml
+craft:
+ wait: 0
+ consume:
+ - items: [vial_of_water, jug_of_water]
+ quantity: 1
+ byproducts: [empty_vial, empty_jug]
+ - items: [empty_bucket]
+ quantity: 1
+ message: "You pour water into the bucket."
```
-For stackable outputs, use `output_qty`:
+### Multi-Piece Assembly (3+ items → 1)
```yaml
-id: smith_iron_nails
-type: smithing
-skill: smithing
-level: 24
-xp: 25
-wait: 4
-station: [anvil]
-consume:
- - items: [iron_bar]
- quantity: 1
-output: iron_nails
-output_qty: 15
-message: "You hammer out some iron nails."
+# data/items/quest/ancient_map.yaml
+craft:
+ wait: 0
+ consume:
+ - items: [torn_page_1]
+ quantity: 1
+ - items: [torn_page_2]
+ quantity: 1
+ - items: [torn_page_3]
+ quantity: 1
+ steps:
+ - tick: 1
+ message: "You try to puzzle how the pieces fit together."
+ - tick: 3
+ message: "Aha, this edge lines up here!"
+ message: "You assemble the ancient map."
```
-### MadeFrom — Item-on-item combinations
+### Clean Recipes
-Item-on-item combinations are defined directly on the output item via the `made_from` field. No recipe file needed.
+Clean herb recipes use `type: clean` and are handled as background actions (one herb per cycle, directly mutating inventory):
```yaml
-# data/items/bread_dough.yaml
-id: bread_dough
-name: bread dough
-color: "222"
-made_from:
- - items: [pot_of_flour]
- quantity: 1
- - items: [bucket_of_water, pitcher_of_water, vial_of_water]
- quantity: 1
+# data/items/materials/guam.yaml (clean guam)
+craft:
+ type: clean
+ skill: pharmacy
+ level: 3
+ xp: 3
+ wait: 2
+ consume:
+ - items: [grimy_guam]
+ quantity: 1
+ message: "You clean the grimy guam leaf."
```
-The player uses `use flour on water` to combine them. The system finds that `bread_dough` can be made from these ingredients and produces it.
-
### Food/Healing Items
Items with `heal_value` and `eat_message` can be consumed via the `eat` command.
@@ -257,3 +227,13 @@ value: 5
heal_value: 5
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.
+
+**Indexes:**
+- `byType["pharmacy"]` → all pharmacy-craftable items (used by `mix`)
+- `byInput["guam"]` → all items that consume 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