feat/firemaking #99

Merged
sickday merged 4 commits from feat/firemaking into main 2026-08-02 15:02:14 +00:00
Owner
No description provided.
The first skill that puts a loc into the world rather than taking one out.
Sixteen logs, one tinderbox, a fire that burns for a minute or two and
leaves ashes on the tile it stood on.

The burnable set comes from the cache. A log announces itself by carrying
a Light ground action, and the eighteen items that carry it are already
era-filtered: every log the live wiki lists that this cache lacks --
jatoba, blisterwood, camphor, ironwood, rosewood -- was added years after
this cache was cut, so the scan is its own date check. What it will not
answer is which log an action belongs to, and two different items here are
called Logs: 1511, and 2511, which is the tutorial's and whose fire is a
different loc entirely. So the rows are stated by id and the converter
checks the cache still calls each one what the table says; the run also
lists every Light carrier the table declines, with a reason for each.

A fire blocks nothing. Loc 26185 is interact_type 0 and the client clips
from the same field, so both sides agree a fire is walked over -- which is
what makes placing one cheap: no collision flag is recomputed, and
Collision.add_loc/2 and remove_loc/2 are two lines each on the call
swap_loc/3 already made. It also means the step west after lighting is a
courtesy the script performs rather than a consequence of the tile being
taken, and a player boxed in on all four sides stands in their own fire.

LocChanges learned what "the cache has nothing here" means. A door can be
compared against the cache to decide its row should go; a fire has no such
comparison, because the tile's cache state is its absence. So original is
now loc() | :none with place/2 and unplace/1 as the callers. Only current
is ever sent to a client, so the widening is invisible on the wire.

Every log rolls the same chance -- 65/256 at level 1 rising to a capped
513/256 at 99, certain from 43 -- so low and high sit on the table rather
than on a row, and what a log costs is its level requirement rather than a
worse roll. Coloured logs never fail and carry that as a flag.

The cadence is three ticks to the first attempt and one between the rest.
Neither number is published; what is published is 1,500 logs an hour as a
theoretical maximum, and three to catch plus one for the step west to land
is exactly that from level 43 up. Both are marked as reasoned in the table
header.

No new contract. Contract.Skilling's chain runs unchanged with two of its
five gates pointing elsewhere: present? is the floor rather than a
standing loc, and room? is the tile rather than the backpack. The tile
rule is the wiki's own -- fires need empty ground -- so any loc at all
refuses, which is what makes a fire on a fire fall out rather than need
its own rule. It is enforced at all three entry points because a fire is
walked over, so a player can be standing on one when they try.

World.Fires is World.Depletion's mirror and takes the logs off the floor
itself rather than trusting the session to have done it, because rev 149
lets two players share a tile and both can roll a success on one tick.
Eleven candles, torches and lanterns a tinderbox lights, and the reverse.

The cache names this family from the other side. A log announces itself
with a Light ground action; a light source announces itself by its lit half
carrying Extinguish, and twelve items here carry it -- eleven pairs and one
piece of quest content. What the cache will not do is pair them, and no id
rule exists to fall back on: 36 Candle becomes 33 Lit candle going down
three, while 4529 Candle lantern becomes 4531 going up two. So the pairs are
stated and both halves are verified, by name and by action.

The action half of that check is not belt and braces. Four items in this
cache are called Candle lantern -- 4529/4531 are the ordinary pair and
4532/4534 the black-candle one -- and Torch alone matches three unrelated
items, only one of which is a light source. A name-only check would pair the
wrong things and still look right.

Lighting one is a level gate and nothing else. It pays no experience, rolls
nothing and has no cadence, so Contract.Skilling is not involved: a tinderbox
either lights it or the player is told they lack the level. That is why this
is its own table rather than a column on the firemaking one, and why the
whole of it is a lookup and a Container.put/3.

Putting one out asks for nothing, which is the game's rule. The bullseye
lantern is the interesting case: the wiki calls it inextinguishable and this
cache gives it an Extinguish option anyway, and both are true -- what cannot
put it out is the world, and the world is not the player. The flag on the row
says which of those it means.

