Rifts DS

✅ 可玩

riftsds

更新 fdf581a 2026-09-12 源码 下载 ZIP 上游 grav1tyzero/rifts-ds

▶ 开始游玩 · Play Now

A Dead Souls 3.9 lineage mudlib (same ds386/dsI/dsII/dsIII/dshakkard/deadsouls_fluffos engine family) reskinned onto Palladium's post-apocalyptic Rifts tabletop RPG setting -- not a duplicate of the already-onboarded rifts2, an unrelated DarkeLIB/Nightmare-lineage codebase that happens to use the same tabletop setting. What's actually playable is the stock Dead Souls demo world (campus/town/Praxis/Ylsrim/default/amigara) plus thin continent-domain stubs (Africa, Americas, Atlantis, Australia, China, Europe, Japan, Mexico, Russia, SouthAmericas and more), with real Rifts-flavored touches layered onto that working base: a P.P.E. (Potential Psychic Energy) resource on the HUD and score sheet alongside HP/SP, a custom greathorneddragon race alongside the stock dwarf/elf/human set, and 'universal credits' as the town's currency. The town hub has Horace's General Supply Shop, the First Village Bank (with Zoe the bank teller), a Healer's Guild selling remedies, and a talking beggar on Saquivor Road who hands new arrivals a real map item. The repo itself is a genuine slice of someone's personal, in-progress mud rather than a finished release or a pristine archive -- it arrived with three live wizard accounts (gravity, thurtea, shrub) already in play and only ten total upstream commits, the last few just balancing tweaks ('multiple fixes credits added mana now ppe'). Its most striking piece of content, though, doesn't work: domains/omega is 150 files describing a detailed multi-floor sci-fi space station ('Omega Station') with NPCs, a shop, and an elevator, backed by a further 33-file domains/common item/ship library -- but every file in both trees inherits base classes (LIVING, M_VENDOR, ARMOUR, ELEVATOR, and more) that are never defined anywhere in the repository, so none of it compiles or loads; the author's own half-built compat.h shim shows they were actively working toward supporting it before development stopped. The bundled domains/std Lima-style builder-school example area (315 files, stock to Dead Souls 3.9 itself) hits the identical missing-framework problem.

README

A Dead Souls mudlib (the same well-known generic LPC starter codebase as ds386/dsI/dsII/dsIII elsewhere in this collection, here on version 3.9) reskinned onto Palladium's post-apocalyptic Rifts tabletop RPG setting: characters track a P.P.E. (Potential Psychic Energy) resource alongside the usual HP/SP, and the race list includes a custom greathorneddragon option alongside the stock Dead Souls choices (dwarf, elf, human, half-elf, half-orc, halfling, orc, poleepkwa).

Not to be confused with rifts2 (RiftsMUD2) elsewhere in this collection -- that's a completely different, unrelated codebase (DarkeLIB/Nightmare lineage) that happens to use the same Rifts setting.

Important: the "Omega Station" content does not currently work

This lib ships a large, detailed domains/omega area (a multi-floor sci-fi space station, "Omega Station," with named NPCs, a shop, and an elevator connecting nine levels) plus supporting domains/common sci-fi item/ship content. Neither compiles or loads. Both depend on a whole second set of base object classes (a LIVING/CONTAINER/ ARMOUR/ELEVATOR/vendor-and-container "mixin" framework) that the original repository never actually included -- not a conversion mistake on this project's part, and not something a quick patch can fix (see NOTES.md and AGENTS.md §7.164 for the full technical writeup). The same problem affects a bundled stock-Dead-Souls-3.9 "builder school" tutorial area (domains/std) that ships with this version of the engine. All of this content is left in place, exactly as it arrived, in case a future pass wants to build out the missing framework -- but as of this onboarding, none of it is reachable or playable.

What actually works

Play online

WASM status: playable. Shared WASM driver. socket_address() in secure/sefun/sefun.lpc is #ifdef __PACKAGE_SOCKETS__ so the eager simul_efun compiles without the sockets package (same treatment as dsIII/ds386). Admin login (fluffos / Mud@2026) into First Village Bank, plus score ("Fluffos the unaccomplished") / quit, verified with scripts/wasm_client.js. Play: https://mudlibs.fluffos.info/riftsds/

Admin account

