A running record of changes, additions, and fixes shipped to Ethernalis. Most recent release first.
Player charisma now affects NPC loyalty in two ways. First impression: initial loyalty at NPC generation is offset across the normal Charisma 1-6 range. Charisma 6 adds +10 to a first meeting, while Charisma 1 subtracts 10. A high-charisma player meeting a Wounded NPC (base loyalty 70) will find them immediately willing to join (80+), while a low-charisma player may start from near-zero with a Rival. Quest rewards: loyalty gained from completing quests and mini-quests is scaled across the same range. Charisma 1 grants half loyalty, Charisma 3-4 stays near normal, and Charisma 6 grants 50% more. The log message after a mini-quest now shows the actual charisma-scaled gain.
Accepted mini-quests now correctly appear in the journal tab. The root cause was a missing flag write in the accept handler: the code wrote to player.tag_descriptions[key] but never set player.tags[key] = 1. The journal template iterates tags and only renders entries where the value equals 1, so every mini-quest entry was silently discarded. The completion handler now clears the tag flag as well, removing the entry cleanly. Journal text is also rewritten as first-person narrative — for example: Eathrel is looking for a scroll, I said I would keep my eyes open.
Mini-quests could never be fulfilled due to a fundamental architecture mismatch. The helper functions were scanning player.cards — the room-slot dictionary keyed by directional letters — instead of player.equipment, the flat array that holds the player's actual inventory. A secondary guard checking location === 'inventory' was also always failing because that field is only set when an item is transferred to an NPC, never on pickup. All three helpers (playerCanFulfillMiniQuest, playerFindBiteItem, transferItemForMiniQuest) now operate on the equipment array, using an index rather than a slot key.
NPCs could pick up structural room cards such as the Ancient Stairwell. The scavenging loops now explicitly skip cards with types exit, container, location, and event, in addition to the existing NPC and enemy guards. The provisions scan also gains the missing center-slot skip that the tool-item scan already had, preventing the room's own location card from being consumed. NPC pickup log messages are now proximity-gated — they no longer appear in the player's log when the NPC is in a different room.
Two perks had their IDs wired incorrectly in the game logic. Cosy (perk 22) and Hothead (perk 23) — which reduce temperature discomfort in warm environments — were checking the wrong perk IDs and never fired. Executioner (perk 30, +1 damage with axes) was accidentally checked under perk 8 (Maintainer), meaning Maintainers received the axe bonus and Executioners received nothing.
Fourteen perks that were defined in the database but had no server-side implementation are now active. Soldier (43) grants a permanent +10% melee skill on unlock, reflected directly on the character sheet. Sword Master (45) adds +1 melee damage and a +10% melee buff when a short or long blade is equipped. Veteran (44) grants +1 melee damage unconditionally. Martial Artist (47) grants +3 damage when fighting unarmed. Shadow (48) grants a permanent +10% sneak buff each tick. Runner (49) removes the stamina drain penalty while sprinting. Wrestler (46) now grants the Knockdown skill on unlock. Consumer (35) now correctly gates inedible consumption behind the perk — without it the action is blocked server-side. Tamer (52) sets caught animals as non-hostile on capture. Bookworm (14) gives a 1% chance for a collected scroll to convert into a Spellbook/Tome. Scout (40) now reveals enemy skull badges on adjacent undiscovered rooms on the map. Cosy (22) and Hothead (23) warmth tolerance fixed (see above). Perks 41 (Leatherworking) and 42 (Ethosian Artisanship) already gate their respective recipes via the existing requires_perk system — no code change needed. Perk 54 (Thief) was already fully implemented with a dexterity-based success roll.
The title splash overlay now correctly fires when entering a sub-dungeon for the first time (mines, labs, ruins). Previously the splash was silently skipped because the scene transition bypassed the session join path that emits it. Re-entering an existing sub-dungeon already worked; this fixes the first-entry case.
Connected dungeons reached via exit cards are now named according to their position on the region map. Within 150 px of a named settlement the dungeon takes a compound name — either Theme of Settlement (e.g. "Crypt of Holenholt") or Settlement Theme (e.g. "Holenholt Crypt"), chosen at random; multi-word themes always use the "of" form to avoid awkward labels. Beyond 150 px a distance bracket is appended: (Wilderness) past 300 px, (Deep Wilderness) past 500 px, (Frontier) past 1 000 px. Sub-dungeons are excluded — they retain the entrance card title. Initial quests named on the world map are unaffected.
Characters with destiny points are no longer simply defeated when their health reaches zero. Instead a destiny point burns away, the character's health is partially restored, and they are pulled from the brink and transported to a hand-crafted Destiny Trial dungeon. Three scenario types are authored: Captured by the Undead (prison/torture chamber — escape before dawn), Left for Supper (creature lair — flee before it returns), and Saved by a Stranger (a makeshift shelter with a short escape route). The destiny point is persisted to the database immediately; a second death in the trial dungeon with no remaining points resolves as a normal defeat. Destiny trial dungeons are flagged via a destiny_scenario column on the dungeon_presets table and selected randomly at runtime.
The Requires tags and Requires not fields in the event option inspector now render as searchable tag pickers rather than raw-ID chip inputs. Clicking + Add tag opens a filterable dropdown showing every quest tag by name and description. Chips display the tag name instead of a bare number. Existing options with saved comma-separated IDs load correctly into the new UI. The vm.tagName() helper was also corrected to read the tag field from the tags table.
Dragging a wire from an event option and releasing on an empty part of the canvas now places the new event card at the drop point. Previously the card always appeared near the origin (0,0) corner because the drag state was reset before the drop coordinates were read.
The preset map editor header now includes a Randomize checkbox that toggles the randomize column on dungeon_presets. When enabled the preset is post-processed with random card fills after loading into a run. The field is saved in both INSERT and UPDATE paths and restored when a preset is loaded.
Autorun event cards now trigger at the start of the player's turn (inside notify_turn_progress) rather than only on the first explicit leave action. This ensures destiny dungeon entrance cut-scenes present immediately without requiring the player to take an action first. An autorun card sets autorun = 0 after firing so it cannot re-trigger on subsequent turns.
Exit and sub-exit cards placed in hand-authored preset dungeons now receive the standard Exit action link during post-processing if no UI was explicitly set in the preset JSON. Previously the card would turn over but show no usable action.
Villages and towers now generate differently based on whether the settlement survived the apocalypse. A corrupted settlement produces a hostile dungeon as before. A surviving settlement generates a peaceful map: no enemies, 3–5 NPCs instead of 1–3, and an atmosphere of cautious life rather than ruin.
18 new NPC portrait images added for the Northern Village deck. Deck metadata now carries an npc_portrait_suffix field that controls which portrait set is used per deck. Village NPCs draw from the villager pool; all other decks continue to use the survivor portraits.
NPCs generated in a village dungeon now receive a surname (e.g. "Mira Millwood", "Jon Thatcher"), making them feel like members of a community rather than generic wanderers.
When an NPC is asked for news, there is now a 30% chance they mention another named NPC in the dungeon by name.
The Arcane Tower deck now has its own NPC archetype weights: survivors, emissaries, and the wounded dominate, reflecting a garrison rather than a random crowd. Outlaws and rivals are absent.
A new interior deck (deck 9) unlocked when starting a dungeon run near a Tower settlement. Each floor is a procedurally generated 6×6 grid of arcane rooms. 13 new location types including Tower Entry Hall, Arcane Corridor, Mages Quarters, Conjuring Chamber, Sealed Vault, and Treasure Vault.
7 new enemies: Arcane Sentinel, Tower Guard, Tower Warden (key holder), Runic Golem, Tower Mage, Arcane Wraith, and the unique boss Tower Sorcerer.
Each floor's staircase is locked — defeat the Tower Warden to claim the Tower Warden Key and ascend. 3 new loot items: Runic Crystal, Arcane Tome, Ancient Arcane Seal.