Cooking: the skill with no verb #151

Merged
sickday merged 2 commits from feat/cooking into main 2026-08-06 21:40:41 +00:00
Owner

Phase 2's seventh skill. 51 foods on the heat, 54 preparation pairs and one
fermentation clock: fish and meat, bread and pitta, nine pies, four pizzas,
two cakes, stew and curry, baked potatoes and their toppings, scrambled egg,
sweetcorn, kebabs, spit roasts, and wine.

It is the first production skill that can fail into a product, which is
the only thing the make loop did not already do.

Nothing in this cache can be clicked to cook

Not one range, oven, stove, cooker or fire in rev 149 carries a menu action —
every one of the 36 stations has an empty action list, and a sweep of every
loc in the archive for an action containing cook returns zero rows. There is
no verb to match the way woodcutting matches Chop or the spinning wheel
matches Spin.

So using raw food on the station is the entire interface, which is the
Potter's Wheel's situation, and it is why World.Cooking names its stations by
id. The lists are derived by name, which the id rule normally forbids —
right here because that rule is about items and NPCs, and safe only because
World.CookingTest regenerates both lists from the cache.

Two consequences: nothing competes with screen_for_loc/1, because there is no
station click to route; and World.Content.Cook.offer/3 answers nil for a
pair it does not own, so it sits ahead of the station screens in use_on/3
without swallowing a bucket of sand on a furnace.

Interface 307 is a seventh make picker

307 is 309 with the verb changed — eight components, the same types, the
same button types, the same 96-pixel bottom edge, Cook All / Cook X / Cook 5 / Cook 1, and "How many would you like to cook?". Found by scanning every
interface in the archive for the word.

A picker family is now {verb, count => interface} and the layout walk matches
button_text against the caller's verb rather than a "Make " constant.
Cooking's family has one member, because one raw item cooks into one thing.

307's title carries button_type 0 where 309's carries 6, so the
question is not clickable here and that whole family of bugs cannot fire.

This is also the era-correct screen: the Make-All rework shipped 2017-10-19,
three months after this cache.

The burn model