Warning: Mud@2026 is a published default for local play. Change it
before exposing this to a public network.

Run locally

cd libs/riftsds
~/src/fluffos/build-debug/src/driver config.fluffos

Game port: 40257.

NOTES · 移植与修复记录

grav1tyzero/rifts-dsriftsds

Source: git clone https://github.com/grav1tyzero/rifts-ds (cloned 2026-08-30). Mudlib root is the repo's lib/ directory; bin/, extra/ (a wolfpaw/ subdir containing classic MudOS driver-build headers macros.h/configure.h/system_libs -- driver source, not mudlib code), fluffos-2.23-ds03/ (a full bundled driver source tree), win32/, and the top-level license/readme/batch files were not converted. Slug riftsds, number 955, port 40257.

Dead Souls 3.9 lineage -- same base engine family as the already- onboarded ds386/dsI/dsII/dsIII/dshakkard/deadsouls_fluffos (§2.1 lineage confirmation: secure/daemon/master.lpc is near-byte-identical to ds386's own pre-fix raw baseline, differing only in the same staticnosave/.c.lpc conversion artifacts). The ASCII boot banner reports "Mudlib: Dead Souls 3.9" (a later point release than ds386's 3.8.6), which explains the bundled domains/std Lima-style builder-school example area that ds386 doesn't have. Not a duplicate of the already-onboarded rifts2 (tsathoqqua/RiftsMUD2) -- that lib is DarkeLIB/Nightmare lineage, a completely different codebase that happens to use the same tabletop setting.

