aboutsummaryrefslogtreecommitdiff
path: root/worldbuilding_guide/recipes.md
blob: e442c8cc21fdd2261066759b77265b453b0be141 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# The House of Icarus - World Building Guide

## Crafting

Crafting information lives on the **output item's YAML file** via the `craft:` block. There is no separate recipe directory. To find out how to make `bronze_dagger`, open `data/items/equipment/bronze_dagger.yaml` and look for the `craft:` block.

### Craft YAML Reference

| Field          | Type           | Description                                     |
| -------------- | -------------- | ----------------------------------------------- |
| `type`         | string         | Craft type (pharmacy, smithing, cooking, smelting, crafting, fletching, construction, clean, or "" for skill-less) |
| `skill`        | string         | Skill checked (defaults to type if omitted)     |
| `level`        | int            | Required skill level                            |
| `xp`           | int            | XP awarded on success                           |
| `wait`         | float64        | Ticks per craft cycle                           |
| `station`      | []string       | Station object IDs required (optional)          |
| `tool`         | string         | Required tool_type (optional)                   |
| `consume`      | []ConsumeEntry | Ingredients consumed (see below)                |
| `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 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

Each consume entry defines an ingredient slot with multiple valid items. The first matching item found in the player's inventory is consumed.

```yaml
consume:
  - items: [item_id, alternative_id, ...]
    quantity: 1
    byproducts: [byproduct_for_item, byproduct_for_alt, ...]
```

`byproducts` is optional. When present, it matches the `items` list by index — consuming `items[0]` returns `byproducts[0]` to inventory. Use `""` for items with no byproduct:

```yaml
consume:
  - items: [bucket_of_water, vial_of_water]
    quantity: 1
    byproducts: [empty_bucket, ""]   # bucket returns empty, vial is consumed entirely
  - items: [herb]
    quantity: 1                       # no byproducts — herb is consumed entirely
```

### Skill-Based Production

```yaml
# 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."
```

### Station-Based Production

```yaml
# 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."
```

```yaml
# 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."
```

For stackable outputs, use `output_qty`:

```yaml
# 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."
```

### Tool-Based Production

```yaml
# 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
```

The `tool` field requires the player to have an item with that `tool_type` equipped or in inventory. The tool is NOT consumed.

### Skill-Less Combinations (Item-on-Item)

Omit `type` (or leave it empty) for combinations with no skill check, no XP, and 100% success:

```yaml
# 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."
```

### Multi-Piece Assembly (3+ items → 1)

```yaml
# 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."
```

### Clean Recipes

Clean herb recipes use `type: clean` and are handled as background actions (one herb per cycle, directly mutating inventory):

```yaml
# data/items/materials/guam.yaml  (clean guam)
craft:
  type: clean
  skill: pharmacy
  level: 3
  xp: 3
  wait: 2
  consume:
    - items: [grimy_guam]
      quantity: 1
  message: "You clean the grimy guam leaf."
```

### Food/Healing Items

Items with `heal_value` and `eat_message` can be consumed via the `eat` command.

```yaml
id: bread
name: bread
color: "222"
description: "A fresh loaf of bread, still warm from the oven."
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