GHAMRA STUDIOS

· v0.10.1

The mana bar that refilled itself

Round 4 of a fight in a dungeon: the Hollow Knight already down, the player, Sera Ashford and Durn Cleft-jaw against an Ash Hound at 6 of 18 HP, and the hotbar with Talk marked free.

On 27 September a playtest turned up four bugs, and we fixed all four in one commit that afternoon. The one worth the most words is the smallest: spend every point of mana in a fight, and you walked out of it with a full bar.

Only exactly zero did it. End a fight on one point and you kept one point. End it on none and the game handed the whole pool back.

Zero meant two things

A character’s current mana is stored as a number in the save, and that column had a convention: 0 means “whole”, and the actual maximum is worked out the next time the party is built.

Beds use it. Waking up after a defeat uses it. Every existing save is full of it.

The trouble was the other writer. When a fight ended, the encounter wrote each character’s live mana straight back to that same column. A caster who finished on exactly 0 wrote 0, which the next read took to mean “whole”, and the bar came back full.

Health never had the bug, and that is why nobody noticed it sooner. A character who survives a fight is stored with at least 1 HP, so a live health value can never collide with the sentinel. Mana can be spent to nothing and the character lives on, so it collided every time.

A second sentinel, not a migration

The fix gives an empty pool its own value. A spent pool is now stored as MP_SPENT, which is -1. Every place that writes a live mana value goes through one function, CombatStats.stored_mp, which turns 0 into -1. Every place that reads one goes through CombatStats.carried_mp, which turns 0 into the maximum and clamps everything else into range, so -1 comes back as 0.

What we deliberately did not change is the meaning of 0. It still means whole, so the bed, the defeat wake-up and every save row already on disk behave exactly as before. No migration, no repair pass. The change touched more than a dozen files across combat, rest and the repositories, and a new test file, test_a_spent_mana_pool_stays_spent.gd, pins that an empty pool stays empty through the round trip.

The other three

The rest came from places where a model writes what people say, and we are keeping those to what the player saw.

Reflect did not know the purse. Pressing Reflect consults the player’s own inner voice about what to do next. It gave advice with no idea how much gold the player was carrying. It does now.

A referral refused the errand beside it, or doubled it. When a character sent the player on to someone else and offered an errand in the same breath, two things could happen. In one conversation the errand was refused outright, and the gold Riddler had promised went with it. In another, the journal showed both “Sent to Sorith” and “Whisper to Sorith”, each paying XP. Now the errand takes over the referral: one ask, one card, with the reward on it.

Gold changed hands twice, or not at all. Sorith paid 15 of the promised 20 gold before the job was done, and the quest then paid the last 5 on completion. Separately, a character could describe paying coin that was already paid away, or handing over the same pouch twice, and the ledger rightly refused it without saying so. Both are fixed, and when a character mentions coin that does not change hands, the conversation now says so in a note instead of leaving the player waiting for a pouch that never arrives.

← All entries