This repo is a live/played development snapshot, not a pristine archive. It arrived with three real pre-existing wizard accounts (gravity, thurtea, shrub -- all full admins per secure/cfg/groups.cfg), a huge number of realms/thurtea/tmp/*.lpc scratch files (leftover ed/eval-session temp files from live development, hundreds of randomly-named files), and only 10 total git commits upstream ("money pops up a lot more than I thought", "multiple fixes credits added mana now ppe", etc.) -- this reads as one developer's personal in-progress mud, not a finished release.

1. Conversion

convert_lib.sh ran normally (GB18030 default encoding -- this lib is pure English/ASCII, so the encoding pass was a no-op except for a handful of genuinely non-Chinese legacy-8-bit files, see below): 5605 already-UTF-8, 7 converted, 9 lossy, 33 skipped-binary. 3219 .lpc files after rename, 747 literal .c" references fixed, 32 local angle-bracket includes converted, 1605 files touched by the staticnosave sweep.

Lossy-conversion files, all pre-existing (not conversion fallout), all fixed:

2. Ported ~160 already-proven ds386 driver-compat fixes wholesale

Confirmed via a fresh from-scratch reconversion of ds386/raw (into a scratch dir, matching this session's own convert_lib.sh output byte- for-byte) that large parts of this lib's shared "engine" tree (outside domains/) are byte-identical to ds386's own PRE-FIX baseline. A Python script walked every file under secure/, lib/, cmds/, daemon/, std/, obj/, verbs/, powers/, cfg/, include/, ftp/, open/, shadows/, doc/, and domains/; wherever this lib's file was byte-identical to ds386's freshly-reconverted (unfixed) version of the same path AND ds386's own committed (fixed) version of that path differs from the unfixed baseline, this lib's file was directly overwritten with ds386's fixed version. 160 files ported this way, including the highest-impact fixes from ds386's own onboarding: secure/daemon/master.lpc (uid stubs, runtime_config.h already swapped for the driver's canonical copy), secure/sefun/ economy.lpc (the to_int()-missing float-corruption bug, AGENTS.md §7.121), lib/editor.lpc/lib/interactive.lpc/lib/nmsh.lpc (the OLD_ED single-entry-ed() rewrite, AGENTS.md §6.2 -- see below for 2 files that needed the SAME fix applied by hand since their content had locally diverged from ds386's baseline), secure/lib/connect.real (the static/array-idiom fixes for Dead Souls' self-rewriting login object), secure/include/lib.h (LIB_REMOTE stale path), lib/events/ look.lpc+look_in.lpc (the SetLong/SetInternalDesc narrow-string- type bug, AGENTS.md §7.81-class), daemon/command.lpc (the .c.lpc filename-slice bug that broke the stock score command), and secure/sefun/sockets.lpc/instances.lpc (WASM sockets-package- absent gutting -- not yet verified under WASM here, see below).

~724 other shared-tree files diverged from ds386's baseline (this lib is a materially newer/extended Dead Souls snapshot -- new systems not present in ds386 at all: a magic-point/P.P.E. resource, parties, voting, teacher/apprentice skill training) and were left untouched by the port; each one that actually failed to compile was triaged individually (§4 below).

3. Manual fixes beyond the ported set

4. SEVERE FINDING: domains/omega, domains/common, and the bundled domains/std example area are ALL non-functional -- a whole missing base-object framework, never committed to this repo

This is the single most important finding of this onboarding. See AGENTS.md §7.164 for the full catalogued writeup; summary here.

The task brief for this onboarding (and the repo's own file layout) strongly suggested domains/omega -- 150 files describing "Omega Station," a detailed multi-floor sci-fi space station with NPCs, a shop, and a working elevator -- was this lib's standout content. It is not functional. Every one of the 150 files (148 with real code, 2 are empty 0-byte stubs) fails to compile, because they inherit classes that are #defined nowhere in the entire repository: LIVING, M_VENDOR, CONTAINER, M_OPENABLE, M_WEARABLE, M_GETTABLE, ARMOUR, INDOOR_ROOM, ELEVATOR. Checked exhaustively: the whole mudlib tree, the bundled fluffos-2.23-ds03/ driver source, and even the non-mudlib top-level extra/wolfpaw/ directory (which turned out to be classic MudOS driver-BUILD headers -- macros.h/configure.h -- not mudlib code at all, a red herring this session chased down before ruling it out).

domains/common (33 files, shared sci-fi item/ship infrastructure -- WEAPON, DRINK, ITEM_DECAY_FOOD, SPACESHIP) hits the identical problem, evidently meant to back domains/omega. And bundled stock Dead Souls 3.9 itself ships a large Lima-style "builder school" tutorial/example area at domains/std (315 files -- a completely different missing-macro set: KEY, OBJ, PARAM_OBJ, BOOK, VEHICLE, ADVERSARY, STOCK_MASTER, RANGED_WEAPON, LIVING, INDOOR_ROOM) -- same failure shape, different intended framework (this one looks like it's meant to be a well-known classic LPMud "Lima"/LPUniversity teaching area, judging by the school/A/F/M/O/R/ W/X letter-subdirectory convention and a lima_guide.lpc/domains/ std/lima/ presence).

Together these three trees account for 496 of the lib's 519 total lpcc_check.sh compile failures (148 + 33 + 315) -- one root cause, not 496 separate bugs.

This is not a gap this project's conversion introduced. secure/ include/compat.h (gated live: COMPAT_MODE 1 in global.h, genuinely wired into every object's global include) proves the repo's own author was ACTIVELY building a compatibility shim for exactly this purpose -- it correctly maps ROOM/OBJECT/DAEMON/MONSTER/VAULT and ~50 lowercase set_*/query_* verb names (set_shortSetShort, etc.) onto this lib's own Dead Souls CamelCase API -- but it simply never got the additional ~18 class macros these three content trees actually need. The upstream repo's own git log (only 10 commits total: "Initial commit" bulk-imports these large trees, then all follow-up commits are small numeric tweaks -- "money pops up a lot more than I thought", "multiple fixes credits added mana now ppe") is consistent with one developer's personal, mid-construction mud: the big content trees were bulk-imported once and the compat layer was never finished before development moved on to other systems.

Confirmed live, not just via the batch compile sweep. Logged in as the seeded fluffos admin and ran goto /domains/omega/room/ elevator_center -- the driver echoed the exact compile error interactively (error: syntax error, unexpected L_IDENTIFIER ... inherit ELEVATOR;) followed by *No program in object '/domains/ omega/room/elevator_center'! on the subsequent reference, then Could not load that location. goto /domains/omega/npc/hugo failed the same way. As a control, goto /domains/Praxis/supply2 (stock Dead Souls content, fixed above) loaded correctly and printed its real room description -- confirming the failure is specific to the three missing-framework trees, not a general problem with goto/this driver/this lib's boot.

