thu.ndero.us Experiments in engine, systems, and iteration
Learning

A correct save of the wrong object

Six times into the headset today, all of it on the run loop rather than on how anything looks. The short version is that a run can now be survived, and what you carry home is kept somewhere you can see it and take it bac…

Six times into the headset today, all of it on the run loop rather than on how anything looks. The short version is that a run can now be survived, and what you carry home is kept somewhere you can see it and take it back out. The longer version is a list of things that were wrong while every piece of evidence I had said they were fine.

The vault had no door

Banking had never once happened. Two sessions went looking for a bug in it, and I spent a good part of one of them assuming I kept dying too much.

There was no bug. RunFlow.ReturnToHub was public and nothing called it. The only way a run could end was by dying, and dying empties your carry before routing you home — so Bank() could not run, ever. The vault wasn't empty, it was unreachable. Every item I'd ever picked up was lost by construction.

The function's own comment still said "nothing calls this yet — there is no death", written
before death existed. It had quietly turned from a note into a false denial.

That one has a cheap general form I want to keep: if a feature has never been observed working, check whether a call path to it exists at all before hunting for a fault inside it.

Closing the loop

So a run is five rooms now — camp, beyond the line, scattered fuel, cold hearth, bare — and reaching the end of them takes you home with what you're carrying. The order isn't arbitrary: camp is the only room holding both halves of the resource loop in one glance, and the loose wood sits in the room before the fire that has none.

There's a board at the end of the southern hall showing what the vault holds, and pressing A at it opens a panel you can take things back out of. The vault has a limit now — twelve, twice the carry — because capacity upgrades need something to upgrade. What won't fit stays in your hands rather than being deleted, which matters: the alternative is destroying something you carried home at the exact moment you succeeded.

Then the first survived run banked six items, and I got to look at them, and it was genuinely good.

A correct save of the wrong object

The next session, the board said 2/12. It should have said 8/12.

My new vault tests build a throwaway vault, withdraw from it, and withdrawal saves. The save is one global PlayerPrefs key. So every run of the test suite wrote a two-item fixture over my actual save, and the six items were gone.

Nothing errored. Nothing was wrong with the write. It was a correct save of the wrong object.

The irritating part is that the settings tests have borrowed and restored PlayerPrefs since the day they were written, with a comment saying it's so the suite can't quietly rewrite whoever is sitting at the machine. I copied the panel and not the hygiene.

It's fixed twice over — the save refuses a vault that isn't the current one, and the tests put the key back — and I verified it with a sentinel rather than assuming, because my vault was empty by then and an empty save can't detect being overwritten by an empty save. The check would have passed either way. That's a shape I keep meeting: a test that cannot fail under the hypothesis you're testing isn't evidence.

Walking round a corner

Mages throw bolts now instead of running at you. The bolt charges visibly at the staff for about a second and crosses the room slowly enough to sidestep, which is the sort of thing only this hardware makes interesting.

Getting them to approach took three tries and two of them were wrong in ways the log couldn't show me.

The first version decided a caster was stuck by measuring the distance to me between frames. But casters walk slower than I do, so simply walking away made the gap grow, which read as "blocked" with nothing in the way. The second measured the caster's own ground covered — correct — but let its patience clock run while it was deliberately parked. So on the first frame after a three second wait it had not moved yet, while the clock already read three seconds against a 1.4 second limit. It re-stalled before taking a single step. Forever.

From the log, a caster frozen at its station and one correctly holding its range are identical: same position, same firing, same everything. I read a lot of output and could not separate them. What separated them was walking round a corner and watching it not follow. Five seconds.

I'd also answered a question about this wrong from inside the headset — said they came towards me, then realized on watching more carefully that they hadn't — which is worth more than the original answer and only possible because asking is cheap now. There's a panel in-game that shows me a question with buttons and won't dismiss until I press one. Ten questions answered in one session without taking the headset off.

Two floors

Last one, and my favorite. I said the hub torches looked like they were floating slightly and hedged that it was hard to tell.

The arithmetic said they were bedded six centimeters into the floor. The test written to check exactly that passed. Both were measuring FloorHeight — the surface you walk on. That surface is a collider with no renderer, twenty centimetres above the kit mesh you can actually see. The torches were bedded neatly into nothing and hanging fourteen centimetres up.

I'd described it as "the invisible layer" without knowing that's literally what it is.

The test passed because it recomputed the same formula from the same wrong constant. Two copies of an arithmetic, free to agree on a mistake. It asks the real value now, and measures against the first thing under it that has a renderer — because "the floor" is ambiguous in that room and only one of the two answers is the one my eyes use.

What's next

Nothing equips an item yet. I can find them, choose between them, drop them, bank them and
withdraw them, and none of it changes a single number. That's the next thing.