diff options
267 files changed, 1372 insertions, 1957 deletions
@@ -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. 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, 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. **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. @@ -91,7 +91,7 @@ data/mobs/<id>.yaml Mob definitions (combat stats, drops, behavior, steal 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 (cooking, smithing, crafting, fletching, etc.) +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) @@ -516,7 +516,7 @@ 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/` with the new `type` +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`. 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 @@ -524,6 +524,40 @@ Production skills share a unified system in `core_production.go`: 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`. +## Recipe Loading and Lookup + +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. + +**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. + +**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. + ## Adding a New Command 1. Add a `cmd_*.go` file in `internal/game/` with the handler function `func (g *Game) executeXxx(sess *net.Session, args []string, rawInput string)` diff --git a/data/recipes/clean/clean_avantoe.yaml b/data/recipes/clean/avantoe.yaml index 61f8f87..fe00db5 100644 --- a/data/recipes/clean/clean_avantoe.yaml +++ b/data/recipes/clean/avantoe.yaml @@ -1,4 +1,4 @@ -id: clean_avantoe +id: avantoe type: clean skill: pharmacy level: 48 diff --git a/data/recipes/clean/clean_cadantine.yaml b/data/recipes/clean/cadantine.yaml index a013b5d..8acb2c5 100644 --- a/data/recipes/clean/clean_cadantine.yaml +++ b/data/recipes/clean/cadantine.yaml @@ -1,4 +1,4 @@ -id: clean_cadantine +id: cadantine type: clean skill: pharmacy level: 65 diff --git a/data/recipes/clean/clean_dwarf_weed.yaml b/data/recipes/clean/dwarf_weed.yaml index 4ea9bb7..70c6c42 100644 --- a/data/recipes/clean/clean_dwarf_weed.yaml +++ b/data/recipes/clean/dwarf_weed.yaml @@ -1,4 +1,4 @@ -id: clean_dwarf_weed +id: dwarf_weed type: clean skill: pharmacy level: 70 diff --git a/data/recipes/clean/clean_guam.yaml b/data/recipes/clean/guam.yaml index d8bb492..410e309 100644 --- a/data/recipes/clean/clean_guam.yaml +++ b/data/recipes/clean/guam.yaml @@ -1,4 +1,4 @@ -id: clean_guam +id: guam type: clean skill: pharmacy level: 3 diff --git a/data/recipes/clean/clean_harralander.yaml b/data/recipes/clean/harralander.yaml index 15d9d22..f60a737 100644 --- a/data/recipes/clean/clean_harralander.yaml +++ b/data/recipes/clean/harralander.yaml @@ -1,4 +1,4 @@ -id: clean_harralander +id: harralander type: clean skill: pharmacy level: 20 diff --git a/data/recipes/clean/clean_irit.yaml b/data/recipes/clean/irit.yaml index f75603b..5853b89 100644 --- a/data/recipes/clean/clean_irit.yaml +++ b/data/recipes/clean/irit.yaml @@ -1,4 +1,4 @@ -id: clean_irit +id: irit type: clean skill: pharmacy level: 40 diff --git a/data/recipes/clean/clean_kwuarm.yaml b/data/recipes/clean/kwuarm.yaml index 1a11a84..04e50ad 100644 --- a/data/recipes/clean/clean_kwuarm.yaml +++ b/data/recipes/clean/kwuarm.yaml @@ -1,4 +1,4 @@ -id: clean_kwuarm +id: kwuarm type: clean skill: pharmacy level: 54 diff --git a/data/recipes/clean/clean_lantadyme.yaml b/data/recipes/clean/lantadyme.yaml index 076f20d..499dee9 100644 --- a/data/recipes/clean/clean_lantadyme.yaml +++ b/data/recipes/clean/lantadyme.yaml @@ -1,4 +1,4 @@ -id: clean_lantadyme +id: lantadyme type: clean skill: pharmacy level: 67 diff --git a/data/recipes/clean/clean_marrentill.yaml b/data/recipes/clean/marrentill.yaml index f1565dd..291ea21 100644 --- a/data/recipes/clean/clean_marrentill.yaml +++ b/data/recipes/clean/marrentill.yaml @@ -1,4 +1,4 @@ -id: clean_marrentill +id: marrentill type: clean skill: pharmacy level: 5 diff --git a/data/recipes/clean/clean_ranarr.yaml b/data/recipes/clean/ranarr.yaml index 2e639d8..989de1e 100644 --- a/data/recipes/clean/clean_ranarr.yaml +++ b/data/recipes/clean/ranarr.yaml @@ -1,4 +1,4 @@ -id: clean_ranarr +id: ranarr type: clean skill: pharmacy level: 25 diff --git a/data/recipes/clean/clean_snapdragon.yaml b/data/recipes/clean/snapdragon.yaml index 995ac8f..8819630 100644 --- a/data/recipes/clean/clean_snapdragon.yaml +++ b/data/recipes/clean/snapdragon.yaml @@ -1,4 +1,4 @@ -id: clean_snapdragon +id: snapdragon type: clean skill: pharmacy level: 59 diff --git a/data/recipes/clean/clean_tarromin.yaml b/data/recipes/clean/tarromin.yaml index 1ad55e1..89bdce3 100644 --- a/data/recipes/clean/clean_tarromin.yaml +++ b/data/recipes/clean/tarromin.yaml @@ -1,4 +1,4 @@ -id: clean_tarromin +id: tarromin type: clean skill: pharmacy level: 11 diff --git a/data/recipes/clean/clean_toadflax.yaml b/data/recipes/clean/toadflax.yaml index 11aafca..77fb955 100644 --- a/data/recipes/clean/clean_toadflax.yaml +++ b/data/recipes/clean/toadflax.yaml @@ -1,4 +1,4 @@ -id: clean_toadflax +id: toadflax type: clean skill: pharmacy level: 30 diff --git a/data/recipes/clean/clean_torstol.yaml b/data/recipes/clean/torstol.yaml index c9bac28..8833eb4 100644 --- a/data/recipes/clean/clean_torstol.yaml +++ b/data/recipes/clean/torstol.yaml @@ -1,4 +1,4 @@ -id: clean_torstol +id: torstol type: clean skill: pharmacy level: 75 diff --git a/data/recipes/construction/construct_mahogany_planks.yaml b/data/recipes/construction/mahogany_planks.yaml index 6634f78..3008a07 100644 --- a/data/recipes/construction/construct_mahogany_planks.yaml +++ b/data/recipes/construction/mahogany_planks.yaml @@ -1,4 +1,4 @@ -id: construct_mahogany_planks +id: mahogany_planks type: construction level: 50 xp: 30 diff --git a/data/recipes/construction/construct_mahogany_table.yaml b/data/recipes/construction/mahogany_table.yaml index 89d08e4..4b08c06 100644 --- a/data/recipes/construction/construct_mahogany_table.yaml +++ b/data/recipes/construction/mahogany_table.yaml @@ -1,4 +1,4 @@ -id: construct_mahogany_table +id: mahogany_table type: construction level: 52 xp: 120 diff --git a/data/recipes/construction/construct_oak_planks.yaml b/data/recipes/construction/oak_planks.yaml index 77c2fcb..da50f88 100644 --- a/data/recipes/construction/construct_oak_planks.yaml +++ b/data/recipes/construction/oak_planks.yaml @@ -1,4 +1,4 @@ -id: construct_oak_planks +id: oak_planks type: construction level: 15 xp: 10 diff --git a/data/recipes/construction/construct_oak_shelf.yaml b/data/recipes/construction/oak_shelf.yaml index 042e64f..c9f6242 100644 --- a/data/recipes/construction/construct_oak_shelf.yaml +++ b/data/recipes/construction/oak_shelf.yaml @@ -1,4 +1,4 @@ -id: construct_oak_shelf +id: oak_shelf type: construction level: 18 xp: 40 diff --git a/data/recipes/construction/construct_oak_table.yaml b/data/recipes/construction/oak_table.yaml index 8eafe15..6428200 100644 --- a/data/recipes/construction/construct_oak_table.yaml +++ b/data/recipes/construction/oak_table.yaml @@ -1,4 +1,4 @@ -id: construct_oak_table +id: oak_table type: construction level: 20 xp: 50 diff --git a/data/recipes/construction/construct_planks.yaml b/data/recipes/construction/planks.yaml index 3ed8058..9d202ff 100644 --- a/data/recipes/construction/construct_planks.yaml +++ b/data/recipes/construction/planks.yaml @@ -1,4 +1,4 @@ -id: construct_planks +id: planks type: construction level: 1 xp: 5 diff --git a/data/recipes/construction/construct_teak_planks.yaml b/data/recipes/construction/teak_planks.yaml index aec0888..1780d8b 100644 --- a/data/recipes/construction/construct_teak_planks.yaml +++ b/data/recipes/construction/teak_planks.yaml @@ -1,4 +1,4 @@ -id: construct_teak_planks +id: teak_planks type: construction level: 35 xp: 20 diff --git a/data/recipes/construction/construct_teak_table.yaml b/data/recipes/construction/teak_table.yaml index 88d4a2a..ce579e1 100644 --- a/data/recipes/construction/construct_teak_table.yaml +++ b/data/recipes/construction/teak_table.yaml @@ -1,4 +1,4 @@ -id: construct_teak_table +id: teak_table type: construction level: 38 xp: 80 diff --git a/data/recipes/construction/construct_wooden_chair.yaml b/data/recipes/construction/wooden_chair.yaml index 3a9120d..a1db252 100644 --- a/data/recipes/construction/construct_wooden_chair.yaml +++ b/data/recipes/construction/wooden_chair.yaml @@ -1,4 +1,4 @@ -id: construct_wooden_chair +id: wooden_chair type: construction level: 8 xp: 30 diff --git a/data/recipes/construction/construct_wooden_shelf.yaml b/data/recipes/construction/wooden_shelf.yaml index c8ea836..3ea97cc 100644 --- a/data/recipes/construction/construct_wooden_shelf.yaml +++ b/data/recipes/construction/wooden_shelf.yaml @@ -1,4 +1,4 @@ -id: construct_wooden_shelf +id: wooden_shelf type: construction level: 4 xp: 20 diff --git a/data/recipes/construction/construct_wooden_table.yaml b/data/recipes/construction/wooden_table.yaml index 21d6c53..73050a7 100644 --- a/data/recipes/construction/construct_wooden_table.yaml +++ b/data/recipes/construction/wooden_table.yaml @@ -1,4 +1,4 @@ -id: construct_wooden_table +id: wooden_table type: construction level: 6 xp: 25 diff --git a/data/recipes/cooking/cook_anchovies.yaml b/data/recipes/cooking/anchovies.yaml index bd4ea41..26bcc39 100644 --- a/data/recipes/cooking/cook_anchovies.yaml +++ b/data/recipes/cooking/anchovies.yaml @@ -1,4 +1,4 @@ -id: cook_anchovies +id: anchovies type: cooking level: 1 xp: 30 diff --git a/data/recipes/cooking/cook_bread.yaml b/data/recipes/cooking/bread.yaml index d1c68bf..0a27439 100644 --- a/data/recipes/cooking/cook_bread.yaml +++ b/data/recipes/cooking/bread.yaml @@ -1,4 +1,4 @@ -id: cook_bread +id: bread type: cooking level: 1 xp: 40 diff --git a/data/recipes/cooking/cook_herring.yaml b/data/recipes/cooking/herring.yaml index 3113092..486476c 100644 --- a/data/recipes/cooking/cook_herring.yaml +++ b/data/recipes/cooking/herring.yaml @@ -1,4 +1,4 @@ -id: cook_herring +id: herring type: cooking level: 5 xp: 50 diff --git a/data/recipes/cooking/cook_salmon.yaml b/data/recipes/cooking/salmon.yaml index 32c0fb0..c3362c0 100644 --- a/data/recipes/cooking/cook_salmon.yaml +++ b/data/recipes/cooking/salmon.yaml @@ -1,4 +1,4 @@ -id: cook_salmon +id: salmon type: cooking level: 25 xp: 90 diff --git a/data/recipes/cooking/cook_sardine.yaml b/data/recipes/cooking/sardine.yaml index fcda9b5..c03b7f0 100644 --- a/data/recipes/cooking/cook_sardine.yaml +++ b/data/recipes/cooking/sardine.yaml @@ -1,4 +1,4 @@ -id: cook_sardine +id: sardine type: cooking level: 1 xp: 40 diff --git a/data/recipes/cooking/cook_shrimps.yaml b/data/recipes/cooking/shrimps.yaml index feb4506..19ae396 100644 --- a/data/recipes/cooking/cook_shrimps.yaml +++ b/data/recipes/cooking/shrimps.yaml @@ -1,4 +1,4 @@ -id: cook_shrimps +id: shrimps type: cooking level: 1 xp: 30 diff --git a/data/recipes/cooking/cook_trout.yaml b/data/recipes/cooking/trout.yaml index cec6cd4..72587d5 100644 --- a/data/recipes/cooking/cook_trout.yaml +++ b/data/recipes/cooking/trout.yaml @@ -1,4 +1,4 @@ -id: cook_trout +id: trout type: cooking level: 15 xp: 70 diff --git a/data/recipes/crafting/craft_ball_of_wool.yaml b/data/recipes/crafting/ball_of_wool.yaml index ef25972..09fca8b 100644 --- a/data/recipes/crafting/craft_ball_of_wool.yaml +++ b/data/recipes/crafting/ball_of_wool.yaml @@ -1,4 +1,4 @@ -id: craft_ball_of_wool +id: ball_of_wool type: crafting level: 1 xp: 3 diff --git a/data/recipes/crafting/craft_bowstring.yaml b/data/recipes/crafting/bowstring.yaml index caca1a4..dfb4ea7 100644 --- a/data/recipes/crafting/craft_bowstring.yaml +++ b/data/recipes/crafting/bowstring.yaml @@ -1,4 +1,4 @@ -id: craft_bowstring +id: bowstring type: crafting level: 1 xp: 15 diff --git a/data/recipes/crafting/craft_coif.yaml b/data/recipes/crafting/coif.yaml index 4aa914a..8e6cac9 100644 --- a/data/recipes/crafting/craft_coif.yaml +++ b/data/recipes/crafting/coif.yaml @@ -1,4 +1,4 @@ -id: craft_coif +id: coif type: crafting level: 38 xp: 37 diff --git a/data/recipes/crafting/craft_cut_diamond.yaml b/data/recipes/crafting/cut_diamond.yaml index 175a518..e1e7343 100644 --- a/data/recipes/crafting/craft_cut_diamond.yaml +++ b/data/recipes/crafting/cut_diamond.yaml @@ -1,4 +1,4 @@ -id: craft_cut_diamond +id: cut_diamond type: crafting level: 43 xp: 108 diff --git a/data/recipes/crafting/craft_cut_emerald.yaml b/data/recipes/crafting/cut_emerald.yaml index f725103..405fb53 100644 --- a/data/recipes/crafting/craft_cut_emerald.yaml +++ b/data/recipes/crafting/cut_emerald.yaml @@ -1,4 +1,4 @@ -id: craft_cut_emerald +id: cut_emerald type: crafting level: 27 xp: 68 diff --git a/data/recipes/crafting/craft_cut_ruby.yaml b/data/recipes/crafting/cut_ruby.yaml index bfc5e88..02ab139 100644 --- a/data/recipes/crafting/craft_cut_ruby.yaml +++ b/data/recipes/crafting/cut_ruby.yaml @@ -1,4 +1,4 @@ -id: craft_cut_ruby +id: cut_ruby type: crafting level: 63 xp: 85 diff --git a/data/recipes/crafting/craft_cut_sapphire.yaml b/data/recipes/crafting/cut_sapphire.yaml index bfd9a00..330834f 100644 --- a/data/recipes/crafting/craft_cut_sapphire.yaml +++ b/data/recipes/crafting/cut_sapphire.yaml @@ -1,4 +1,4 @@ -id: craft_cut_sapphire +id: cut_sapphire type: crafting level: 20 xp: 50 diff --git a/data/recipes/crafting/craft_diamond_amulet_u.yaml b/data/recipes/crafting/diamond_amulet_u.yaml index 533ce2e..9c90b8c 100644 --- a/data/recipes/crafting/craft_diamond_amulet_u.yaml +++ b/data/recipes/crafting/diamond_amulet_u.yaml @@ -1,4 +1,4 @@ -id: craft_diamond_amulet_u +id: diamond_amulet_u type: crafting level: 70 xp: 100 diff --git a/data/recipes/crafting/craft_diamond_bracelet.yaml b/data/recipes/crafting/diamond_bracelet.yaml index f9486d3..0a15546 100644 --- a/data/recipes/crafting/craft_diamond_bracelet.yaml +++ b/data/recipes/crafting/diamond_bracelet.yaml @@ -1,4 +1,4 @@ -id: craft_diamond_bracelet +id: diamond_bracelet type: crafting level: 58 xp: 95 diff --git a/data/recipes/crafting/craft_diamond_necklace.yaml b/data/recipes/crafting/diamond_necklace.yaml index bdd4425..9c15245 100644 --- a/data/recipes/crafting/craft_diamond_necklace.yaml +++ b/data/recipes/crafting/diamond_necklace.yaml @@ -1,4 +1,4 @@ -id: craft_diamond_necklace +id: diamond_necklace type: crafting level: 56 xp: 90 diff --git a/data/recipes/crafting/craft_diamond_ring.yaml b/data/recipes/crafting/diamond_ring.yaml index eaf3a59..3045b74 100644 --- a/data/recipes/crafting/craft_diamond_ring.yaml +++ b/data/recipes/crafting/diamond_ring.yaml @@ -1,4 +1,4 @@ -id: craft_diamond_ring +id: diamond_ring type: crafting level: 43 xp: 85 diff --git a/data/recipes/crafting/craft_emerald_amulet_u.yaml b/data/recipes/crafting/emerald_amulet_u.yaml index 82773fb..4530fa2 100644 --- a/data/recipes/crafting/craft_emerald_amulet_u.yaml +++ b/data/recipes/crafting/emerald_amulet_u.yaml @@ -1,4 +1,4 @@ -id: craft_emerald_amulet_u +id: emerald_amulet_u type: crafting level: 31 xp: 70 diff --git a/data/recipes/crafting/craft_emerald_bracelet.yaml b/data/recipes/crafting/emerald_bracelet.yaml index 3c2f7b4..498b1ce 100644 --- a/data/recipes/crafting/craft_emerald_bracelet.yaml +++ b/data/recipes/crafting/emerald_bracelet.yaml @@ -1,4 +1,4 @@ -id: craft_emerald_bracelet +id: emerald_bracelet type: crafting level: 30 xp: 65 diff --git a/data/recipes/crafting/craft_emerald_necklace.yaml b/data/recipes/crafting/emerald_necklace.yaml index 7815c16..2d0596e 100644 --- a/data/recipes/crafting/craft_emerald_necklace.yaml +++ b/data/recipes/crafting/emerald_necklace.yaml @@ -1,4 +1,4 @@ -id: craft_emerald_necklace +id: emerald_necklace type: crafting level: 29 xp: 60 diff --git a/data/recipes/crafting/craft_emerald_ring.yaml b/data/recipes/crafting/emerald_ring.yaml index 2590490..c5cee62 100644 --- a/data/recipes/crafting/craft_emerald_ring.yaml +++ b/data/recipes/crafting/emerald_ring.yaml @@ -1,4 +1,4 @@ -id: craft_emerald_ring +id: emerald_ring type: crafting level: 27 xp: 55 diff --git a/data/recipes/crafting/craft_gold_amulet_u.yaml b/data/recipes/crafting/gold_amulet_u.yaml index 69daa89..d960be6 100644 --- a/data/recipes/crafting/craft_gold_amulet_u.yaml +++ b/data/recipes/crafting/gold_amulet_u.yaml @@ -1,4 +1,4 @@ -id: craft_gold_amulet_u +id: gold_amulet_u type: crafting level: 8 xp: 30 diff --git a/data/recipes/crafting/craft_gold_bracelet.yaml b/data/recipes/crafting/gold_bracelet.yaml index e3de6d0..9669943 100644 --- a/data/recipes/crafting/craft_gold_bracelet.yaml +++ b/data/recipes/crafting/gold_bracelet.yaml @@ -1,4 +1,4 @@ -id: craft_gold_bracelet +id: gold_bracelet type: crafting level: 7 xp: 25 diff --git a/data/recipes/crafting/craft_gold_necklace.yaml b/data/recipes/crafting/gold_necklace.yaml index ab52bef..e2a96c4 100644 --- a/data/recipes/crafting/craft_gold_necklace.yaml +++ b/data/recipes/crafting/gold_necklace.yaml @@ -1,4 +1,4 @@ -id: craft_gold_necklace +id: gold_necklace type: crafting level: 6 xp: 20 diff --git a/data/recipes/crafting/craft_gold_ring.yaml b/data/recipes/crafting/gold_ring.yaml index 943c31d..dcb3c09 100644 --- a/data/recipes/crafting/craft_gold_ring.yaml +++ b/data/recipes/crafting/gold_ring.yaml @@ -1,4 +1,4 @@ -id: craft_gold_ring +id: gold_ring type: crafting level: 5 xp: 15 diff --git a/data/recipes/crafting/craft_hard_leather_body.yaml b/data/recipes/crafting/hard_leather_body.yaml index d1287df..869867e 100644 --- a/data/recipes/crafting/craft_hard_leather_body.yaml +++ b/data/recipes/crafting/hard_leather_body.yaml @@ -1,4 +1,4 @@ -id: craft_hard_leather_body +id: hard_leather_body type: crafting level: 28 xp: 35 diff --git a/data/recipes/crafting/craft_hard_leather_shield.yaml b/data/recipes/crafting/hard_leather_shield.yaml index 7dbd66a..b46222f 100644 --- a/data/recipes/crafting/craft_hard_leather_shield.yaml +++ b/data/recipes/crafting/hard_leather_shield.yaml @@ -1,4 +1,4 @@ -id: craft_hard_leather_shield +id: hard_leather_shield type: crafting level: 41 xp: 40 diff --git a/data/recipes/crafting/craft_leather_body.yaml b/data/recipes/crafting/leather_body.yaml index 0f9774e..4f87c8f 100644 --- a/data/recipes/crafting/craft_leather_body.yaml +++ b/data/recipes/crafting/leather_body.yaml @@ -1,4 +1,4 @@ -id: craft_leather_body +id: leather_body type: crafting level: 14 xp: 25 diff --git a/data/recipes/crafting/craft_leather_boots.yaml b/data/recipes/crafting/leather_boots.yaml index b935a08..569fb81 100644 --- a/data/recipes/crafting/craft_leather_boots.yaml +++ b/data/recipes/crafting/leather_boots.yaml @@ -1,4 +1,4 @@ -id: craft_leather_boots +id: leather_boots type: crafting level: 7 xp: 16 diff --git a/data/recipes/crafting/craft_leather_chaps.yaml b/data/recipes/crafting/leather_chaps.yaml index fac2a9d..82fdac9 100644 --- a/data/recipes/crafting/craft_leather_chaps.yaml +++ b/data/recipes/crafting/leather_chaps.yaml @@ -1,4 +1,4 @@ -id: craft_leather_chaps +id: leather_chaps type: crafting level: 18 xp: 27 diff --git a/data/recipes/crafting/craft_leather_cowl.yaml b/data/recipes/crafting/leather_cowl.yaml index 9e5de25..70fc500 100644 --- a/data/recipes/crafting/craft_leather_cowl.yaml +++ b/data/recipes/crafting/leather_cowl.yaml @@ -1,4 +1,4 @@ -id: craft_leather_cowl +id: leather_cowl type: crafting level: 9 xp: 19 diff --git a/data/recipes/crafting/craft_leather_gloves.yaml b/data/recipes/crafting/leather_gloves.yaml index 4b4fdfc..fa4cd37 100644 --- a/data/recipes/crafting/craft_leather_gloves.yaml +++ b/data/recipes/crafting/leather_gloves.yaml @@ -1,4 +1,4 @@ -id: craft_leather_gloves +id: leather_gloves type: crafting level: 1 xp: 14 diff --git a/data/recipes/crafting/craft_leather_vambraces.yaml b/data/recipes/crafting/leather_vambraces.yaml index 1227271..6167294 100644 --- a/data/recipes/crafting/craft_leather_vambraces.yaml +++ b/data/recipes/crafting/leather_vambraces.yaml @@ -1,4 +1,4 @@ -id: craft_leather_vambraces +id: leather_vambraces type: crafting level: 11 xp: 22 diff --git a/data/recipes/crafting/craft_pie_dish.yaml b/data/recipes/crafting/pie_dish.yaml index 2b31d17..5f49d62 100644 --- a/data/recipes/crafting/craft_pie_dish.yaml +++ b/data/recipes/crafting/pie_dish.yaml @@ -1,4 +1,4 @@ -id: craft_pie_dish +id: pie_dish type: crafting level: 7 xp: 10 diff --git a/data/recipes/crafting/craft_plant_pot.yaml b/data/recipes/crafting/plant_pot.yaml index ddfcefa..2c4b228 100644 --- a/data/recipes/crafting/craft_plant_pot.yaml +++ b/data/recipes/crafting/plant_pot.yaml @@ -1,4 +1,4 @@ -id: craft_plant_pot +id: plant_pot type: crafting level: 19 xp: 18 diff --git a/data/recipes/crafting/craft_pot.yaml b/data/recipes/crafting/pot.yaml index 1b80eef..3215036 100644 --- a/data/recipes/crafting/craft_pot.yaml +++ b/data/recipes/crafting/pot.yaml @@ -1,4 +1,4 @@ -id: craft_pot +id: pot type: crafting level: 1 xp: 6 diff --git a/data/recipes/crafting/craft_ruby_amulet_u.yaml b/data/recipes/crafting/ruby_amulet_u.yaml index 38a864e..2958d77 100644 --- a/data/recipes/crafting/craft_ruby_amulet_u.yaml +++ b/data/recipes/crafting/ruby_amulet_u.yaml @@ -1,4 +1,4 @@ -id: craft_ruby_amulet_u +id: ruby_amulet_u type: crafting level: 50 xp: 85 diff --git a/data/recipes/crafting/craft_ruby_bracelet.yaml b/data/recipes/crafting/ruby_bracelet.yaml index bcc963e..7f36bce 100644 --- a/data/recipes/crafting/craft_ruby_bracelet.yaml +++ b/data/recipes/crafting/ruby_bracelet.yaml @@ -1,4 +1,4 @@ -id: craft_ruby_bracelet +id: ruby_bracelet type: crafting level: 42 xp: 80 diff --git a/data/recipes/crafting/craft_ruby_necklace.yaml b/data/recipes/crafting/ruby_necklace.yaml index 60e47a3..7be562e 100644 --- a/data/recipes/crafting/craft_ruby_necklace.yaml +++ b/data/recipes/crafting/ruby_necklace.yaml @@ -1,4 +1,4 @@ -id: craft_ruby_necklace +id: ruby_necklace type: crafting level: 40 xp: 75 diff --git a/data/recipes/crafting/craft_ruby_ring.yaml b/data/recipes/crafting/ruby_ring.yaml index 61d2b46..78a6339 100644 --- a/data/recipes/crafting/craft_ruby_ring.yaml +++ b/data/recipes/crafting/ruby_ring.yaml @@ -1,4 +1,4 @@ -id: craft_ruby_ring +id: ruby_ring type: crafting level: 63 xp: 70 diff --git a/data/recipes/crafting/craft_sapphire_amulet_u.yaml b/data/recipes/crafting/sapphire_amulet_u.yaml index 07ef6bf..36302d8 100644 --- a/data/recipes/crafting/craft_sapphire_amulet_u.yaml +++ b/data/recipes/crafting/sapphire_amulet_u.yaml @@ -1,4 +1,4 @@ -id: craft_sapphire_amulet_u +id: sapphire_amulet_u type: crafting level: 24 xp: 65 diff --git a/data/recipes/crafting/craft_sapphire_bracelet.yaml b/data/recipes/crafting/sapphire_bracelet.yaml index 3bc1067..2e1031e 100644 --- a/data/recipes/crafting/craft_sapphire_bracelet.yaml +++ b/data/recipes/crafting/sapphire_bracelet.yaml @@ -1,4 +1,4 @@ -id: craft_sapphire_bracelet +id: sapphire_bracelet type: crafting level: 23 xp: 60 diff --git a/data/recipes/crafting/craft_sapphire_necklace.yaml b/data/recipes/crafting/sapphire_necklace.yaml index 8006bf7..2671e6b 100644 --- a/data/recipes/crafting/craft_sapphire_necklace.yaml +++ b/data/recipes/crafting/sapphire_necklace.yaml @@ -1,4 +1,4 @@ -id: craft_sapphire_necklace +id: sapphire_necklace type: crafting level: 22 xp: 55 diff --git a/data/recipes/crafting/craft_sapphire_ring.yaml b/data/recipes/crafting/sapphire_ring.yaml index cc67b73..f755b6f 100644 --- a/data/recipes/crafting/craft_sapphire_ring.yaml +++ b/data/recipes/crafting/sapphire_ring.yaml @@ -1,4 +1,4 @@ -id: craft_sapphire_ring +id: sapphire_ring type: crafting level: 20 xp: 40 diff --git a/data/recipes/crafting/craft_unfired_pie_dish.yaml b/data/recipes/crafting/unfired_pie_dish.yaml index 7ba5032..d698cf4 100644 --- a/data/recipes/crafting/craft_unfired_pie_dish.yaml +++ b/data/recipes/crafting/unfired_pie_dish.yaml @@ -1,4 +1,4 @@ -id: craft_unfired_pie_dish +id: unfired_pie_dish type: crafting level: 7 xp: 15 diff --git a/data/recipes/crafting/craft_unfired_plant_pot.yaml b/data/recipes/crafting/unfired_plant_pot.yaml index db7a38e..e0675db 100644 --- a/data/recipes/crafting/craft_unfired_plant_pot.yaml +++ b/data/recipes/crafting/unfired_plant_pot.yaml @@ -1,4 +1,4 @@ -id: craft_unfired_plant_pot +id: unfired_plant_pot type: crafting level: 19 xp: 18 diff --git a/data/recipes/crafting/craft_unfired_pot.yaml b/data/recipes/crafting/unfired_pot.yaml index 5b2762d..04604e3 100644 --- a/data/recipes/crafting/craft_unfired_pot.yaml +++ b/data/recipes/crafting/unfired_pot.yaml @@ -1,4 +1,4 @@ -id: craft_unfired_pot +id: unfired_pot type: crafting level: 1 xp: 6 diff --git a/data/recipes/fletching/arrow_shafts.yaml b/data/recipes/fletching/arrow_shafts.yaml new file mode 100644 index 0000000..c382340 --- /dev/null +++ b/data/recipes/fletching/arrow_shafts.yaml @@ -0,0 +1,52 @@ +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 new file mode 100644 index 0000000..2197de1 --- /dev/null +++ b/data/recipes/fletching/bolt_feathering.yaml @@ -0,0 +1,63 @@ +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/fletch_adamant_arrow.yaml b/data/recipes/fletching/fletch_adamant_arrow.yaml deleted file mode 100644 index 57f899b..0000000 --- a/data/recipes/fletching/fletch_adamant_arrow.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_adamant_arrow -type: fletching -level: 60 -xp: 10 -wait: 2 -consume: - - items: [headless_arrow] - quantity: 15 - - items: [adamant_arrowtips] - quantity: 15 -output: adamant_arrow -output_qty: 15 -message: "You attach the adamant arrowtips to the headless arrows." diff --git a/data/recipes/fletching/fletch_adamant_bolts.yaml b/data/recipes/fletching/fletch_adamant_bolts.yaml deleted file mode 100644 index d96c811..0000000 --- a/data/recipes/fletching/fletch_adamant_bolts.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_adamant_bolts -type: fletching -level: 61 -xp: 70 -wait: 0 -consume: - - items: [adamant_bolts_unf] - quantity: 10 - - items: [feather] - quantity: 10 -output: adamant_bolts -output_qty: 10 -message: "You attach feathers to the adamant bolts." diff --git a/data/recipes/fletching/fletch_arrow_shaft.yaml b/data/recipes/fletching/fletch_arrow_shaft.yaml deleted file mode 100644 index ebf3861..0000000 --- a/data/recipes/fletching/fletch_arrow_shaft.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_arrow_shaft -type: fletching -tool: knife -level: 1 -xp: 5 -wait: 5 -consume: - - items: [logs] - quantity: 1 -output: arrow_shaft -output_qty: 15 -message: "You carefully cut the logs into arrow shafts." diff --git a/data/recipes/fletching/fletch_arrow_shaft_magic.yaml b/data/recipes/fletching/fletch_arrow_shaft_magic.yaml deleted file mode 100644 index 0621859..0000000 --- a/data/recipes/fletching/fletch_arrow_shaft_magic.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_arrow_shaft_magic -type: fletching -tool: knife -level: 75 -xp: 30 -wait: 5 -consume: - - items: [magic_logs] - quantity: 1 -output: arrow_shaft -output_qty: 40 -message: "You carefully cut the magic logs into arrow shafts." diff --git a/data/recipes/fletching/fletch_arrow_shaft_maple.yaml b/data/recipes/fletching/fletch_arrow_shaft_maple.yaml deleted file mode 100644 index b6671da..0000000 --- a/data/recipes/fletching/fletch_arrow_shaft_maple.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_arrow_shaft_maple -type: fletching -tool: knife -level: 45 -xp: 20 -wait: 5 -consume: - - items: [maple_logs] - quantity: 1 -output: arrow_shaft -output_qty: 30 -message: "You carefully cut the maple logs into arrow shafts." diff --git a/data/recipes/fletching/fletch_arrow_shaft_oak.yaml b/data/recipes/fletching/fletch_arrow_shaft_oak.yaml deleted file mode 100644 index cf38335..0000000 --- a/data/recipes/fletching/fletch_arrow_shaft_oak.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_arrow_shaft_oak -type: fletching -tool: knife -level: 15 -xp: 10 -wait: 5 -consume: - - items: [oak_logs] - quantity: 1 -output: arrow_shaft -output_qty: 20 -message: "You carefully cut the oak logs into arrow shafts." diff --git a/data/recipes/fletching/fletch_arrow_shaft_willow.yaml b/data/recipes/fletching/fletch_arrow_shaft_willow.yaml deleted file mode 100644 index 0c39a13..0000000 --- a/data/recipes/fletching/fletch_arrow_shaft_willow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_arrow_shaft_willow -type: fletching -tool: knife -level: 30 -xp: 15 -wait: 5 -consume: - - items: [willow_logs] - quantity: 1 -output: arrow_shaft -output_qty: 25 -message: "You carefully cut the willow logs into arrow shafts." diff --git a/data/recipes/fletching/fletch_arrow_shaft_yew.yaml b/data/recipes/fletching/fletch_arrow_shaft_yew.yaml deleted file mode 100644 index 785325e..0000000 --- a/data/recipes/fletching/fletch_arrow_shaft_yew.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_arrow_shaft_yew -type: fletching -tool: knife -level: 60 -xp: 25 -wait: 5 -consume: - - items: [yew_logs] - quantity: 1 -output: arrow_shaft -output_qty: 35 -message: "You carefully cut the yew logs into arrow shafts." diff --git a/data/recipes/fletching/fletch_bronze_arrow.yaml b/data/recipes/fletching/fletch_bronze_arrow.yaml deleted file mode 100644 index 4b9e924..0000000 --- a/data/recipes/fletching/fletch_bronze_arrow.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_bronze_arrow -type: fletching -level: 1 -xp: 1 -wait: 2 -consume: - - items: [headless_arrow] - quantity: 15 - - items: [bronze_arrowtips] - quantity: 15 -output: bronze_arrow -output_qty: 15 -message: "You attach the bronze arrowtips to the headless arrows." diff --git a/data/recipes/fletching/fletch_bronze_bolts.yaml b/data/recipes/fletching/fletch_bronze_bolts.yaml deleted file mode 100644 index cdcf6db..0000000 --- a/data/recipes/fletching/fletch_bronze_bolts.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_bronze_bolts -type: fletching -level: 9 -xp: 5 -wait: 0 -consume: - - items: [bronze_bolts_unf] - quantity: 10 - - items: [feather] - quantity: 10 -output: bronze_bolts -output_qty: 10 -message: "You attach feathers to the bronze bolts." diff --git a/data/recipes/fletching/fletch_diamond_bolt_tips.yaml b/data/recipes/fletching/fletch_diamond_bolt_tips.yaml deleted file mode 100644 index 0ccabe2..0000000 --- a/data/recipes/fletching/fletch_diamond_bolt_tips.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_diamond_bolt_tips -type: fletching -tool: chisel -level: 65 -xp: 7 -wait: 5 -consume: - - items: [uncut_diamond] - quantity: 1 -output: diamond_bolt_tips -output_qty: 12 -message: "You cut the diamond into bolt tips." diff --git a/data/recipes/fletching/fletch_diamond_bolts.yaml b/data/recipes/fletching/fletch_diamond_bolts.yaml deleted file mode 100644 index 8c98fc2..0000000 --- a/data/recipes/fletching/fletch_diamond_bolts.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_diamond_bolts -type: fletching -level: 65 -xp: 7 -wait: 2 -consume: - - items: [adamant_bolts] - quantity: 10 - - items: [diamond_bolt_tips] - quantity: 10 -output: diamond_bolts -output_qty: 10 -message: "You tip the adamant bolts with diamond." diff --git a/data/recipes/fletching/fletch_emerald_bolt_tips.yaml b/data/recipes/fletching/fletch_emerald_bolt_tips.yaml deleted file mode 100644 index d48cf73..0000000 --- a/data/recipes/fletching/fletch_emerald_bolt_tips.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_emerald_bolt_tips -type: fletching -tool: chisel -level: 66 -xp: 5 -wait: 5 -consume: - - items: [uncut_emerald] - quantity: 1 -output: emerald_bolt_tips -output_qty: 12 -message: "You cut the emerald into bolt tips." diff --git a/data/recipes/fletching/fletch_emerald_bolts.yaml b/data/recipes/fletching/fletch_emerald_bolts.yaml deleted file mode 100644 index 8e7e446..0000000 --- a/data/recipes/fletching/fletch_emerald_bolts.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_emerald_bolts -type: fletching -level: 66 -xp: 5 -wait: 2 -consume: - - items: [mithril_bolts] - quantity: 10 - - items: [emerald_bolt_tips] - quantity: 10 -output: emerald_bolts -output_qty: 10 -message: "You tip the mithril bolts with emerald." diff --git a/data/recipes/fletching/fletch_iron_arrow.yaml b/data/recipes/fletching/fletch_iron_arrow.yaml deleted file mode 100644 index fbb1c2c..0000000 --- a/data/recipes/fletching/fletch_iron_arrow.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_iron_arrow -type: fletching -level: 15 -xp: 2 -wait: 2 -consume: - - items: [headless_arrow] - quantity: 15 - - items: [iron_arrowtips] - quantity: 15 -output: iron_arrow -output_qty: 15 -message: "You attach the iron arrowtips to the headless arrows." diff --git a/data/recipes/fletching/fletch_iron_bolts.yaml b/data/recipes/fletching/fletch_iron_bolts.yaml deleted file mode 100644 index bb90403..0000000 --- a/data/recipes/fletching/fletch_iron_bolts.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_iron_bolts -type: fletching -level: 39 -xp: 15 -wait: 0 -consume: - - items: [iron_bolts_unf] - quantity: 10 - - items: [feather] - quantity: 10 -output: iron_bolts -output_qty: 10 -message: "You attach feathers to the iron bolts." diff --git a/data/recipes/fletching/fletch_longbow.yaml b/data/recipes/fletching/fletch_longbow.yaml deleted file mode 100644 index fa46921..0000000 --- a/data/recipes/fletching/fletch_longbow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_longbow -type: fletching -level: 10 -xp: 10 -wait: 2 -consume: - - items: [longbow_u] - quantity: 1 - - items: [bowstring] - quantity: 1 -output: longbow -message: "You string the longbow." diff --git a/data/recipes/fletching/fletch_longbow_u.yaml b/data/recipes/fletching/fletch_longbow_u.yaml deleted file mode 100644 index a893b49..0000000 --- a/data/recipes/fletching/fletch_longbow_u.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: fletch_longbow_u -type: fletching -tool: knife -level: 10 -xp: 10 -wait: 3 -consume: - - items: [logs] - quantity: 1 -output: longbow_u -message: "You carefully carve the log into a longbow." diff --git a/data/recipes/fletching/fletch_magic_longbow.yaml b/data/recipes/fletching/fletch_magic_longbow.yaml deleted file mode 100644 index 8e16b9c..0000000 --- a/data/recipes/fletching/fletch_magic_longbow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_magic_longbow -type: fletching -level: 85 -xp: 91 -wait: 2 -consume: - - items: [magic_longbow_u] - quantity: 1 - - items: [bowstring] - quantity: 1 -output: magic_longbow -message: "You string the magic longbow." diff --git a/data/recipes/fletching/fletch_magic_longbow_u.yaml b/data/recipes/fletching/fletch_magic_longbow_u.yaml deleted file mode 100644 index ce2e247..0000000 --- a/data/recipes/fletching/fletch_magic_longbow_u.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: fletch_magic_longbow_u -type: fletching -tool: knife -level: 85 -xp: 91 -wait: 3 -consume: - - items: [magic_logs] - quantity: 1 -output: magic_longbow_u -message: "You carefully carve the magic log into a longbow." diff --git a/data/recipes/fletching/fletch_magic_shortbow.yaml b/data/recipes/fletching/fletch_magic_shortbow.yaml deleted file mode 100644 index 58abce9..0000000 --- a/data/recipes/fletching/fletch_magic_shortbow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_magic_shortbow -type: fletching -level: 80 -xp: 83 -wait: 2 -consume: - - items: [magic_shortbow_u] - quantity: 1 - - items: [bowstring] - quantity: 1 -output: magic_shortbow -message: "You string the magic shortbow." diff --git a/data/recipes/fletching/fletch_magic_shortbow_u.yaml b/data/recipes/fletching/fletch_magic_shortbow_u.yaml deleted file mode 100644 index 3fbf6cd..0000000 --- a/data/recipes/fletching/fletch_magic_shortbow_u.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: fletch_magic_shortbow_u -type: fletching -tool: knife -level: 80 -xp: 83 -wait: 3 -consume: - - items: [magic_logs] - quantity: 1 -output: magic_shortbow_u -message: "You carefully carve the magic log into a shortbow." diff --git a/data/recipes/fletching/fletch_maple_longbow.yaml b/data/recipes/fletching/fletch_maple_longbow.yaml deleted file mode 100644 index 123d531..0000000 --- a/data/recipes/fletching/fletch_maple_longbow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_maple_longbow -type: fletching -level: 55 -xp: 58 -wait: 2 -consume: - - items: [maple_longbow_u] - quantity: 1 - - items: [bowstring] - quantity: 1 -output: maple_longbow -message: "You string the maple longbow." diff --git a/data/recipes/fletching/fletch_maple_longbow_u.yaml b/data/recipes/fletching/fletch_maple_longbow_u.yaml deleted file mode 100644 index 4c61993..0000000 --- a/data/recipes/fletching/fletch_maple_longbow_u.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: fletch_maple_longbow_u -type: fletching -tool: knife -level: 55 -xp: 58 -wait: 3 -consume: - - items: [maple_logs] - quantity: 1 -output: maple_longbow_u -message: "You carefully carve the maple log into a longbow." diff --git a/data/recipes/fletching/fletch_maple_shortbow.yaml b/data/recipes/fletching/fletch_maple_shortbow.yaml deleted file mode 100644 index aebe50a..0000000 --- a/data/recipes/fletching/fletch_maple_shortbow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_maple_shortbow -type: fletching -level: 50 -xp: 50 -wait: 2 -consume: - - items: [maple_shortbow_u] - quantity: 1 - - items: [bowstring] - quantity: 1 -output: maple_shortbow -message: "You string the maple shortbow." diff --git a/data/recipes/fletching/fletch_maple_shortbow_u.yaml b/data/recipes/fletching/fletch_maple_shortbow_u.yaml deleted file mode 100644 index 36270dd..0000000 --- a/data/recipes/fletching/fletch_maple_shortbow_u.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: fletch_maple_shortbow_u -type: fletching -tool: knife -level: 50 -xp: 50 -wait: 3 -consume: - - items: [maple_logs] - quantity: 1 -output: maple_shortbow_u -message: "You carefully carve the maple log into a shortbow." diff --git a/data/recipes/fletching/fletch_mithril_arrow.yaml b/data/recipes/fletching/fletch_mithril_arrow.yaml deleted file mode 100644 index 87a46e2..0000000 --- a/data/recipes/fletching/fletch_mithril_arrow.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_mithril_arrow -type: fletching -level: 45 -xp: 7 -wait: 2 -consume: - - items: [headless_arrow] - quantity: 15 - - items: [mithril_arrowtips] - quantity: 15 -output: mithril_arrow -output_qty: 15 -message: "You attach the mithril arrowtips to the headless arrows." diff --git a/data/recipes/fletching/fletch_mithril_bolts.yaml b/data/recipes/fletching/fletch_mithril_bolts.yaml deleted file mode 100644 index a2afcb8..0000000 --- a/data/recipes/fletching/fletch_mithril_bolts.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_mithril_bolts -type: fletching -level: 54 -xp: 50 -wait: 0 -consume: - - items: [mithril_bolts_unf] - quantity: 10 - - items: [feather] - quantity: 10 -output: mithril_bolts -output_qty: 10 -message: "You attach feathers to the mithril bolts." diff --git a/data/recipes/fletching/fletch_oak_longbow.yaml b/data/recipes/fletching/fletch_oak_longbow.yaml deleted file mode 100644 index f1c96ba..0000000 --- a/data/recipes/fletching/fletch_oak_longbow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_oak_longbow -type: fletching -level: 25 -xp: 25 -wait: 2 -consume: - - items: [oak_longbow_u] - quantity: 1 - - items: [bowstring] - quantity: 1 -output: oak_longbow -message: "You string the oak longbow." diff --git a/data/recipes/fletching/fletch_oak_longbow_u.yaml b/data/recipes/fletching/fletch_oak_longbow_u.yaml deleted file mode 100644 index 8f0b234..0000000 --- a/data/recipes/fletching/fletch_oak_longbow_u.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: fletch_oak_longbow_u -type: fletching -tool: knife -level: 25 -xp: 25 -wait: 3 -consume: - - items: [oak_logs] - quantity: 1 -output: oak_longbow_u -message: "You carefully carve the oak log into a longbow." diff --git a/data/recipes/fletching/fletch_oak_shortbow.yaml b/data/recipes/fletching/fletch_oak_shortbow.yaml deleted file mode 100644 index 198657f..0000000 --- a/data/recipes/fletching/fletch_oak_shortbow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_oak_shortbow -type: fletching -level: 20 -xp: 16 -wait: 2 -consume: - - items: [oak_shortbow_u] - quantity: 1 - - items: [bowstring] - quantity: 1 -output: oak_shortbow -message: "You string the oak shortbow." diff --git a/data/recipes/fletching/fletch_oak_shortbow_u.yaml b/data/recipes/fletching/fletch_oak_shortbow_u.yaml deleted file mode 100644 index 4f96a09..0000000 --- a/data/recipes/fletching/fletch_oak_shortbow_u.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: fletch_oak_shortbow_u -type: fletching -tool: knife -level: 20 -xp: 16 -wait: 3 -consume: - - items: [oak_logs] - quantity: 1 -output: oak_shortbow_u -message: "You carefully carve the oak log into a shortbow." diff --git a/data/recipes/fletching/fletch_ruby_bolt_tips.yaml b/data/recipes/fletching/fletch_ruby_bolt_tips.yaml deleted file mode 100644 index bc9f695..0000000 --- a/data/recipes/fletching/fletch_ruby_bolt_tips.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_ruby_bolt_tips -type: fletching -tool: chisel -level: 73 -xp: 6 -wait: 5 -consume: - - items: [uncut_ruby] - quantity: 1 -output: ruby_bolt_tips -output_qty: 12 -message: "You cut the ruby into bolt tips." diff --git a/data/recipes/fletching/fletch_ruby_bolts.yaml b/data/recipes/fletching/fletch_ruby_bolts.yaml deleted file mode 100644 index ac1d150..0000000 --- a/data/recipes/fletching/fletch_ruby_bolts.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_ruby_bolts -type: fletching -level: 73 -xp: 6 -wait: 2 -consume: - - items: [adamant_bolts] - quantity: 10 - - items: [ruby_bolt_tips] - quantity: 10 -output: ruby_bolts -output_qty: 10 -message: "You tip the adamant bolts with ruby." diff --git a/data/recipes/fletching/fletch_rune_arrow.yaml b/data/recipes/fletching/fletch_rune_arrow.yaml deleted file mode 100644 index aa2795e..0000000 --- a/data/recipes/fletching/fletch_rune_arrow.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_rune_arrow -type: fletching -level: 75 -xp: 12 -wait: 2 -consume: - - items: [headless_arrow] - quantity: 15 - - items: [rune_arrowtips] - quantity: 15 -output: rune_arrow -output_qty: 15 -message: "You attach the rune arrowtips to the headless arrows." diff --git a/data/recipes/fletching/fletch_rune_bolts.yaml b/data/recipes/fletching/fletch_rune_bolts.yaml deleted file mode 100644 index 20bb7b9..0000000 --- a/data/recipes/fletching/fletch_rune_bolts.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_rune_bolts -type: fletching -level: 69 -xp: 100 -wait: 0 -consume: - - items: [rune_bolts_unf] - quantity: 10 - - items: [feather] - quantity: 10 -output: rune_bolts -output_qty: 10 -message: "You attach feathers to the rune bolts." diff --git a/data/recipes/fletching/fletch_sapphire_bolt_tips.yaml b/data/recipes/fletching/fletch_sapphire_bolt_tips.yaml deleted file mode 100644 index dca6fef..0000000 --- a/data/recipes/fletching/fletch_sapphire_bolt_tips.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_sapphire_bolt_tips -type: fletching -tool: chisel -level: 63 -xp: 4 -wait: 5 -consume: - - items: [uncut_sapphire] - quantity: 1 -output: sapphire_bolt_tips -output_qty: 12 -message: "You cut the sapphire into bolt tips." diff --git a/data/recipes/fletching/fletch_sapphire_bolts.yaml b/data/recipes/fletching/fletch_sapphire_bolts.yaml deleted file mode 100644 index 712578f..0000000 --- a/data/recipes/fletching/fletch_sapphire_bolts.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_sapphire_bolts -type: fletching -level: 63 -xp: 4 -wait: 2 -consume: - - items: [mithril_bolts] - quantity: 10 - - items: [sapphire_bolt_tips] - quantity: 10 -output: sapphire_bolts -output_qty: 10 -message: "You tip the mithril bolts with sapphire." diff --git a/data/recipes/fletching/fletch_shortbow.yaml b/data/recipes/fletching/fletch_shortbow.yaml deleted file mode 100644 index e96317d..0000000 --- a/data/recipes/fletching/fletch_shortbow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_shortbow -type: fletching -level: 5 -xp: 5 -wait: 2 -consume: - - items: [shortbow_u] - quantity: 1 - - items: [bowstring] - quantity: 1 -output: shortbow -message: "You string the shortbow." diff --git a/data/recipes/fletching/fletch_shortbow_u.yaml b/data/recipes/fletching/fletch_shortbow_u.yaml deleted file mode 100644 index 3ab4931..0000000 --- a/data/recipes/fletching/fletch_shortbow_u.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: fletch_shortbow_u -type: fletching -tool: knife -level: 5 -xp: 5 -wait: 3 -consume: - - items: [logs] - quantity: 1 -output: shortbow_u -message: "You carefully carve the log into a shortbow." diff --git a/data/recipes/fletching/fletch_steel_arrow.yaml b/data/recipes/fletching/fletch_steel_arrow.yaml deleted file mode 100644 index 9bd229a..0000000 --- a/data/recipes/fletching/fletch_steel_arrow.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_steel_arrow -type: fletching -level: 30 -xp: 5 -wait: 2 -consume: - - items: [headless_arrow] - quantity: 15 - - items: [steel_arrowtips] - quantity: 15 -output: steel_arrow -output_qty: 15 -message: "You attach the steel arrowtips to the headless arrows." diff --git a/data/recipes/fletching/fletch_steel_bolts.yaml b/data/recipes/fletching/fletch_steel_bolts.yaml deleted file mode 100644 index 9bfdafe..0000000 --- a/data/recipes/fletching/fletch_steel_bolts.yaml +++ /dev/null @@ -1,13 +0,0 @@ -id: fletch_steel_bolts -type: fletching -level: 46 -xp: 35 -wait: 0 -consume: - - items: [steel_bolts_unf] - quantity: 10 - - items: [feather] - quantity: 10 -output: steel_bolts -output_qty: 10 -message: "You attach feathers to the steel bolts." diff --git a/data/recipes/fletching/fletch_willow_longbow.yaml b/data/recipes/fletching/fletch_willow_longbow.yaml deleted file mode 100644 index 28071ac..0000000 --- a/data/recipes/fletching/fletch_willow_longbow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_willow_longbow -type: fletching -level: 40 -xp: 41 -wait: 2 -consume: - - items: [willow_longbow_u] - quantity: 1 - - items: [bowstring] - quantity: 1 -output: willow_longbow -message: "You string the willow longbow." diff --git a/data/recipes/fletching/fletch_willow_longbow_u.yaml b/data/recipes/fletching/fletch_willow_longbow_u.yaml deleted file mode 100644 index ef808c0..0000000 --- a/data/recipes/fletching/fletch_willow_longbow_u.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: fletch_willow_longbow_u -type: fletching -tool: knife -level: 40 -xp: 41 -wait: 3 -consume: - - items: [willow_logs] - quantity: 1 -output: willow_longbow_u -message: "You carefully carve the willow log into a longbow." diff --git a/data/recipes/fletching/fletch_willow_shortbow.yaml b/data/recipes/fletching/fletch_willow_shortbow.yaml deleted file mode 100644 index a2b2c2b..0000000 --- a/data/recipes/fletching/fletch_willow_shortbow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_willow_shortbow -type: fletching -level: 35 -xp: 33 -wait: 2 -consume: - - items: [willow_shortbow_u] - quantity: 1 - - items: [bowstring] - quantity: 1 -output: willow_shortbow -message: "You string the willow shortbow." diff --git a/data/recipes/fletching/fletch_willow_shortbow_u.yaml b/data/recipes/fletching/fletch_willow_shortbow_u.yaml deleted file mode 100644 index 95609b3..0000000 --- a/data/recipes/fletching/fletch_willow_shortbow_u.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: fletch_willow_shortbow_u -type: fletching -tool: knife -level: 35 -xp: 33 -wait: 3 -consume: - - items: [willow_logs] - quantity: 1 -output: willow_shortbow_u -message: "You carefully carve the willow log into a shortbow." diff --git a/data/recipes/fletching/fletch_yew_longbow.yaml b/data/recipes/fletching/fletch_yew_longbow.yaml deleted file mode 100644 index 208c623..0000000 --- a/data/recipes/fletching/fletch_yew_longbow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_yew_longbow -type: fletching -level: 65 -xp: 75 -wait: 2 -consume: - - items: [yew_longbow_u] - quantity: 1 - - items: [bowstring] - quantity: 1 -output: yew_longbow -message: "You string the yew longbow." diff --git a/data/recipes/fletching/fletch_yew_longbow_u.yaml b/data/recipes/fletching/fletch_yew_longbow_u.yaml deleted file mode 100644 index bdce4f8..0000000 --- a/data/recipes/fletching/fletch_yew_longbow_u.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: fletch_yew_longbow_u -type: fletching -tool: knife -level: 65 -xp: 75 -wait: 3 -consume: - - items: [yew_logs] - quantity: 1 -output: yew_longbow_u -message: "You carefully carve the yew log into a longbow." diff --git a/data/recipes/fletching/fletch_yew_shortbow.yaml b/data/recipes/fletching/fletch_yew_shortbow.yaml deleted file mode 100644 index fbe5f62..0000000 --- a/data/recipes/fletching/fletch_yew_shortbow.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: fletch_yew_shortbow -type: fletching -level: 60 -xp: 67 -wait: 2 -consume: - - items: [yew_shortbow_u] - quantity: 1 - - items: [bowstring] - quantity: 1 -output: yew_shortbow -message: "You string the yew shortbow." diff --git a/data/recipes/fletching/fletch_yew_shortbow_u.yaml b/data/recipes/fletching/fletch_yew_shortbow_u.yaml deleted file mode 100644 index 774e3a5..0000000 --- a/data/recipes/fletching/fletch_yew_shortbow_u.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: fletch_yew_shortbow_u -type: fletching -tool: knife -level: 60 -xp: 67 -wait: 3 -consume: - - items: [yew_logs] - quantity: 1 -output: yew_shortbow_u -message: "You carefully carve the yew log into a shortbow." diff --git a/data/recipes/fletching/gem_bolt_tips.yaml b/data/recipes/fletching/gem_bolt_tips.yaml new file mode 100644 index 0000000..5482140 --- /dev/null +++ b/data/recipes/fletching/gem_bolt_tips.yaml @@ -0,0 +1,36 @@ +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 new file mode 100644 index 0000000..7ed5adc --- /dev/null +++ b/data/recipes/fletching/gem_tipped_bolts.yaml @@ -0,0 +1,43 @@ +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/fletch_headless_arrow.yaml b/data/recipes/fletching/headless_arrow.yaml index 84d88f5..0c1e363 100644 --- a/data/recipes/fletching/fletch_headless_arrow.yaml +++ b/data/recipes/fletching/headless_arrow.yaml @@ -1,4 +1,4 @@ -id: fletch_headless_arrow +id: headless_arrow type: fletching level: 1 xp: 1 diff --git a/data/recipes/fletching/strung_bows.yaml b/data/recipes/fletching/strung_bows.yaml new file mode 100644 index 0000000..c7e2c3d --- /dev/null +++ b/data/recipes/fletching/strung_bows.yaml @@ -0,0 +1,111 @@ +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 new file mode 100644 index 0000000..8e4530b --- /dev/null +++ b/data/recipes/fletching/tipped_arrows.yaml @@ -0,0 +1,63 @@ +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 new file mode 100644 index 0000000..3195c8d --- /dev/null +++ b/data/recipes/fletching/unstrung_bows.yaml @@ -0,0 +1,88 @@ +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/mix_amp_potion.yaml b/data/recipes/mixing/amp_potion.yaml index 6145296..40aeb84 100644 --- a/data/recipes/mixing/mix_amp_potion.yaml +++ b/data/recipes/mixing/amp_potion.yaml @@ -1,4 +1,4 @@ -id: mix_amp_potion +id: amp_potion type: pharmacy skill: pharmacy level: 12 diff --git a/data/recipes/mixing/mix_bio_serum.yaml b/data/recipes/mixing/bio_serum.yaml index 6f5a3ea..f7f728a 100644 --- a/data/recipes/mixing/mix_bio_serum.yaml +++ b/data/recipes/mixing/bio_serum.yaml @@ -1,4 +1,4 @@ -id: mix_bio_serum +id: bio_serum type: pharmacy skill: pharmacy level: 5 diff --git a/data/recipes/mixing/mix_full_restore.yaml b/data/recipes/mixing/full_restore.yaml index b9e320e..c5ed30b 100644 --- a/data/recipes/mixing/mix_full_restore.yaml +++ b/data/recipes/mixing/full_restore.yaml @@ -1,4 +1,4 @@ -id: mix_full_restore +id: full_restore type: pharmacy skill: pharmacy level: 63 diff --git a/data/recipes/mixing/mix_nano_restore.yaml b/data/recipes/mixing/nano_restore.yaml index 4a5ebb0..c14f151 100644 --- a/data/recipes/mixing/mix_nano_restore.yaml +++ b/data/recipes/mixing/nano_restore.yaml @@ -1,4 +1,4 @@ -id: mix_nano_restore +id: nano_restore type: pharmacy skill: pharmacy level: 22 diff --git a/data/recipes/mixing/mix_restoration_compound.yaml b/data/recipes/mixing/restoration_compound.yaml index c945cac..0613d47 100644 --- a/data/recipes/mixing/mix_restoration_compound.yaml +++ b/data/recipes/mixing/restoration_compound.yaml @@ -1,4 +1,4 @@ -id: mix_restoration_compound +id: restoration_compound type: pharmacy skill: pharmacy level: 81 diff --git a/data/recipes/mixing/mix_science_serum.yaml b/data/recipes/mixing/science_serum.yaml index b65f2d2..311f3c4 100644 --- a/data/recipes/mixing/mix_science_serum.yaml +++ b/data/recipes/mixing/science_serum.yaml @@ -1,4 +1,4 @@ -id: mix_science_serum +id: science_serum type: pharmacy skill: pharmacy level: 76 diff --git a/data/recipes/mixing/mix_shield_potion.yaml b/data/recipes/mixing/shield_potion.yaml index fc44abf..f334b5a 100644 --- a/data/recipes/mixing/mix_shield_potion.yaml +++ b/data/recipes/mixing/shield_potion.yaml @@ -1,4 +1,4 @@ -id: mix_shield_potion +id: shield_potion type: pharmacy skill: pharmacy level: 30 diff --git a/data/recipes/mixing/mix_stim_cell.yaml b/data/recipes/mixing/stim_cell.yaml index ccca53a..ddf37f8 100644 --- a/data/recipes/mixing/mix_stim_cell.yaml +++ b/data/recipes/mixing/stim_cell.yaml @@ -1,4 +1,4 @@ -id: mix_stim_cell +id: stim_cell type: pharmacy skill: pharmacy level: 26 diff --git a/data/recipes/mixing/mix_stim_potion.yaml b/data/recipes/mixing/stim_potion.yaml index 437b985..ad6abd3 100644 --- a/data/recipes/mixing/mix_stim_potion.yaml +++ b/data/recipes/mixing/stim_potion.yaml @@ -1,4 +1,4 @@ -id: mix_stim_potion +id: stim_potion type: pharmacy skill: pharmacy level: 3 diff --git a/data/recipes/mixing/mix_super_amp.yaml b/data/recipes/mixing/super_amp.yaml index 5e05019..dc4ef81 100644 --- a/data/recipes/mixing/mix_super_amp.yaml +++ b/data/recipes/mixing/super_amp.yaml @@ -1,4 +1,4 @@ -id: mix_super_amp +id: super_amp type: pharmacy skill: pharmacy level: 55 diff --git a/data/recipes/mixing/mix_super_bio_serum.yaml b/data/recipes/mixing/super_bio_serum.yaml index 96e6632..3c763a6 100644 --- a/data/recipes/mixing/mix_super_bio_serum.yaml +++ b/data/recipes/mixing/super_bio_serum.yaml @@ -1,4 +1,4 @@ -id: mix_super_bio_serum +id: super_bio_serum type: pharmacy skill: pharmacy level: 48 diff --git a/data/recipes/mixing/mix_super_shield.yaml b/data/recipes/mixing/super_shield.yaml index 61b3686..706d402 100644 --- a/data/recipes/mixing/mix_super_shield.yaml +++ b/data/recipes/mixing/super_shield.yaml @@ -1,4 +1,4 @@ -id: mix_super_shield +id: super_shield type: pharmacy skill: pharmacy level: 66 diff --git a/data/recipes/mixing/mix_super_stim.yaml b/data/recipes/mixing/super_stim.yaml index bac9f8f..8ef6819 100644 --- a/data/recipes/mixing/mix_super_stim.yaml +++ b/data/recipes/mixing/super_stim.yaml @@ -1,4 +1,4 @@ -id: mix_super_stim +id: super_stim type: pharmacy skill: pharmacy level: 45 diff --git a/data/recipes/mixing/mix_super_stim_cell.yaml b/data/recipes/mixing/super_stim_cell.yaml index 26640ea..ec4a515 100644 --- a/data/recipes/mixing/mix_super_stim_cell.yaml +++ b/data/recipes/mixing/super_stim_cell.yaml @@ -1,4 +1,4 @@ -id: mix_super_stim_cell +id: super_stim_cell type: pharmacy skill: pharmacy level: 52 diff --git a/data/recipes/mixing/mix_targeting_serum.yaml b/data/recipes/mixing/targeting_serum.yaml index 06f8421..e91e20e 100644 --- a/data/recipes/mixing/mix_targeting_serum.yaml +++ b/data/recipes/mixing/targeting_serum.yaml @@ -1,4 +1,4 @@ -id: mix_targeting_serum +id: targeting_serum type: pharmacy skill: pharmacy level: 72 diff --git a/data/recipes/mixing/mix_tech_serum.yaml b/data/recipes/mixing/tech_serum.yaml index a57d2f9..3a21619 100644 --- a/data/recipes/mixing/mix_tech_serum.yaml +++ b/data/recipes/mixing/tech_serum.yaml @@ -1,4 +1,4 @@ -id: mix_tech_serum +id: tech_serum type: pharmacy skill: pharmacy level: 38 diff --git a/data/recipes/smelting/smelt_adamantite.yaml b/data/recipes/smelting/adamantite.yaml index 5ff675b..af9283d 100644 --- a/data/recipes/smelting/smelt_adamantite.yaml +++ b/data/recipes/smelting/adamantite.yaml @@ -1,4 +1,4 @@ -id: smelt_adamantite +id: adamantite type: smelting skill: smithing level: 65 diff --git a/data/recipes/smelting/smelt_bronze.yaml b/data/recipes/smelting/bronze.yaml index bdb33af..af28ca0 100644 --- a/data/recipes/smelting/smelt_bronze.yaml +++ b/data/recipes/smelting/bronze.yaml @@ -1,4 +1,4 @@ -id: smelt_bronze +id: bronze type: smelting skill: smithing level: 1 diff --git a/data/recipes/smelting/smelt_gold.yaml b/data/recipes/smelting/gold.yaml index 91229e8..9ebb96b 100644 --- a/data/recipes/smelting/smelt_gold.yaml +++ b/data/recipes/smelting/gold.yaml @@ -1,4 +1,4 @@ -id: smelt_gold +id: gold type: smelting skill: smithing level: 45 diff --git a/data/recipes/smelting/smelt_iron.yaml b/data/recipes/smelting/iron.yaml index ce0ca11..fa210d6 100644 --- a/data/recipes/smelting/smelt_iron.yaml +++ b/data/recipes/smelting/iron.yaml @@ -1,4 +1,4 @@ -id: smelt_iron +id: iron type: smelting skill: smithing level: 20 diff --git a/data/recipes/smelting/smelt_mithril.yaml b/data/recipes/smelting/mithril.yaml index 6285c1b..8e11e61 100644 --- a/data/recipes/smelting/smelt_mithril.yaml +++ b/data/recipes/smelting/mithril.yaml @@ -1,4 +1,4 @@ -id: smelt_mithril +id: mithril type: smelting skill: smithing level: 50 diff --git a/data/recipes/smelting/smelt_runite.yaml b/data/recipes/smelting/runite.yaml index b683221..36d44a4 100644 --- a/data/recipes/smelting/smelt_runite.yaml +++ b/data/recipes/smelting/runite.yaml @@ -1,4 +1,4 @@ -id: smelt_runite +id: runite type: smelting skill: smithing level: 80 diff --git a/data/recipes/smelting/smelt_silver.yaml b/data/recipes/smelting/silver.yaml index f381bd0..05a76ef 100644 --- a/data/recipes/smelting/smelt_silver.yaml +++ b/data/recipes/smelting/silver.yaml @@ -1,4 +1,4 @@ -id: smelt_silver +id: silver type: smelting skill: smithing level: 25 diff --git a/data/recipes/smelting/smelt_steel.yaml b/data/recipes/smelting/steel.yaml index ec1c2df..86ee366 100644 --- a/data/recipes/smelting/smelt_steel.yaml +++ b/data/recipes/smelting/steel.yaml @@ -1,4 +1,4 @@ -id: smelt_steel +id: steel type: smelting skill: smithing level: 40 diff --git a/data/recipes/smithing/adamant.yaml b/data/recipes/smithing/adamant.yaml new file mode 100644 index 0000000..bd9debe --- /dev/null +++ b/data/recipes/smithing/adamant.yaml @@ -0,0 +1,48 @@ +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 new file mode 100644 index 0000000..da1b55f --- /dev/null +++ b/data/recipes/smithing/bronze.yaml @@ -0,0 +1,48 @@ +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 new file mode 100644 index 0000000..e39facd --- /dev/null +++ b/data/recipes/smithing/iron.yaml @@ -0,0 +1,48 @@ +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 new file mode 100644 index 0000000..dfa4a6d --- /dev/null +++ b/data/recipes/smithing/mithril.yaml @@ -0,0 +1,48 @@ +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 new file mode 100644 index 0000000..fdffafc --- /dev/null +++ b/data/recipes/smithing/rune.yaml @@ -0,0 +1,48 @@ +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/smith_adamant_arrowtips.yaml b/data/recipes/smithing/smith_adamant_arrowtips.yaml deleted file mode 100644 index 466cfa2..0000000 --- a/data/recipes/smithing/smith_adamant_arrowtips.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_adamant_arrowtips -type: smithing -level: 75 -xp: 62 -wait: 4 -station: [anvil] -consume: - - items: [adamantite_bar] - quantity: 1 -output: adamant_arrowtips -output_qty: 15 -message: "You hammer out some adamant arrowtips." diff --git a/data/recipes/smithing/smith_adamant_bolts_unf.yaml b/data/recipes/smithing/smith_adamant_bolts_unf.yaml deleted file mode 100644 index 2fe1730..0000000 --- a/data/recipes/smithing/smith_adamant_bolts_unf.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_adamant_bolts_unf -type: smithing -level: 76 -xp: 62 -wait: 4 -station: [anvil] -consume: - - items: [adamantite_bar] - quantity: 1 -output: adamant_bolts_unf -output_qty: 10 -message: "You hammer out some adamant bolts." diff --git a/data/recipes/smithing/smith_adamant_dagger.yaml b/data/recipes/smithing/smith_adamant_dagger.yaml deleted file mode 100644 index fb68dbf..0000000 --- a/data/recipes/smithing/smith_adamant_dagger.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_adamant_dagger -type: smithing -level: 70 -xp: 62 -wait: 4 -station: [anvil] -consume: - - items: [adamantite_bar] - quantity: 1 -output: adamant_dagger -message: "You hammer out an adamant dagger." diff --git a/data/recipes/smithing/smith_adamant_full_helm.yaml b/data/recipes/smithing/smith_adamant_full_helm.yaml deleted file mode 100644 index 2d849e5..0000000 --- a/data/recipes/smithing/smith_adamant_full_helm.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_adamant_full_helm -type: smithing -level: 77 -xp: 125 -wait: 4 -station: [anvil] -consume: - - items: [adamantite_bar] - quantity: 2 -output: adamant_full_helm -message: "You hammer out an adamant full helm." diff --git a/data/recipes/smithing/smith_adamant_med_helm.yaml b/data/recipes/smithing/smith_adamant_med_helm.yaml deleted file mode 100644 index 505691c..0000000 --- a/data/recipes/smithing/smith_adamant_med_helm.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_adamant_med_helm -type: smithing -level: 73 -xp: 62 -wait: 4 -station: [anvil] -consume: - - items: [adamantite_bar] - quantity: 1 -output: adamant_med_helm -message: "You hammer out an adamant med helm." diff --git a/data/recipes/smithing/smith_adamant_nails.yaml b/data/recipes/smithing/smith_adamant_nails.yaml deleted file mode 100644 index 2273b67..0000000 --- a/data/recipes/smithing/smith_adamant_nails.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_adamant_nails -type: smithing -level: 74 -xp: 62 -wait: 4 -station: [anvil] -consume: - - items: [adamantite_bar] - quantity: 1 -output: adamant_nails -output_qty: 15 -message: "You hammer out some adamant nails." diff --git a/data/recipes/smithing/smith_adamant_platebody.yaml b/data/recipes/smithing/smith_adamant_platebody.yaml deleted file mode 100644 index 33dc400..0000000 --- a/data/recipes/smithing/smith_adamant_platebody.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_adamant_platebody -type: smithing -level: 88 -xp: 312 -wait: 4 -station: [anvil] -consume: - - items: [adamantite_bar] - quantity: 5 -output: adamant_platebody -message: "You hammer out an adamant platebody." diff --git a/data/recipes/smithing/smith_adamant_sword.yaml b/data/recipes/smithing/smith_adamant_sword.yaml deleted file mode 100644 index e853ea6..0000000 --- a/data/recipes/smithing/smith_adamant_sword.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_adamant_sword -type: smithing -level: 74 -xp: 62 -wait: 4 -station: [anvil] -consume: - - items: [adamantite_bar] - quantity: 1 -output: adamant_sword -message: "You hammer out an adamant sword." diff --git a/data/recipes/smithing/smith_bronze_arrowtips.yaml b/data/recipes/smithing/smith_bronze_arrowtips.yaml deleted file mode 100644 index 77d724f..0000000 --- a/data/recipes/smithing/smith_bronze_arrowtips.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_bronze_arrowtips -type: smithing -level: 5 -xp: 12 -wait: 4 -station: [anvil] -consume: - - items: [bronze_bar] - quantity: 1 -output: bronze_arrowtips -output_qty: 15 -message: "You hammer out some bronze arrowtips." diff --git a/data/recipes/smithing/smith_bronze_bolts_unf.yaml b/data/recipes/smithing/smith_bronze_bolts_unf.yaml deleted file mode 100644 index 340d14f..0000000 --- a/data/recipes/smithing/smith_bronze_bolts_unf.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_bronze_bolts_unf -type: smithing -level: 6 -xp: 12 -wait: 4 -station: [anvil] -consume: - - items: [bronze_bar] - quantity: 1 -output: bronze_bolts_unf -output_qty: 10 -message: "You hammer out some bronze bolts." diff --git a/data/recipes/smithing/smith_bronze_dagger.yaml b/data/recipes/smithing/smith_bronze_dagger.yaml deleted file mode 100644 index 4842927..0000000 --- a/data/recipes/smithing/smith_bronze_dagger.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_bronze_dagger -type: smithing -level: 1 -xp: 12 -wait: 4 -station: [anvil] -consume: - - items: [bronze_bar] - quantity: 1 -output: bronze_dagger -message: "You hammer out a bronze dagger." diff --git a/data/recipes/smithing/smith_bronze_full_helm.yaml b/data/recipes/smithing/smith_bronze_full_helm.yaml deleted file mode 100644 index 6ef58ed..0000000 --- a/data/recipes/smithing/smith_bronze_full_helm.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_bronze_full_helm -type: smithing -level: 7 -xp: 25 -wait: 4 -station: [anvil] -consume: - - items: [bronze_bar] - quantity: 2 -output: bronze_full_helm -message: "You hammer out a bronze full helm." diff --git a/data/recipes/smithing/smith_bronze_med_helm.yaml b/data/recipes/smithing/smith_bronze_med_helm.yaml deleted file mode 100644 index 6abbc60..0000000 --- a/data/recipes/smithing/smith_bronze_med_helm.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_bronze_med_helm -type: smithing -level: 3 -xp: 12 -wait: 4 -station: [anvil] -consume: - - items: [bronze_bar] - quantity: 1 -output: bronze_med_helm -message: "You hammer out a bronze med helm." diff --git a/data/recipes/smithing/smith_bronze_nails.yaml b/data/recipes/smithing/smith_bronze_nails.yaml deleted file mode 100644 index c75a957..0000000 --- a/data/recipes/smithing/smith_bronze_nails.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_bronze_nails -type: smithing -level: 4 -xp: 12 -wait: 4 -station: [anvil] -consume: - - items: [bronze_bar] - quantity: 1 -output: bronze_nails -output_qty: 15 -message: "You hammer out some bronze nails." diff --git a/data/recipes/smithing/smith_bronze_platebody.yaml b/data/recipes/smithing/smith_bronze_platebody.yaml deleted file mode 100644 index c9d7d39..0000000 --- a/data/recipes/smithing/smith_bronze_platebody.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_bronze_platebody -type: smithing -level: 18 -xp: 62 -wait: 4 -station: [anvil] -consume: - - items: [bronze_bar] - quantity: 5 -output: bronze_platebody -message: "You hammer out a bronze platebody." diff --git a/data/recipes/smithing/smith_bronze_sword.yaml b/data/recipes/smithing/smith_bronze_sword.yaml deleted file mode 100644 index 322ca9f..0000000 --- a/data/recipes/smithing/smith_bronze_sword.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_bronze_sword -type: smithing -level: 4 -xp: 12 -wait: 4 -station: [anvil] -consume: - - items: [bronze_bar] - quantity: 1 -output: bronze_sword -message: "You hammer out a bronze sword." diff --git a/data/recipes/smithing/smith_iron_arrowtips.yaml b/data/recipes/smithing/smith_iron_arrowtips.yaml deleted file mode 100644 index d11286e..0000000 --- a/data/recipes/smithing/smith_iron_arrowtips.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_iron_arrowtips -type: smithing -level: 25 -xp: 25 -wait: 4 -station: [anvil] -consume: - - items: [iron_bar] - quantity: 1 -output: iron_arrowtips -output_qty: 15 -message: "You hammer out some iron arrowtips." diff --git a/data/recipes/smithing/smith_iron_bolts_unf.yaml b/data/recipes/smithing/smith_iron_bolts_unf.yaml deleted file mode 100644 index 9529cab..0000000 --- a/data/recipes/smithing/smith_iron_bolts_unf.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_iron_bolts_unf -type: smithing -level: 26 -xp: 25 -wait: 4 -station: [anvil] -consume: - - items: [iron_bar] - quantity: 1 -output: iron_bolts_unf -output_qty: 10 -message: "You hammer out some iron bolts." diff --git a/data/recipes/smithing/smith_iron_dagger.yaml b/data/recipes/smithing/smith_iron_dagger.yaml deleted file mode 100644 index 3694ca5..0000000 --- a/data/recipes/smithing/smith_iron_dagger.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_iron_dagger -type: smithing -level: 20 -xp: 25 -wait: 4 -station: [anvil] -consume: - - items: [iron_bar] - quantity: 1 -output: iron_dagger -message: "You hammer out an iron dagger." diff --git a/data/recipes/smithing/smith_iron_full_helm.yaml b/data/recipes/smithing/smith_iron_full_helm.yaml deleted file mode 100644 index cfa27f5..0000000 --- a/data/recipes/smithing/smith_iron_full_helm.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_iron_full_helm -type: smithing -level: 27 -xp: 50 -wait: 4 -station: [anvil] -consume: - - items: [iron_bar] - quantity: 2 -output: iron_full_helm -message: "You hammer out an iron full helm." diff --git a/data/recipes/smithing/smith_iron_med_helm.yaml b/data/recipes/smithing/smith_iron_med_helm.yaml deleted file mode 100644 index 54b6654..0000000 --- a/data/recipes/smithing/smith_iron_med_helm.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_iron_med_helm -type: smithing -level: 22 -xp: 25 -wait: 4 -station: [anvil] -consume: - - items: [iron_bar] - quantity: 1 -output: iron_med_helm -message: "You hammer out an iron med helm." diff --git a/data/recipes/smithing/smith_iron_nails.yaml b/data/recipes/smithing/smith_iron_nails.yaml deleted file mode 100644 index cb6ad89..0000000 --- a/data/recipes/smithing/smith_iron_nails.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_iron_nails -type: 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." diff --git a/data/recipes/smithing/smith_iron_platebody.yaml b/data/recipes/smithing/smith_iron_platebody.yaml deleted file mode 100644 index dd8a7d6..0000000 --- a/data/recipes/smithing/smith_iron_platebody.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_iron_platebody -type: smithing -level: 33 -xp: 125 -wait: 4 -station: [anvil] -consume: - - items: [iron_bar] - quantity: 5 -output: iron_platebody -message: "You hammer out an iron platebody." diff --git a/data/recipes/smithing/smith_iron_sword.yaml b/data/recipes/smithing/smith_iron_sword.yaml deleted file mode 100644 index c2d4bc5..0000000 --- a/data/recipes/smithing/smith_iron_sword.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_iron_sword -type: smithing -level: 24 -xp: 25 -wait: 4 -station: [anvil] -consume: - - items: [iron_bar] - quantity: 1 -output: iron_sword -message: "You hammer out an iron sword." diff --git a/data/recipes/smithing/smith_mithril_arrowtips.yaml b/data/recipes/smithing/smith_mithril_arrowtips.yaml deleted file mode 100644 index 2d27604..0000000 --- a/data/recipes/smithing/smith_mithril_arrowtips.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_mithril_arrowtips -type: smithing -level: 55 -xp: 50 -wait: 4 -station: [anvil] -consume: - - items: [mithril_bar] - quantity: 1 -output: mithril_arrowtips -output_qty: 15 -message: "You hammer out some mithril arrowtips." diff --git a/data/recipes/smithing/smith_mithril_bolts_unf.yaml b/data/recipes/smithing/smith_mithril_bolts_unf.yaml deleted file mode 100644 index 03afe7f..0000000 --- a/data/recipes/smithing/smith_mithril_bolts_unf.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_mithril_bolts_unf -type: smithing -level: 56 -xp: 50 -wait: 4 -station: [anvil] -consume: - - items: [mithril_bar] - quantity: 1 -output: mithril_bolts_unf -output_qty: 10 -message: "You hammer out some mithril bolts." diff --git a/data/recipes/smithing/smith_mithril_dagger.yaml b/data/recipes/smithing/smith_mithril_dagger.yaml deleted file mode 100644 index a437969..0000000 --- a/data/recipes/smithing/smith_mithril_dagger.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_mithril_dagger -type: smithing -level: 50 -xp: 50 -wait: 4 -station: [anvil] -consume: - - items: [mithril_bar] - quantity: 1 -output: mithril_dagger -message: "You hammer out a mithril dagger." diff --git a/data/recipes/smithing/smith_mithril_full_helm.yaml b/data/recipes/smithing/smith_mithril_full_helm.yaml deleted file mode 100644 index f9d6b74..0000000 --- a/data/recipes/smithing/smith_mithril_full_helm.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_mithril_full_helm -type: smithing -level: 57 -xp: 100 -wait: 4 -station: [anvil] -consume: - - items: [mithril_bar] - quantity: 2 -output: mithril_full_helm -message: "You hammer out a mithril full helm." diff --git a/data/recipes/smithing/smith_mithril_med_helm.yaml b/data/recipes/smithing/smith_mithril_med_helm.yaml deleted file mode 100644 index c4ebc30..0000000 --- a/data/recipes/smithing/smith_mithril_med_helm.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_mithril_med_helm -type: smithing -level: 53 -xp: 50 -wait: 4 -station: [anvil] -consume: - - items: [mithril_bar] - quantity: 1 -output: mithril_med_helm -message: "You hammer out a mithril med helm." diff --git a/data/recipes/smithing/smith_mithril_nails.yaml b/data/recipes/smithing/smith_mithril_nails.yaml deleted file mode 100644 index 6c9f225..0000000 --- a/data/recipes/smithing/smith_mithril_nails.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_mithril_nails -type: smithing -level: 54 -xp: 50 -wait: 4 -station: [anvil] -consume: - - items: [mithril_bar] - quantity: 1 -output: mithril_nails -output_qty: 15 -message: "You hammer out some mithril nails." diff --git a/data/recipes/smithing/smith_mithril_platebody.yaml b/data/recipes/smithing/smith_mithril_platebody.yaml deleted file mode 100644 index 751a7ea..0000000 --- a/data/recipes/smithing/smith_mithril_platebody.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_mithril_platebody -type: smithing -level: 68 -xp: 250 -wait: 4 -station: [anvil] -consume: - - items: [mithril_bar] - quantity: 5 -output: mithril_platebody -message: "You hammer out a mithril platebody." diff --git a/data/recipes/smithing/smith_mithril_sword.yaml b/data/recipes/smithing/smith_mithril_sword.yaml deleted file mode 100644 index fdf96ff..0000000 --- a/data/recipes/smithing/smith_mithril_sword.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_mithril_sword -type: smithing -level: 54 -xp: 50 -wait: 4 -station: [anvil] -consume: - - items: [mithril_bar] - quantity: 1 -output: mithril_sword -message: "You hammer out a mithril sword." diff --git a/data/recipes/smithing/smith_rune_arrowtips.yaml b/data/recipes/smithing/smith_rune_arrowtips.yaml deleted file mode 100644 index 93554a9..0000000 --- a/data/recipes/smithing/smith_rune_arrowtips.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_rune_arrowtips -type: smithing -level: 90 -xp: 75 -wait: 4 -station: [anvil] -consume: - - items: [runite_bar] - quantity: 1 -output: rune_arrowtips -output_qty: 15 -message: "You hammer out some rune arrowtips." diff --git a/data/recipes/smithing/smith_rune_bolts_unf.yaml b/data/recipes/smithing/smith_rune_bolts_unf.yaml deleted file mode 100644 index 93047ce..0000000 --- a/data/recipes/smithing/smith_rune_bolts_unf.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_rune_bolts_unf -type: smithing -level: 91 -xp: 75 -wait: 4 -station: [anvil] -consume: - - items: [runite_bar] - quantity: 1 -output: rune_bolts_unf -output_qty: 10 -message: "You hammer out some rune bolts." diff --git a/data/recipes/smithing/smith_rune_dagger.yaml b/data/recipes/smithing/smith_rune_dagger.yaml deleted file mode 100644 index a87b356..0000000 --- a/data/recipes/smithing/smith_rune_dagger.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_rune_dagger -type: smithing -level: 85 -xp: 75 -wait: 4 -station: [anvil] -consume: - - items: [runite_bar] - quantity: 1 -output: rune_dagger -message: "You hammer out a rune dagger." diff --git a/data/recipes/smithing/smith_rune_full_helm.yaml b/data/recipes/smithing/smith_rune_full_helm.yaml deleted file mode 100644 index 79ae3b0..0000000 --- a/data/recipes/smithing/smith_rune_full_helm.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_rune_full_helm -type: smithing -level: 92 -xp: 150 -wait: 4 -station: [anvil] -consume: - - items: [runite_bar] - quantity: 2 -output: rune_full_helm -message: "You hammer out a rune full helm." diff --git a/data/recipes/smithing/smith_rune_med_helm.yaml b/data/recipes/smithing/smith_rune_med_helm.yaml deleted file mode 100644 index 74e639a..0000000 --- a/data/recipes/smithing/smith_rune_med_helm.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_rune_med_helm -type: smithing -level: 88 -xp: 75 -wait: 4 -station: [anvil] -consume: - - items: [runite_bar] - quantity: 1 -output: rune_med_helm -message: "You hammer out a rune med helm." diff --git a/data/recipes/smithing/smith_rune_nails.yaml b/data/recipes/smithing/smith_rune_nails.yaml deleted file mode 100644 index 2681a57..0000000 --- a/data/recipes/smithing/smith_rune_nails.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_rune_nails -type: smithing -level: 89 -xp: 75 -wait: 4 -station: [anvil] -consume: - - items: [runite_bar] - quantity: 1 -output: rune_nails -output_qty: 15 -message: "You hammer out some rune nails." diff --git a/data/recipes/smithing/smith_rune_platebody.yaml b/data/recipes/smithing/smith_rune_platebody.yaml deleted file mode 100644 index 4a36082..0000000 --- a/data/recipes/smithing/smith_rune_platebody.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_rune_platebody -type: smithing -level: 99 -xp: 375 -wait: 4 -station: [anvil] -consume: - - items: [runite_bar] - quantity: 5 -output: rune_platebody -message: "You hammer out a rune platebody." diff --git a/data/recipes/smithing/smith_rune_sword.yaml b/data/recipes/smithing/smith_rune_sword.yaml deleted file mode 100644 index 20f94f6..0000000 --- a/data/recipes/smithing/smith_rune_sword.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_rune_sword -type: smithing -level: 89 -xp: 75 -wait: 4 -station: [anvil] -consume: - - items: [runite_bar] - quantity: 1 -output: rune_sword -message: "You hammer out a rune sword." diff --git a/data/recipes/smithing/smith_steel_arrowtips.yaml b/data/recipes/smithing/smith_steel_arrowtips.yaml deleted file mode 100644 index 1ec292f..0000000 --- a/data/recipes/smithing/smith_steel_arrowtips.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_steel_arrowtips -type: smithing -level: 35 -xp: 37 -wait: 4 -station: [anvil] -consume: - - items: [steel_bar] - quantity: 1 -output: steel_arrowtips -output_qty: 15 -message: "You hammer out some steel arrowtips." diff --git a/data/recipes/smithing/smith_steel_bolts_unf.yaml b/data/recipes/smithing/smith_steel_bolts_unf.yaml deleted file mode 100644 index cc071ef..0000000 --- a/data/recipes/smithing/smith_steel_bolts_unf.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_steel_bolts_unf -type: smithing -level: 36 -xp: 37 -wait: 4 -station: [anvil] -consume: - - items: [steel_bar] - quantity: 1 -output: steel_bolts_unf -output_qty: 10 -message: "You hammer out some steel bolts." diff --git a/data/recipes/smithing/smith_steel_dagger.yaml b/data/recipes/smithing/smith_steel_dagger.yaml deleted file mode 100644 index 6e2f1c4..0000000 --- a/data/recipes/smithing/smith_steel_dagger.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_steel_dagger -type: smithing -level: 30 -xp: 37 -wait: 4 -station: [anvil] -consume: - - items: [steel_bar] - quantity: 1 -output: steel_dagger -message: "You hammer out a steel dagger." diff --git a/data/recipes/smithing/smith_steel_full_helm.yaml b/data/recipes/smithing/smith_steel_full_helm.yaml deleted file mode 100644 index c95d0d0..0000000 --- a/data/recipes/smithing/smith_steel_full_helm.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_steel_full_helm -type: smithing -level: 37 -xp: 75 -wait: 4 -station: [anvil] -consume: - - items: [steel_bar] - quantity: 2 -output: steel_full_helm -message: "You hammer out a steel full helm." diff --git a/data/recipes/smithing/smith_steel_med_helm.yaml b/data/recipes/smithing/smith_steel_med_helm.yaml deleted file mode 100644 index 3415ec8..0000000 --- a/data/recipes/smithing/smith_steel_med_helm.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_steel_med_helm -type: smithing -level: 33 -xp: 37 -wait: 4 -station: [anvil] -consume: - - items: [steel_bar] - quantity: 1 -output: steel_med_helm -message: "You hammer out a steel med helm." diff --git a/data/recipes/smithing/smith_steel_nails.yaml b/data/recipes/smithing/smith_steel_nails.yaml deleted file mode 100644 index 2da69c8..0000000 --- a/data/recipes/smithing/smith_steel_nails.yaml +++ /dev/null @@ -1,12 +0,0 @@ -id: smith_steel_nails -type: smithing -level: 34 -xp: 37 -wait: 4 -station: [anvil] -consume: - - items: [steel_bar] - quantity: 1 -output: steel_nails -output_qty: 15 -message: "You hammer out some steel nails." diff --git a/data/recipes/smithing/smith_steel_platebody.yaml b/data/recipes/smithing/smith_steel_platebody.yaml deleted file mode 100644 index da11d11..0000000 --- a/data/recipes/smithing/smith_steel_platebody.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_steel_platebody -type: 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." diff --git a/data/recipes/smithing/smith_steel_sword.yaml b/data/recipes/smithing/smith_steel_sword.yaml deleted file mode 100644 index 63270bc..0000000 --- a/data/recipes/smithing/smith_steel_sword.yaml +++ /dev/null @@ -1,11 +0,0 @@ -id: smith_steel_sword -type: smithing -level: 34 -xp: 37 -wait: 4 -station: [anvil] -consume: - - items: [steel_bar] - quantity: 1 -output: steel_sword -message: "You hammer out a steel sword." diff --git a/data/recipes/smithing/steel.yaml b/data/recipes/smithing/steel.yaml new file mode 100644 index 0000000..204b427 --- /dev/null +++ b/data/recipes/smithing/steel.yaml @@ -0,0 +1,48 @@ +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/data/rooms/town/1.yaml b/data/rooms/town/1.yaml index ec97af2..ba5212d 100644 --- a/data/rooms/town/1.yaml +++ b/data/rooms/town/1.yaml @@ -36,3 +36,6 @@ item_spawns: - id: graceful_cape quantity: 1 respawn_ticks: 60 + - id: deck + quantity: 1 + respawn_ticks: 60 diff --git a/data/rooms/town/9.yaml b/data/rooms/town/9.yaml index 9a19943..7a24fda 100644 --- a/data/rooms/town/9.yaml +++ b/data/rooms/town/9.yaml @@ -6,7 +6,6 @@ exits: west: 8 north: 150 south: 151 - northeast: 152 objects: - id: scrap_pile - id: scrap_pile diff --git a/internal/action/recipe.go b/internal/action/recipe.go index 5a69195..8779d53 100644 --- a/internal/action/recipe.go +++ b/internal/action/recipe.go @@ -1,6 +1,8 @@ package action import ( + "fmt" + "os" "path/filepath" "gopkg.in/yaml.v3" @@ -30,13 +32,68 @@ type RecipeDef struct { 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)} + 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) { @@ -53,13 +110,10 @@ func (s *RecipeStore) LoadByType(recipeType string) ([]RecipeDef, error) { return result, nil } - all, err := s.loadAllRaw() - if err != nil { - return nil, err - } + s.ensureLoaded() + all := s.cache["_all"] if recipeType == "_all" { - s.cache[recipeType] = all result := make([]RecipeDef, len(all)) copy(result, all) return result, nil @@ -82,15 +136,52 @@ func (s *RecipeStore) loadAllRaw() ([]RecipeDef, error) { var recipes []RecipeDef WalkYAMLDir(dir, func(path, id string, data []byte) error { var r RecipeDef - if err := yaml.Unmarshal(data, &r); err != nil { + if err := yaml.Unmarshal(data, &r); err == nil && r.Type != "" { + recipes = append(recipes, r) return nil } - recipes = append(recipes, r) + + 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 { diff --git a/internal/color/color.go b/internal/color/color.go index 9496a1d..58c040c 100644 --- a/internal/color/color.go +++ b/internal/color/color.go @@ -87,7 +87,7 @@ func nearestANSIBg(index int) int { return fg - 30 + 40 } -func nearestCubeComponent(v int) int { +func nearestCube(v int) int { if v < 48 { return 0 } @@ -107,9 +107,9 @@ func nearestCubeComponent(v int) int { } func NearestXterm256(r, g, b int) int { - ri := nearestCubeComponent(r) - gi := nearestCubeComponent(g) - bi := nearestCubeComponent(b) + ri := nearestCube(r) + gi := nearestCube(g) + bi := nearestCube(b) cubeIdx := 16 + ri*36 + gi*6 + bi cubeDist := colorDist(r, g, b, cubeValues[ri], cubeValues[gi], cubeValues[bi]) @@ -325,10 +325,10 @@ func renderGradient(mode string, spec ColorSpec, text string) string { } func ExpandTags(mode string, text string) string { - return ExpandTagsWithDefault(mode, NoColor(), text) + return ExpandTagsDefault(mode, NoColor(), text) } -func ExpandTagsWithDefault(mode string, def ColorSpec, text string) string { +func ExpandTagsDefault(mode string, def ColorSpec, text string) string { if !strings.Contains(text, "{") { if !def.Empty() { return Render(mode, def, text) diff --git a/internal/config/config.go b/internal/config/config.go index 124d45d..cbf15c5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -61,10 +61,10 @@ func DefaultColors() ColorsConfig { type GameConfig struct { TickLength int `yaml:"tick_length"` - StartupValidation StartupValidationConfig `yaml:"startup_validation"` + StartupValidation ValidationConfig `yaml:"startup_validation"` } -type StartupValidationConfig struct { +type ValidationConfig struct { CheckSources []int `yaml:"check_sources"` IgnoreUnreachable []int `yaml:"ignore_unreachable"` } @@ -97,7 +97,7 @@ func Default() *Config { return &Config{ Game: GameConfig{ TickLength: 600, - StartupValidation: StartupValidationConfig{ + StartupValidation: ValidationConfig{ CheckSources: []int{1}, IgnoreUnreachable: []int{}, }, diff --git a/internal/game/action.go b/internal/game/action.go index f25cabc..2f5dbaf 100644 --- a/internal/game/action.go +++ b/internal/game/action.go @@ -57,7 +57,7 @@ func (g *Game) startAction(sess *net.Session, verb, target string) { if p.Action != nil { g.cancelAction(p) } - g.cancelBackgroundAction(p) + g.cancelBgAction(p) lower := strings.ToLower(target) instanceIdx := -1 @@ -206,7 +206,7 @@ func (g *Game) cancelAction(p *player.Player) { p.ClearMoveState() } -func (g *Game) cancelBackgroundAction(p *player.Player) { +func (g *Game) cancelBgAction(p *player.Player) { p.BackgroundAction = nil p.BackgroundActionState = nil } diff --git a/internal/game/action_agility.go b/internal/game/action_agility.go index 37fbab9..43c9cfd 100644 --- a/internal/game/action_agility.go +++ b/internal/game/action_agility.go @@ -52,13 +52,7 @@ func (g *Game) startObstacle(sess *net.Session, p *player.Player, info *Obstacle TargetName: info.CourseName, } - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s starts %s on the %s.", p.Name, gerund, info.CourseName))) - } - } - } + g.broadcastAction(sess, "\n%s starts %s on the %s.", p.Name, gerund, info.CourseName) } func (g *Game) advanceObstacle(sess *net.Session, p *player.Player) { diff --git a/internal/game/action_burn.go b/internal/game/action_burn.go index c31d7d5..71b5a35 100644 --- a/internal/game/action_burn.go +++ b/internal/game/action_burn.go @@ -28,7 +28,7 @@ func (g *Game) doBurn(sess *net.Session, p *player.Player, input string) { if p.Action != nil { g.cancelAction(p) } - g.cancelBackgroundAction(p) + g.cancelBgAction(p) itemID, fromGround, ambiguous := g.resolveBurnTarget(p, input) if ambiguous { @@ -47,7 +47,7 @@ func (g *Game) doStoke(sess *net.Session, p *player.Player, input string) { if p.Action != nil { g.cancelAction(p) } - g.cancelBackgroundAction(p) + g.cancelBgAction(p) if !g.hasFireObject(p.RoomID) { sess.WriteLine("There's no fire here to stoke.") @@ -120,13 +120,7 @@ func (g *Game) startBurn(sess *net.Session, p *player.Player, itemID string, fro p.ActionState = &ActionState{Type: ActionBurning} - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s starts building a fire.", p.Name))) - } - } - } + g.broadcastAction(sess, "\n%s starts building a fire.", p.Name) p.Action = &action.Action{ Type: "burn", @@ -166,13 +160,7 @@ func (g *Game) startStoke(sess *net.Session, p *player.Player, itemID string) { p.ActionState = &ActionState{Type: ActionStoking} - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s tends to the fire.", p.Name))) - } - } - } + g.broadcastAction(sess, "\n%s tends to the fire.", p.Name) p.Action = &action.Action{ Type: "stoke", @@ -260,11 +248,7 @@ func (g *Game) advanceBurn(sess *net.Session, p *player.Player) { } sess.WriteLine(msg) - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s started a fire!", p.Name))) - } - } + g.broadcastAction(sess, "\n%s started a fire!", p.Name) return } @@ -458,12 +442,12 @@ func (g *Game) cancelStokers(roomID int) { func (g *Game) resolveBurnTarget(p *player.Player, input string) (itemID string, fromGround bool, ambiguous bool) { if input != "" { - return g.resolveNamedBurnTarget(p, input) + return g.namedBurnTarget(p, input) } - return g.resolveDefaultBurnTarget(p) + return g.defaultBurnTarget(p) } -func (g *Game) collectBurnableGround(roomID int, name string) []string { +func (g *Game) burnableGround(roomID int, name string) []string { var out []string for itemID := range g.World.GroundItems(roomID) { def, err := g.ItemStore.Load(itemID) @@ -477,7 +461,7 @@ func (g *Game) collectBurnableGround(roomID int, name string) []string { return out } -func (g *Game) collectBurnableInv(p *player.Player, name string) []string { +func (g *Game) burnableInv(p *player.Player, name string) []string { var out []string seen := make(map[string]bool) for _, slot := range p.Inventory { @@ -496,8 +480,8 @@ func (g *Game) collectBurnableInv(p *player.Player, name string) []string { return out } -func (g *Game) resolveNamedBurnTarget(p *player.Player, input string) (itemID string, fromGround bool, ambiguous bool) { - groundMatches := g.collectBurnableGround(p.RoomID, input) +func (g *Game) namedBurnTarget(p *player.Player, input string) (itemID string, fromGround bool, ambiguous bool) { + groundMatches := g.burnableGround(p.RoomID, input) if len(groundMatches) > 1 { return "", false, true } @@ -505,7 +489,7 @@ func (g *Game) resolveNamedBurnTarget(p *player.Player, input string) (itemID st return groundMatches[0], true, false } - invMatches := g.collectBurnableInv(p, input) + invMatches := g.burnableInv(p, input) if len(invMatches) > 1 { return "", false, true } @@ -516,8 +500,8 @@ func (g *Game) resolveNamedBurnTarget(p *player.Player, input string) (itemID st return "", false, false } -func (g *Game) resolveDefaultBurnTarget(p *player.Player) (itemID string, fromGround bool, ambiguous bool) { - groundBurnable := g.collectBurnableGround(p.RoomID, "") +func (g *Game) defaultBurnTarget(p *player.Player) (itemID string, fromGround bool, ambiguous bool) { + groundBurnable := g.burnableGround(p.RoomID, "") if len(groundBurnable) == 1 { return groundBurnable[0], true, false } @@ -525,7 +509,7 @@ func (g *Game) resolveDefaultBurnTarget(p *player.Player) (itemID string, fromGr return "", false, true } - invBurnable := g.collectBurnableInv(p, "") + invBurnable := g.burnableInv(p, "") if len(invBurnable) == 1 { return invBurnable[0], false, false } @@ -537,7 +521,7 @@ func (g *Game) resolveDefaultBurnTarget(p *player.Player) (itemID string, fromGr } func (g *Game) resolveStokeTarget(p *player.Player, input string) (string, bool) { - invBurnable := g.collectBurnableInv(p, input) + invBurnable := g.burnableInv(p, input) if len(invBurnable) == 1 { return invBurnable[0], false } diff --git a/internal/game/action_clean.go b/internal/game/action_clean.go index 104dcb0..7330883 100644 --- a/internal/game/action_clean.go +++ b/internal/game/action_clean.go @@ -21,7 +21,7 @@ func (g *Game) advanceClean(sess *net.Session, p *player.Player) { allRecipes, err := g.RecipeStore.LoadAll() if err != nil { - g.cancelBackgroundAction(p) + g.cancelBgAction(p) return } @@ -51,7 +51,7 @@ func (g *Game) advanceClean(sess *net.Session, p *player.Player) { if recipe == nil { sess.WriteLine("\nYou've finished cleaning herbs.") - g.cancelBackgroundAction(p) + g.cancelBgAction(p) return } @@ -105,7 +105,7 @@ func (g *Game) advanceClean(sess *net.Session, p *player.Player) { if !hasMore { sess.WriteLine("\nYou've finished cleaning herbs.") - g.cancelBackgroundAction(p) + g.cancelBgAction(p) return } diff --git a/internal/game/action_default_target.go b/internal/game/action_default_target.go index 2ca0bd3..a184f32 100644 --- a/internal/game/action_default_target.go +++ b/internal/game/action_default_target.go @@ -8,7 +8,7 @@ func verbToSkill(verb string) string { return verbSkill[verb] } -func (g *Game) resolveDefaultTarget(roomID int, verb string) string { +func (g *Game) defaultTarget(roomID int, verb string) string { skill := verbToSkill(verb) if skill == "" { return "" diff --git a/internal/game/action_farm.go b/internal/game/action_farm.go index d79150f..21604aa 100644 --- a/internal/game/action_farm.go +++ b/internal/game/action_farm.go @@ -51,7 +51,7 @@ func (g *Game) ensureFarmState(p *player.Player, prefix string) { } } -func (g *Game) findActiveFarmPrefixes(p *player.Player) []string { +func (g *Game) findFarmPrefixes(p *player.Player) []string { seen := make(map[string]bool) var prefixes []string for key := range p.Flags { @@ -94,7 +94,7 @@ func (g *Game) FarmTick() { } func (g *Game) advanceFarmGrowth(sess *net.Session, p *player.Player) { - prefixes := g.findActiveFarmPrefixes(p) + prefixes := g.findFarmPrefixes(p) for _, prefix := range prefixes { seedID, _ := p.Flags[prefix+"_seed"].(string) if seedID == "" { @@ -492,7 +492,7 @@ func (g *Game) farmMatchPrefix(p *player.Player, input string) (prefix string, d return "", "", -1 } -func (g *Game) farmLookSuffixForObj(p *player.Player, defID string, index int) string { +func (g *Game) farmObjSuffix(p *player.Player, defID string, index int) string { if defID == "tool_shed" { return g.toolShedLookSuffix(p) } @@ -538,7 +538,7 @@ func (g *Game) showFarmPatches(sess *net.Session, p *player.Player) { g.ensureFarmState(p, prefix) idxStr := "" - if obj.Index > 0 || g.countFarmPatchesOfType(objInstances, obj.DefID) > 1 { + if obj.Index > 0 || g.countPatchesByType(objInstances, obj.DefID) > 1 { idxStr = fmt.Sprintf(" %d", obj.Index+1) } @@ -618,7 +618,7 @@ func (g *Game) showFarmPatches(sess *net.Session, p *player.Player) { } } -func (g *Game) countFarmPatchesOfType(instances []world.ObjState, defID string) int { +func (g *Game) countPatchesByType(instances []world.ObjState, defID string) int { count := 0 for _, obj := range instances { if obj.DefID == defID && farmPatchDefIDs[obj.DefID] { diff --git a/internal/game/action_fletch.go b/internal/game/action_fletch.go index aa5dfc7..3d8c30f 100644 --- a/internal/game/action_fletch.go +++ b/internal/game/action_fletch.go @@ -115,13 +115,7 @@ func (g *Game) startTimedFletch(sess *net.Session, p *player.Player, recipe *act } p.BackgroundActionState = &ActionState{Type: ActionFletching, TargetName: outputName} - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s starts fletching.", p.Name))) - } - } - } + g.broadcastAction(sess, "\n%s starts fletching.", p.Name) sess.WriteLine(fmt.Sprintf("\nYou begin fletching %s.", outputName)) } @@ -132,9 +126,9 @@ func (g *Game) advanceFletch(sess *net.Session, p *player.Player) { wait, _ := p.BackgroundAction.Data["wait"].(float64) remaining, _ := p.BackgroundAction.Data["remaining"].(int) - recipe := g.loadProductionRecipe(recipeID) + recipe := g.loadRecipe(recipeID) if recipe == nil { - g.cancelBackgroundAction(p) + g.cancelBgAction(p) return } @@ -146,7 +140,7 @@ func (g *Game) advanceFletch(sess *net.Session, p *player.Player) { if !g.canDoFletchRecipe(p, recipe) { sess.WriteLine("\nYou've run out of fletching materials.") - g.cancelBackgroundAction(p) + g.cancelBgAction(p) return } @@ -175,7 +169,7 @@ func (g *Game) advanceFletch(sess *net.Session, p *player.Player) { freeSlot := p.FirstFreeSlot() if freeSlot == -1 { sess.WriteLine("\nYour inventory is too full!") - g.cancelBackgroundAction(p) + g.cancelBgAction(p) return } p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: recipe.Output, Quantity: outputQty}) @@ -205,14 +199,14 @@ func (g *Game) advanceFletch(sess *net.Session, p *player.Player) { p.BackgroundAction.Data["remaining"] = remaining if remaining <= 0 { sess.WriteLine("\nYou've finished fletching.") - g.cancelBackgroundAction(p) + g.cancelBgAction(p) return } } if !g.canContinueProduction(p, recipe) { sess.WriteLine("\nYou've run out of fletching materials.") - g.cancelBackgroundAction(p) + g.cancelBgAction(p) return } diff --git a/internal/game/action_gather.go b/internal/game/action_gather.go index e169beb..9b47de2 100644 --- a/internal/game/action_gather.go +++ b/internal/game/action_gather.go @@ -53,42 +53,11 @@ func (g *Game) startGather(sess *net.Session, p *player.Player, obj *object.Obje } wait := cfg.BaseWait + var toolName string if len(cfg.Tools) > 0 { - var toolSpeed float64 = -1 - if itemID, ok := p.Equipment[object.SlotMainHand]; ok { - def, err := g.ItemStore.Load(itemID) - if err == nil { - for _, t := range cfg.Tools { - if def.ToolType == t { - toolSpeed = def.ToolSpeed - break - } - } - } - } - if toolSpeed < 0 { - for i := 0; i < 28; i++ { - slot := p.InvSlot(i) - if slot == nil { - continue - } - def, err := g.ItemStore.Load(slot.ItemID) - if err != nil { - continue - } - for _, t := range cfg.Tools { - if def.ToolType == t { - toolSpeed = def.ToolSpeed - break - } - } - if toolSpeed >= 0 { - break - } - } - } - if toolSpeed < 0 { + toolSpeed, name, found := g.findTool(p, cfg.Tools) + if !found { names := make([]string, len(cfg.Tools)) for i, t := range cfg.Tools { names[i] = strings.ReplaceAll(t, "_", " ") @@ -97,6 +66,7 @@ func (g *Game) startGather(sess *net.Session, p *player.Player, obj *object.Obje sess.WriteLine(fmt.Sprintf("You need a %s to do that.", toolList)) return } + toolName = name wait = cfg.BaseWait - toolSpeed if wait < 1 { wait = 1 @@ -118,38 +88,6 @@ func (g *Game) startGather(sess *net.Session, p *player.Player, obj *object.Obje sess.WriteLine("Your inventory is too full!") return } - - toolName := "" - if itemID, ok := p.Equipment[object.SlotMainHand]; ok { - if def, err := g.ItemStore.Load(itemID); err == nil { - for _, t := range cfg.Tools { - if def.ToolType == t { - toolName = def.Name - break - } - } - } - } - if toolName == "" { - for _, slot := range p.Inventory { - if slot == nil || slot.Quantity <= 0 { - continue - } - def, err := g.ItemStore.Load(slot.ItemID) - if err != nil { - continue - } - for _, t := range cfg.Tools { - if def.ToolType == t { - toolName = def.Name - break - } - } - if toolName != "" { - break - } - } - } verb := cfg.Skill switch cfg.Skill { case "woodcutting": @@ -157,13 +95,7 @@ func (g *Game) startGather(sess *net.Session, p *player.Player, obj *object.Obje } p.ActionState = &ActionState{Type: ActionGathering, TargetName: obj.Name, ToolName: toolName, Verb: verb} - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s starts %s the %s.", p.Name, verb, obj.Name))) - } - } - } + g.broadcastAction(sess, "\n%s starts %s the %s.", p.Name, verb, obj.Name) data := map[string]any{ "obj_def_id": obj.ID, diff --git a/internal/game/action_search.go b/internal/game/action_search.go index d7dabe0..5cb8644 100644 --- a/internal/game/action_search.go +++ b/internal/game/action_search.go @@ -35,13 +35,7 @@ func (g *Game) startSearch(sess *net.Session, p *player.Player, itemID string, s p.ActionState = &ActionState{Type: ActionSearching, TargetName: def.Name} - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s searches a %s.", p.Name, def.Name))) - } - } - } + g.broadcastAction(sess, "\n%s searches a %s.", p.Name, def.Name) } func (g *Game) advanceSearch(sess *net.Session, p *player.Player) { diff --git a/internal/game/action_steal.go b/internal/game/action_steal.go index 3510ce7..a95be25 100644 --- a/internal/game/action_steal.go +++ b/internal/game/action_steal.go @@ -259,13 +259,7 @@ func (g *Game) startSteal(sess *net.Session, p *player.Player, targetType string sess.WriteLine(fmt.Sprintf("You attempt to steal from the %s...", targetName)) p.ActionState = &ActionState{Type: ActionStealing, TargetName: targetName} - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s glances around the room.", p.Name))) - } - } - } + g.broadcastAction(sess, "\n%s glances around the room.", p.Name) p.Action = &action.Action{ Type: "steal", diff --git a/internal/game/action_use.go b/internal/game/action_use.go index 368162b..a3858a9 100644 --- a/internal/game/action_use.go +++ b/internal/game/action_use.go @@ -36,13 +36,7 @@ func (g *Game) startUse(sess *net.Session, p *player.Player, obj *object.ObjectD p.ActionState = &ActionState{Type: ActionUsing, TargetName: obj.Name} - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s uses the %s.", p.Name, obj.Name))) - } - } - } + g.broadcastAction(sess, "\n%s uses the %s.", p.Name, obj.Name) p.Action = &action.Action{ Type: "use", diff --git a/internal/game/cmd_agility.go b/internal/game/cmd_agility.go index 084c5cf..e347322 100644 --- a/internal/game/cmd_agility.go +++ b/internal/game/cmd_agility.go @@ -31,7 +31,7 @@ func (g *Game) doObstacle(sess *net.Session, verb string) { if p.Action != nil { g.cancelAction(p) } - g.cancelBackgroundAction(p) + g.cancelBgAction(p) g.startObstacle(sess, p, info) } diff --git a/internal/game/cmd_attack.go b/internal/game/cmd_attack.go index f9cfba2..89cde46 100644 --- a/internal/game/cmd_attack.go +++ b/internal/game/cmd_attack.go @@ -126,7 +126,7 @@ func (g *Game) findMob(sess *net.Session, input string, roomID int) *world.MobIn func (g *Game) startCombat(sess *net.Session, p *player.Player, mob *world.MobInstance) { g.cancelRest(p.Name) - g.cancelBackgroundAction(p) + g.cancelBgAction(p) combat.EnterCombat(p.Name, mob.InstanceID) p.AttackTimer = 0 @@ -162,13 +162,7 @@ func (g *Game) startCombat(sess *net.Session, p *player.Player, mob *world.MobIn } p.ActionState = &ActionState{Type: ActionCombating, TargetName: mob.Name} - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s attacks %s!", p.Name, mobDisplayName(mob, true)))) - } - } - } + g.broadcastAction(sess, "\n%s attacks %s!", p.Name, mobDisplayName(mob, true)) g.Ticks.Subscribe(1, func() bool { cs := combat.GetCombat(p.Name) @@ -567,7 +561,7 @@ func (g *Game) endCombat(sess *net.Session, p *player.Player, mob *world.MobInst } if mob.Drops.Remains != "" { - g.World.AddReservedGroundItem(p.RoomID, mob.Drops.Remains, 1, p.Name) + g.World.AddReservedItem(p.RoomID, mob.Drops.Remains, 1, p.Name) def, _ := g.ItemStore.Load(mob.Drops.Remains) name := mob.Drops.Remains if def != nil { @@ -588,7 +582,7 @@ func (g *Game) endCombat(sess *net.Session, p *player.Player, mob *world.MobInst if qty <= 0 { qty = 1 } - g.World.AddReservedGroundItem(p.RoomID, entry.ItemID, qty, p.Name) + g.World.AddReservedItem(p.RoomID, entry.ItemID, qty, p.Name) def, _ := g.ItemStore.Load(entry.ItemID) name := entry.ItemID if def != nil { diff --git a/internal/game/cmd_clean.go b/internal/game/cmd_clean.go index 47896bb..951b41d 100644 --- a/internal/game/cmd_clean.go +++ b/internal/game/cmd_clean.go @@ -1,7 +1,6 @@ package game import ( - "fmt" "strings" "thehouseoficarus/internal/action" @@ -54,7 +53,7 @@ func (g *Game) doClean(sess *net.Session, input string) { return } - g.cancelBackgroundAction(p) + g.cancelBgAction(p) p.BackgroundAction = &action.Action{ Type: "clean", @@ -67,13 +66,7 @@ func (g *Game) doClean(sess *net.Session, input string) { } p.BackgroundActionState = &ActionState{Type: ActionCleaning} - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s starts cleaning herbs.", p.Name))) - } - } - } + g.broadcastAction(sess, "\n%s starts cleaning herbs.", p.Name) sess.WriteLine("\nYou begin cleaning herbs.") } diff --git a/internal/game/cmd_consume.go b/internal/game/cmd_consume.go index 0a7d335..3782237 100644 --- a/internal/game/cmd_consume.go +++ b/internal/game/cmd_consume.go @@ -283,11 +283,5 @@ func (g *Game) applyPotion(sess *net.Session, p *player.Player, itemID string, d p.ConsumeCooldown = 3 p.ActionState = &ActionState{Type: ActionEating} - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s drinks a %s.", p.Name, def.Name))) - } - } - } + g.broadcastAction(sess, "\n%s drinks a %s.", p.Name, def.Name) } diff --git a/internal/game/cmd_cook.go b/internal/game/cmd_cook.go index 288afb0..04c98bb 100644 --- a/internal/game/cmd_cook.go +++ b/internal/game/cmd_cook.go @@ -61,28 +61,13 @@ func (g *Game) doCook(sess *net.Session, input string) { } } - if len(recipes) == 0 { - sess.WriteLine("You can't cook that.") - 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 cook on the %s?", stationName), names) + title := fmt.Sprintf("What would you like to cook on the %s?", stationName) + 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) { seen := make(map[string]bool) - var entries []recipeEntry + var recipes []action.RecipeDef for _, r := range allRecipes { if r.Type != "cooking" { @@ -98,41 +83,9 @@ func (g *Game) showCookMenu(sess *net.Session, p *player.Player, allRecipes []ac continue } seen[r.ID] = true - entries = append(entries, recipeEntry{g.recipeName(sess, &r), r}) - } - - if len(entries) == 0 { - sess.WriteLine("You don't have anything you can cook.") - return + recipes = append(recipes, r) } - if len(entries) == 1 { - if p.OptionBool("cook_all") { - g.startProductionFromRecipe(sess, p, &entries[0].Recipe, 0) - return - } - g.promptHowMany(sess, entries[0].Recipe.ID) - return - } - - sess.State = net.StateRecipeChoice - var names []string - for _, e := range entries { - names = append(names, e.ItemName) - } - g.showMenuTable(sess, fmt.Sprintf("What would you like to cook on the %s?", stationName), names) - ids := make([]string, len(entries)) - for i, e := range entries { - ids[i] = e.Recipe.ID - } - sess.PendingMenu = recipeMenuData(ids) -} - -func (g *Game) recipeName(sess *net.Session, r *action.RecipeDef) string { - if r.Output != "" { - if def, err := g.ItemStore.Load(r.Output); err == nil { - return g.itemColorize(sess, def, def.Name) - } - } - return r.DisplayName() + title := fmt.Sprintf("What would you like to cook on the %s?", stationName) + g.showRecipeChoice(sess, p, recipes, "You don't have anything you can cook.", title, "cook_all") } diff --git a/internal/game/cmd_craft.go b/internal/game/cmd_craft.go index 447d980..d061a9c 100644 --- a/internal/game/cmd_craft.go +++ b/internal/game/cmd_craft.go @@ -17,14 +17,14 @@ func (g *Game) executeConstruct(sess *net.Session, args []string, rawInput strin } func (g *Game) doCraft(sess *net.Session, input string) { - g.doGenericStationSkill(sess, input, "crafting", player.Crafting, "last_craft", "Crafting", "craft", "Craft") + g.doStationSkill(sess, input, "crafting", player.Crafting, "last_craft", "Crafting", "craft", "Craft") } func (g *Game) doConstruct(sess *net.Session, input string) { - g.doGenericStationSkill(sess, input, "construction", player.Construction, "last_construct", "Construction", "construct", "Construct") + g.doStationSkill(sess, input, "construction", player.Construction, "last_construct", "Construction", "construct", "Construct") } -func (g *Game) doGenericStationSkill(sess *net.Session, input string, recipeType string, skill player.SkillName, flagKey, label, verbPast, verbCap string) { +func (g *Game) doStationSkill(sess *net.Session, input string, recipeType string, skill player.SkillName, flagKey, label, verbPast, verbCap string) { p := sess.Player g.cancelAction(p) @@ -39,14 +39,14 @@ func (g *Game) doGenericStationSkill(sess *net.Session, input string, recipeType if r.Type != recipeType { continue } - if !g.genericRecipeVisible(p, &r) { + if !g.recipeVisible(p, &r) { continue } recipes = append(recipes, r) } if input != "" { - g.doGenericWithInput(sess, p, recipes, input, skill, flagKey, label, verbCap) + g.doWithInput(sess, p, recipes, input, skill, flagKey, label, verbCap) return } @@ -55,8 +55,8 @@ func (g *Game) doGenericStationSkill(sess *net.Session, input string, recipeType for i := range recipes { if recipes[i].ID == lastRecipeID { r := &recipes[i] - if g.canDoGenericRecipe(p, r, skill) { - g.startGenericRecipe(sess, p, r, 0, flagKey) + if g.canDoRecipe(p, r, skill) { + g.startRecipe(sess, p, r, 0, flagKey) return } break @@ -64,7 +64,7 @@ func (g *Game) doGenericStationSkill(sess *net.Session, input string, recipeType } } - available := g.availableGenericRecipes(p, recipes) + available := g.availableRecipes(p, recipes) if len(available) == 0 { sess.WriteLine("You don't have any materials to " + verbPast + ".") return @@ -72,14 +72,14 @@ func (g *Game) doGenericStationSkill(sess *net.Session, input string, recipeType optionKey := verbPast + "_all" if p.OptionBool(optionKey) && len(available) == 1 { - g.startGenericRecipe(sess, p, &available[0], 0, flagKey) + g.startRecipe(sess, p, &available[0], 0, flagKey) return } g.showProductionTable(sess, p, available, label, recipeType, flagKey, verbCap, false) } -func (g *Game) doGenericWithInput(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, recipes []action.RecipeDef, input string, skill player.SkillName, flagKey, label, verbCap string) { qty, productName := parseQty(input) productName = strings.TrimSpace(productName) @@ -102,7 +102,7 @@ func (g *Game) doGenericWithInput(sess *net.Session, p *player.Player, recipes [ var available []action.RecipeDef for _, r := range matched { - if g.canDoGenericRecipe(p, &r, skill) { + if g.canDoRecipe(p, &r, skill) { available = append(available, r) } } @@ -113,14 +113,14 @@ func (g *Game) doGenericWithInput(sess *net.Session, p *player.Player, recipes [ } if len(available) == 1 { - g.startGenericRecipe(sess, p, &available[0], qty, flagKey) + g.startRecipe(sess, p, &available[0], qty, flagKey) return } g.showProductionTable(sess, p, available, label, label, flagKey, verbCap, false) } -func (g *Game) genericRecipeVisible(p *player.Player, r *action.RecipeDef) bool { +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 == "" { @@ -133,7 +133,7 @@ func (g *Game) genericRecipeVisible(p *player.Player, r *action.RecipeDef) bool return true } -func (g *Game) canDoGenericRecipe(p *player.Player, r *action.RecipeDef, skill player.SkillName) bool { +func (g *Game) canDoRecipe(p *player.Player, r *action.RecipeDef, skill player.SkillName) bool { if p.Level(skill) < r.Level { return false } @@ -152,7 +152,7 @@ func (g *Game) canDoGenericRecipe(p *player.Player, r *action.RecipeDef, skill p return true } -func (g *Game) availableGenericRecipes(p *player.Player, allRecipes []action.RecipeDef) []action.RecipeDef { +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) { @@ -162,7 +162,7 @@ func (g *Game) availableGenericRecipes(p *player.Player, allRecipes []action.Rec return result } -func (g *Game) startGenericRecipe(sess *net.Session, p *player.Player, recipe *action.RecipeDef, count int, flagKey string) { +func (g *Game) startRecipe(sess *net.Session, p *player.Player, recipe *action.RecipeDef, count int, flagKey string) { p.EnsureFlags() p.Flags[flagKey] = recipe.ID g.AccountStore.SaveCharacter(p) @@ -170,7 +170,7 @@ func (g *Game) startGenericRecipe(sess *net.Session, p *player.Player, recipe *a g.startProductionFromRecipe(sess, p, recipe, count) } -func (g *Game) findCraftRecipesForItems(p *player.Player, itemAID, itemBID string) []action.RecipeDef { +func (g *Game) findCraftRecipes(p *player.Player, itemAID, itemBID string) []action.RecipeDef { allRecipes, err := g.RecipeStore.LoadAll() if err != nil { return nil diff --git a/internal/game/cmd_description.go b/internal/game/cmd_description.go index 5d0c5c1..1fc24ae 100644 --- a/internal/game/cmd_description.go +++ b/internal/game/cmd_description.go @@ -20,10 +20,10 @@ func (g *Game) doDescription(sess *net.Session) { } sess.WriteLine("") sess.Write("Enter a new description (or press enter to keep current): ") - sess.State = net.StateChangeDescription + sess.State = net.StateChangeDesc } -func (g *Game) handleDescriptionChange(sess *net.Session, input string) { +func (g *Game) handleDescChange(sess *net.Session, input string) { if input != "" { p := sess.Player p.Description = input diff --git a/internal/game/cmd_drop.go b/internal/game/cmd_drop.go index 551b014..ca3c244 100644 --- a/internal/game/cmd_drop.go +++ b/internal/game/cmd_drop.go @@ -194,7 +194,7 @@ func (g *Game) executeDrop(sess *net.Session, args []string, rawInput string) { } } -func (g *Game) handleDropAllConfirm(sess *net.Session, input string) { +func (g *Game) handleDropAll(sess *net.Session, input string) { p := sess.Player if p == nil { sess.State = net.StateGame diff --git a/internal/game/cmd_fletch.go b/internal/game/cmd_fletch.go index e218dca..5a4bc19 100644 --- a/internal/game/cmd_fletch.go +++ b/internal/game/cmd_fletch.go @@ -184,7 +184,7 @@ func (g *Game) availableFletchRecipes(p *player.Player, allFletch []action.Recip return result } -func (g *Game) findFletchRecipesForItems(p *player.Player, itemAID, itemBID string) []action.RecipeDef { +func (g *Game) findFletchRecipes(p *player.Player, itemAID, itemBID string) []action.RecipeDef { allRecipes, err := g.RecipeStore.LoadAll() if err != nil { return nil diff --git a/internal/game/cmd_get.go b/internal/game/cmd_get.go index f3485fe..30bc8c2 100644 --- a/internal/game/cmd_get.go +++ b/internal/game/cmd_get.go @@ -77,7 +77,7 @@ func (g *Game) doGet(sess *net.Session, input string) { for i := 0; i < 28; i++ { slot := p.InvSlot(i) if slot != nil && slot.ItemID == itemID { - if _, ok := g.World.RemoveReservedGroundItem(p.RoomID, itemID, qty, p.Name); !ok { + if _, ok := g.World.RemoveReservedItem(p.RoomID, itemID, qty, p.Name); !ok { sess.WriteLine("That's not yours!") return } @@ -92,7 +92,7 @@ func (g *Game) doGet(sess *net.Session, input string) { sess.WriteLine("Your inventory is full.") return } - if _, ok := g.World.RemoveReservedGroundItem(p.RoomID, itemID, qty, p.Name); !ok { + if _, ok := g.World.RemoveReservedItem(p.RoomID, itemID, qty, p.Name); !ok { sess.WriteLine("That's not yours!") return } @@ -112,7 +112,7 @@ func (g *Game) doGet(sess *net.Session, input string) { if fs == -1 { break } - if _, ok := g.World.RemoveReservedGroundItem(p.RoomID, itemID, 1, p.Name); !ok { + if _, ok := g.World.RemoveReservedItem(p.RoomID, itemID, 1, p.Name); !ok { break } p.SetInvSlot(fs, &player.InventorySlot{ItemID: itemID, Quantity: 1}) @@ -193,7 +193,7 @@ func (g *Game) doGetAllNamed(sess *net.Session, input string) { for i := 0; i < 28; i++ { slot := p.InvSlot(i) if slot != nil && slot.ItemID == itemID { - removed, ok := g.World.RemoveReservedGroundItem(p.RoomID, itemID, wanted, p.Name) + removed, ok := g.World.RemoveReservedItem(p.RoomID, itemID, wanted, p.Name) if !ok { sess.WriteLine("That's not yours!") return @@ -209,7 +209,7 @@ func (g *Game) doGetAllNamed(sess *net.Session, input string) { sess.WriteLine("Your inventory is full.") return } - removed, ok := g.World.RemoveReservedGroundItem(p.RoomID, itemID, wanted, p.Name) + removed, ok := g.World.RemoveReservedItem(p.RoomID, itemID, wanted, p.Name) if !ok { sess.WriteLine("That's not yours!") return @@ -230,7 +230,7 @@ func (g *Game) doGetAllNamed(sess *net.Session, input string) { if fs == -1 { break } - _, ok := g.World.RemoveReservedGroundItem(p.RoomID, itemID, 1, p.Name) + _, ok := g.World.RemoveReservedItem(p.RoomID, itemID, 1, p.Name) if !ok { break } @@ -266,7 +266,7 @@ func (g *Game) doGetAll(sess *net.Session) { continue } if itemID == "credits" { - taken, ok := g.World.RemoveReservedGroundItem(p.RoomID, itemID, 999999, p.Name) + taken, ok := g.World.RemoveReservedItem(p.RoomID, itemID, 999999, p.Name) if !ok { continue } @@ -286,7 +286,7 @@ func (g *Game) doGetAll(sess *net.Session) { for i := 0; i < 28; i++ { slot := p.InvSlot(i) if slot != nil && slot.ItemID == itemID { - _, ok := g.World.RemoveReservedGroundItem(p.RoomID, itemID, qty, p.Name) + _, ok := g.World.RemoveReservedItem(p.RoomID, itemID, qty, p.Name) if !ok { qty = 0 stacked = true @@ -313,7 +313,7 @@ func (g *Game) doGetAll(sess *net.Session) { g.AccountStore.SaveCharacter(p) return } - _, ok := g.World.RemoveReservedGroundItem(p.RoomID, itemID, qty, p.Name) + _, ok := g.World.RemoveReservedItem(p.RoomID, itemID, qty, p.Name) if !ok { break } @@ -339,7 +339,7 @@ func (g *Game) doGetAll(sess *net.Session) { } take := 1 - _, ok := g.World.RemoveReservedGroundItem(p.RoomID, itemID, take, p.Name) + _, ok := g.World.RemoveReservedItem(p.RoomID, itemID, take, p.Name) if !ok { break } @@ -364,7 +364,7 @@ func (g *Game) doGetAll(sess *net.Session) { } func (g *Game) pickupCredits(sess *net.Session, p *player.Player, itemID string) { - qty, ok := g.World.RemoveReservedGroundItem(p.RoomID, itemID, 999999, p.Name) + qty, ok := g.World.RemoveReservedItem(p.RoomID, itemID, 999999, p.Name) if !ok { sess.WriteLine("That's not yours!") return diff --git a/internal/game/cmd_jack.go b/internal/game/cmd_jack.go index 16f8b2a..f33ea85 100644 --- a/internal/game/cmd_jack.go +++ b/internal/game/cmd_jack.go @@ -52,7 +52,7 @@ func (g *Game) doJack(sess *net.Session, target string) { } g.cancelAction(p) - g.cancelBackgroundAction(p) + g.cancelBgAction(p) minigame := tDef.Minigame() display := minigame.Init(hackLevel) @@ -68,13 +68,7 @@ func (g *Game) doJack(sess *net.Session, target string) { p.ActionState = &ActionState{Type: ActionHacking, TargetName: tDef.Name} sess.State = net.StateHacking - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess { - other.WriteLine(fmt.Sprintf("\n%s jacks into a terminal.", p.Name)) - } - } - } + g.broadcastAction(sess, "\n%s jacks into a terminal.", p.Name) sess.WriteLine(display) sess.Write("\nhack> ") diff --git a/internal/game/cmd_look.go b/internal/game/cmd_look.go index 6913333..505f7a6 100644 --- a/internal/game/cmd_look.go +++ b/internal/game/cmd_look.go @@ -48,7 +48,7 @@ func (g *Game) showRoomDescription(sess *net.Session, p *player.Player, room *wo roomDescSpec := g.resolveColor(sess, "room_desc") descLines := make([]string, len(rawLines)) for i, l := range rawLines { - descLines[i] = color.ExpandTagsWithDefault(mode, roomDescSpec, l) + descLines[i] = color.ExpandTagsDefault(mode, roomDescSpec, l) } wroteDesc := false if p.OptionString("tiny_map") != "off" { @@ -546,7 +546,7 @@ func (g *Game) doLookTarget(sess *net.Session, input string) { } } - farmSuffix := g.farmLookSuffixForObj(p, st.DefID, st.Index) + farmSuffix := g.farmObjSuffix(p, st.DefID, st.Index) if farmSuffix != "" { sess.WriteLine(farmSuffix) } diff --git a/internal/game/cmd_mix.go b/internal/game/cmd_mix.go index 7bf1f7b..337bb18 100644 --- a/internal/game/cmd_mix.go +++ b/internal/game/cmd_mix.go @@ -3,7 +3,6 @@ package game import ( "strings" - "thehouseoficarus/internal/action" "thehouseoficarus/internal/net" "thehouseoficarus/internal/player" ) @@ -13,110 +12,5 @@ func (g *Game) executeMix(sess *net.Session, args []string, rawInput string) { } func (g *Game) doMix(sess *net.Session, input string) { - p := sess.Player - g.cancelAction(p) - - allRecipes, err := g.RecipeStore.LoadAll() - if err != nil { - sess.WriteLine("Error loading recipes.") - return - } - - if input == "" { - g.showMixMenu(sess, p, allRecipes) - return - } - - qty, itemName := parseQty(input) - _ = qty - - var matched []action.RecipeDef - for _, r := range allRecipes { - if r.Type != "pharmacy" { - continue - } - outDef, _ := g.ItemStore.Load(r.Output) - name := r.Output - if outDef != nil { - name = outDef.Name - } - if action.WordPrefixMatch(itemName, name) { - matched = append(matched, r) - } - } - - if len(matched) == 0 { - sess.WriteLine("You can't mix that.") - return - } - - var available []action.RecipeDef - for _, r := range matched { - if r.HasAllItems(p.HasItem) && p.Level(player.Pharmacy) >= r.Level { - available = append(available, r) - } - } - - if len(available) == 0 { - sess.WriteLine("You don't have the materials for that.") - return - } - - if len(available) == 1 { - g.promptHowMany(sess, available[0].ID) - return - } - - sess.State = net.StateRecipeChoice - var names []string - for _, r := range available { - names = append(names, g.recipeName(sess, &r)) - } - g.showMenuTable(sess, "What would you like to mix?", names) - sess.PendingMenu = recipeMenuData(recipeDefIDs(available)) -} - -func (g *Game) showMixMenu(sess *net.Session, p *player.Player, allRecipes []action.RecipeDef) { - seen := make(map[string]bool) - var entries []recipeEntry - - for _, r := range allRecipes { - if r.Type != "pharmacy" { - continue - } - if seen[r.ID] { - continue - } - if !r.HasAllItems(p.HasItem) { - continue - } - seen[r.ID] = true - entries = append(entries, recipeEntry{g.recipeName(sess, &r), r}) - } - - if len(entries) == 0 { - sess.WriteLine("You don't have anything you can mix.") - return - } - - if len(entries) == 1 { - if p.OptionBool("mix_all") { - g.startProductionFromRecipe(sess, p, &entries[0].Recipe, 0) - return - } - g.promptHowMany(sess, entries[0].Recipe.ID) - return - } - - sess.State = net.StateRecipeChoice - var names []string - for _, e := range entries { - names = append(names, e.ItemName) - } - g.showMenuTable(sess, "What would you like to mix?", names) - ids := make([]string, len(entries)) - for i, e := range entries { - ids[i] = e.Recipe.ID - } - sess.PendingMenu = recipeMenuData(ids) + g.doStationSkill(sess, input, "pharmacy", player.Pharmacy, "last_mix", "Mixing", "mix", "Mix") } diff --git a/internal/game/cmd_quit.go b/internal/game/cmd_quit.go index 46403f4..187d766 100644 --- a/internal/game/cmd_quit.go +++ b/internal/game/cmd_quit.go @@ -1,8 +1,6 @@ package game import ( - "fmt" - "thehouseoficarus/internal/combat" "thehouseoficarus/internal/engine" "thehouseoficarus/internal/net" @@ -24,13 +22,7 @@ func (g *Game) doQuit(sess *net.Session) { p.ActionState = &ActionState{Type: ActionResting} g.cancelRest(p.Name) - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s sits down to rest.", p.Name))) - } - } - } + g.broadcastAction(sess, "\n%s sits down to rest.", p.Name) ticksLeft := 10 id := g.Ticks.Subscribe(engine.ToTicks(1), func() bool { diff --git a/internal/game/cmd_registry.go b/internal/game/cmd_registry.go index c4f7700..ea741d9 100644 --- a/internal/game/cmd_registry.go +++ b/internal/game/cmd_registry.go @@ -149,7 +149,7 @@ func (g *Game) executeCommand(sess *net.Session, cmd string, args []string, rawI switch a { case "gather": if len(args) == 0 { - target := g.resolveDefaultTarget(p.RoomID, cmd) + target := g.defaultTarget(p.RoomID, cmd) if target == "" { sess.WriteLine(fmt.Sprintf("%s what?", cmd)) return @@ -176,7 +176,7 @@ func (g *Game) executeGather(sess *net.Session, args []string, rawInput string) p := sess.Player g.cancelAction(p) if len(args) == 0 { - target := g.resolveDefaultTarget(p.RoomID, verb) + target := g.defaultTarget(p.RoomID, verb) if target == "" { sess.WriteLine(fmt.Sprintf("%s what?", verb)) return diff --git a/internal/game/cmd_smelt.go b/internal/game/cmd_smelt.go index a72cf53..f1ff4e2 100644 --- a/internal/game/cmd_smelt.go +++ b/internal/game/cmd_smelt.go @@ -53,69 +53,29 @@ func (g *Game) doSmelt(sess *net.Session, input string) { if r.Type != "smelting" || !stationMatch(stationDefID, r.Station) || !r.MatchesEntry(itemID) { continue } - if !r.HasAllItemsQty(p.CountItem) || p.Level(player.SkillName(r.EffectiveSkill())) < r.Level { + if !r.HasAllItemsQty(p.CountItem) || p.Level(player.SkillName(r.EffectiveSkill())) < r.Level { continue } recipes = append(recipes, r) } - if len(recipes) == 0 { - sess.WriteLine("You can't smelt that.") - 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, "What would you like to smelt?", names) + 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) { seen := make(map[string]bool) - var entries []recipeEntry + var recipes []action.RecipeDef for _, r := range allRecipes { if r.Type != "smelting" || !stationMatch(stationDefID, r.Station) { continue } - if seen[r.ID] || !r.HasAllItemsQty(p.CountItem) || p.Level(player.SkillName(r.EffectiveSkill())) < r.Level { + if seen[r.ID] || !r.HasAllItemsQty(p.CountItem) || p.Level(player.SkillName(r.EffectiveSkill())) < r.Level { continue } seen[r.ID] = true - entries = append(entries, recipeEntry{g.recipeName(sess, &r), r}) - } - - if len(entries) == 0 { - sess.WriteLine("You don't have anything you can smelt.") - return - } - - if len(entries) == 1 { - if p.OptionBool("smelt_all") { - g.startProductionFromRecipe(sess, p, &entries[0].Recipe, 0) - return - } - g.promptHowMany(sess, entries[0].Recipe.ID) - return + recipes = append(recipes, r) } - sess.State = net.StateRecipeChoice - var names []string - for _, e := range entries { - names = append(names, e.ItemName) - } - g.showMenuTable(sess, "What would you like to smelt?", names) - ids := make([]string, len(entries)) - for i, e := range entries { - ids[i] = e.Recipe.ID - } - sess.PendingMenu = recipeMenuData(ids) + 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 a2eaad7..c7a32cd 100644 --- a/internal/game/cmd_smith.go +++ b/internal/game/cmd_smith.go @@ -63,7 +63,7 @@ func (g *Game) doSmith(sess *net.Session, input string) { if len(barTypes) == 1 { if p.OptionBool("smith_all") { - if recipe := g.findSingleSmithRecipe(p, barTypes[0], allRecipes); recipe != nil { + if recipe := g.findSmithRecipe(p, barTypes[0], allRecipes); recipe != nil { g.startProductionFromRecipe(sess, p, recipe, 0) return } @@ -148,7 +148,7 @@ func barMenuData(barIDs []string) []map[string]string { return out } -func (g *Game) findSingleSmithRecipe(p *player.Player, barID string, allRecipes []action.RecipeDef) *action.RecipeDef { +func (g *Game) findSmithRecipe(p *player.Player, barID string, allRecipes []action.RecipeDef) *action.RecipeDef { var makeable []*action.RecipeDef skillLevel := p.Level(player.Smithing) for i, r := range allRecipes { diff --git a/internal/game/cmd_trigger_combat.go b/internal/game/cmd_trigger_combat.go index 62c5083..444f80d 100644 --- a/internal/game/cmd_trigger_combat.go +++ b/internal/game/cmd_trigger_combat.go @@ -23,7 +23,7 @@ func (g *Game) scienceAttack(sess *net.Session, p *player.Player, mob *world.Mob g.consumeJunkCost(p, mod) - equipSciBonus := g.totalEquipScienceAttack(p) + equipSciBonus := g.totalScienceAttack(p) attRoll := (p.Level(player.Science) + g.buffLevelBonus(p, "science") + 8) * (equipSciBonus + 64) mobSciDef := mob.ScienceDefense diff --git a/internal/game/cmd_trigger_enchant.go b/internal/game/cmd_trigger_enchant.go index 535616b..a1a6950 100644 --- a/internal/game/cmd_trigger_enchant.go +++ b/internal/game/cmd_trigger_enchant.go @@ -41,13 +41,9 @@ func (g *Game) triggerEnchant(sess *net.Session, p *player.Player, mod *ModDef, g.cancelAction(p) } - g.consumeJunkCost(p, mod) - inv.ItemID = outputID - sciXP := mod.BaseXP - g.awardSkillXP(sess, p, player.Science, sciXP) - g.AccountStore.SaveCharacter(p) + g.triggerModReward(sess, p, mod, 1.0) outputDef, _ := g.ItemStore.Load(outputID) outputName := outputID @@ -61,9 +57,6 @@ func (g *Game) triggerEnchant(sess *net.Session, p *player.Player, mod *ModDef, } sess.WriteLine(fmt.Sprintf("You enchant the %s and it becomes a %s!", inputName, outputName)) - if p.OptionBool("xp_drops") { - sess.WriteLine(g.colorize(sess, "xp", fmt.Sprintf("+%dxp sci", sciXP))) - } } func (g *Game) triggerChipBolts(sess *net.Session, p *player.Player, mod *ModDef, chip chipEntry) { @@ -82,8 +75,6 @@ func (g *Game) triggerChipBolts(sess *net.Session, p *player.Player, mod *ModDef g.cancelAction(p) } - g.consumeJunkCost(p, mod) - p.RemoveItem(chip.Input, chip.Qty) placed := false for i := 0; i < 28; i++ { @@ -99,9 +90,7 @@ func (g *Game) triggerChipBolts(sess *net.Session, p *player.Player, mod *ModDef p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: chip.Output, Quantity: chip.Qty}) } - sciXP := mod.BaseXP - g.awardSkillXP(sess, p, player.Science, sciXP) - g.AccountStore.SaveCharacter(p) + g.triggerModReward(sess, p, mod, 1.0) inputDef, _ := g.ItemStore.Load(chip.Input) name := chip.Input @@ -110,7 +99,4 @@ func (g *Game) triggerChipBolts(sess *net.Session, p *player.Player, mod *ModDef } sess.WriteLine(fmt.Sprintf("You chip %d %s with arcane circuitry.", chip.Qty, name)) - if p.OptionBool("xp_drops") { - sess.WriteLine(g.colorize(sess, "xp", fmt.Sprintf("+%dxp sci", sciXP))) - } } diff --git a/internal/game/cmd_trigger_transport.go b/internal/game/cmd_trigger_transport.go index 2ca8db2..2354c27 100644 --- a/internal/game/cmd_trigger_transport.go +++ b/internal/game/cmd_trigger_transport.go @@ -18,14 +18,7 @@ func (g *Game) triggerTransport(sess *net.Session, p *player.Player, mod *ModDef g.cancelAction(p) } - g.consumeJunkCost(p, mod) - - sciXP := mod.BaseXP - g.awardSkillXP(sess, p, player.Science, sciXP) - if p.OptionBool("xp_drops") { - sess.WriteLine(g.colorize(sess, "xp", fmt.Sprintf("+%dxp sci", sciXP))) - } - g.AccountStore.SaveCharacter(p) + g.triggerModReward(sess, p, mod, 1.0) if g.Hub != nil { for _, other := range g.Hub.PlayersInRoom(p.RoomID) { diff --git a/internal/game/cmd_trigger_utility.go b/internal/game/cmd_trigger_utility.go index efb23e7..ce59975 100644 --- a/internal/game/cmd_trigger_utility.go +++ b/internal/game/cmd_trigger_utility.go @@ -14,7 +14,7 @@ func (g *Game) triggerUtility(sess *net.Session, p *player.Player, mod *ModDef, case "low_process", "high_process": g.triggerProcessing(sess, p, mod, targetArg) case "bones_to_nutrients": - g.triggerBonesToNutrients(sess, p, mod) + g.triggerBonesToNuts(sess, p, mod) case "em_grab": g.triggerEmGrab(sess, p, mod, targetArg) case "superheat": @@ -51,8 +51,6 @@ func (g *Game) triggerProcessing(sess *net.Session, p *player.Player, mod *ModDe g.cancelAction(p) } - g.consumeJunkCost(p, mod) - creditValue := itemDef.Value if mod.ID == "low_process" { creditValue = itemDef.Value / 2 @@ -69,19 +67,13 @@ func (g *Game) triggerProcessing(sess *net.Session, p *player.Player, mod *ModDe p.Credits += creditValue - sciXP := mod.BaseXP - g.awardSkillXP(sess, p, player.Science, sciXP) - g.AccountStore.SaveCharacter(p) + g.triggerModReward(sess, p, mod, 1.0) sess.WriteLine(fmt.Sprintf("You process the %s. You receive %s credits.", itemDef.Name, g.colorize(sess, "credits_pickup", fmt.Sprint(creditValue)))) - - if p.OptionBool("xp_drops") { - sess.WriteLine(g.colorize(sess, "xp", fmt.Sprintf("+%dxp sci", sciXP))) - } } -func (g *Game) triggerBonesToNutrients(sess *net.Session, p *player.Player, mod *ModDef) { +func (g *Game) triggerBonesToNuts(sess *net.Session, p *player.Player, mod *ModDef) { if combat.GetCombat(p.Name) != nil { sess.WriteLine("You can't do that during combat!") return @@ -97,8 +89,6 @@ func (g *Game) triggerBonesToNutrients(sess *net.Session, p *player.Player, mod g.cancelAction(p) } - g.consumeJunkCost(p, mod) - for i := 0; i < 28; i++ { slot := p.InvSlot(i) if slot != nil && slot.ItemID == "bones" { @@ -106,14 +96,9 @@ func (g *Game) triggerBonesToNutrients(sess *net.Session, p *player.Player, mod } } - sciXP := mod.BaseXP * boneCount - g.awardSkillXP(sess, p, player.Science, sciXP) - g.AccountStore.SaveCharacter(p) + g.triggerModReward(sess, p, mod, float64(boneCount)) sess.WriteLine(fmt.Sprintf("You convert %d bones into nutrient bars.", boneCount)) - if p.OptionBool("xp_drops") { - sess.WriteLine(g.colorize(sess, "xp", fmt.Sprintf("+%dxp sci", sciXP))) - } } func (g *Game) triggerEmGrab(sess *net.Session, p *player.Player, mod *ModDef, targetArg string) { @@ -150,8 +135,6 @@ func (g *Game) triggerEmGrab(sess *net.Session, p *player.Player, mod *ModDef, t g.cancelAction(p) } - g.consumeJunkCost(p, mod) - qty := groundItems[matchedID] g.World.RemoveGroundItem(p.RoomID, matchedID, qty) @@ -164,14 +147,9 @@ func (g *Game) triggerEmGrab(sess *net.Session, p *player.Player, mod *ModDef, t freeSlot := p.FirstFreeSlot() p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: matchedID, Quantity: qty}) - sciXP := mod.BaseXP - g.awardSkillXP(sess, p, player.Science, sciXP) - g.AccountStore.SaveCharacter(p) + g.triggerModReward(sess, p, mod, 1.0) sess.WriteLine(fmt.Sprintf("You magnetically pull the %s toward you.", name)) - if p.OptionBool("xp_drops") { - sess.WriteLine(g.colorize(sess, "xp", fmt.Sprintf("+%dxp sci", sciXP))) - } } func (g *Game) triggerSuperheat(sess *net.Session, p *player.Player, mod *ModDef, targetArg string) { @@ -224,8 +202,6 @@ func (g *Game) triggerSuperheat(sess *net.Session, p *player.Player, mod *ModDef g.cancelAction(p) } - g.consumeJunkCost(p, mod) - for _, e := range recipe.Consume { for _, itemID := range e.Items { qty := e.Quantity @@ -254,12 +230,11 @@ func (g *Game) triggerSuperheat(sess *net.Session, p *player.Player, mod *ModDef p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: recipe.Output, Quantity: outputQty}) } - sciXP := mod.BaseXP - g.awardSkillXP(sess, p, player.Science, sciXP) + var extraGains []xpGain if recipe.XP > 0 { - g.awardSkillXP(sess, p, player.SkillName(recipe.Skill), recipe.XP) + extraGains = []xpGain{{recipe.Skill, recipe.XP}} } - g.AccountStore.SaveCharacter(p) + g.triggerModReward(sess, p, mod, 1.0, extraGains...) outputDef, _ := g.ItemStore.Load(recipe.Output) outputName := recipe.Output @@ -273,13 +248,6 @@ func (g *Game) triggerSuperheat(sess *net.Session, p *player.Player, mod *ModDef } sess.WriteLine(fmt.Sprintf("You superheat the %s and produce a %s.", inputName, outputName)) - if p.OptionBool("xp_drops") { - parts := []string{fmt.Sprintf("+%dxp sci", sciXP)} - if recipe.XP > 0 { - parts = append(parts, fmt.Sprintf("+%dxp %s", recipe.XP, player.SkillAbbr[player.SkillName(recipe.Skill)])) - } - sess.WriteLine(g.colorize(sess, "xp", "("+strings.Join(parts, ", ")+")")) - } } func (g *Game) triggerPlankMake(sess *net.Session, p *player.Player, mod *ModDef, targetArg string) { @@ -320,8 +288,6 @@ func (g *Game) triggerPlankMake(sess *net.Session, p *player.Player, mod *ModDef g.cancelAction(p) } - g.consumeJunkCost(p, mod) - p.RemoveItem(inv.ItemID, 1) p.Credits -= info.cost @@ -332,16 +298,9 @@ func (g *Game) triggerPlankMake(sess *net.Session, p *player.Player, mod *ModDef } p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: info.plankID, Quantity: 1}) - sciXP := mod.BaseXP - g.awardSkillXP(sess, p, player.Science, sciXP) - g.awardSkillXP(sess, p, player.Construction, 10) - g.AccountStore.SaveCharacter(p) + g.triggerModReward(sess, p, mod, 1.0, xpGain{Skill: string(player.Construction), XP: 10}) sess.WriteLine(fmt.Sprintf("You convert the log into %s.", info.name)) - if p.OptionBool("xp_drops") { - parts := []string{fmt.Sprintf("+%dxp sci", sciXP), "+10xp con"} - sess.WriteLine(g.colorize(sess, "xp", "("+strings.Join(parts, ", ")+")")) - } return } @@ -360,8 +319,6 @@ func (g *Game) triggerPlankMake(sess *net.Session, p *player.Player, mod *ModDef g.cancelAction(p) } - g.consumeJunkCost(p, mod) - p.RemoveItem(logID, qty) p.Credits -= totalCost @@ -376,20 +333,12 @@ func (g *Game) triggerPlankMake(sess *net.Session, p *player.Player, mod *ModDef processed++ } - sciXP := mod.BaseXP * qty - g.awardSkillXP(sess, p, player.Science, sciXP) - conXP := 10 * qty - g.awardSkillXP(sess, p, player.Construction, conXP) - g.AccountStore.SaveCharacter(p) + g.triggerModReward(sess, p, mod, float64(qty), xpGain{Skill: string(player.Construction), XP: 10 * qty}) sess.WriteLine(fmt.Sprintf("You convert %d logs into %s for %d credits.", processed, info.name, totalCost)) if processed < qty { sess.WriteLine(fmt.Sprintf("Your inventory is full. The remaining %d planks fall to the ground.", qty-processed)) } - if p.OptionBool("xp_drops") { - parts := []string{fmt.Sprintf("+%dxp sci", sciXP), fmt.Sprintf("+%dxp con", conXP)} - sess.WriteLine(g.colorize(sess, "xp", "("+strings.Join(parts, ", ")+")")) - } return } diff --git a/internal/game/cmd_use.go b/internal/game/cmd_use.go index 4f830a8..e653c93 100644 --- a/internal/game/cmd_use.go +++ b/internal/game/cmd_use.go @@ -83,7 +83,7 @@ func (g *Game) useRoomObject(sess *net.Session, p *player.Player, input string) } recipes := g.stationRecipes(p, objSt.DefID) if len(recipes) > 0 { - g.showStationRecipeMenu(sess, p, recipes, def.Name) + g.showRecipeMenu(sess, p, recipes, def.Name) return true } bt := def.BehaviorType() @@ -105,13 +105,7 @@ func (g *Game) useRoomObject(sess *net.Session, p *player.Player, input string) g.applyNodeAction(sess, ui.Action) } p.ActionState = &ActionState{Type: ActionUsing, TargetName: def.Name} - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s uses the %s.", p.Name, def.Name))) - } - } - } + g.broadcastAction(sess, "\n%s uses the %s.", p.Name, def.Name) return true } if len(def.UseInteractions) > 0 { @@ -160,7 +154,7 @@ func (g *Game) stationRecipes(p *player.Player, stationDefID string) []action.Re return results } -func (g *Game) showStationRecipeMenu(sess *net.Session, p *player.Player, recipes []action.RecipeDef, stationName string) { +func (g *Game) showRecipeMenu(sess *net.Session, p *player.Player, recipes []action.RecipeDef, stationName string) { if len(recipes) == 0 { sess.WriteLine(fmt.Sprintf("You don't have anything you can make at the %s.", stationName)) return @@ -320,8 +314,8 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName, itemBID := matchesB[0].ID - craftRecipes := g.findCraftRecipesForItems(p, itemAID, itemBID) - fletchRecipes := g.findFletchRecipesForItems(p, itemAID, itemBID) + craftRecipes := g.findCraftRecipes(p, itemAID, itemBID) + fletchRecipes := g.findFletchRecipes(p, itemAID, itemBID) if len(craftRecipes) > 0 && len(fletchRecipes) > 0 { var menu []map[string]string @@ -353,12 +347,12 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName, if len(craftRecipes) > 0 { var available []action.RecipeDef for _, r := range craftRecipes { - if g.canDoGenericRecipe(p, &r, player.Crafting) { + if g.canDoRecipe(p, &r, player.Crafting) { available = append(available, r) } } if len(available) == 1 { - g.startGenericRecipe(sess, p, &available[0], 0, "last_craft") + g.startRecipe(sess, p, &available[0], 0, "last_craft") return } if len(available) > 1 { @@ -429,7 +423,7 @@ func (g *Game) findCombineResults(sess *net.Session, p *player.Player, itemAID, } } } - if aEntry >= 0 && bEntry >= 0 && aEntry != bEntry && madeFromItemsAvailable(p, def.MadeFrom) { + if aEntry >= 0 && bEntry >= 0 && aEntry != bEntry && madeFromAvailable(p, def.MadeFrom) { matched = append(matched, def) } } @@ -444,7 +438,7 @@ func combineMenuData(defs []*object.ItemDef) []map[string]string { return out } -func madeFromItemsAvailable(p *player.Player, madeFrom []object.MadeFromEntry) bool { +func madeFromAvailable(p *player.Player, madeFrom []object.MadeFromEntry) bool { for _, entry := range madeFrom { found := false for _, id := range entry.Items { diff --git a/internal/game/core_login_account.go b/internal/game/core_login_account.go index 87f8c62..a2c1bd8 100644 --- a/internal/game/core_login_account.go +++ b/internal/game/core_login_account.go @@ -83,7 +83,7 @@ func (g *Game) handlePassword(sess *net.Session, input string) { g.showMenu(sess) } -func (g *Game) handleNewAccountPass(sess *net.Session, input string) { +func (g *Game) handleNewPass(sess *net.Session, input string) { input = strings.TrimSpace(input) if input == "" { sess.Conn.SetEcho(true) @@ -98,11 +98,11 @@ func (g *Game) handleNewAccountPass(sess *net.Session, input string) { return } sess.PendingPass = input - sess.State = net.StateNewAccountConfirm + sess.State = net.StateNewAccount sess.Write("Confirm password: ") } -func (g *Game) handleNewAccountConfirm(sess *net.Session, input string) { +func (g *Game) handleNewAccount(sess *net.Session, input string) { input = strings.TrimSpace(input) if input == "" { sess.Conn.SetEcho(true) diff --git a/internal/game/core_messages.go b/internal/game/core_messages.go deleted file mode 100644 index 1c9e85a..0000000 --- a/internal/game/core_messages.go +++ /dev/null @@ -1,13 +0,0 @@ -package game - -const ( - MsgErrLoadRecipes = "Error loading recipes." - MsgNoCombat = "You can't do that during combat!" - MsgNoDontHave = "You don't have any '%s'." - MsgNeverMind = "Never mind." - MsgSomethingWrongObject = "Something is wrong with this object." - MsgSomethingWrongThat = "Something is wrong with that." - MsgInventoryFull = "Your inventory is too full!" - MsgAmbiguous = "That's ambiguous, which one?" - MsgWhichOne = "Which one?" -) diff --git a/internal/game/core_production.go b/internal/game/core_production.go index 227c4ad..59517d5 100644 --- a/internal/game/core_production.go +++ b/internal/game/core_production.go @@ -18,7 +18,7 @@ import ( func (g *Game) startProduction(sess *net.Session, p *player.Player, recipe *action.RecipeDef, actionType, displayVerb, startMsg, endMsg string, count int) { g.cancelAction(p) - g.cancelBackgroundAction(p) + g.cancelBgAction(p) skill := recipe.EffectiveSkill() if skill != "" { @@ -65,13 +65,7 @@ func (g *Game) startProduction(sess *net.Session, p *player.Player, recipe *acti p.ActionState = &ActionState{Type: ActionProducing, TargetName: outputName, Verb: displayVerb} - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s starts %s.", p.Name, displayVerb))) - } - } - } + 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) { @@ -106,14 +100,9 @@ func (g *Game) startProductionFromRecipe(sess *net.Session, p *player.Player, re g.startProduction(sess, p, recipe, actionType, displayVerb, startMsg, endMsg, count) } -func (g *Game) loadProductionRecipe(recipeID string) *action.RecipeDef { - all, err := g.RecipeStore.LoadAll() - if err == nil { - for _, r := range all { - if r.ID == recipeID { - return &r - } - } +func (g *Game) loadRecipe(recipeID string) *action.RecipeDef { + if r := g.RecipeStore.GetByID(recipeID); r != nil { + return r } if strings.HasPrefix(recipeID, "combine_") { itemID := strings.TrimPrefix(recipeID, "combine_") @@ -132,7 +121,7 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool { endMsg, _ := p.Action.Data["end_msg"].(string) remaining, _ := p.Action.Data["remaining"].(int) - recipe := g.loadProductionRecipe(recipeID) + recipe := g.loadRecipe(recipeID) if recipe == nil { g.cancelAction(p) return false @@ -350,25 +339,19 @@ 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 { - all, _ := g.RecipeStore.LoadAll() - for _, r := range all { - if r.ID == rid { - if def, err := g.ItemStore.Load(r.Output); err == nil { - return def.Name - } - return r.Output + 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 { - all, _ := g.RecipeStore.LoadAll() - for _, r := range all { - if r.ID == rid { - if def, err := g.ItemStore.Load(r.Output); err == nil { - return def.Name - } - return r.Output + 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 barID, ok := entry["bar_id"]; ok { @@ -399,12 +382,9 @@ func (g *Game) dispatchMenuEntry(sess *net.Session, entry map[string]string) { } if rid, ok := entry["fletch_recipe_id"]; ok { p := sess.Player - allRecipes, _ := g.RecipeStore.LoadAll() - for _, r := range allRecipes { - if r.ID == rid { - g.startFletchAction(sess, p, &r, 0) - return - } + if r := g.RecipeStore.GetByID(rid); r != nil { + g.startFletchAction(sess, p, r, 0) + return } g.reprompt(sess) return @@ -416,7 +396,7 @@ func (g *Game) dispatchMenuEntry(sess *net.Session, entry map[string]string) { g.reprompt(sess) } -func (g *Game) formatRecipeMaterials(r *action.RecipeDef) string { +func (g *Game) formatMaterials(r *action.RecipeDef) string { var parts []string for _, e := range r.Consume { itemName := e.Items[0] @@ -461,7 +441,7 @@ func (g *Game) showProductionTable(sess *net.Session, p *player.Player, recipes productName = fmt.Sprintf("%s x%d", productName, outputQty) } - matStr := g.formatRecipeMaterials(&r) + matStr := g.formatMaterials(&r) levelStr := fmt.Sprint(r.Level) numStr := fmt.Sprintf("%d", i+1) @@ -524,20 +504,11 @@ func (g *Game) handleProductChoice(sess *net.Session, input string) { input = strings.TrimSpace(input) - allRecipes, err := g.RecipeStore.LoadAll() - if err != nil { - g.reprompt(sess) - return - } - var recipes []action.RecipeDef for _, entry := range menuData { rid := entry["recipe_id"] - for _, r := range allRecipes { - if r.ID == rid { - recipes = append(recipes, r) - break - } + if r := g.RecipeStore.GetByID(rid); r != nil { + recipes = append(recipes, *r) } } @@ -587,10 +558,15 @@ func (g *Game) handleProductChoice(sess *net.Session, input string) { } } -func (g *Game) hasToolType(p *player.Player, toolType string) bool { +func (g *Game) findTool(p *player.Player, toolTypes []string) (toolSpeed float64, toolName string, found bool) { + toolSpeed = -1 if itemID, ok := p.Equipment[object.SlotMainHand]; ok { - if def, err := g.ItemStore.Load(itemID); err == nil && def.ToolType == toolType { - return true + if def, err := g.ItemStore.Load(itemID); err == nil { + for _, t := range toolTypes { + if def.ToolType == t { + return def.ToolSpeed, def.Name, true + } + } } } for i := 0; i < 28; i++ { @@ -598,11 +574,22 @@ func (g *Game) hasToolType(p *player.Player, toolType string) bool { if slot == nil { continue } - if def, err := g.ItemStore.Load(slot.ItemID); err == nil && def.ToolType == toolType { - return true + def, err := g.ItemStore.Load(slot.ItemID) + if err != nil { + continue + } + for _, t := range toolTypes { + if def.ToolType == t { + return def.ToolSpeed, def.Name, true + } } } - return false + return -1, "", false +} + +func (g *Game) hasToolType(p *player.Player, toolType string) bool { + _, _, found := g.findTool(p, []string{toolType}) + return found } type recipeEntry struct { ItemName string @@ -729,6 +716,37 @@ 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 { + return g.itemColorize(sess, def, def.Name) + } + } + return r.DisplayName() +} + +func (g *Game) showRecipeChoice(sess *net.Session, p *player.Player, recipes []action.RecipeDef, emptyMsg, title string, autoOption string) { + if len(recipes) == 0 { + sess.WriteLine(emptyMsg) + return + } + if len(recipes) == 1 { + if autoOption != "" && p.OptionBool(autoOption) { + g.startProductionFromRecipe(sess, p, &recipes[0], 0) + return + } + g.promptHowMany(sess, recipes[0].ID) + return + } + sess.State = net.StateRecipeChoice + sess.PendingMenu = recipeMenuData(recipeDefIDs(recipes)) + var names []string + for i := range recipes { + names = append(names, g.recipeName(sess, &recipes[i])) + } + g.showMenuTable(sess, title, names) +} + func (g *Game) handleHowMany(sess *net.Session, input string) { p := sess.Player recipeID := sess.PendingRecipeID @@ -758,17 +776,7 @@ func (g *Game) handleHowMany(sess *net.Session, input string) { } recipe = g.buildCombineRecipe(itemDef) } else { - all, err := g.RecipeStore.LoadAll() - if err != nil { - g.reprompt(sess) - return - } - for _, r := range all { - if r.ID == recipeID { - recipe = &r - break - } - } + recipe = g.RecipeStore.GetByID(recipeID) } if recipe == nil { diff --git a/internal/game/core_startup.go b/internal/game/core_startup.go index ab52762..ef33ff7 100644 --- a/internal/game/core_startup.go +++ b/internal/game/core_startup.go @@ -8,6 +8,7 @@ import ( "strings" "thehouseoficarus/internal/action" + "thehouseoficarus/internal/world" ) type StartupIssue struct { @@ -19,29 +20,29 @@ type StartupIssue struct { func (g *Game) ValidateStartup() []StartupIssue { var issues []StartupIssue - issues = append(issues, validateDuplicateIDs(g.DataDir, "items", "item")...) - issues = append(issues, validateDuplicateIDs(g.DataDir, "rooms", "room")...) - issues = append(issues, validateDuplicateIDs(g.DataDir, "mobs", "mob")...) - issues = append(issues, validateDuplicateIDs(g.DataDir, "objects", "object")...) - issues = append(issues, validateDuplicateIDs(g.DataDir, "drops", "drop table")...) - issues = append(issues, validateDuplicateIDs(g.DataDir, "recipes", "recipe")...) - issues = append(issues, validateDuplicateIDs(g.DataDir, "courses", "course")...) - issues = append(issues, validateDuplicateIDs(g.DataDir, "modules", "module")...) - issues = append(issues, validateDuplicateIDs(g.DataDir, "help", "help topic")...) - - issues = append(issues, validateRoomReferences(g)...) - issues = append(issues, validateMobReferences(g)...) - issues = append(issues, validateObjectReferences(g)...) - issues = append(issues, validateItemReferences(g)...) - issues = append(issues, validateRecipeReferences(g)...) - issues = append(issues, validateDropTableReferences(g)...) - issues = append(issues, validateCourseReferences(g)...) + issues = append(issues, validateIDs(g.DataDir, "items", "item")...) + issues = append(issues, validateIDs(g.DataDir, "rooms", "room")...) + 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")...) + + issues = append(issues, validateRooms(g)...) + issues = append(issues, validateMobs(g)...) + issues = append(issues, validateObjects(g)...) + issues = append(issues, validateItems(g)...) + issues = append(issues, validateRecipes(g)...) + issues = append(issues, validateDropTables(g)...) + issues = append(issues, validateCourses(g)...) issues = append(issues, validateRoomWiring(g)...) return issues } -func validateDuplicateIDs(dataDir, subDir, label string) []StartupIssue { +func validateIDs(dataDir, subDir, label string) []StartupIssue { dir := filepath.Join(dataDir, subDir) dups := action.CheckDuplicateIDs(dir) var issues []StartupIssue @@ -60,7 +61,7 @@ func validateDuplicateIDs(dataDir, subDir, label string) []StartupIssue { return issues } -func validateRoomReferences(g *Game) []StartupIssue { +func validateRooms(g *Game) []StartupIssue { var issues []StartupIssue roomIndex := g.World.RoomIndex() itemIDs := g.ItemStore.IDSet() @@ -86,6 +87,16 @@ func validateRoomReferences(g *Game) []StartupIssue { } for dir, exit := range room.Exits { + switch dir { + case world.North, world.South, world.East, world.West, world.Up, world.Down: + default: + issues = append(issues, StartupIssue{ + Level: "ERROR", + Type: "reference", + Message: fmt.Sprintf("Room %d: invalid exit direction %q (only north/south/east/west/up/down supported)", id, dir), + }) + continue + } if exit.Room <= 0 { continue } @@ -159,7 +170,7 @@ func validateRoomReferences(g *Game) []StartupIssue { return issues } -func validateMobReferences(g *Game) []StartupIssue { +func validateMobs(g *Game) []StartupIssue { var issues []StartupIssue itemIDs := g.ItemStore.IDSet() dropIDs := dropTableIDSet(g.DataDir) @@ -233,7 +244,7 @@ func validateMobReferences(g *Game) []StartupIssue { return issues } -func validateObjectReferences(g *Game) []StartupIssue { +func validateObjects(g *Game) []StartupIssue { var issues []StartupIssue objIDs := g.ObjectStore.IDSet() itemIDs := g.ItemStore.IDSet() @@ -301,7 +312,7 @@ func validateObjectReferences(g *Game) []StartupIssue { } if obj.Gather != nil { - issues = append(issues, validateGatherConfig(fmt.Sprintf("Object %q: gather", id), obj.Gather, itemIDs, dropIDs)...) + issues = append(issues, validateGather(fmt.Sprintf("Object %q: gather", id), obj.Gather, itemIDs, dropIDs)...) } if obj.Talk != nil { @@ -316,7 +327,7 @@ func validateObjectReferences(g *Game) []StartupIssue { return issues } -func validateItemReferences(g *Game) []StartupIssue { +func validateItems(g *Game) []StartupIssue { var issues []StartupIssue itemIDs := g.ItemStore.IDSet() dropIDs := dropTableIDSet(g.DataDir) @@ -393,7 +404,7 @@ func validateItemReferences(g *Game) []StartupIssue { return issues } -func validateRecipeReferences(g *Game) []StartupIssue { +func validateRecipes(g *Game) []StartupIssue { var issues []StartupIssue itemIDs := g.ItemStore.IDSet() @@ -456,7 +467,7 @@ func validateRecipeReferences(g *Game) []StartupIssue { return issues } -func validateDropTableReferences(g *Game) []StartupIssue { +func validateDropTables(g *Game) []StartupIssue { var issues []StartupIssue dropIDs := dropTableIDSet(g.DataDir) itemIDs := g.ItemStore.IDSet() @@ -494,7 +505,7 @@ func validateDropTableReferences(g *Game) []StartupIssue { return issues } -func validateCourseReferences(g *Game) []StartupIssue { +func validateCourses(g *Game) []StartupIssue { var issues []StartupIssue roomIndex := g.World.RoomIndex() @@ -582,7 +593,7 @@ func validateRoomWiring(g *Game) []StartupIssue { return issues } -func validateGatherConfig(prefix string, cfg *action.GatherConfig, itemIDs map[string]bool, dropIDs map[string]bool) []StartupIssue { +func validateGather(prefix string, cfg *action.GatherConfig, itemIDs map[string]bool, dropIDs map[string]bool) []StartupIssue { var issues []StartupIssue if cfg.Bait != "" && !itemIDs[cfg.Bait] { diff --git a/internal/game/core_types.go b/internal/game/core_types.go index d41b18a..898078b 100644 --- a/internal/game/core_types.go +++ b/internal/game/core_types.go @@ -22,7 +22,7 @@ type xpGain struct { type deathDrop struct { itemID string quantity int - totalVal int + totalValue int isEquip bool equipSlot object.EquipSlot invSlot int diff --git a/internal/game/core_utils.go b/internal/game/core_utils.go index 409422e..c169580 100644 --- a/internal/game/core_utils.go +++ b/internal/game/core_utils.go @@ -104,6 +104,22 @@ func formatPickupList(sess *net.Session, picked []string) { } } +func (g *Game) broadcastAction(sess *net.Session, format string, args ...interface{}) { + if g.Hub == nil { + return + } + p := sess.Player + if p == nil { + return + } + msg := fmt.Sprintf(format, args...) + for _, other := range g.Hub.PlayersInRoom(p.RoomID) { + if other != sess && other.Player != nil { + other.WriteLine(g.colorize(other, "broadcast", msg)) + } + } +} + func (g *Game) newInventorySlot(itemID string, qty int) *player.InventorySlot { slot := &player.InventorySlot{ItemID: itemID, Quantity: qty} if def, err := g.ItemStore.Load(itemID); err == nil && def.MaxQuality > 0 { diff --git a/internal/game/game.go b/internal/game/game.go index 5d6e21e..ac8622e 100644 --- a/internal/game/game.go +++ b/internal/game/game.go @@ -47,7 +47,7 @@ type Game struct { WorldFlags map[string]any ColorConfig *config.ColorsConfig DataDir string - ValidationConfig config.StartupValidationConfig + ValidationConfig config.ValidationConfig restTimers map[string]uint64 charsMu sync.Mutex loggedInChars map[string]*net.Session @@ -60,7 +60,7 @@ type Game struct { hackingStates map[string]*hacking.Session } -func New(dataDir string, colorConfig *config.ColorsConfig, valConfig config.StartupValidationConfig) *Game { +func New(dataDir string, colorConfig *config.ColorsConfig, valConfig config.ValidationConfig) *Game { g := &Game{ World: world.New(dataDir), ObjectStore: object.NewObjectStore(dataDir), @@ -110,9 +110,9 @@ func (g *Game) HandleSession(sess *net.Session, input string) { case net.StatePassword: g.handlePassword(sess, input) case net.StateNewAccountPass: - g.handleNewAccountPass(sess, input) - case net.StateNewAccountConfirm: - g.handleNewAccountConfirm(sess, input) + g.handleNewPass(sess, input) + case net.StateNewAccount: + g.handleNewAccount(sess, input) case net.StateMenu: g.handleMenu(sess, input) case net.StateNewCharName: @@ -129,8 +129,8 @@ func (g *Game) HandleSession(sess *net.Session, input string) { g.handlePurgeAccount(sess, input) case net.StateGame: g.handleGameCommand(sess, input) - case net.StateChangeDescription: - g.handleDescriptionChange(sess, input) + case net.StateChangeDesc: + g.handleDescChange(sess, input) case net.StateTalk: g.handleTalkInput(sess, input) case net.StateShop: @@ -138,7 +138,7 @@ func (g *Game) HandleSession(sess *net.Session, input string) { case net.StateBank: g.handleBankInput(sess, input) case net.StateDropAllConfirm: - g.handleDropAllConfirm(sess, input) + g.handleDropAll(sess, input) case net.StateRecipeChoice: g.handleRecipeChoice(sess, input) case net.StateHowMany: diff --git a/internal/game/hacking/hacking_liars_dice.go b/internal/game/hacking/liars_dice.go index bcfbd40..bcfbd40 100644 --- a/internal/game/hacking/hacking_liars_dice.go +++ b/internal/game/hacking/liars_dice.go diff --git a/internal/game/hacking/hacking_mastermind.go b/internal/game/hacking/mastermind.go index 8d4eb1c..8d4eb1c 100644 --- a/internal/game/hacking/hacking_mastermind.go +++ b/internal/game/hacking/mastermind.go diff --git a/internal/game/hacking/hacking_wumpus.go b/internal/game/hacking/wumpus.go index 91b9243..91b9243 100644 --- a/internal/game/hacking/hacking_wumpus.go +++ b/internal/game/hacking/wumpus.go diff --git a/internal/game/sys_combat.go b/internal/game/sys_combat.go index 030bed1..702871c 100644 --- a/internal/game/sys_combat.go +++ b/internal/game/sys_combat.go @@ -31,7 +31,7 @@ func (g *Game) dropItemsOnDeath(p *player.Player) { items = append(items, deathDrop{ itemID: inv.ItemID, quantity: inv.Quantity, - totalVal: val, + totalValue: val, invSlot: slot, }) } @@ -44,7 +44,7 @@ func (g *Game) dropItemsOnDeath(p *player.Player) { items = append(items, deathDrop{ itemID: itemID, quantity: 1, - totalVal: val, + totalValue: val, isEquip: true, equipSlot: eqSlot, }) @@ -55,7 +55,7 @@ func (g *Game) dropItemsOnDeath(p *player.Player) { } sort.Slice(items, func(i, j int) bool { - return items[i].totalVal > items[j].totalVal + return items[i].totalValue > items[j].totalValue }) for i := 3; i < len(items); i++ { diff --git a/internal/game/sys_science.go b/internal/game/sys_science.go index e35105a..376d7c7 100644 --- a/internal/game/sys_science.go +++ b/internal/game/sys_science.go @@ -9,6 +9,7 @@ import ( "gopkg.in/yaml.v3" "thehouseoficarus/internal/action" + "thehouseoficarus/internal/net" "thehouseoficarus/internal/object" "thehouseoficarus/internal/player" ) @@ -182,7 +183,7 @@ func (g *Game) hasDeckEquipped(p *player.Player) bool { return def.WeaponType == object.WeaponScience } -func (g *Game) equippedProvidesJunk(p *player.Player) string { +func (g *Game) providesJunk(p *player.Player) string { itemID, ok := p.Equipment[object.SlotMainHand] if !ok { return "" @@ -206,7 +207,7 @@ func (g *Game) effectiveJunkCost(p *player.Player, mod *ModDef) map[string]int { delete(cost, "scrap_metal") } - providesJunk := g.equippedProvidesJunk(p) + providesJunk := g.providesJunk(p) if providesJunk != "" { delete(cost, providesJunk) } @@ -235,6 +236,31 @@ func (g *Game) consumeJunkCost(p *player.Player, mod *ModDef) bool { return true } +func (g *Game) triggerModReward(sess *net.Session, p *player.Player, mod *ModDef, multiplier float64, extraGains ...xpGain) int { + g.consumeJunkCost(p, mod) + + sciXP := int(float64(mod.BaseXP) * multiplier) + if sciXP < 1 { + sciXP = 1 + } + g.awardSkillXP(sess, p, player.Science, sciXP) + + for _, gain := range extraGains { + g.awardSkillXP(sess, p, player.SkillName(gain.Skill), gain.XP) + } + + g.AccountStore.SaveCharacter(p) + + if p.OptionBool("xp_drops") { + parts := []string{fmt.Sprintf("+%dxp %s", sciXP, player.SkillAbbr[player.Science])} + for _, gain := range extraGains { + parts = append(parts, fmt.Sprintf("+%dxp %s", gain.XP, player.SkillAbbr[player.SkillName(gain.Skill)])) + } + sess.WriteLine(g.colorize(sess, "xp", "("+strings.Join(parts, ", ")+")")) + } + return sciXP +} + func (g *Game) junkCostString(p *player.Player, mod *ModDef) string { cost := g.effectiveJunkCost(p, mod) delete(cost, "scrap_metal") @@ -259,7 +285,7 @@ func (g *Game) junkCostString(p *player.Player, mod *ModDef) string { return strings.Join(parts, ", ") } -func (g *Game) totalEquipScienceAttack(p *player.Player) int { +func (g *Game) totalScienceAttack(p *player.Player) int { total := 0 for _, itemID := range p.Equipment { def, err := g.ItemStore.Load(itemID) diff --git a/internal/net/server.go b/internal/net/server.go index f82f82b..6706a3a 100644 --- a/internal/net/server.go +++ b/internal/net/server.go @@ -18,7 +18,7 @@ const ( StateAccountName SessionState = iota StatePassword StateNewAccountPass - StateNewAccountConfirm + StateNewAccount StateMenu StateNewCharName StateNewCharConfirm @@ -28,7 +28,7 @@ const ( StateDeleteChar StatePurgeAccount StateGame - StateChangeDescription + StateChangeDesc StateTalk StateDropAllConfirm StateRecipeChoice diff --git a/internal/player/validate.go b/internal/player/validate.go index d836089..1a2a5b9 100644 --- a/internal/player/validate.go +++ b/internal/player/validate.go @@ -6,10 +6,10 @@ import ( "unicode" ) -var validNameRE = regexp.MustCompile(`^[A-Za-z0-9 ]{1,30}$`) +var validNameRe = regexp.MustCompile(`^[A-Za-z0-9 ]{1,30}$`) func ValidName(name string) error { - if !validNameRE.MatchString(name) { + if !validNameRe.MatchString(name) { return fmt.Errorf("name must be 1-30 alphanumeric characters or spaces") } return nil diff --git a/internal/world/ground.go b/internal/world/ground.go index 6c3ecf3..879cc71 100644 --- a/internal/world/ground.go +++ b/internal/world/ground.go @@ -63,7 +63,7 @@ func (w *World) GroundItems(roomID int) map[string]int { return out } -func (w *World) AddReservedGroundItem(roomID int, itemID string, qty int, owner string) { +func (w *World) AddReservedItem(roomID int, itemID string, qty int, owner string) { w.mu.Lock() defer w.mu.Unlock() e := &groundEntry{ @@ -87,7 +87,7 @@ func (w *World) AddGroundItem(roomID int, itemID string, qty int) { w.groundItems[roomID] = append(w.groundItems[roomID], e) } -func (w *World) RemoveReservedGroundItem(roomID int, itemID string, qty int, owner string) (int, bool) { +func (w *World) RemoveReservedItem(roomID int, itemID string, qty int, owner string) (int, bool) { w.mu.Lock() defer w.mu.Unlock() removed := 0 @@ -151,14 +151,14 @@ func (w *World) RemoveGroundItem(roomID int, itemID string, qty int) int { return removed } -func (w *World) tickGroundItemsLocked() { +func (w *World) tickGroundItems() { for _, entries := range w.groundItems { for _, e := range entries { if e.respawnTimer > 0 { e.respawnTimer-- if e.respawnTimer <= 0 { e.quantity = e.respawnQty - w.mergeNearbyItemsLocked(entries, e) + w.mergeNearbyItems(entries, e) } } if e.despawnTimer > 0 { @@ -177,7 +177,7 @@ func (w *World) tickGroundItemsLocked() { } } -func (w *World) mergeNearbyItemsLocked(entries []*groundEntry, target *groundEntry) { +func (w *World) mergeNearbyItems(entries []*groundEntry, target *groundEntry) { for _, other := range entries { if other != target && other.quantity > 0 && strings.EqualFold(other.itemID, target.itemID) && (other.reserveTimer <= 0 || other.reservedFor == "") && diff --git a/internal/world/objects.go b/internal/world/objects.go index 568ea40..72e4a6d 100644 --- a/internal/world/objects.go +++ b/internal/world/objects.go @@ -39,10 +39,10 @@ func (w *World) ObjStateKey(roomID int, defID string, index int) string { func (w *World) EnsureObjectStates(roomID int, defIDs []string) { w.mu.Lock() defer w.mu.Unlock() - w.ensureObjectStatesLocked(roomID, defIDs) + w.ensureObjStates(roomID, defIDs) } -func (w *World) ensureObjectStatesLocked(roomID int, defIDs []string) { +func (w *World) ensureObjStates(roomID int, defIDs []string) { if w.objStates == nil { w.objStates = make(map[string]*ObjState) } diff --git a/internal/world/room.go b/internal/world/room.go index f006acc..9226a16 100644 --- a/internal/world/room.go +++ b/internal/world/room.go @@ -35,9 +35,7 @@ var OppositeExit = map[ExitDir]ExitDir{ } var ExitOrder = []ExitDir{ - "northwest", North, "northeast", - East, "southeast", South, "southwest", - West, Up, Down, + North, South, East, West, Up, Down, } type SpawnDef struct { diff --git a/internal/world/world.go b/internal/world/world.go index 0b40918..b305c4b 100644 --- a/internal/world/world.go +++ b/internal/world/world.go @@ -124,6 +124,6 @@ func (w *World) RoomIndex() map[int]bool { func (w *World) Tick() { w.mu.Lock() defer w.mu.Unlock() - w.tickGroundItemsLocked() + w.tickGroundItems() w.tickObjStatesLocked() } diff --git a/worldbuilding_guide/recipes.md b/worldbuilding_guide/recipes.md index 29b513a..335a6cc 100644 --- a/worldbuilding_guide/recipes.md +++ b/worldbuilding_guide/recipes.md @@ -4,7 +4,7 @@ Recipes define how items are processed on stations to produce new items. They are the foundation for cooking, smithing, crafting, and fletching. -Recipe files live in `data/recipes/<type>/<id>.yaml` (e.g., `data/recipes/smithing/smith_bronze_dagger.yaml`). +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 @@ -51,9 +51,71 @@ 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. + +```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 ... +``` + +**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) ```yaml @@ -127,6 +189,10 @@ success: 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. +**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. + +The individual format is also supported: + ```yaml id: smith_steel_platebody type: smithing |