No attempt was made to author the missing framework. Guessing at correct implementations for LIVING/CONTAINER/ARMOUR/ELEVATOR/ M_VENDOR/etc. (and the completely separate KEY/OBJ/ADVERSARY/ STOCK_MASTER/etc. set for domains/std) from ~500 call sites' worth of usage would mean writing a second mudlib engine from scratch, not fixing a bug -- this project's established policy (ds386's own LIB_CAPTURE/jar.lpc precedent: "implementing an entire mechanic from a one-line hint would be inventing content") applies at much larger scale here. domains/omega and domains/common are left exactly as shipped (uncompilable, documented); nothing was deleted.

What DOES actually work: the stock Dead Souls 3.9 base itself, and every domain built on its real, existing API -- Africa/Americas/Atlantis/Australia/AustraliaOutback/China/ Europe/Japan/Mexico/Russia/SouthAmericas (thin stock-base stubs, as expected and documented for Dead-Souls-lineage variants elsewhere in this corpus), campus/town/Praxis/Ylsrim/default/ amigara (the standard Dead Souls demo areas, same as ds386), plus this lib's own Rifts-flavored additions layered onto the REAL API: Rifts races (greathorneddragon alongside the stock dwarf/elf/human/ etc. set, secure/cfg/races/greathorneddragon), a P.P.E. (Potential Psychic Energy) resource shown on the prompt and score sheet alongside HP/SP, and Rifts-flavored currency/vendor content in the town.

5. lpcc_check.sh compile sweep

Ran under (ulimit -v 8388608; ...) given the tree size (3219 files, larger than ds386's 2427). Final: 2700/3219 pass, 519 fail, all 519 fully triaged and explained:

Zero unexplained failures.

6. Live playthrough (native driver)

Full continuous session via scripts/mudclient.py against ~/src/fluffos/build-debug/src/driver config.fluffos.

1. Fresh boot: log/debug.log clean, zero uncaught errors (only the expected benign nosave-on-function warnings, AGENTS.md §4.3), Accepting telnet connections/Initializations complete. 2. Registered a fresh test character, Qintestomegab, through the full flow (name → confirm → age gate → screen-reader prompt → password/confirm → gender → email → race pick human → two news pagers) -- landed in the start room wearing a t-shirt and jeans, the HUD prompt showing hp/ppe/sp (P.P.E. being this lib's Rifts- specific addition to the stock HP/SP pair). 3. look: correct room description/exits every time, across multiple rooms (start room, Horace's General Supply Shop, First Village Bank with "Zoe the bank teller" present). 4. score: correct full character sheet (name, level, race, class, native town, faith, birth date, training points, feeling/food/ drink/alcohol status, XP-to-next-level, customization points) -- confirmed both as a fresh player and as the seeded admin. 5. i (inventory): correct (starting Player's Handbook + worn t-shirt and jeans). 6. Reconnect path verified distinct from fresh registration (a second connection while the first session was still open correctly showed Reconnected./[X has rejoined]). 7. Admin verification: seeded fluffos per §3 above; confirmed live via the admin-only room-path annotation on look, the admin-FAQ welcome banner, and successful goto into real (stock) content. 8. Omega domain: directly navigated via goto (see §4) -- confirmed non-functional live, exactly matching the compile-sweep finding. This was the task's own explicit ask ("navigate into the omega domain specifically to verify its content actually works") and the honest answer is that it does not, for reasons entirely external to anything this onboarding pass could fix. 9. quit: clean disconnect (Please come back another time!, [Fluffos quits Rifts DS], worn items correctly stripped on the way out).

7. WASM status

Measured 2026-09-03 against the shared ~/src/fluffos/build-wasm. secure/sefun/sockets.lpc was already stubbed, but the eager secure/sefun/sefun.lpc still called efun::socket_address() unconditionally -- "Unknown efun: socket_address", simul_efun not loadable, boot failed. Guarded with #ifdef __PACKAGE_SOCKETS__ (same as dsIII/ds386). After that, scripts/wasm_client.js logged in as fluffos / Mud@2026 into First Village Bank, score showed "Fluffos the unaccomplished", hp: 440/440, quit printed "Please come back another time!" secure/daemon/instances.lpc still fails to compile on socket_status() during quit's channel broadcast — a graceful skip, not on the login path. Shop/combat/death were not exercised this pass. Do not loop-reboot (I3).

§10.7 deep functional test (2026-08-31, round two)

Full continuous playthrough against the shared ~/src/fluffos/build- debug/src/driver config.fluffos (no dedicated worktree needed), deliberately exercising the WORKING stock-Dead-Souls content per this session's own brief -- the confirmed-broken domains/omega/domains/ common/domains/std gap (§4 above, AGENTS.md §7.164) was not re-litigated or re-poked at beyond a single deliberate negative-control clone (below). A fresh throwaway mortal registration (Vantharion, human) through the full chargen ritual (name validation, age gate, screen-reader prompt, password, gender, email, race pick) landed correctly in the start room; enter town reached the real, working village hub. Also re-verified the seeded fluffos admin account (§1.5/AGENTS.md's Dead-Souls admin-seeding note) still logs in correctly and shows the wizard room-path annotation.

What was tested and confirmed working -- zero programming bugs found this pass

Minor, unresolved cosmetic observations (not fixed -- neither blocks anything or has a confirmed severe effect; scope discipline against guessing)

Not reached this session, flagged honestly

An extended idle window (not a soak-test bug)

The driver was left running considerably longer than the standard ~200s idle target this pass (a background-task notification delay outside this session's control stretched the wait to several hours of real wall-clock time) -- reported here for the record since it's an unusually long uptime, not because anything was found: debug.log remained completely absent the entire time, and memory growth (~1.5GB -> ~5.6GB RSS) is consistent with this driver's own already-documented ambient NPC heart_beat()/lazy-compile behavior (AGENTS.md §10.8) over that much extra wall-clock time, not a new leak specific to this lib.

Files modified this pass

None -- this pass found zero programming bugs. Runtime state generated during testing (the fluffos admin's updated save, the throwaway Vantharion character's save/mailbox, and a few daemon .o save files) was reverted before committing, per this project's own runtime-state policy.

§10.7 deep functional test (2026-09-01, round three) -- guild/class progression was completely broken, fixed

This pass deliberately targeted the two angles round two's own "Not reached" section flagged: skill/guild-class progression, and a completed two-sided (buy+sell) shop transaction. It also tried tell (including a nonexistent recipient) and a battery of malformed/empty command inputs. Both flagged gaps turned into real findings.

BUG 1 (SEVERE): every Praxis guild-join point silently failed to

ever change the player's class -- SetClass() called where

ChangeClass() was required

Symptom: domains/Praxis/fighter_join.lpc's become fighter (and the identical pattern in cleric_join.lpc, mage_join.lpc, monk_join.lpc, kataan_join.lpc, rogue_join.lpc) always printed its success message ("The Great Warrior initiates you into the class of fighters.") but score/skills afterward still showed the character as Explorer with none of the class's real skills -- 100% reproducible, on a fresh level-1 character, every time, for every one of the six guild-join rooms.

Root cause: all six become()/join() handlers call this_player()->SetClass(class_name) directly. lib/classes.lpc's SetClass() is NOT the entry point for a fresh character's *first* class -- its own header comment says so ("pass by reference... please see the FAQ") and its logic proves it: if(Class){ if(!high_mortalp()) return Class; ... } -- if the player already has ANY non-zero Class value (and every character does, since lib/player.lpc:317 sets Class = "explorer" for everyone at creation), SetClass() treats the call as multi-classing (bolting a second class onto an existing one) and silently refuses unless the caller is high_mortalp() (a non-creator player above level 24 -- Rifts DS's own "veteran mortal" tier for genuine multi-classing, secure/sefun/ pointers.lpc:95). A fresh level-1 (or even a creator, since high_mortalp() explicitly excludes creatorp() accounts too -- confirmed live: fluffos itself could never pass this gate either) explorer can never satisfy that gate, so the assignment is dropped on the floor every time, with the success message printed regardless because it's written to the screen *before* the doomed SetClass() call.

The correct entry point for a fresh explorer's first class is ChangeClass() (same file, lib/classes.lpc): it explicitly does Class = 0 before delegating to SetClass(), which is exactly what bypasses the multi-class gate for a legitimate first-time assignment, then restores the player's level via ChangeLevel(lvl). Proof this is the established, working convention in this very lib: domains/town/npc/herkimer.lpc:111 (the real, working, already round-two-tested Mage's Guild join command, "ask herkimer to join") calls ob->ChangeClass("mage"), not SetClass(). The six Praxis files are the only call sites in the whole corpus using the wrong one (grep -rln ChangeClass turns up exactly lib/classes.lpc, herkimer.lpc, Ylsrim/npc/roshd.lpc, and secure/lib/connect.lpc -- none of the six broken join files).

Fix: this_player()->SetClass("X") -> this_player()-> ChangeClass("X") in:

Live-verified: fresh boot, admin fluffos (level 1, Explorer) -> goto /domains/Praxis/fighter_join -> become fighter -> score now correctly shows Class: [Fighter] -> skills now correctly shows the real fighter primary skill set (blade attack, blunt attack, melee attack, multi-hand, multi-weapon, etc., all at the class file's configured 4/8 starting level) in place of Explorer's skills. Before the fix, score stayed Explorer and skills stayed unchanged no matter how many times become fighter was run. debug.log stayed empty and stdout showed no new errors across the whole test (expected -- this was a silent no-op, not a crash, per AGENTS.md §10.9's "invisible failure" class).

Known residual content gap, NOT fixed (out of scope, same shape as the already-documented domains/omega/common/std framework gap but much smaller): secure/cfg/classes/ only ships cleric, explorer, fighter, mage, and thief class definitions -- monk_join.lpc, kataan_join.lpc, and rogue_join.lpc call ChangeClass("monk")/("kataan")/("rogue"), none of which have a matching config file. Even with the SetClass->ChangeClass fix applied, CLASSES_D->SetClass() (daemon/classes.lpc:123) returns immediately for an unconfigured class name, so become monk/become kataan/become rogue still silently leave the player as Explorer -- this is a missing-content-data gap (nobody ever authored a monk/ kataan/rogue class file), not a wrong-function-call bug, and authoring three balanced class skill-lists from scratch would be inventing content, not fixing one -- left alone per this project's established policy. fighter/cleric/mage (and presumably thief, not separately live-tested but config-file-complete and structurally identical) are now fully functional via their Praxis join rooms.

BUG 2 (moderate, fixed as part of the same investigation):

query_cap_name()/query_name()/query_gender() are undefined

on the player object -- three missing entries in the lib's own

compat shim

While root-causing BUG 1, call me->query_cap_name() (admin call command, a direct function_exists() probe) confirmed this function plain does not exist anywhere on the player object -- grep -rln "->query_cap_name(" domains/Praxis turns up 51 call sites across 30 files (plus one in realms/gravity/cmds/testooc.lpc), all from the same bulk-imported content lineage documented in §4 above. Two siblings, query_name() (40 call sites) and query_gender() (8 call sites), are equally undefined.

This is the exact same class of gap as the already-documented secure/include/compat.h shim (§4: "the repo's own author was ACTIVELY building a compatibility shim... but it simply never got the additional ~18 class macros" for the three fully-broken trees) -- except this time the shim already has 50 other query_*/set_* mappings covering exactly this kind of naming-convention mismatch (#define query_class GetClass, #define query_open GetOpen, etc.), it just never got these three specific ones added, even though the Praxis tree that needs them is otherwise fully working, non-broken content. Confirmed live: before the fix, preview()'s say(this_player ()->query_cap_name()+" seeks to learn about fighter.", ...) line never produced its "X seeks to learn..." room broadcast to bystanders (silently absorbed); query_class() -- which DOES have a compat.h mapping already -- correctly resolved and printed "Welcome, explorer!" the whole time, proving the macro-substitution mechanism itself works fine and is exactly what was missing for the other three names.

Fix: added three lines to secure/include/compat.h, following the file's own established pattern exactly:

#define query_name                      GetName
#define query_cap_name                  GetCapName
#define query_gender                    GetGender

GetName(), GetCapName(), and GetGender() are all real, extensively-used lfuns already defined on the player object (lib/ player.lpc:531,563, lib/race.lpc:216) -- confirmed via grep -rln GetCapName returning 60+ genuine call sites across the working parts of this same lib (verbs/players/bump.lpc, secure/cmds/players/ tell.lpc, secure/daemon/chat.lpc, etc.), so this is restoring the established convention, not inventing a new one. Two stray hits inside already-broken domains/std/domains/common files (which don't compile at all, per §4) are harmless dead code either way. Because global.h (which conditionally #includes compat.h under COMPAT_MODE) is this driver's configured global include file, every .lpc file in the mudlib picks up the three new mappings automatically on next compile -- no per-file edits needed beyond the one header.

Live-verified: post-fix, call me->query_cap_name() (raw function_exists() probe) still correctly reports "not in OBJ" (a runtime string handed to call_other is never macro-expanded -- expected, since #define is a compile-time textual substitution over literal source identifiers, not a runtime symbol lookup), but the *compiled* become()/preview() functions in fighter_join.lpc -- which contain the literal identifier query_cap_name in their own source, and get recompiled fresh against the updated compat.h on driver restart -- now correctly resolve to GetCapName() at compile time. No regressions: full driver reboot, zero new compile errors, zero debug.log/stdout errors across the whole remaining test session.

Skill training (organic teacher-NPC route), verified working

With fluffos reverted to Explorer (which carries magic attack/ magic defense as innate secondary skills) and 5 training points granted via call me->AddTrainingPoints(5) (the admin-command shortcut this project's own brief for this pass explicitly sanctions in place of a lengthy real XP grind), goto /domains/town/room/ training -> ask radagast to train magic attack correctly matched against lib/base_trainer.lpc's eventTrain() (found the skill in the player's own skill list, found available training points, started a real multi-stage ContinueTraining() call_out sequence) and printed "Radagast begins teaching you about the skill of magic attack." -- confirming the organic NPC-trainer skill-progression path (distinct from both the Healer's Guild buy-a-service shop and the Praxis guild-join mechanism above) is genuinely wired up and functional. Not chased to full completion (the training sequence runs across several TRAINING_WAIT-spaced call_out()s) since the mechanism's entry path was the object of the test, not the multi-minute wait.

Two-sided shop transaction, completed and verified end-to-end

Registered a fresh throwaway mortal (Testshopper, human), granted currency via call %testshopper->AddCurrency("universal credits", N) (admin shortcut, same sanctioned pattern as above -- note the lowercase key name: find_player()/to_object()'s %name token lookup is case-sensitive on the saved keyname, %Testshopper capitalized fails with "Cannot identify any object" while %testshopper succeeds). At the Healer's Guild (domains/town/room/ healer, reached via enter town -> north -> north -> enter guild):

Both the earlier round-two insufficient-funds refusal and this pass's fully-funded buy+sell round trip are now live-verified for the same shop, closing out this checklist item completely.

tell, briefly

tell testshopper hello there (creator -> mortal, both connected) worked correctly in both directions and correctly charged 15 PPE (a deliberate Rifts-flavored "telepathic tell costs psychic energy" design choice, not a bug). tell <nonexistent-name> hello fell through to a confusing but 30-year-old stock secure/cmds/players/ tell.lpc (Descartes of Borg 950523) code path that attempts an inter-mud-instance tell lookup and then prints the generic "Tell whom what?" instead of a clear "no such player" -- odd UX, but it's unmodified stock Dead Souls library logic (not this lib's own bug), causes no crash, and is exactly the kind of pre-existing-library "design as shipped" case this project's scope explicitly says to leave alone.

Adversarial/malformed input, briefly

become (no arg), become fighter fighter fighter, become ''''"";;--, buy (no args), buy 99999999999999999999 from james, sell (no args) all produced sane rejection messages ("Become what?", "You cannot become that here.", "Buy what from whom?", "There is no james here." -- correctly, since james wasn't in the room this was tried from -- "Sell what to whom?") with zero crashes and zero debug.log/stdout growth.

Files modified this pass

Runtime state generated during testing (fluffos's class reverted back to Explorer via call me->ChangeClass("explorer") before finishing, the throwaway Testshopper character's save/postal files, and the incidental daemon/RELEASE_NOTES_HTTP/mudlist.txt .o/txt churn from having the driver up) was reverted before committing, per this project's own runtime-state policy.

Heartbeat hobbled(this_player()) (2026-09-04)

Two-sided shop already verified 2026-09-01; not re-run. Same lib/body.lpc heart_beat hobbled(this_object()) plus disable.lpc objectp guard as dsIII. Verb-file checks left alone. Admin fluffos / Mud@2026 logged in after the edit (village bank / Zoe). Save-file churn from this boot left uncommitted.