From 470bc5bd99b793e46305310b5fbeb3068eba45f1 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Sat, 20 Jun 2026 23:43:35 -0400 Subject: refactor: removed separate crafting recipes and combined them into item YAML --- worldbuilding_guide/recipes.md | 328 +++++++++++++++++++---------------------- 1 file changed, 154 insertions(+), 174 deletions(-) (limited to 'worldbuilding_guide') diff --git a/worldbuilding_guide/recipes.md b/worldbuilding_guide/recipes.md index 335a6cc..e442c8c 100644 --- a/worldbuilding_guide/recipes.md +++ b/worldbuilding_guide/recipes.md @@ -1,36 +1,52 @@ # The House of Icarus - World Building Guide -## Recipes +## Crafting -Recipes define how items are processed on stations to produce new items. They are the foundation for cooking, smithing, crafting, and fletching. +Crafting information lives on the **output item's YAML file** via the `craft:` block. There is no separate recipe directory. To find out how to make `bronze_dagger`, open `data/items/equipment/bronze_dagger.yaml` and look for the `craft:` block. -Recipe files live in `data/recipes//.yaml`. Two formats are supported: **individual** (one recipe per file) and **consolidated** (multiple related recipes per file). Consolidated format is preferred when many recipes share the same type, station, and materials (e.g., all bronze smithing products in one file). - -### Recipe vs MadeFrom - -- **Recipes** (`data/recipes/`) use a station (fire, range, furnace, anvil, etc.). The player must be near the station object. -- **MadeFrom** (on the item YAML directly) is for item-on-item combinations that need no station. The player combines ingredients from their inventory. - -### Recipe YAML Reference +### Craft YAML Reference | Field | Type | Description | | -------------- | -------------- | ----------------------------------------------- | -| `id` | string | Unique recipe identifier | -| `type` | string | Recipe type (cooking, smithing, crafting, etc.) | -| `skill` | string | Skill checked for success | +| `type` | string | Craft type (pharmacy, smithing, cooking, smelting, crafting, fletching, construction, clean, or "" for skill-less) | +| `skill` | string | Skill checked (defaults to type if omitted) | | `level` | int | Required skill level | | `xp` | int | XP awarded on success | -| `wait` | float64 | Ticks per craft cycle (default 6) | -| `station` | []string | Station object IDs required | +| `wait` | float64 | Ticks per craft cycle | +| `station` | []string | Station object IDs required (optional) | +| `tool` | string | Required tool_type (optional) | | `consume` | []ConsumeEntry | Ingredients consumed (see below) | -| `output` | string | ItemID produced on success | | `output_qty` | int | Quantity produced (default 1, for stackables) | | `fail` | string | ItemID produced on failure (optional) | | `message` | string | Success message | | `fail_message` | string | Failure message | +| `steps` | []CraftStep | Multi-step messages at tick intervals (optional)| | `success` | SuccessFormula | Optional skill check formula (see Behaviors) | -The recipe's name in menus comes from the output item's `name` field, colored using the output item's `color`. +The output is the item itself — no `output` field needed. The item ID IS the craft ID. + +### CraftStep — Multi-Step Messages + +Optional interim messages fired at specific tick offsets during the craft cycle. The `tick` is the number of ticks elapsed since the start of the cycle. + +```yaml +craft: + type: "" + wait: 6 + consume: + - items: [map_piece_1] + quantity: 1 + - items: [map_piece_2] + quantity: 1 + - items: [map_piece_3] + quantity: 1 + steps: + - tick: 1 + message: "You try to puzzle how the pieces fit together." + - tick: 3 + message: "Aha, this edge lines up here!" + message: "You assemble the map!" +``` ### ConsumeEntry — Multi-Item Ingredient Slots @@ -51,199 +67,153 @@ consume: quantity: 1 byproducts: [empty_bucket, ""] # bucket returns empty, vial is consumed entirely - items: [herb] - quantity: 1 # no byproducts — herb is consumed entirely + quantity: 1 # no byproducts — herb is consumed entirely ``` -### Consolidated Format - -For recipe categories where many recipes share the same `type`, `station`, and `wait` (e.g., smithing all bronze items from bronze bars, fletching all arrow shafts from logs), use a **consolidated** file. Shared fields are set once at the top level; each product entry provides only the fields that differ. - -**Loader behavior:** At load time, the loader expands each `products` entry into an individual `RecipeDef` with the same shape as the individual format. The production engine sees no difference — all lookup, matching, and execution code works identically. - -**Inheritance:** Child fields override parent. `consume` at the top level provides a default ingredient list; any product with different `consume` (e.g., a platebody needing 5 bars instead of 1) specifies its own. `output_qty`, `message`, `fail`, and `level` are per-product. +### Skill-Based Production ```yaml -# data/recipes/smithing/bronze.yaml — consolidated: 8 products in one file - -type: smithing # shared by all products -station: [anvil] # shared by all products -wait: 4 # shared by all products -consume: # default ingredient list — overridden per-product if needed - - items: [bronze_bar] - quantity: 1 - -products: - - id: smith_bronze_dagger - output: bronze_dagger - level: 1 - xp: 12 - # inherits consume: 1 bronze_bar from parent - - - id: smith_bronze_nails - output: bronze_nails - level: 4 - xp: 12 - output_qty: 15 # stackable output, 15 per cycle - - - id: smith_bronze_full_helm - output: bronze_full_helm - level: 7 - xp: 25 - consume: # overrides parent — 2 bars instead of 1 - - items: [bronze_bar] - quantity: 2 - - - id: smith_bronze_platebody - output: bronze_platebody - level: 18 - xp: 62 - consume: # overrides parent — 5 bars - - items: [bronze_bar] - quantity: 5 - - # ... dagger, sword, med_helm, arrowtips, bolts_unf ... +# data/items/consumables/stim_potion.yaml +craft: + type: pharmacy + skill: pharmacy + level: 3 + xp: 25 + wait: 4 + consume: + - items: [guam_potion_unf] + quantity: 1 + - items: [eye_of_newt] + quantity: 1 + message: "You mix a stim potion." ``` -**Use consolidated files when:** -- Many recipes share the same `type`, `station`, and `material` -- Adding a new tier means copy-pasting identical consume/station/wait fields -- The relationship between recipes is easier to understand as a group - -**Use individual files when:** -- The recipe is unique and doesn't share structure with others (e.g., compound smelting recipes with different ore ratios) -- You want fine-grained version control on a single recipe -- The recipe is complex enough that a consolidated file would be harder to read - -Individual and consolidated files coexist — the loader handles both transparently. - -### Station-based recipe (cooking on a fire) +### Station-Based Production ```yaml -id: cook_trout -type: cooking -skill: cooking -level: 15 -xp: 70 -wait: 6 -station: [fire, cooking_range] -consume: - - items: [raw_trout] - quantity: 1 -output: trout -fail: burnt_fish -message: "Cooked to perfection. It looks great!" -fail_message: "You accidentally burn the trout." +# data/items/materials/bronze_bar.yaml +craft: + type: smelting + skill: smithing + level: 1 + xp: 6 + wait: 4 + station: [furnace] + consume: + - items: [copper_ore] + quantity: 1 + - items: [tin_ore] + quantity: 1 + message: "You smelt a bronze bar." ``` -### Station-specific recipe (range only, not fire) - ```yaml -id: cook_bread -type: cooking -skill: cooking -level: 1 -xp: 40 -wait: 6 -station: [cooking_range] -consume: - - items: [bread_dough] - quantity: 1 -output: bread -fail: burnt_meat -message: "You bake the dough into a fresh loaf of bread." -fail_message: "You burn the bread to a crisp." +# data/items/equipment/bronze_dagger.yaml +craft: + type: smithing + skill: smithing + level: 1 + xp: 12 + wait: 4 + station: [anvil] + consume: + - items: [bronze_bar] + quantity: 1 + message: "You hammer out a bronze dagger." ``` -### Smelting recipe (ore to bar at a furnace) - -Smelting recipes use `type: smelting` and `station: [furnace]`. The `smelt` command or `use on furnace` triggers them. Multi-ore recipes use `qty` on consume entries. +For stackable outputs, use `output_qty`: ```yaml -id: smelt_steel -type: smelting -skill: smithing -level: 40 -xp: 17 -wait: 4 -station: [furnace] -consume: - - items: [iron_ore] - quantity: 1 - - items: [coal] - quantity: 2 -output: steel_bar -message: "You remove a white hot steel bar!" -fail_message: "You fail to smelt a usable bar." +# data/items/ammo/bronze_nails.yaml +craft: + type: smithing + skill: smithing + level: 4 + xp: 12 + wait: 4 + station: [anvil] + consume: + - items: [bronze_bar] + quantity: 1 + output_qty: 15 + message: "You hammer out some bronze nails." ``` -Iron smelting uses a `success` formula for its 50% failure rate: +### Tool-Based Production ```yaml -success: - base: 0.5 - per_level: 0 - cap: 0.5 +# data/items/ammo/arrow_shafts.yaml +craft: + type: fletching + level: 1 + xp: 5 + wait: 3 + tool: knife + consume: + - items: [logs, oak_logs, willow_logs] + quantity: 1 + output_qty: 15 ``` -### Smithing recipe (bar to item at an anvil) - -Smithing recipes use `type: smithing` and `station: [anvil]`. The `smith` command or `use on anvil` triggers them. Requires a hammer (`tool_type: hammer`) in inventory. +The `tool` field requires the player to have an item with that `tool_type` equipped or in inventory. The tool is NOT consumed. -**Recommended:** Use consolidated format (one file per metal type) since all products for a given metal share the same bar, station, and wait. See [Consolidated Format](#consolidated-format) above. +### Skill-Less Combinations (Item-on-Item) -The individual format is also supported: +Omit `type` (or leave it empty) for combinations with no skill check, no XP, and 100% success: ```yaml -id: smith_steel_platebody -type: smithing -skill: smithing -level: 48 -xp: 187 -wait: 4 -station: [anvil] -consume: - - items: [steel_bar] - quantity: 5 -output: steel_platebody -message: "You hammer out a steel platebody." +# data/items/consumables/bucket_of_water.yaml +craft: + wait: 0 + consume: + - items: [vial_of_water, jug_of_water] + quantity: 1 + byproducts: [empty_vial, empty_jug] + - items: [empty_bucket] + quantity: 1 + message: "You pour water into the bucket." ``` -For stackable outputs, use `output_qty`: +### Multi-Piece Assembly (3+ items → 1) ```yaml -id: smith_iron_nails -type: smithing -skill: smithing -level: 24 -xp: 25 -wait: 4 -station: [anvil] -consume: - - items: [iron_bar] - quantity: 1 -output: iron_nails -output_qty: 15 -message: "You hammer out some iron nails." +# data/items/quest/ancient_map.yaml +craft: + wait: 0 + consume: + - items: [torn_page_1] + quantity: 1 + - items: [torn_page_2] + quantity: 1 + - items: [torn_page_3] + quantity: 1 + steps: + - tick: 1 + message: "You try to puzzle how the pieces fit together." + - tick: 3 + message: "Aha, this edge lines up here!" + message: "You assemble the ancient map." ``` -### MadeFrom — Item-on-item combinations +### Clean Recipes -Item-on-item combinations are defined directly on the output item via the `made_from` field. No recipe file needed. +Clean herb recipes use `type: clean` and are handled as background actions (one herb per cycle, directly mutating inventory): ```yaml -# data/items/bread_dough.yaml -id: bread_dough -name: bread dough -color: "222" -made_from: - - items: [pot_of_flour] - quantity: 1 - - items: [bucket_of_water, pitcher_of_water, vial_of_water] - quantity: 1 +# data/items/materials/guam.yaml (clean guam) +craft: + type: clean + skill: pharmacy + level: 3 + xp: 3 + wait: 2 + consume: + - items: [grimy_guam] + quantity: 1 + message: "You clean the grimy guam leaf." ``` -The player uses `use flour on water` to combine them. The system finds that `bread_dough` can be made from these ingredients and produces it. - ### Food/Healing Items Items with `heal_value` and `eat_message` can be consumed via the `eat` command. @@ -257,3 +227,13 @@ value: 5 heal_value: 5 eat_message: "You eat the bread. Warm and satisfying." ``` + +### Architecture + +The `CraftIndex` (`internal/game/craft_index.go`) is built once at startup from all item YAMLs that have a `craft:` block. It provides `O(1)` lookups by type, input item, and station. All crafting commands query the CraftIndex — no runtime file scanning. + +**Indexes:** +- `byType["pharmacy"]` → all pharmacy-craftable items (used by `mix`) +- `byInput["guam"]` → all items that consume guam (used by `use` to find combinations) +- `byStation["anvil"]` → all items craftable at an anvil (used by `use bar on anvil`) +- `FindByTwoInputs(a, b)` → items consuming both inputs in different consume entries -- cgit v1.2.3