Usurper Reborn - v0.27.1 Release Notes
Bug Fixes & Improvements
Seth Able Exploit Fix
- Seth Able exploitable via generic NPC talk menu: Players could bypass Seth Able's dedicated challenge system (which has a 3 fights/day limit, level scaling, and diminishing returns) by pressing
[0] Talkat the Inn, selecting Seth from the generic NPC list, and choosing[C] Challenge. The generic challenge created a monster withnr: 100(level 100 XP/gold rewards) using Seth's weak base stats, with no daily limit and no diminishing returns. Fixed in two places:InnLocation.ChallengeNPC()now detects Seth Able and redirects to the dedicatedChallengeSethAble()systemBaseLocation.TalkToNPC()now filters outIsSpecialNPCNPCs from the generic talk list, preventing special NPCs with dedicated interaction paths from appearing in the generic menu
Hall of Fame Leaderboard Cap
- Website Hall of Fame capped at top 25 players: The leaderboard on the website previously had no limit and would display every registered player. Now limited to the top 25 by level and experience, both in the SQL query (
LIMIT 25) and as a frontend safety net (.slice(0, 25)).
Linux Auto-Updater Crash Fix
- Auto-updater crashes on Linux: The
CreateUnixUpdater()method inVersionChecker.csgenerated shell scripts that failed on Linux due to three bugs:- CRLF line endings: C# verbatim strings (
$@"") embed the source file's line endings. When compiled on Windows (CRLF source), the generated shell script contained\r\nline endings, causingbash: /tmp/.../updater.sh: /bin/bash^M: bad interpretererrors on Linux. Fixed by using astring[]array joined with explicit"\n". - Process detection failure:
pgrep -x "UsurperReborn"silently fails on Linux because-xrequires an exact 15-character match (Linux truncates process names). Changed topgrep -fwhich matches the full command line. - File copy glob failure:
cp -rf "/path/"*doesn't expand the glob because it's inside quotes. Changed tocp -rf "/path/". "/dest/"which copies directory contents without glob expansion.
- CRLF line endings: C# verbatim strings (
Server Deployment File Fixes
- CRLF line endings in server config files:
scripts-server/nginx-usurper.confandscripts-server/usurper-web.servicehad Windows CRLF line endings that would cause parse errors on Linux (nginx config errors, systemd service failures). Converted to LF.
Armor/Weapon Shop Duplicate Items (Studded Belt x200)
- Shops flooded with duplicate equipment: The armor and weapon shops displayed thousands of duplicate items (e.g., 200 pages of "Studded Belt") due to a save system bug. Three root causes fixed:
- NPC equipment saved as dynamic:
SaveSystem.csused>= 10000to identify dynamic equipment IDs, but base equipment for Waist (10000+), Face (11000+), Cloak (12000+), Neck (13000+), and Ring (14000+) slots all fell in that range. Dynamic equipment actually starts at 100000. Every NPC's base armor was being saved as "dynamic equipment," re-registered on load as duplicates, and snowballing across save/load cycles. Fixed threshold to>= 100000. - Player save included all dynamic items: The player save captured ALL dynamic equipment in the database (including NPC items), not just equipment the player had equipped. Fixed to only save the player's own equipped dynamic items.
- Shop queries included dynamic items:
EquipmentDatabase.GetBySlot()andGetWeaponsByHandedness()returned all equipment including dynamic loot items. Now excludes dynamic items by default so shops only display the base catalog.
- NPC equipment saved as dynamic:
Spouse/Lover Death Message Fix
- Misleading permanent death messages for spouses/lovers: When a spouse or lover died in combat, the messages said "gone forever" and "will never return" despite them being resurrectable at Home or the Team Corner. Messages now say "has fallen... Perhaps they can still be saved..."
Dungeon Ascending Restriction Fix
- Players couldn't ascend past level - 10 floors: The
ChangeDungeonLevel()function clamped the target floor toplayerLevel - 10on the low end, preventing players from ascending to the surface. A level 40 player couldn't go above floor 30. Fixed: players can always ascend to any floor (minimum floor 1). The restriction now only applies to descending (can't go deeper than playerLevel + 10).
Old God Floor Progression Gates
- Players could bypass Old God boss fights: Multiple holes in the dungeon floor gating system allowed players to skip mandatory Old God encounters:
RequiresFloorClear()didn't check Old God floors: The method only checkedSealFloorsandSecretBossFloors(which was missing floors 40, 55, 70, 85, 95, 100). Now checksOldGodFloorsdirectly.DescendStairs()had no floor gate check: Players could use stairs to descend past undefeated Old God floors without any restriction. Now blocks descent with "A powerful presence blocks your descent" message.DescendDeeper()had no floor gate check: Same bypass via the Descend Deeper command. Now blocks descent.ChangeDungeonLevel()didn't check intermediate floors: Players on floor 20 could jump to floor 30, bypassing the Old God gate on floor 25. Now caps target floor at the first undefeated Old God.- Seal floors were hard gates but shouldn't be:
GetMaxAccessibleFloor()treated seal floors (15, 30, 45, 60, 80, 99) as mandatory progression gates, blocking players who skipped a seal. Seals are now soft gates - players can skip them (only affects which endings are available). Only Old God floors (25, 40, 55, 70, 85, 95, 100) are hard progression gates.
Old God Encounter Hint
- No indication of Old God on boss floors: When entering a boss room on an Old God floor (25, 40, 55, 70, 85, 95, 100) without meeting the prerequisite (e.g., defeating the previous god), the player just got a regular boss fight with no hint that something special should be there. Now displays: "You sense an ancient presence sealed away in this chamber... Perhaps you must prove yourself elsewhere before it reveals itself."
Online Play Color Fix
- Online Play had no colors (black and white text): When connecting to the online server via SSH (either through the website terminal or direct SSH), all game text appeared in plain white with no colors. Root cause: the server-side
TerminalEmulatorusedConsole.ForegroundColor(which only affects the local console) instead of ANSI escape codes (which travel through the SSH pipe to the client). The existingDoorMode.IsInDoorModecheck controlled ANSI output, but online mode (--online --stdio) doesn't set a drop file, soIsInDoorModewas false. Fix: added a newDoorMode.ShouldUseAnsiOutputproperty that returns true for both BBS door mode AND online mode. Changed 6 locations inTerminalEmulator.csandGodotStubs.csfromIsInDoorModetoShouldUseAnsiOutput.IsInDoorModeitself was left unchanged to avoid side effects on character name locking, save paths, and SysOp detection.
Duplicate Login Prevention
- Players could log into the same character twice simultaneously: No check prevented a player from opening multiple sessions with the same character (e.g., one via website terminal and one via direct SSH). This could cause save data corruption as both sessions read/write the same player record. Fix: added
IsPlayerOnline(username)check toSqlSaveBackend.csthat queries theonline_playerstable for an active heartbeat within 120 seconds. The check is enforced at both authentication entry points:OnlineAuthScreen.DoLogin()- rejects login after successful password verification if already onlineProgram.RunDoorModeAsync()- rejects--userpre-authenticated sessions if already online- Displays "This character is already logged in from another session" with instructions to disconnect first
- Fails open on database errors (won't block login if the DB query fails)
Purify Spell Fix
- Purify spell had no effect: The Cleric's Purify spell (spell #3) could succeed its D20 roll but produced no actual game effect. Root cause: the spell set
SpecialEffect = "cure_disease"but neither spell effect handler inCombatEngine.cshad acase "cure_disease":- the effect silently fell through. Fix: added acure_diseasehandler that clears all disease status effects (Diseased,Poisoned), boolean disease flags (Blind,Plague,Smallpox,Measles,Leprosy,LoversBane), and the poison counter.
Auto-Combat Potion Usage
- Auto-combat never used healing potions: Auto-combat mode was hardcoded to only create
Attackactions every round, with no consideration for player HP. Players and companions would die in extended auto-combat fights when a potion could have saved them. Fix: auto-combat now checks if HP is below 50% of max and the player has potions available. If so, it uses a QuickHeal action (one potion, no prompts) instead of attacking. Also added missingHeal/QuickHealcases to the multi-monster combat action processor. Companion/NPC potion thresholds also raised from 35%/25% to 50% to match.
Spell Failure Feedback
- Spell failures gave no explanation: When spells fizzled, only generic messages like "the spell fails!" were shown. The D20 roll information was calculated internally but never displayed. Fix: spell failure messages now include the roll details (e.g.,
[Roll: 3 + 2 = 5 vs DC 11]), teaching players how the training system works and that improving proficiency increases success rates.
Troll Racial Regeneration
- Troll race had no passive regeneration: Despite being a classic RPG trope (and monster Trolls having a
Regenerationability), Troll player characters had no regeneration mechanic - just static stat bonuses. Fix: Troll players now passively regenerate 1-3 HP per combat round (scales with level:1 + Level/20, max 3). Race help text updated to mention regeneration, and character creation now shows*regenerationtag (matching Gnoll's*poisonous bitepattern).
Boss Room Monster HP Display Bug
- Boss room monsters showed HP exceeding MaxHP (e.g., 3069/2046): When entering a boss room,
DungeonLocation.csapplied a 1.5x HP multiplier to all monsters in the room (m.HP = (long)(m.HP * 1.5)) but never updatedMaxHPto match. This caused the combat status display to show current HP greater than max HP (e.g.,3069/2046). The HP bar would also render incorrectly (overfilled). Fix:MaxHPis now set to match the boosted HP value after the boss room multiplier is applied.
Veloura (Floor 40) Old God Never Spawns
- Second Old God (Veloura) could never be encountered: After defeating Maelketh on floor 25, the prerequisite check for Veloura on floor 40 looked for story flags
maelketh_defeatedormaelketh_encountered, but neither flag was ever set. The actual flags set on Maelketh resolution aremaelketh_destroyed(defeat path) andmaelketh_saved(save path), set viaSetStoryFlag($"{boss.Type.ToString().ToLower()}_destroyed"). Fix: changed Veloura's prerequisite to checkOldGodStates[Maelketh].Statusdirectly (matching how Thorgrim, Noctura, Aurelion, and Terravok already work), accepting any resolved status (Defeated, Saved, Allied, Awakened, Consumed).
Veloura Save Quest Chain
- Sparing Old Gods was instant instead of a quest: Choosing the empathy/spare dialogue path for an Old God (e.g., Veloura's "What if the curse could be broken?" → "I will find this artifact and free you") immediately set the god to
GodStatus.Saved, even though the dialogue explicitly tells the player to go find an artifact. The floor was cleared and the god disappeared before the quest could begin. Fix: sparing now setsGodStatus.Awakened(quest in progress) instead ofSaved. The full save quest chain is:- Floor 40: Spare Veloura via dialogue →
GodStatus.Awakened, floor passable,[QUEST STARTED]message - Floor 65: Discover the Soulweaver's Loom artifact (triggered by
veloura_save_questflag). Optional moral paradox about the Loom's cost (Lyris's sacrifice) plays afterward. - Floor 40 (return): With the Loom in hand, Veloura reappears for a special save completion scene →
GodStatus.Saved, +XP, +100 Chivalry, Ocean Philosophy fragment
CanEncounterBoss()now allows re-encounter with Awakened gods when the player has the required artifact- Floor 65 story event changed from requiring the SoulweaversLoom to granting it (was a chicken-and-egg problem)
- Moral paradox (
velouras_cure) no longer changes god state directly; the actual save happens on the return visit
- Floor 40: Spare Veloura via dialogue →
Quick Potion Command
- No way to use potions outside combat: Players had to enter combat or navigate to the Healer to use healing potions. Fix: added
/potion(alias/pot) slash command that works from any location. Uses one potion, shows HP and remaining potion count. Handles edge cases (full HP, no potions). Added to/helpcommand list.
Unidentified Weapon Equip Exploit
- Unidentified weapons could be equipped immediately: Picking up an unidentified weapon from a loot drop showed the
(E)quip immediatelyoption, and equipping it bypassed the identification system entirely, revealing the item's true name and stats. Similarly, unidentified items in the inventory could be equipped via the[E] Equip Itemoption. Fix: the equip option is now hidden on loot drops for unidentified items, and grayed out with "(must identify first)" in the inventory menu. Both paths block equipping with a message directing the player to the Magic Shop. Display names useGetUnidentifiedName()throughout to prevent stat/name leaks.
Hall of Recruitment Removed
- Hall of Recruitment crash and redundancy: The Hall of Recruitment at the Inn was a Godot-era leftover that used
GodotHelpers.GetNode<TeamSystem>(), causing an immediate crash when trying to hire a mercenary. The feature was also redundant with the Team Corner, which already handles NPC team recruitment. The Hall of Recruitment has been fully removed from the Inn menu, location manager, and navigation system.
Files Changed
Scripts/Core/GameConfig.cs- Version 0.27.1-alphaScripts/Locations/InnLocation.cs- Seth Able exploit redirect in ChallengeNPC()Scripts/Locations/BaseLocation.cs- Filter IsSpecialNPC from generic talk list, /potion quick commandScripts/Core/GameConfig.cs- Troll race help text updated with regenerationScripts/Systems/VersionChecker.cs- Linux auto-updater CRLF, pgrep, and cp fixesScripts/Systems/SaveSystem.cs- NPC dynamic equipment threshold fix (10000 → 100000), player save only includes equipped dynamic itemsScripts/Data/EquipmentData.cs- GetBySlot/GetWeaponsByHandedness/GetAccessories/GetBestAffordable exclude dynamic items by defaultScripts/Systems/CombatEngine.cs- Spouse/lover death messages, cure_disease handler, auto-combat potion logic, Troll regen, Heal/QuickHeal in multi-monster combatScripts/Systems/SpellSystem.cs- Roll info shown on spell failure messagesScripts/Systems/CharacterCreationSystem.cs- Troll *regeneration tag in race selectionScripts/Locations/DungeonLocation.cs- Old God floor progression gates (RequiresFloorClear, DescendStairs, DescendDeeper, ChangeDungeonLevel, GetMaxAccessibleFloor), ascending fix, Old God hint, boss room MaxHP sync fixweb/ssh-proxy.js- LIMIT 25 on leaderboard SQL queryweb/index.html- .slice(0, 25) frontend leaderboard capScripts/BBS/DoorMode.cs- AddedShouldUseAnsiOutputproperty for online mode ANSI outputScripts/UI/TerminalEmulator.cs- 5 locations changed fromIsInDoorModetoShouldUseAnsiOutputScripts/Utils/GodotStubs.cs- SafeClearScreen() usesShouldUseAnsiOutputScripts/Systems/SqlSaveBackend.cs- AddedIsPlayerOnline()duplicate session checkScripts/Systems/OnlineAuthScreen.cs- Duplicate login check after authenticationConsole/Bootstrap/Program.cs- Duplicate login check for--userpre-authenticated pathScripts/Systems/OldGodBossSystem.cs- Veloura prerequisite fix, spared→Awakened, CompleteSaveQuest(), GetArtifactForSave(), CanEncounterBoss allows Awakened re-encounterScripts/Systems/MoralParadoxSystem.cs- velouras_cure paradox: removed GodSaved/GodNotSaved effects, updated text to direct player back to floor 40scripts-server/nginx-usurper.conf- CRLF → LF line ending fixscripts-server/usurper-web.service- CRLF → LF line ending fixScripts/Systems/CombatEngine.cs- Hide equip option for unidentified loot, use unidentified display namesScripts/Systems/InventorySystem.cs- Gray out equip for unidentified items, block with explanation messageScripts/Locations/HallOfRecruitmentLocation.cs- DELETED - Godot-era leftover causing crashesScripts/Locations/InnLocation.cs- Removed Hall of Recruitment menu/navScripts/Systems/LocationManager.cs- Removed Hall of Recruitment registration and navigationScripts/Systems/DialogueSystem.cs- FindNode fix for Veloura save quest dialogue tree traversal