Contract.Production still has no roll and has not grown one. The loop gained
two optional fields — failure_outputs, and a burn whose chance depends on
the level rather than being a constant. succeeded?/4 answers in three
branches: burn, then chance (iron smelting's), then certainty.

The mechanic is sourced. The Hosidius Kitchen's page states its buff as "a
flat increase to the low and high values ... that are used in the skilling
success function for each type of food"
, worth "roughly 5%" at +12 —
12 / 256 is 4.7%, so cooking rolls on the same 256-point scale
Contract.Skilling already works in. Stop-burn levels are published
exhaustively and were read out of the wiki's wikitext rather than its
rendering, because the Range column's own footnote inverts what N/A means:
read the obvious way, the table says cooked meat cannot be cooked on a range.
The same read gives the rule nothing else states — the Baked goods table has no
fire column at all, so bread, pies, pizza and cake are range-only and an absent
station is a refusal rather than a certainty.

Two constants are fitted rather than published and are marked as such in
the data file and the contract: low (179/256) and never (104). Both come
out of one two-point fit against the only external anchors available — shark
burning about 30% at 80 and about 6% at 99 — and then reproduce the rest of the
skill unaided, with lobster at 60 landing at 12.5% against a reported 12-15%.
Where one parameter is weakest is named too: a never-stopping food well above
shark's level comes out a few points harsh. A sitting that counts burnt fish is
what replaces them.

Cooking gauntlets replace a stop level rather than shifting it, and turn shark
and anglerfish from never-stopping into 94 and 97.

Wine

The one drink the skill trains on, and it is not cooked. Grapes in a jug of
water make an unfermented wine immediately and award nothing; twenty ticks
later it becomes a jug of wine worth 200, or a bad one below level 35.

The wiki states the quirk that decides the shape: "Every wine made resets the
twelve second fermentation of the previously made wines."
So it is one clock
per player rather than one per jug — World.Content.Ferment re-arms on every
squeeze and ferments the whole backpack when it fires. :soft, because wine
does not wait for the bank.

It needed one seam: after_make, a module a recipe may name that is called
once the make lands. One shipped recipe uses it, and there is nowhere else it
could live.

An unstackable output costs its whole amount

net_slots/2 counted output entries rather than amounts. Slicing a
pineapple gives four rings and a ring does not stack, so that make needs four
slots and reads as one. Right for every row in the tree until now, because
nothing earlier made or ate more than one of anything unstackable.

It failed safely — World.Container refuses the partial add afterwards and
nothing is committed — but a gate that passes and leaves a second refusal to do
its job is still the wrong gate. Both sides now count amounts; the input side
changes only the steel bar, and only permissively. An existing test found it.

Dialyzer found two more: Contract.Production.recipe() declared experience
as non_neg_integer() when every shipped row in every file is a float — it
survived because recipes are evaluated out of data files at runtime, and
World.Content.Cook is the first module to build one in code. And
World.Recipes.entry() was a closed map type, so adding verb made
offer/2 report will never return.

Half the skill is unreachable, and none of it is cooking's fault

Measured by walking every input against the shops, the floor spawns, the drop
tables, cooking's own outputs and the fishing table: 24 inputs have no source
in this world.
Farming (redberries, watermelon, sweetcorn); a dairy churn
(pat of butter, which gates all six potato toppings, because every topping goes
on a buttered one); the bowl chains (scrambled egg, chilli con carne, mushroom
& onion, tuna and corn); Hunter and quest meats; the seven fish the fishing
table declined; and spice, which nothing anywhere provides.

The skill is playable from a bank and trainable from scratch on fish, meat,
bread, pies, pizza, cake, stew and potatoes.

Two of the twenty-four were this branch's own and are fixed here: the four spit
roasts had heat rows and no iron spit + raw meat pair to make a skewer, and
pineapple pizza wanted chunks or rings that nothing sliced. Every test passed
with four rows whose input the world could not produce — only the reachability
walk found them.

Deliberately not built

Karambwan (a quest picks between two outcomes off one raw item); giant carp (the
only table stating its experience states 0); gnome cooking and cocktails (a
shaker is a stateful container nothing here models); fried onions and mushrooms
(their bowl-borne input is not in this cache); nettle tea (no burnt nettle item
exists, so a failure has nothing to fail into); the Hosidius Kitchen (a diary
reward, and there is no diary system — though its page is where the mechanic
came from); the dairy churn and the windmill.

Not verified in play

No live sitting has happened. Three specific unknowns: the cook animation
(883 is derived from its 122-cycle length matching the four-tick period,
884 is the untested alternative), whether 307 draws a product name where
309 does, and whether a range refuses at reach.

Roast bird meat is 60 experience here and its own item page says 62.5; the
loser is recorded in the data file rather than dropped.

Gates

mix format, compile --force --warnings-as-errors and mix credo are clean.
mix dialyzer is down to the two pre-existing findings in #131.

Phase 2's seventh skill. 51 foods on the heat, 54 preparation pairs and one fermentation clock: fish and meat, bread and pitta, nine pies, four pizzas, two cakes, stew and curry, baked potatoes and their toppings, scrambled egg, sweetcorn, kebabs, spit roasts, and wine. It is the first production skill that can **fail into a product**, which is the only thing the make loop did not already do. ## Nothing in this cache can be clicked to cook Not one range, oven, stove, cooker or fire in rev 149 carries a menu action — every one of the 36 stations has an empty action list, and a sweep of every loc in the archive for an action containing `cook` returns zero rows. There is no verb to match the way woodcutting matches `Chop` or the spinning wheel matches `Spin`. So **using raw food on the station is the entire interface**, which is the Potter's Wheel's situation, and it is why `World.Cooking` names its stations by id. The lists are derived by *name*, which the id rule normally forbids — right here because that rule is about items and NPCs, and safe only because `World.CookingTest` regenerates both lists from the cache. Two consequences: nothing competes with `screen_for_loc/1`, because there is no station click to route; and `World.Content.Cook.offer/3` answers `nil` for a pair it does not own, so it sits ahead of the station screens in `use_on/3` without swallowing a bucket of sand on a furnace. ## Interface 307 is a seventh make picker `307` is `309` with the verb changed — eight components, the same types, the same button types, the same 96-pixel bottom edge, `Cook All / Cook X / Cook 5 / Cook 1`, and *"How many would you like to cook?"*. Found by scanning every interface in the archive for the word. A picker family is now `{verb, count => interface}` and the layout walk matches `button_text` against the caller's verb rather than a `"Make "` constant. Cooking's family has one member, because one raw item cooks into one thing. `307`'s title carries `button_type 0` where `309`'s carries `6`, so the question is not clickable here and that whole family of bugs cannot fire. This is also the era-correct screen: the Make-All rework shipped 2017-10-19, three months after this cache. ## The burn model `Contract.Production` still has no roll and has not grown one. The loop gained two optional fields — `failure_outputs`, and a `burn` whose chance depends on the *level* rather than being a constant. `succeeded?/4` answers in three branches: `burn`, then `chance` (iron smelting's), then certainty. The mechanic is sourced. The Hosidius Kitchen's page states its buff as *"a flat increase to the low and high values ... that are used in the skilling success function for each type of food"*, worth *"roughly 5%"* at `+12` — `12 / 256` is 4.7%, so cooking rolls on the same 256-point scale `Contract.Skilling` already works in. Stop-burn levels are published exhaustively and were read out of the wiki's **wikitext** rather than its rendering, because the `Range` column's own footnote inverts what `N/A` means: read the obvious way, the table says cooked meat cannot be cooked on a range. The same read gives the rule nothing else states — the Baked goods table has no fire column at all, so bread, pies, pizza and cake are range-only and an absent station is a refusal rather than a certainty. Two constants are **fitted rather than published** and are marked as such in the data file and the contract: `low` (179/256) and `never` (104). Both come out of one two-point fit against the only external anchors available — shark burning about 30% at 80 and about 6% at 99 — and then reproduce the rest of the skill unaided, with lobster at 60 landing at 12.5% against a reported 12-15%. Where one parameter is weakest is named too: a never-stopping food well above shark's level comes out a few points harsh. A sitting that counts burnt fish is what replaces them. Cooking gauntlets *replace* a stop level rather than shifting it, and turn shark and anglerfish from never-stopping into 94 and 97. ## Wine The one drink the skill trains on, and it is not cooked. Grapes in a jug of water make an unfermented wine immediately and award nothing; twenty ticks later it becomes a jug of wine worth 200, or a bad one below level 35. The wiki states the quirk that decides the shape: *"Every wine made resets the twelve second fermentation of the previously made wines."* So it is one clock per player rather than one per jug — `World.Content.Ferment` re-arms on every squeeze and ferments the whole backpack when it fires. `:soft`, because wine does not wait for the bank. It needed one seam: `after_make`, a module a recipe may name that is called once the make lands. One shipped recipe uses it, and there is nowhere else it could live. ## An unstackable output costs its whole amount `net_slots/2` counted output *entries* rather than *amounts*. Slicing a pineapple gives four rings and a ring does not stack, so that make needs four slots and reads as one. Right for every row in the tree until now, because nothing earlier made or ate more than one of anything unstackable. It failed *safely* — `World.Container` refuses the partial add afterwards and nothing is committed — but a gate that passes and leaves a second refusal to do its job is still the wrong gate. Both sides now count amounts; the input side changes only the steel bar, and only permissively. An existing test found it. Dialyzer found two more: `Contract.Production.recipe()` declared `experience` as `non_neg_integer()` when every shipped row in every file is a float — it survived because recipes are evaluated out of data files at runtime, and `World.Content.Cook` is the first module to build one in code. And `World.Recipes.entry()` was a closed map type, so adding `verb` made `offer/2` report `will never return`. ## Half the skill is unreachable, and none of it is cooking's fault Measured by walking every input against the shops, the floor spawns, the drop tables, cooking's own outputs and the fishing table: **24 inputs have no source in this world.** Farming (redberries, watermelon, sweetcorn); a dairy churn (pat of butter, which gates all six potato toppings, because every topping goes on a buttered one); the bowl chains (scrambled egg, chilli con carne, mushroom & onion, tuna and corn); Hunter and quest meats; the seven fish the fishing table declined; and spice, which nothing anywhere provides. The skill is playable from a bank and trainable from scratch on fish, meat, bread, pies, pizza, cake, stew and potatoes. Two of the twenty-four were this branch's own and are fixed here: the four spit roasts had heat rows and no `iron spit + raw meat` pair to make a skewer, and pineapple pizza wanted chunks or rings that nothing sliced. Every test passed with four rows whose input the world could not produce — only the reachability walk found them. ## Deliberately not built Karambwan (a quest picks between two outcomes off one raw item); giant carp (the only table stating its experience states 0); gnome cooking and cocktails (a shaker is a stateful container nothing here models); fried onions and mushrooms (their bowl-borne input is not in this cache); nettle tea (no burnt nettle item exists, so a failure has nothing to fail into); the Hosidius Kitchen (a diary reward, and there is no diary system — though its page is where the mechanic came from); the dairy churn and the windmill. ## Not verified in play No live sitting has happened. Three specific unknowns: the cook animation (`883` is derived from its 122-cycle length matching the four-tick period, `884` is the untested alternative), whether `307` draws a product name where `309` does, and whether a range refuses at reach. Roast bird meat is 60 experience here and its own item page says 62.5; the loser is recorded in the data file rather than dropped. ## Gates `mix format`, `compile --force --warnings-as-errors` and `mix credo` are clean. `mix dialyzer` is down to the two pre-existing findings in #131.
Interface 307 is 309 with the verb changed: eight components, the same
types, the same button types, the same 96-pixel bottom edge, "Cook All /
Cook X / Cook 5 / Cook 1", and "How many would you like to cook?" for its
title. Found by scanning every interface in the archive for the word.