Item ops now resolve their verb off the item's own definition rather than a
table of ids, which is what the module's own moduledoc has claimed since
equipping was written: verb/2 is inventory_actions index n - 1, and
Extinguish joins Wear and Wield in reaching a handler that way.

Two flags ride on the rows for the dark to read later rather than being
computed there: gas?, which marks an open flame, and extinguishable?.
feat(firemaking): darkness, and what a light source is for
All checks were successful
ci / gates (pull_request) Successful in 45s
8a71387144
The consumer light sources were built waiting for. Three overlay interfaces,
one two-bit varbit, a 2,022-tile cave and a clock.

The client draws darkness as a full-viewport overlay and this cache carries
three: 96, 97 and 98, each one interface holding a single type-3 rectangle
with no listeners and no click mask. Varbit 278 -- varp 425, bits 1-2 -- is
two bits wide, which is exactly the four states. Nothing in the cache reads
either of them: every interface listener and every script was scanned for
varp 425 and varbit 278 and none touches them, so the client does not draw
itself and the whole mechanism is the server's, the shape the skill guide
has.

The ids run backwards against brightness. 96 is the darkest and the lowest
number, so the obvious 96/97/98 = 1/2/3 is wrong at both ends. That was
asserted from symbol names and then confirmed against a client by drawing all
three in one slot, because a wrong pairing renders perfectly while doing the
wrong thing -- the friends-and-ignore failure, which shipped for exactly that
reason.

The frame component has to be translated. The slot is chosen against the
fixed frame 548, where component 12 is a child of the viewport; in the
resizable 161 component 12 is a child of component 10, somewhere else
entirely, and the untranslated number is a packet the client accepts and
draws nowhere. Cache.GameFrame.translate/3 and its enums are the answer, the
same ones the tabs already use.

The area is flood-filled rather than drawn. A cave is neither a region nor a
rectangle: this one spills 1076, 943 and 3 tiles across three map squares and
its bounding box is three times its area. So the converter walks it out of
the collision map from the entrance's own landing tile, which the transport
table already carried, and the tile count is the leak detector.

A light source clears the overlay, and that is a distinction worth keeping
sharp. What a player sees depends on their light; whether the insects may
bite depends on where they are standing. Drawn from the area alone, a player
who climbed down with a lit candle got a black screen and no insects -- the
worst of both answers, found in a sitting and invisible to every test that
asked the area what level it was.

The clock is soft at interval one, armed at login, with its counter in its
own script arguments so the player struct's field cliff is untouched and
"logging out resets the timer" comes free. Soft rather than normal because a
normal timer halts on an open interface and the insects are not waiting for
the bank to close. Fifteen ticks to the warning, thirty to the swarm, then a
damage a tick: two thresholds rather than one, because collapsing them makes
the cave twice as lethal as it should be.

The automatic climb-out the wiki describes is deliberately absent. It is
undatable, and it contradicts the same page's account of walking in unlit and
taking damage -- the rope lands you two tiles from its own three-tile radius,
so that account describes something unreachable. A test pins that nobody is
teleported so it cannot come back without the argument being read.

Also adds admin.spec to the permission table, which was missing and had been
failing Command.RegistryTest since special attacks landed.
Merge branch 'main' into feat/firemaking
All checks were successful
ci / gates (pull_request) Successful in 45s
ci / gates (push) Successful in 44s
d53a08b5cc
One conflict, and it is the good kind: both branches independently added
admin.spec to the permission table, which had been failing
Command.RegistryTest since special attacks landed. Two people finding the
same missing node is better evidence it was real than either finding it
alone. Resolved by keeping it once and adding admin.darkness beside it.

Two more files overlapped and merged without conflict, checked rather than
trusted. World.Content.Item.use_on/3 keeps the firemaking and light-source
clauses ahead of main's renamed screen_for_pair and for_pair, and main's
screen_for_loc -> screen_for_item_on_loc rename survives on the
item-on-object path. World.Player.Advance gained lines beside the
ground-item Light clause and both sides are intact.

Nothing semantic collided. Main only touched the production and make
machinery -- Cache.Widget, Contract.Production, World.Recipes and the make
screen -- none of which firemaking, light sources or darkness read.
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!99
No description provided.