Skip to content

Beta Version 0.65.2 - Release Notes

Choose a tag to compare

@binary-knight binary-knight released this 26 Jun 06:57
· 19 commits to main since this release

Release Notes - v0.65.2 (Countdown)

Third release of the Beta -> 1.0 "Countdown" cycle. It bundles two batches of work. First, a detailed co-op (leader/follower) play-session feedback report: a pair of players ran a leader (DPS) plus follower (healer) session and wrote up five issues -- four fixed (echo recruiting, the follower combat HUD, the username leaks, the follower loot-claim input, and the end-of-fight text ordering), and one (the apparent loot "duplication") investigated and confirmed to be a display-only non-bug, with its messaging clarified so it no longer reads that way. Second, a batch of targeted fixes: five from GitHub bug reports (issues #103, #108, #109, #110, and #111), seven from in-game bug reports (an untranslated dungeon event, a companion potion-stash bug, life steal triggering on an evaded attack, auto-level-up not respecting the off setting for multiplayer followers, dungeon rest spots not healing the party, off-hand strike damage differing between skills and normal attacks, and shop staves being one-handed while loot staves were two-handed), and a server-stability and log-noise pass driven by server-monitor alerts (one real session-ending crash plus several false-alarm error logs).

Player echoes failed to recruit after a character took a family surname (regression)

A player reported that a specific character's "echo" could no longer be recruited at Team Corner: recruiting it produced "save data cannot be found," and the failure persisted across logouts and saves.

Root cause: a v0.65.1 regression from the new family-surname feature. The echo recruit stores the recruited character's DISPLAY name in the party list, but the echo loader looks the save up by account USERNAME. Before v0.65.1 those usually matched; v0.65.1 made DisplayName include the family surname (e.g. display "Imperius Ashwick" vs account "imperius"), so any surnamed character's echo now failed to load, and the stuck name lingered in the recruit list (so Team Corner kept refusing a re-recruit). Fix: when the direct username lookup misses, the echo loader now resolves the stored display name to the canonical username (via the existing ResolvePlayerUsername helper, which matches on username OR display_name) and retries. This is robust to any display-vs-username divergence, not just surnames, and it self-heals echoes that were already stuck from earlier sessions (the next dungeon entry loads them correctly). Not leader/follower-specific; affects all echo recruiting.

Follower combat HUD now shows the leader's (and the whole party's) HP

A follower healer reported that during combat they could not see the leader's health on the combat status screen; the only place the leader's HP appeared was a small text line printed after each full round.

Cause: the in-combat status screen a follower sees lists the monsters, the follower's own stats, and the follower's ally list, but the leader is the combat owner and is deliberately not in that ally list, so the leader was never drawn. (The heal-target list added in v0.65.1 does include the leader, but only appears when heal options are available.) Fix: the follower's combat HUD now prints a compact, persistent party-HP line every turn, including the leader plus every teammate and the follower, color-coded by health percentage (and showing DOWN for the fallen), so a healer always sees who needs attention. Display-only; the leader's view and single-player are untouched.

Account usernames no longer shown in place of character names

A player noted (as a privacy concern) that their account login name appeared publicly in several spots instead of their character name, which matters because some people use a real name or email as a login.

Fixed the player-facing leaks:

  • The follower's in-dungeon footer now reads "Following " instead of the leader's account name.
  • The realm-wide logout broadcast ("X has left the realm") now uses the character's display name.
  • The dungeon-leave notices (a follower leaving, and the leader ending the run) now use the character's display name.

(The group-leave notice and the Discord departure relay already used the character name, so they were already correct.) A remaining minor edge: /who can still show the raw login name for a connection that has authenticated but not yet picked a character; that case has no character name to show and is queued for a placeholder pass.

Follower loot claims are no longer intermittently "not recognized" (Topic 3B)

A follower reported that claiming an offered drop worked only sometimes; other times their keypress was "not recognized" and the loot was passed on.