So a picker family is {verb, count => interface} rather than one map, and
the layout walk matches button_text against the caller's verb instead of
against a "Make " constant. Cooking's family has exactly one member,
because one raw item cooks into exactly one thing.

One difference removes a trap rather than adding one: 307's title carries
button_type 0 where 309's carries 6, so on the cooking picker the words
"How many would you like to cook?" are not clickable at all.

This is the era-correct screen for the usual reason -- the Make-All
rework shipped 2017-10-19, three months after this cache.
feat(cooking): the skill with no verb, from raw food to burnt
All checks were successful
ci / gates (pull_request) Successful in 59s
build / image (push) Successful in 37s
ci / gates (push) Successful in 1m4s
66aaf6636b
Phase 2's seventh skill. 51 foods on the heat, 54 preparation pairs and
one fermentation clock: fish and meat, bread and pitta, nine pies, four
pizzas, two cakes, stew and curry, baked potatoes and their toppings,
scrambled egg, sweetcorn, kebabs, spit roasts, and wine.

The first production skill that can fail into a product, which is the one
thing the make loop did not already do.

NOTHING IN THIS CACHE CAN BE CLICKED TO COOK. Not one range, oven, stove,
cooker or fire carries a menu action, and a sweep of every loc in the
archive for an action containing "cook" returns zero rows. Using raw food
on the station is therefore the entire interface -- the Potter's Wheel's
situation -- and it is why World.Cooking names its stations by id. The
lists are derived by NAME, which the id rule normally forbids: right here
because that rule is about items and NPCs, and safe only because
World.CookingTest regenerates both lists from the cache.

THE BURN MODEL. Contract.Production still has no roll and has not grown
one; the loop gained failure_outputs and a `burn` whose chance depends on
the level. The mechanic is sourced -- the Hosidius Kitchen's page states
its buff as "a flat increase to the low and high values ... that are used
in the skilling success function for each type of food", worth "roughly
5%" at +12, and 12/256 is 4.7% -- so cooking rolls on the same 256-point
scale Contract.Skilling already works in.

Stop-burn levels are published exhaustively and were read out of the
wiki's WIKITEXT rather than its rendering, because the Range column's own
footnote inverts what N/A means: read the obvious way, the table says
cooked meat cannot be cooked on a range. The same read gives the rule
nothing else states -- the Baked goods table has no fire column at all,
so bread, pies, pizza and cake are range-only and an absent station is a
refusal rather than a certainty.

Two constants are FITTED rather than published and are marked as such in
both the data file and the contract: low (179/256) and never (104). Both
come out of one two-point fit against the only external anchors there
are, and then reproduce the rest of the skill unaided. Where one
parameter is weakest is named too. A sitting that counts burnt fish is
what replaces them.

WINE is the one drink the skill trains on and it is not cooked. The wiki
states the quirk that decides the shape -- "Every wine made resets the
twelve second fermentation of the previously made wines" -- so it is one
clock per player rather than one per jug. World.Content.Ferment re-arms
on every squeeze and ferments the whole backpack when it fires.

TWO CHANGES OUTSIDE THE SKILL, both found rather than chosen:

net_slots/2 counted output entries rather than amounts. Slicing a
pineapple gives four rings and a ring does not stack, so that make needs
four slots and read as one -- right for every row in the tree until now,
because nothing earlier made or ate more than one of anything
unstackable. It failed safely, since World.Container refuses the partial
add afterwards and nothing is committed, but a gate that passes and
leaves a second refusal to do its job is still the wrong gate. Both sides
now count amounts; the input side changes only the steel bar, and only
permissively. An existing test found it.

Contract.Production.recipe() declared experience as non_neg_integer()
when every shipped row in every file is a float. It survived because a
recipe is evaluated out of a data file at runtime, where Dialyzer never
sees it; World.Content.Cook is the first module to build one in code and
failed the contract immediately. World.Recipes.entry() was also a closed
map type, so adding `verb` made offer/2 report `will never return`.

HALF THE SKILL IS UNREACHABLE and none of it is cooking's fault. Measured
by walking every input against the shops, the floor spawns, the drop
tables, cooking's own outputs and the fishing table: 24 inputs have no
source in this world -- Farming, a dairy churn, the bowl chains, Hunter
and quest meats, the seven fish the fishing table declined, and spice.
The skill is playable from a bank and trainable from scratch on fish,
meat, bread, pies, pizza, cake, stew and potatoes.

Two of those twenty-four were this branch's own and are fixed here: the
four spit roasts had heat rows and no iron-spit pair to make a skewer,
and pineapple pizza wanted chunks or rings that nothing sliced. Every
test passed with four rows whose input the world could not produce; only
the reachability walk found them.

Not verified in play: no live sitting has happened, which leaves the cook
animation, whether 307 draws a product name where 309 does, and whether a
range refuses at reach.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Revenant/Server!151
No description provided.