Root cause: the follower's combat input travels over a one-slot channel, and the engine toggled "I am waiting for input" on and off per prompt. A leftover keypress (for example, from rapidly answering one prompt while the next was being prepared) could sit in that one slot and be consumed first by the next read, shifting every subsequent read by one. So the follower's loot "take" was read as the stale leftover (invalid -> auto-pass), and their real keypress fed the following prompt. Fix: the engine now drains any stale buffered input immediately before each follower combat read (both the action menu and the loot prompt), so every read matches the prompt the follower is actually looking at. A combat-reviewer pass confirmed this only ever discards genuinely stale input (the follower's real response cannot have arrived yet), and as a bonus it clears a latent full-channel stall the old behavior could produce.

Co-op combat text now renders in order at the end of a fight (Topic 2)

Players reported that toward the end of a battle, follower action lines could appear below the boss-kill summary, out of sequence.

Root cause: the leader receives a grouped player's action lines through an incoming-message queue that is only flushed when the leader next reads input, but the boss-kill summary renders before that next read -- so the final round's follower actions printed after the summary. Separately, followers never saw the boss-kill summary at all (it was drawn only on the leader's screen). Fixes: the leader now flushes any queued co-op messages right before drawing the boss-kill summary, so the final follower actions appear above it in order; and a compact summary line is now broadcast to grouped followers so they see the outcome too (it arrives after the action lines in their stream, in order). The mechanical outcome (kill order, rewards) was always correct; this is purely display ordering. A combat-reviewer pass confirmed the flush has exclusive access to the leader's stream (no race), the follower broadcast cannot double-display on the leader, and both are no-ops in single-player.

Loot "duplication" (Topic 3A) was never a bug -- but the messaging is now clearer

Investigated and confirmed there was no real duplication: when a follower wins a roll, the item is added to exactly one inventory (the follower's), and gold is split across the party. The confusion was on screen -- the leader sees the full item render (it goes to whoever has first claim) under an "ITEM FOUND!" banner, so it read like the leader's own find, and the same details were broadcast to the other party members with no label. The messaging now makes ownership explicit: when a drop belongs to a follower, the leader sees a clear "[Group loot] {name} has first claim on this drop. You receive it only if they pass it to you." line up front; the recipient follower sees "This drop is yours to claim."; and the broadcast to the rest of the party is labeled "[Group loot] {name} found a drop:" so nobody mistakes it for their own. Display-only; the actual grant and gold split are unchanged. 3 new loc keys in all 5 languages.

Loot can no longer be handed to a player echo (where it would be lost)

A player reported that when a dropped item is passed on and an ally is offered the chance to pick it up, a recruited player "echo" could be chosen as the recipient. Echoes are read-only snapshots loaded from the real character's save, so their equipment cannot be persisted: anything equipped onto an echo during the run is silently lost when the echo reloads. The reporter rightly asked that echoes not be eligible loot recipients.

Root cause: the post-combat loot flow had two places that treated an echo like any other teammate. (1) The automatic ally pick-up that fires when you Pass on a drop evaluated echoes as candidates (it already skipped live grouped players and royal-bodyguard mercenaries, but not echoes), so it would offer the item to an echo. (2) The loot-drop screen's </> "Change character" recipient cycler built its list from the player plus every teammate, so you could also manually cycle to an echo and Equip or Take the item onto it. Either route ended with the item placed on a character whose gear is never saved, so it vanished.

Fix: echoes are now excluded from both loot paths. The automatic ally pick-up skips echoes the same way it already skips grouped players and mercenaries, and the </> recipient cycler no longer includes echoes, so it can never land on one. This matches the existing party-equipment manager, which already refuses to manage an echo's gear. Legitimate recipients are unchanged: companions, Team Corner NPCs, spouses/lovers, and live grouped players remain eligible exactly as before. Display-only ally offers (round-robin loot among live grouped players) were already echo-safe and are untouched. No new strings; this is a pure eligibility fix.

A combat-reviewer pass confirmed both the single-monster and multi-monster victory paths route through the one fixed pick-up method, that the cycler's recipient index stays valid after the echoes are filtered out, and that no other loot path (the round-robin offer, the grouped-follower equip/take, the pass-down cascade, or the take/equip handlers) can still place a drop onto an echo.

Dungeon Reset Scroll now reliably respawns monsters on re-entry

A player reported that using the Dungeon Reset Scroll at the Magic Shop did not reliably reset the chosen floor: examine spots and events were present on re-entry but monsters were not, until a logout and login. In one case a second logout/login still did not fix it, and the floor stayed empty (the reset had effectively been lost).

Root cause: the reset scroll itself was working correctly. It modifies the persistent per-floor state (it back-dates the floor's last-visited time so the floor is due to respawn, and it directly marks every room as not-cleared). The problem was an in-memory cache. While you are in a session, the dungeon keeps a cached copy of the current floor. Leaving the dungeon to visit the Magic Shop saved that floor's state but did not discard the cached copy, and returning to the same floor reused the cache instead of rebuilding from the persistent state. So the cached, already-cleared floor was shown (no monsters), and the reset looked like it did nothing. Worse, if the player then moved or left again, the save routine rewrote the persistent state from that stale cached floor, overwriting the reset for good, which is why a relog could not recover it in the second case. A fresh login worked because a new session starts with no cached floor, so it rebuilt from the persistent state and the monsters appeared.

Fix: when re-entering a floor whose persistent state says it is due to respawn (which is exactly what the reset scroll sets up, and also what a natural respawn does after the floor's cooldown elapses), the dungeon now rebuilds the floor from the persistent state instead of trusting the cache. The freshly rebuilt floor is then what gets saved, so the reset can no longer be overwritten. This is deliberately narrow: a normal trip to town and back to the same floor, with no reset and no respawn due, still resumes silently without replaying the floor-entry screen. The reset scroll also now saves immediately after a successful reset, so the change is durable even if the player disconnects before the next routine save. Permanently-cleared floors (seal and secret-boss floors, which the scroll already refuses to reset) are unaffected. Note: a floor whose reset was already lost to this bug in a prior session cannot be retroactively recovered; using a fresh scroll on it now works correctly.

"Press any key" now accepts any key in BBS Standard I/O (stdio) door mode

A player running the game as a BBS door in Standard I/O (stdio) mode reported that the "Press any key..." prompts, starting with the splash screen, only advanced on Enter rather than on any key.

Cause: in stdio/redirected-input door mode, the "press any key" prompt read a whole line of input, which only completes when the user presses Enter. The BBS forwards each keystroke as it is typed, but the game kept reading until it saw a newline, so every non-Enter key was ignored. Fix: these prompts now read a single keypress in stdio mode and return on the first key the BBS sends, so any key advances. Carriage-return/line-feed pairs and escape sequences (arrow and function keys) are fully consumed so no stray input leaks into the following prompt. Local play on a real terminal (Steam/WezTerm) benefits too: the splash and other "press any key" prompts accept a single key there as well. MUD, SSH, and web clients are unchanged; those are line-buffered on the client side and continue to use Enter as before. Native socket (telnet) door connections, which use a separate input path, are also unchanged.

World boss deaths from damage-over-time no longer cost a resurrection

A player reported that dying to the world boss normally ends with the healers dragging them to safety (no resurrection lost), but a death caused by burn damage instead triggered a real death and a divine intervention, costing a resurrection.

Cause: a world boss fight is non-lethal by design. Dropping to 0 HP ends the session with the healers pulling you out, you revive at 25% HP with a short re-entry cooldown, and it never consumes a resurrection. That protection was applied when the killing hit came from the boss or its aura, but a damage-over-time tick (burn or poison) is processed at the start of the round, before that check, and it simply ended the round at 0 HP, so it fell through to the normal death handling, which is what consumed the resurrection. Fix: a death from a burn or poison tick during a world boss fight now takes the exact same non-lethal "dragged to safety" outcome as boss-dealt damage. Both paths share one code path now, so any way of reaching 0 HP in a world boss fight is handled identically and never costs a resurrection.

Player-to-player trades now send items exactly as they are (stats, enchantments, cursed effects)

A player reported that sending an item to another player via a package lost data: the quality looked reset, some stats were missing, and a cursed item kept its "Cursed" name but lost its cursed effects. The item was not sent as-is.

Cause: the trade package built a hand-written copy of each item that carried only ten fields (name, type, value, attack, armor, HP, mana, strength, defence, dexterity), and the receiving side rebuilt the item from the same ten. Everything else was dropped both on the way out and on the way in: Wisdom, Charisma, Agility, Stamina, shield and block values, the minimum-level requirement, the cursed and identified flags, and, most importantly, the item's full effect list (all enchantments and procs, the bonus Constitution/Intelligence/all-stats, and the negative cursed-stat entries). That is why stats vanished and a cursed item kept its name (which is part of the copied name text) while losing its actual cursed penalties. The displayed quality is derived from the item's name and its attack/armor, which were copied, but with the enchantments and bonus stats stripped, received items genuinely looked downgraded. Fix: trading now uses one complete item conversion that carries every field, including the full effect list, so an item is sent exactly as it is. This covers all package outcomes: accepting, the sender cancelling, and a package being declined or expiring back to the sender. Items already pending in a trade when this ships still work; they simply carry the fields they were originally sent with.

Dungeon moral-choice events are now translated

A player in a non-English session reported that a dungeon moral-dilemma event (the ghost of a fallen adventurer who asks you to carry a ring to their family) showed in English. The whole moral-choice event system was affected: four scenarios (a dying creature pleading for mercy, a chest of gold stolen from orphans, a magical trap you can disarm or leave armed, and the fallen adventurer's spirit), each with its situation narration, the two choice labels, and the outcome text, were hardcoded English. The surrounding menu chrome was already localized; only these scenario strings were not. They all now resolve through the localization system, with full Hungarian, Spanish, French, and Italian translations (30 new strings per language).

Giving potions to a companion's stash no longer wipes their existing potions

A player reported that issuing potions to a companion's stash in the dungeon replaced the companion's count instead of adding to it: a companion holding 11 potions, given 4, ended up with 4 (shown as 4/15) instead of 15.

Cause: the dungeon's "issue potions to a teammate's stash" action read the companion's real stored potion count for the on-screen display (correctly showing 11), but performed the actual transfer on a separate in-combat copy of that companion whose potion field had drifted out of sync with the stored count (it read 0). Adding the 4 to that stale copy produced 4, and saving the copy back to the companion then overwrote the real 11 with 4. Fix: the transfer now adds to the same authoritative count the player is shown, so the companion ends with the correct total (11 + 4 = 15), and the in-combat copy is re-synced to the true count at the same time. The instant "give mana to a teammate" action was never affected (it restores current mana and never touches the stored potion count).

Life steal no longer triggers when an attack passes through an evading enemy

A player reported that when an attack misses because it passes through an evasive enemy (an Incorporeal/Phase-type monster the swing passes through harmlessly), life steal still healed the attacker.

Cause: a monster with an active evasion buff can make an attack pass through for zero damage. The internal single-target damage routine rolled that evasion and applied no damage, but it reported nothing back to the caller, so the basic attack went on to apply its post-hit effects (life steal and the other weapon-enchant procs, plus the Mystic Shaman weapon-enchant bonus and the Ancestral Guidance heal) on the intended damage as if the swing had connected. Fix: the damage routine now reports whether the attack landed, and every attack that follows it with post-hit effects skips them when the swing was evaded. This covers the player's basic attack (and its Shaman enchant and Ancestral Guidance procs), the dual-wield off-hand follow-up on melee abilities, and teammate attacks. The single-target attack path already handled this correctly (it checks evasion up front), so both paths now behave the same: an evaded swing deals no damage and triggers no on-hit effects. A combat-reviewer pass confirmed parity across the single-monster, multi-monster, off-hand, and teammate paths and that the ability/PowerStance paths that apply damage directly (and never roll evasion) are correctly left untouched.

Auto-level-up "off" is now respected for followers in multiplayer groups

A follower in a multiplayer group dungeon reported that turning auto-level-up off had no effect: their character still leveled up automatically on gaining enough XP.

Cause: a grouped follower's combat rewards are distributed by a separate path from the solo/leader path, and that path leveled the follower up whenever they had enough XP without checking their auto-level-up preference. (The shared level-up routine never checks the toggle itself; the caller is responsible for it, and the solo/leader path does, but the group-reward path did not.) Fix: the group-reward path now respects the follower's auto-level-up setting, so with it off they bank XP and level up manually at the Level Master, exactly like a solo player. The follower's level-up notification was already tied to an actual level change, so it correctly stays silent when XP is banked instead.

Dungeon rest spots now heal the whole party, not just other players

A player reported that the dungeon rest spots should heal their teammates. The "make camp" action already healed the whole party, but the separate rest-spot event (the safe-haven/meditation chamber the game occasionally drops you into) only healed other human players in a multiplayer group; it skipped companions and NPC teammates entirely, so a solo player's party never recovered there. Fix: the rest-spot event now restores 33% HP/MP/stamina (and cures poison) for every living party member, companions and NPC teammates included, matching the make-camp action. The resting player also gets a short line confirming their companions recovered.

Off-hand strike damage is now consistent between skills and normal attacks

A dual-wielding player reported that off-hand damage looked different when it came from a skill versus a normal attack.

Cause: a normal attack's off-hand swing runs the full damage formula (stat scaling, level, weapon power with level scaling, basic-attack proficiency, critical hits, difficulty, and every active buff), but the off-hand follow-up that fires after a skill (a class ability or Power Strike) used a stripped-down formula that left out proficiency, the proper weapon/level scaling, critical hits, and all buffs. So a skill's off-hand strike hit for noticeably less than a normal off-hand swing. Fix: the off-hand follow-up now performs a full off-hand swing using the exact same formula as a normal off-hand attack, so the two match. The shared formula was factored into a single routine used by both paths, and the normal basic attack is unchanged. AI teammates' off-hand swings are intentionally left on their own lightweight formula, so a companion's off-hand strike still reads as the companion's hit rather than borrowing the player's crit/buff text. A combat-reviewer pass confirmed the refactor leaves the normal attack identical and keeps the player and teammate paths correctly separated, and the full test suite (845 tests) passed.

Magic staves from shops are now two-handed (matching loot and the staff item definitions)

A player reported that staves received as loot were two-handed while staves bought from a shop were one-handed.

Cause: every hand-crafted staff in the game is defined as two-handed, and the rule that decides a weapon's handedness from its type also treats staves as two-handed, so loot staves came out two-handed (correct). But the shop generator assigned handedness by a weapon template's position in a list rather than by its type, and the staff templates happened to sit in the one-handed section, so shop staves came out one-handed by accident. Fix: the shop now derives each weapon's handedness from its type (the same rule loot uses), so shop staves are two-handed and match loot and the hand-crafted staves. Genuine one-handed weapons are unaffected. (A one-handed staff already sitting in a save keeps its handedness until that staff is re-acquired or re-equipped.)

Server stability and log-noise fixes (from server-monitor alerts)

The recurring errors paged by the server monitor were investigated. The one real crash and four sources of false-alarm noise are fixed:

  • Inventory slot crash: in the per-slot inventory manager, typing a backpack item number that was not on screen (for example "B27" when no item 27 was shown for that slot) threw an unhandled error that ended the player's session. It was logged misleadingly as "Failed to load save" and was the most frequent alert. The number is now validated before use, so an out-of-range entry just shows "invalid choice."
  • Normal logouts are no longer logged as crashes: quitting or logging out (for example from prison) is a clean exit, but it was caught by the session catch-all and logged as "[CRASH] Failed to load save," and it flashed an "error loading" screen for a few seconds. It now ends the session quietly.
  • Follower disconnects are no longer logged as errors: a co-op follower dropping their connection (broken pipe / connection reset) is a normal disconnect; it was logged at error level and paged the monitor on every drop. It is now logged quietly, while real stream errors still log as errors.
  • Duplicate-name registration: trying to register a name that is already taken now gives a clear "that name is already taken, please choose a different name" message instead of a generic "registration failed, please try again," and is logged quietly instead of as a server error.
  • Shutdown race: a harmless "database object disposed" error that could occur while the server was shutting down is no longer logged as an error.

Files Changed

  • Scripts/Core/GameConfig.cs -- Version 0.65.2
  • Scripts/Locations/DungeonLocation.cs -- echo loader resolves a stored display name to the account username on lookup miss (fixes surnamed-character echo recruiting + self-heals stuck echoes); follower footer "Following X" uses the leader's character name; dungeon-leave notices use character names
  • Scripts/Server/PlayerSession.cs -- realm logout broadcast uses the character display name instead of the account username
  • Scripts/Systems/CombatEngine.cs -- new RenderFollowerPartyLine draws a compact leader+party HP line on the follower combat HUD each turn; drain stale follower combat-input before the action-menu and loot reads (fixes intermittent loot "not recognized"); flush queued co-op messages before the boss-kill summary and broadcast a compact summary to grouped followers (fixes end-of-fight text ordering)
  • Scripts/UI/TerminalEmulator.cs -- new FlushIncomingMessages() synchronously drains and writes queued co-op broadcasts (used before the boss-kill summary so they print in order)
  • Scripts/Systems/CombatEngine.cs -- group loot now labels who earns a drop (leader-side "first claim" framing, follower-side "yours to claim", recipient-labeled broadcast) so co-op drops are not mistaken for shared/duplicated loot
  • Localization/{en,es,fr,it,hu}.json -- 3 new combat.loot_group_drop_for / loot_broadcast_for / loot_yours_to_claim keys
  • Scripts/Systems/CombatEngine.cs -- exclude player echoes from both post-combat loot paths: the automatic ally pick-up (TryTeammatePickupItem now skips IsEcho teammates alongside the existing grouped-player and mercenary skips) and the loot-drop </> recipient cycler (GetEquipmentDropInput filters IsEcho out of the recipient list), so a dropped item can never be routed to a non-persistent echo and lost (issue #109)
  • Scripts/Locations/DungeonLocation.cs -- on dungeon entry, force a floor rebuild from the persistent state (instead of reusing the in-memory cache) when that state says the floor is due to respawn, so a Dungeon Reset Scroll used while away from the dungeon takes effect on re-entry and can no longer be overwritten from the stale cache; plain town-and-back resumes are unchanged (issue #108)
  • Scripts/Locations/MagicShopLocation.cs -- BuyDungeonResetScroll saves immediately after a successful reset so it persists across an unexpected disconnect before the next routine save (issue #108)
  • Scripts/UI/TerminalEmulator.cs -- new ReadSingleKeyRedirected reads one keypress (no Enter required) for "Press any key" prompts in stdio/redirected and real-console door modes, coalescing CR/LF pairs and consuming escape sequences so the next prompt is not phantom-fed; PressAnyKey now uses it instead of a full-line read (issue #103)
  • Scripts/Systems/WorldBossSystem.cs -- the status-effect (burn/poison DoT) death path now uses the same non-lethal "healers dragged you to safety" outcome (revive to 25% HP, re-entry cooldown, no resurrection consumed) as boss-dealt damage, via a shared helper, so a DoT death in a world boss fight no longer falls through to the real death / divine-intervention flow (issue #110)
  • Scripts/Systems/SaveDataStructures.cs -- new InventoryItemData.FromItem / ToItem full-fidelity converters (every stat field, MinLevel, the cursed/identified flags, and the complete LootEffects list, with the curse flag OR-ed from both Item curse bools) so an item can be carried exactly as it is (issue #111)
  • Scripts/Locations/BaseLocation.cs -- the player-to-player trade flow (package send, accept, and cancel/return) now uses the full FromItem/ToItem conversion instead of a 10-field subset that dropped stats, enchantments, and cursed-stat effects (issue #111)
  • Scripts/Locations/DungeonLocation.cs -- IssuePotionToTeammateStash now bases a companion's new potion total on the authoritative stored count (the value shown to the player) instead of a possibly-stale in-combat wrapper field, so giving potions adds to the existing stash instead of overwriting it
  • Scripts/Systems/CombatEngine.cs -- ApplySingleMonsterDamage now returns whether the attack landed (false when evaded or the target is invalid); the multi-monster basic attack, the dual-wield off-hand ability follow-up, and teammate attacks skip their post-hit enchantment procs (life steal, elemental, Shaman weapon-enchant, Ancestral Guidance) when the swing was evaded, matching the single-target path
  • Scripts/Systems/CombatEngine.cs -- the grouped-player combat-reward path now gates auto-level-up on the follower's AutoLevelUp preference (matching the solo/leader path), so turning auto-level-up off works in multiplayer groups
  • Scripts/Locations/DungeonLocation.cs -- the dungeon rest-spot event (RestSpotEncounter) now heals every living teammate (companions, NPC teammates, and grouped players) at 33% HP/MP/stamina and cures their poison, instead of only grouped human players, matching the make-camp action; the resting player sees a confirmation line
  • Scripts/Systems/CombatEngine.cs -- factored the multi-monster basic-attack damage formula into a shared ComputePlayerSwingDamage helper (the normal basic attack calls it, behavior unchanged); the player's off-hand follow-ups after Power Strike and class abilities now use it for a full off-hand swing instead of a stripped-down formula, so skill off-hand damage matches a normal off-hand attack, while AI teammate off-hand swings keep their lightweight formula to avoid mis-attributing player-perspective crit/proc messages and player-only buffs
  • Scripts/Systems/InventorySystem.cs -- guard the backpack-item dictionary lookup in the slot inventory manager (TryGetValue) so an out-of-range B# entry shows an invalid-choice message instead of throwing KeyNotFoundException and crashing the session
  • Scripts/Core/GameEngine.cs -- the session catch-all now treats a clean GameExitException (quit/logout) as a normal exit instead of logging it as a CRASH and flashing an error screen
  • Scripts/Locations/DungeonLocation.cs -- a group follower's disconnect (IOException / broken pipe / connection reset) in the follower input loop now logs at Info instead of Error
  • Scripts/Systems/SqlSaveBackend.cs -- duplicate-name registration (UNIQUE constraint) returns a clear "name already taken" message and logs at Info; the server-config drain queue ignores a benign disposed-connection race at shutdown
  • Scripts/Systems/ShopItemGenerator.cs -- shop weapon generation now derives handedness from the weapon type (InferHandedness(InferWeaponType(name))) instead of assuming the whole one-handed template range is one-handed, so shop staves are two-handed (matching loot and the hand-crafted staff definitions); genuine one-handed weapons are unchanged
  • Scripts/Systems/FeatureInteractionSystem.cs -- the four dungeon moral-choice scenarios (situation lines, choice labels, and outcome text) now resolve through Loc.Get instead of hardcoded English
  • Localization/{en,es,fr,it,hu}.json -- 30 new dungeon.moral.* keys (the moral-choice scenarios) and a new dungeon.sanctuary_party_recovers key per language