Ode to Gallantry 2017

✅ 可玩

侠客行2017

xkx2017

🔑 fluffos(密码见 README) 更新 6e86eb7 2026-10-27 源码 下载 ZIP

▶ 开始游玩 · Play Now

侠客行("The Story of Hero"),MudOS v21 世系——和本项目里的 `xkx100`、`xkx2000zxb` 是同一核心代码库家族(`combatd.lpc`/`quest.h` 的结构一眼就能看出来),但地图明显更精简:华山、武当、少林、丐帮、峨嵋、明教等门派齐全,却少了 `xkx100` 那种漠北、哈萨克、台湾式的边疆场景,更像一个精简复刻版本;和同家族的 `xkx2000zxb` 不同,这份档案的管理员权限走标准的 `wizlist` 文本文件机制,没有站点 IP 锁定检查,架设起来更省心。

English

The Story of Hero, MudOS v21 lineage — part of the same core codebase family as this project's xkx100 and xkx2000zxb, visible at a glance in the structure of combatd.lpc and quest.h. It shares that core codebase but with a noticeably more compact map: the major sects (Huashan, Wudang, Shaolin, the Beggars' Sect, Emei, the Ming Cult, and more) are all present, but it lacks xkx100's frontier settings like the northern steppes, Kazakhstan, or Taiwan, reading more like a streamlined remake. Unlike its sibling xkx2000zxb, admin privileges here use the standard wizlist text-file mechanism with no site-IP lock, making it simpler to set up.

README

内容亮点

本次修复的关键 bug

1. 和 xkx2000zxb 一模一样的 #include 路径错误: adm/daemons/combatd.lpc 用 #include </quest/quest.h>(绝对 路径写法),这个驱动的 <...> 语法认不得。改成 #include <quest.h>,并挪到 inherit F_DBASE; 之后(避免"全局 变量声明在 inherit 之前"的编译错误)——这两份档案是独立压缩包, 所以分别修复。 2. 一个真实存在的连锁 bug:inherit/misc/bboard.lpc 的 setup() 对 restore() 没有做任何保护。clone/board/kedian_b (客店留言板)的存档文件本身已经损坏("Illegal file format"), 由于 restore() 是 setup() 里最后一行,这个异常一路往上抛, 打断了调用者(kedian_b.lpc 的 create())后面还没执行的 set()/replace_program() 语句,导致这个留言板对象的名字/简称 状态处于半初始化的坏状态——后续任何人在这个房间 look 都会崩 溃,报"Bad argument 1 to capitalize(),Got: 0"(feature/ name.lpc 的 short() 试图 capitalize 一个从未被正确设置的名 字)。已经给 bboard.lpc 的 restore() 加上 catch()——这是个 共享的 inherit,所以这一个修复能保护 mudlib 里所有的留言板分 身,不只是 kedian_b 这一个。那个特定留言板的旧留言内容已经无法 挽回,但房间本身现在能正常初始化了。

这份档案没有 is_chinese()/check_legal_name() bug——"小浮侠"(三 个字)第一次尝试就通过了,不用修。

管理员账号 / Admin account

警告:对外公开架设前请务必修改此密码。

本地运行

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

游戏端口:40145。

NOTES · 移植与修复记录

WASM 修复摘要(迁移自 meta.json 的 group_note)

侠客行("The Story of Hero"),MudOS v21 世系——和 110 号 xkx100、111 号 xkx2000zxb 是同一个核心代码库家族(从共享的 combatd.lpc/quest.h 结构就能看出)。WASM 修复:(1)和 xkx2000zxb 上已经见过的完全相同的错误 #include bug:adm/daemons/combatd.lpc 的 '#include </quest/quest.h>'(绝对路径写法)在这个驱动的 include 目录规则下解析不了——已改成 '#include <quest.h>' 并挪到 'inherit F_DBASE;' 之后以满足随之而来的"全局变量在 inherit 之前"排序规则(完全相同的修法,因为这是两份独立的压缩包,所以各自独立打的补丁)。(2)一处真正由损坏的留言板存档导致的下游后果:inherit/misc/bboard.lpc 的 setup() 未加保护地呼叫了 restore();那份特定损坏的存档(clone/board/kedian_b 的)在解析过程中抛出"Illegal file format",而由于 restore() 是 setup() 里的最后一条语句,这个异常(只被调用链更外层一个很远的 catch() 捕获)在呼叫者(kedian_b.lpc 的 create())剩下的 set()/replace_program() 语句能执行之前就中止了执行,导致这个留言板的名字/短称状态处于损坏状态——之后每次在那个房间 look 都会崩溃报"Bad argument 1 to capitalize(), Got: 0",出在 feature/name.lpc 的 short() 里。已给 bboard.lpc 的 restore() 呼叫包一层 catch()(这是一个共享的继承,所以能保护整个 mudlib 里的每一个留言板分身,不只是 kedian_b 那一个)——那一个特定损坏留言板的旧留言内容已经无法挽回,但房间/留言板物件本身现在能干净地初始化了。没有发现 is_chinese()/check_legal_name() bug——"小浮侠"(三个字)第一次尝试就通过了,不需要修。管理员账号播种:fluffos (admin) 加入 adm/etc/wizlist(这份档案的 securityd.lpc 真的会在开机时读取 WIZLIST,不像 xkx2000zxb 那种基于存档档案的机制——已通过源码确认,而不是假设文本档案方式能用;也没有 xkx2000zxb 那种 valid_wiz_login() 站点锁定闸门)。注册流程在一次连续的 WASM 客户端会话里完整验证过:英文 id→y/n 创建确认→中文名字→密码+确认→接受天赋赠礼(y)→电子邮件(id@address 格式)→性别→带着完整角色属性表和可用的 score/look 指令(包括之前会崩溃的留言板房间)进入游戏世界,全程没有任何意外错误。管理员权限已直接通过 'wizlist' 指令输出确认"目前权限:(admin)"。LPC 格式化工具对全部 3268 个档案运行(写入 3239 个,29 个因为杂乱的历史代码报错,0 个未改动)。没有 :: 父类呼叫拆分命中,没有 CJK 重新加空格命中,没有 case 标签带尾随注释的候选。全部 3 个 map.lpc 档案确认内容完全相同(只是空白差异)。格式化后用同样的完整注册流程重新验证过——干净,管理员权限依然是 (admin)。

深度功能测试(§10.7,本轮):确认这份档案也带着 jqxz2008 家族那一整套 bug(虽然不是同一份代码库)

这份档案的 group_note 自己已经指出和 xkx100/xkx2000zxb 同属"MudOS v21"/ES II 世系,但没提过和 jqxz2008 系列(金庸群侠传)有关系——不过这次深挖发现两者的 adm/daemons/{chinesed,combatd,logind}.lpc、cmds/std/kill.lpc、cmds/skill/{bai,apprentice}.lpc 这几个关键档案结构高度相似,最近几轮在 jqxz2008/jqxz2008dlx/jqxz2008std 上依次确认的 5 个 bug,在这里逐一核对后全部命中:(1) chinesed.lpc 的 create() 对损坏的 data/e2c_dict.o 做未加保护的 restore(),会崩溃第一次真正用到 chinese()/to_chinese() 的地方;(2) /log/nosave/ 目录不存在,combatd.lpc 的 killer_reward()(每次死亡都会跑到)、kill.lpc、bai.lpc/apprentice.lpc 对着这个不存在的目录裸调 write_file(),会让 die() 中途崩溃并在心跳里死循环重现;(3) bai.lpc/apprentice.lpc 里判断是否从风清扬门下叛出的分支有括号位置错误,== 比较被夹进了 query() 的参数列表,分支永远不会触发。全部按已经验证过的相同修法处理(catch(restore()) + mapp() 守卫、四处 write_file() 前加 assure_file()、括号挪到 query() 外面比较返回值)。

这次还额外发现了第 4 类,本档案独有:adm/daemons/logind.lpc 的 §8.9 食物/饮水初始化检查错了对象——if (!user->query("food") && !user->query("water") && ob->query("age") == 14),age 是角色(user)的属性不是登录物件(ob)的,这个分支永远不会触发,新角色的食物/饮水从创建起就永远是空的。改成 user->query("age") == 14。

以及 3 处 §7.68 死亡/复活软锁:d/death/npc/{wgargoyle,newgargoyle,bgargoyle}.lpc 的 death_stage() 都有 if (!ob || !present(ob)) return; 这种把"角色永久离开"和"暂时不在场"混在一起的写法,已按各文件自己的重试间隔(均为 5 秒)拆分成"暂时不在场则重试"。

用真实驱动完整验证了两次全新注册(小四、小五),score 正确显示食物/饮水两条状态全满;用小四实际去攻击 NPC"萧峰"(「铁掌降龙」),角色被打死,干净地触发"你死了"→"鬼门关"死亡场景→NPC"白无常"打招呼——debug.log 全程保持空白,确认 combatd.lpc 的修复生效、没有死循环崩溃。这份档案再次印证了"同一大家族(哪怕不是同一份具体代码库)的多个档案值得直接核对已知 bug 列表"这条经验——这次跨越了两个原本以为不相关的具体世系分支(jqxz2008 系列 vs xkx100/xkx2000zxb 系列)。

未继续测试的部分

时间关系,没有测试拜师、购物、留言板(虽然 group_note 里已经记录过这一块此前修复过存档损坏问题)。

更正(2026-08-05):§7.68 复活软锁"修复"已撤销

上面提到的"鬼魂离开/不在场时被永久放弃复活流程"曾被当作 AGENTS.md §7.68 记录的一类 bug 修复(把单次判定改成每 5 秒重试)。经用户指出并 重新审视:这更可能是有意的游戏设计,不是 bug——大多数这类档案里 鬼魂根本无法自行移动,所以"不在场"要么从未真正发生,要么是"离开去 在阴间游荡,想回来时再走回这个房间、流程会通过 init() 重新从头开始" 这种有意为之的宽松机制,而不是需要强制追上玩家的错误。强行重试还可能 引入新问题:如果鬼魂之后又走回这个房间,旧的重试和 init() 重新触发的 新一轮流程可能同时运行,导致对话重叠错乱。已把这处改动撤销,恢复成 原始的 if (!ob || !present(ob)) return; 单次判定写法(bmxkx2001 除外——那份档案里这确实是一个真实存在、经过实际复现验证的 bug:鬼魂 本身完全无法移动,是另一个不相关的 NPC 强行把鬼魂拖走导致的)。详见 AGENTS.md §7.68 顶部的撤销说明。

§7.86 跨库扫描修复(留言板 post 崩溃)

深度功能测试(2026-08-13,round two,新驱动重测)

针对驱动升级(quest_times/win_times %-operator 修复 + Warning/warning 大小写回退兼容)做的重测。

发现并修复的 PROGRAMMING bug

1. log_error()(adm/obj/master.lpc,实际生效的 master file) 完全没有严重度检查(AGENTS.md §7.34-class):已加上 strsrch(message, "arning:") == -1 判断。 2. log_file()(adm/simul_efun/file.lpc)完全没有 assure_file() 保护(AGENTS.md §7.11-class):已加上前向声明 + assure_file(LOG_DIR + file);。 3. feature/name.lpc 的通用 short() 兜底逻辑对 query("id") 没有做类型检查,任何 id 属性缺失的物件都会在被 look 到时崩 溃——新发现的一类通用性 bug,波及全库任何走这条兜底路径的物件, 不止留言板:str = name(raw) + "(" + capitalize(query("id")) + ")"——capitalize() 拿到非字符串直接抛 *Bad argument 1 to capitalize() Expected: string Got: 0.。Live 测试注册后第一次 look 客店就撞上:留言板 /clone/board/kedian_b 的存档数据本身 已损坏(见下),restore_object() 把 create() 里 set_name() 刚设好的 dbase 映射整个替换成了存档里那份不完整/损坏的映射, id/name 字段全部丢失。这个兜底路径是全库任何"没有自定义 short"物件共用的通用逻辑,不是留言板专属代码,所以修复方式选 在这个通用点上加防御性检查,而不是逐个物件类型修:str = name(raw) + "(" + (stringp(query("id")) ? capitalize(query("id")) : "") + ")"(stringp(query("id")) 这个判断风格和 feature/command.lpc/clone/misc/corpse.lpc 里已有的写法一 致,是这份代码库自己认可的惯用防护模式)。 4. **data/board/*.o 里 12 份存档数据本身已损坏(AGENTS.md §7.7 第三条"restore_object() 会整个替换/清零全域变量"那一类), 修复方式是删除而不是只在 #3 那样加防御**:bonze_b、 gaibang_b、gaibang_r、huashan_b、kedian_b、shaolin_b、 taohua_b、towiz_b、wiz_b、xiaoy_b、xingxiu_b、 xueshan_b 共 12 份(缺少 # 存档头,或者括号/圆括号计数不配 对),逐一核对 raw/ 原始压缩包确认这些档案本身就是这个字节形 状,转档前就已损坏,不是本项目引入的回归。只加 #3 的防御性检查 能防止崩溃,但不能修复"留言板显示名字变成裸档案路径而不是中文名 字"这个副作用(restore_object() 会把 create() 里 set_name() 刚设好的整个 dbase 映射替换成损坏存档里解析出来 的残缺内容)——live 测试确认:只加 #3 的修复时,look 客店显示 /clone/board/kedian_b() [ 没有任何留言 ],删除损坏存档后重 测,正确显示 客店留言板(Board) [ 没有任何留言 ]。 restore_object() 对不存在的文件只会静默返回 0,不会替换 dbase,所以删除这些无法再被正确解析的损坏存档是安全的。另外 6 份留言板存档(baituo_b、kedian2_b、lingjiu_b、tiandi_b、 wudang_b、xiaoyao_b)确认存档头和括号计数都正常,未触碰。

Proactive checks(无需改动)

实测过程

adm/etc/wizlist 里的 fluffos (admin) 一直没有对应存档。本轮通 过完整注册流程创建(id → y → 中文名 → 密码 ×2 → 天赋接受 y → email → 性别 m),落地"客店",score 显示"【天神】"头衔,食物/饮 水满格。随后单独一步做了真实断线重连+密码验证:用刚设的密码 重新连线成功登录,look 确认客店留言板正确显示"客店留言板(Board) [ 没有任何留言 ]"。全程 debug.log 无运行时错误。驱动按精确 PID 结束;管理员存档已提交;data/{login,user}/x/{xiaosi,xiaowu}.o 是 2026-08-05 遗留的、无 git 历史的测试存档,与本轮无关,未触碰。

§7.100 扫描修复(ROOM 基类多余 replace_program())

#define ROOM "/inherit/room/room":删除 818 处多余的、独立成行的 replace_program(ROOM);(保留 inherit ROOM;),与 jqxz2008 系列同一血统同一形状。clone/misc/roommaker.lpc 同样有两套模 板——"造一间空房间"的 heredoc 本来干净,"克隆我所在的房间"命令的 字符串拼接模板把同一枚多余的 replace_program(ROOM); 烤进了每一 个新克隆的房间,已同步修正。已用 build-debug 驱动干净启动验证 (0 个新增编译错误,端口正常监听);未做完整 §10.7 深度游玩测 试。

``§7.112`` residual-gap closure (2026-08-20)

Corpus re-scan (grep -rl 'call_out("death_stage"' ... | filter for missing guard) found unguarded init()-scheduled death_stage() call_out chain(s) in d/death/npc/newgargoyle.lpc that the original two-wave sweep (see AGENTS.md §7.112) missed -- same reconnect-triggered duplicate-chain bug, different filename/lineage. Added the standard query_temp("death_stage_active")/set_temp/delete_temp re-entry guard, adapted per file's own exit points. Compile-verified via lpcc --batch.

§7.30 uninitialized-mapping accessor sweep (2026-08-20)

Corpus-wide mechanical sweep of the feature/skill.lpc shared-lineage bug (confirmed independently on xiakexing2017/jqxz2015/haiyang2 via round-four testing): 4 accessor(s) in this file returned a raw never-initialized mapping instance variable (defaults to int 0, not ([]), until first assigned), crashing any unguarded keys()/sizeof()/indexing caller for a fresh/untrained character. Fixed at the accessor level (mapp(x) ? x : ([])) per the documented remedy. Verified via lpcc --batch static compile check only (not a live boot) as part of a large mechanical sweep; not individually functionally re-tested live on this lib.

深度功能测试(2026-09-04,round three,shop + 拜师)

新角度:醉仙楼购物 + 丐帮左全拜师。§10.7 原文明确写「没有测试拜师、 购物」;2026-08-13 round two 修了 log_error/log_file/short() 和损坏留言板存档,仍然没走这两步。

实测过程

管理员 fluffos / Mud@2026。第一输入是「您的英文名字:」,落地 客店,留言板显示「客店留言板(Board) [ 没有任何留言 ]」(round two 删损坏存档后的状态仍在)。score 「【天神】普通百姓」。

goto /d/city/zuixianlou,list 烤鸡腿八十文铜板 / 牛皮酒袋一两 白银 / 包子五十文 / 鲸鱼十两黄金。clone /clone/money/gold 后 buy jitui 成功(「你从店小二那里买下了一根烤鸡腿」)。当场 i 还挂着「一两黄金」+ 九十九两白银 + 二十文铜板,那是 MONEY_D->player_pay() 把金锭 set_amount(0) 后物件还没析构的显 示;断线重连后黄金条目消失,只剩九十九两白银 + 二十文铜板 (10000−80 = 9920),找零数学正确,不是复制金钱。这份档案的 feature/dealer.lpc 里丐帮「穷叫化」拒绝购买整段是注释掉的,先买 再拜仍然按家族惯例走,没有撞上拒绝。

goto /d/gaibang/inhole,左全在树洞里,apprentice zuo 一次成功: 左全收徒,score 「丐帮第二十代弟子」、师父左全。cmds/usr/save.lpc 真正调用两个 save()。user.o 立刻带上 family_name":"丐帮" / master_name":"左全" / generation":20。断线后再连(「重新连线完 毕」),score 仍是丐帮 / 左全,银子还在。左全只收男性(gender != "男性" 就静默 return),本账号是男的所以没撞上。

本轮没有新的 programming bug。live debug.log 是 libs/xkx2017/log/debug.log(Boot Time Fri Sep 4 01:23:45 2026), 无 error: / Too deep recursion。error_handler 把轨迹交回驱动 debug.log。work/log/log 只有编译期 Unused local variable 警告; work/log/{condition.err,move.bug} 停在 2026-07-30,不是本轮写入。 管理员存档未提交(本轮只记 NOTES)。

深度功能测试(§10.7,2026-10-04)— negative range ends

scripts/lpc_fix_negative_ranges.py: 5 line(s) in 2 file(s) count a range from the end with < (x[a..-1] -> x[a..<1], x[-2..] -> x[<2..]; KB 06 §7.209). In this driver a negative constant end gave "" / ({ }) and a negative start the whole value, so each of these returned the wrong slice; the compile warning A negative constant as the second element of arr[x..y] is gone from the files that compile. Range lvalues (s[0..-1] = text, the prepend idiom) are left alone. A paired compile of the changed files at HEAD and in the working tree showed no new error.

深度功能测试(§10.7,2026-10-05)— tail() simul_efun

scripts/lpc_add_tail.py --apply: cmds/wiz/tail.lpc calls tail(file), an efun of the MudOS this archive ran on that FluffOS does not have (Undefined function tail: the command did not compile). adm/simul_efun/file.lpc now defines void tail(string file): the last 10 lines of the file, written to the player. Load check (HEAD against the working tree): 1 of the 1 tail command file(s) failed to load at HEAD, all load now, and the simul_efun file loads. KB 04 §6.2.

深度功能测试(§10.7,2026-10-05)— 房间列出的物件缺档时房间照常加载

房间基类 inherit/room/room.lpc:make_inventory() 用 catch() 包住 new(file),做不出来就返回 0;reset() 的 case 1: 拿到 0 就跳过这一项(if (!objectp(ob[list[i]])) break;)。档案里没有的 NPC 或物品(或 create() 出错的物件)原先会让列出它的房间整个加载失败(Bad argument 1 to environment(),或对 0 的 call_other()),连带这个房间后面的区域;现在房间照常加载,只是少了那个物件,下次 reset() 再试(KB 05 §7.25;scripts/lpc_resilient_room.py、scripts/lpc_room_reset_guard.py)。 验证:探针(scripts/lpc_room_reset_probe.py:房间基类列出一个不存在的物件,再调用 reset())HEAD 单件 崩溃、多件 崩溃,工作树 单件 OK、多件 OK。

深度功能测试(§10.7,2026-10-05)— 巫师留言板存档 data/board/*.o.gz

巫师留言板 wiz_b / towiz_b 以 .o.gz 存档(1996 年的帖子)。本机驱动 restore_object() 优先读 .o.gz,而留言板用 save_object(file) 写明文 .o,所以本机上新帖每次重启都退回旧快照;网页版驱动没有 zlib,只读 .o,而归档自带的 .o 是读不出的二进制。.gz 内容为 GB18030(转码时没被处理),转成 UTF-8 写成 .o,替换归档里那个读不出的二进制 .o;towiz_b 原文有 4 个残字节,转为替换符。删除 .o.gz(KB 01)。用 lpcc 载入留言板核对条数(jqxz2008:wiz_b 47 条、towiz_b 115 条)。

深度功能测试(§10.7,2026-10-06)— 白驼山掌门欧阳锋编译不过

d/baituo/ouyangfeng.lpc 有两处笔误,整个 NPC 从未编译成功:greeting() 里 write("你是谁?我要杀了你!哈 ̄哈 ̄哈 ̄") break; 缺分号,inquiry_map() 里 object me = this - player();(应为 this_player())。白驼山派的掌门因此不存在,拜师、问路(inquiry_map)都无从谈起。两处改正后,新进程里 HEAD 加载失败、工作树加载成功。同一文件在 14 个 xkx 系的库里一字不差。

深度功能测试(§10.7,2026-10-07)— 空的 if (current_water == 0) {}

3 个房间的 valid_leave() 里有一行什么也不做的 if (current_water == 0) {}(编译警告 Expression has no side effects),删去,行为不变:d/xingxiu/nanjiang2.lpc、d/xingxiu/nanjiang3.lpc、d/xingxiu/shanjiao.lpc。

深度功能测试(§10.7,2026-10-07)— 编译警告整理

scripts/lpc_warnings.py xkx2017 --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 270 个 .lpc 和 10 个 .h(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):253 -> 253 通过,无回退;改过的头文件的 67 个包含者 67 -> 67 通过。启动时警告 17 -> 0;新进程登录(输入名字)正常。

编译警告收尾(2026-10-13)

scripts/toolkit_fix.sh 一轮去掉 15 处多余继承;include/net/ftpdconf.h 把 VALID_READ、VALID_WRITE 定为 1,覆盖 ftpd.h 的 0,先 #undef(同 xiakexing2017),行为不变。能编译的文件警告清零;改动的对象在 HEAD 与工作树各载入一次(15 objects; HEAD PASS 11, tree PASS 11; regress 0; newly loading 0)。

无门派玩家的 family 映射(2026-10-15,KB 05 §7.30 第三种)

mapping fam = ob->query("family"); 之后直接 fam["family_name"]:没有门派(新人或叛师后)的玩家在学习、练功、拜师、进入门派房间时报 Value being indexed is zero,而不是作者写好的提示。2 处在声明行加上 if (!mapp(fam)) fam = ([ ]);(scripts/lpc_fix_family_guard.py)。

其他体系的属性名(2026-10-15,KB 06 §7.241 第三类,--any-file)

另有 3 个文件(绝招、状态、物品、NPC 等,不限于 kungfu/skill)来自 gin/kee/sen/force 体系,同样按 kee→qi、gin/sen→jing、force→neili 改名(scripts/lpc_port_es_props.py --any-file)。

family 映射(再补)(2026-10-15,KB 05 §7.30)

mapping fam; fam = (mapping)ob->query("family");(先声明后赋值)和泥潭式的 query("family", me) 同样直接取下标:无门派的新人拜师(attempt_apprentice/recognize_apprentice)、NPC 的 greeting、房间的 valid_leave、ask_me 等报 Value being indexed is zero。5 处补上 if (!mapp(fam)) fam = ([ ]);(刚 recruit_apprentice() 之后的除外)。

技能名拼错(2026-10-15,KB 06 §7.249)

query_skill() 里的技能名拼错了,或用的是本库改名前的旧名;本库没有这个技能,调用永远得 0:门槛(< N 就拒绝)永远过不去,相关功夫或绝招学不了、用不了;内功互斥(> N 就拒绝)从不生效;公式里这一项一直是 0。经汉字名(提示文字或中文名词典)或纯拼写差异(_/-、少一个连字符、相邻字母颠倒)核对后,改为本库的技能名(scripts/lpc_fix_skill_typo.py):mizongxinfa→mizong-xinfa(longxiang/fanchang)。

family 映射内联取下标(2026-10-15,KB 05 §7.30)

ob->query("family")["master_name"]、me->query("family")["family_name"](泥潭式为 query("family", ob)[...])直接对门派映射取下标:没有门派的人(新玩家、叛师者、大多数 NPC)在 NPC 的打招呼和问话、绝招的门派判断、房间的 valid_leave 里报 Value being indexed is zero,而不是得到拒绝。7 处(6 个文件)改为 dbase 路径查询 query("family/master_name"):有门派时值相同,没有时得 0;原有的判断照常生效(scripts/lpc_fix_family_index.py)。给映射赋值的写法未动。

NPC 带着本库没有的物品,载入失败(2026-10-15,KB 06 §7.73)

3 个文件、3 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()):carry_object() 得 0,接着的调用报 Bad argument 1 to EFUN call_other(),NPC 根本载入不了,该有它的房间里就少了这个人。多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/city/obj/tudao、/clone/cloth/female-cloth、/clone/cloth/cloth)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 0 个,改后 2 个(2 个原来载入不了),没有退步;其余 1 个另有原因(多为 set_skill() 了本库没有的武功)。

去掉的预定义宏 SAVE_EXTENSION(2026-10-18,KB 04 §6.11)

老版 MudOS 给每个 LPC 对象预先定义了 SAVE_EXTENSION(".o");FluffOS 的 ChangeLog(COMPAT_BUSTER 一条)把它删了,改用 __SAVE_EXTENSION__。本库没有自己 #define 这个宏,用到它的文件一律 Undefined variable 'SAVE_EXTENSION',整个对象载入不了。改成 __SAVE_EXTENSION__(scripts/lpc_fix_predefined_macro.py):adm/daemons/network/netmail.lpc、cmds/arch/purge.lpc、d/quanzhou/npc/tang.lpc、d/quanzhou/npc/tang3.lpc、d/taishan/tang3.lpc、include/net/ftpdsupp.h。tang3/tang/meng-zhu 是武林盟主一类的 NPC,原来一个都载入不了;purge 是清除玩家档案的巫师指令;netmail 是网络邮件守护进程(本库缺 mailer.h 等头文件,改后仍载入不了)。改后用 lpcc 逐个载入这些对象:4 个通过,1 个还有别的错误(adm/daemons/network/netmail:/adm/daemons/network/netmail.lpc:17: error: Cannot #include mailer.h)。

水潭里 ging_cost 是 jing_cost 的笔误(2026-10-18)

shuitan 先算 jing_cost,倒水时却扣 receive_damage("jing", ging_cost),ging_cost 从没声明过:Undefined variable 'ging_cost',整个物件载入不了。改成 jing_cost:d/shaolin/obj/shuitan.lpc。改后用 lpcc 逐个载入这些对象:1 个通过。

set_skill() 遇到本库没有文件的武功不再抛错(2026-10-18,KB 06 §7.256)

feature/skill.lpc 里的 set_skill()、map_skill()、prepare_skill() 碰到没有武功文件的名字就 error("F_SKILL: No such skill (…)"),调用它们的 NPC create() 整个中断:NPC 载入不了,刷出它的房间也少一个人。本库有 10 个对象设了本库没有文件的武功(3 个名字,最多的是 xuanyuan-arrow、liangyi-jian、jinwu-daofa)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:10 个里 10 个通过。(scripts/lpc_guard_set_skill.py)

武功名拼错的 NPC(2026-10-19,KB 06 §7.258)

set_skill("kunlun-shnfa", …)、map_skill("dodge", "shaolin-shnfa") 这样的调用把武功名拼错了:本库没有这个武功文件,有的是拼对的那个。以前这种 NPC 在 create() 里报 No such skill 载入不了;武功缺失时跳过之后它们能载入,可是少了那门武功(set_skill 被跳过,map_skill 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 6 处、5 个文件:xinxiu-duzhang→xingxiu-duzhang(2 处)、tiaji-jian→taiji-jian(2 处)、jinyin-baiguzhao→jiuyin-baiguzhao(1 处)、qishang--quan→qishang-quan(1 处)。(scripts/lpc_fix_skill_names.py)

set_name 的 id 参数传成字符串(2026-10-19,KB 06 §7.260)

set_name("海口大碗", "bowl"); 把物品的 id 写成了字符串,本库的 set_name(string name, string *id) 要的是数组,驱动在编译时报 Bad type for argument 2 of set_name ( string * vs string ),整个物件载入不了;旧驱动不检查参数类型,字符串里照样找得到这个词,物品能用 id 称呼。改成 ({ "bowl" }),共 2 个文件:/d/baituo/npc/obj/bowl.lpc、/d/baituo/obj/bowl.lpc。(scripts/lpc_fix_set_name_id.py)

全局头文件丢了 1 个宏(2026-10-19,KB 04 §6.16)

/include/globals.h 里没有定义 MAILDIR,用到它们的物件(名单里 1 个)编译时报 Undefined variable/Undefined function,整个物件载入不了(编译器在第一个未定义的名字上停下,后面的错误和警告都看不到)。同系的库在各自的头文件里都有这几行:照其中和本库共有文件最多的那个库,原样补到 /include/globals.h 最后一个 #define 之后,宏体里出现的路径在本库里都存在。名单里的物件除了这几个宏之外还有别的错误,暂时仍载入不了。(scripts/lpc_restore_defines.py)

RANK_D 对 0 的称呼(2026-10-19,KB 06 §7.273)

adm/daemons/rankd.lpc 的 query_respect()、query_rude()、query_self()、query_self_rude() 里 4 个函数开头补上 if (!objectp(ob)) ob = this_object();。NPC 在 create() 里写 "…" + RANK_D->query_respect(this_player()) + "…"(inquiry 对话、chat_msg),驱动自己跑 reset() 时没有人在场,this_player() 是 0。旧驱动里 0->query("age") 返回 0,称呼走默认分支;新驱动直接报 Bad argument 1 to EFUN call_other(),房间的 make_inventory() 吞掉错误,被打死的 NPC 再也刷新不回来。用 scripts/lpc_reach_census.py --walk 找到(xkx100 的武当弟子、一灯、汴梁书生…);修在守护程序里,不动 1,800 个 NPC 文件。

玩家打造兵器模板没有打造者时(2026-10-19,KB 06 §7.275)

/d/npc/m_weapon/weapon/m_blade.lpc、/d/npc/m_weapon/weapon/m_club.lpc、/d/npc/m_weapon/weapon/m_staff.lpc、/d/npc/m_weapon/weapon/m_sword.lpc、/d/npc/m_weapon/weapon/m_whip.lpc(共 5 个)的 create() 开头 me = this_player(); 之后无条件读 me->query("weapon/name") 等打造数据。房间刷新时没有人在场(me 为 0),或者 NPC 在别的 NPC 的命令环境里 carry_object() 这个模板(NPC 的 weapon 属性是文件名字符串),旧驱动里读出来是 0 或字符串,switch 照样落到 default;新驱动报 Bad argument 1 to EFUN call_other() / Bad argument 1 to switch,NPC 和它所在的房间载入不了(--walk 普查里一个玩家走进房间就触发)。在 //读入数据 之后加 if (!me || !mapp(me->query("weapon"))) { setup(); return; }:没有打造者就留一个没有名字的空模板,玩家打造的流程不变。

补回丢失的标点(2026-10-19,KB 03 §4.6)

转换时丢掉的分号、逗号、引号或文件末尾的 },共 1 个文件,修前 HEAD 载入失败。别的库里同路径的完好副本只比这里少几个标点,按它补回(括号里是那个库):/d/city2/npc/zhengmen.lpc(jqxz2008)。修后与 HEAD 逐个对比载入:这 1 个物件全部载入,没有退步(scripts/lpc_restore_from_twin.py、lpc_fix_eaten_quote.py --comma、lpc_fix_eof_braces.py)。

x != "a" || x != "b" 和 x == "a" && x == "b":同一个值的比较链永远成立/永远不成立(2026-10-20,KB 06 §7.282)

if (arg != "wan" || arg != "丸") return notify_fail(...) 对任何 arg 都为真,dir == "south" && dir == "north" 永远为假:条件下面的守卫要么永远拦、要么永远不拦。作者要的是“既不是 A 也不是 B”(&&)或“是 A 或 B”(||)。!= 链的 || 改成 &&(仅当条件下面是 return 的拒绝/守卫),== 链的 && 改成 ||(scripts/lpc_fix_cmp_chain.py)。改动:/d/mingjiao/shanmen.lpc(1 处)。

行编辑器指令编译失败:(: this_object(), ({ "do_to", me, arg }) :)(2026-10-20,KB 06 §7.284)

Illegal to use local variable in functional.:call_other 形式的函数指针 (: ob, ({ "函数", 参数 }) :) 的数组里只能是常量,写了局部变量 me / arg 整个指令就编译不过,to 这类先开编辑器、再回调自己的指令因此从未能用。改成 (: call_other, this_object(), "do_to", me, arg :)(参数在建立指针时绑定,编辑器再把输入的文字接在最后,do_to(me, arg, text) 收到全文;scripts/lpc_fix_functional_locals.py)。改动:/cmds/usr/to.lpc(1 处)、/cmds/wiz/to.lpc(1 处)。

call_out_info() 的每一项只有 3 个元素(2026-10-20,KB 06 §7.287)

驱动返回的是 ({ 物件, 函数, 延迟 }),不带 call_out 的参数,旧代码读第 4 项 info[i][3](callouts 指令、calloutd、cod 工具)一读就报 Array index out of bounds,指令从第一行起什么也打印不出来。改为 (sizeof(info[i]) > 3 ? info[i][3] : 0)(scripts/lpc_fix_callout_info.py)。改动:/cmds/arch/callouts.lpc(1 处)。

登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)

驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/adm/obj/simul_efun.lpc 里补了一个同名的 simul_efun crypt(key, salt):只有超长的密钥才被截短(先截到 255 个字符,再逐字符截到 sizeof(to_buffer(key)) <= 255,限制按字节算,一个汉字占三个字节),再交给 efun::crypt();普通密码原样散列,已存的密码散列不受影响(scripts/lpc_add_sefun.py crypt)。

sefun cat():缺文件时 write(0) 报错(2026-10-20,KB 06 §7.297)

void cat(string file) { write(read_file(file)); }:文件不存在(压缩包没带的欢迎/公告文件,或只有运行中的 mud 才会写的日志)时 read_file() 回 0,write(0) 在本驱动里报 Bad argument 1 to receive(),调用者(登录对话、cat 指令)被中断;老驱动什么也不打印。改成 write(read_file(file) || "");(scripts/lpc_fix_cat_unchecked.py)。改动:/adm/simul_efun/file.lpc。

翻页器 start_more(read_file(...)):文件缺失时 explode(0)(2026-10-20,KB 06 §7.300)

东方故事系的翻页器 void start_more(string msg) { ... explode(msg, "\n") ... } 被这样调用:start_more(read_file(HELP_DIR + "help/topics"))、start_more(read_file(NEWS))。压缩包没带的文件使 read_file() 回 0,explode(0, ...) 在本驱动里报 Bad argument 1 to explode()(不带参数的 help 指令就是这样出错的),老驱动把 0 当空文本。在 start_more 开头加了 if (!stringp(msg)) return;(scripts/lpc_fix_start_more.py)。改动:/feature/more.lpc。

存档里的乱码文件:restore_object() 报 Illegal file format(2026-10-20,KB 06 §7.316)

存档里有一批数据文件是乱码:开头是 ?adm、?inh、?clo、?obj(#/adm/… 的第一个字节被换成了 ?),后面是随机字节,没有任何驱动读得了。adm/daemons/chinesed 在预载时 restore() 读 data/e2c_dict.o,首次登录时起始房间的留言板 /clone/board/kedian_b 读 data/board/*_b.o;restore_object() 对不是文本的文件抛 Illegal file format,调用处的 catch()(KB 06 §7.255)保住了房间和精灵,但本库 master 的 error_handler() 把调用栈返回给驱动,每次启动和首次登录都在控制台打印一次或两次 错误讯息被拦截。现在共用的 feature/save.lpc 的 restore() 先读文件第一个字节,不是 # 就返回 0(读被 master 拒绝时 read_bytes() 返回 0,仍由 restore_object() 决定,和以前一样);留言板和字典从空开始,第一次 save() 写出合法文件,乱码文件原样留在树里。实测:启动和登录都没有报错,已保存的角色重新连线照常加载。

NPC 的 chat() 在 chat_msg 为空数组时下标越界(2026-10-21,KB 06 §7.334)

NPC 基类的 chat() 是 if (arrayp(msg = query(...))) { if (random(100) < chance) { rnd = random(sizeof(msg)); if (stringp(msg[rnd])) ...,不检查列表是否为空。本库现有的 NPC 文件里没有字面写成空 chat_msg 的(扫描只认 set("chat_msg", ({ })));守卫挡的是运行时才为空的列表和以后新增的 NPC。在 random(100) < chance 前面加 sizeof(msg) &&(scripts/lpc_fix_chat_empty.py;if (random(100) > chance) return 0; 的写法是 !sizeof(msg) ||)。改动:/inherit/char/npc.lpc(共 1 个文件)。

存档里的 .c 文件名改成 .lpc(2026-10-21,KB 03 §4.2 第 9 项)

存档里写着 "/d/city/npc/wei.c" 这样的文件名;extract 时源文件已经全部改成 .lpc,而驱动对带扩展名的路径只认字面,new() / load_object() / move() 都找不到它,调用方多半把失败当成「没有这个东西」悄悄跳过。本库 3 个存档共 6 处按 scripts/lpc_fix_save_dotc.py 改成 .lpc(只改 .lpc 存在而 .c 不存在的字符串;玩家存档、备份、留言板、巫师目录和文件索引缓存不动)。改动:/d/shaolin/npc/obj/kat.o(1 处)、/d/shaolin/obj/kat.o(1 处)、/d/shenlong/obj/lxlx.o(4 处)(共 3 个存档)。

巫师命令不带参数时对 0 调用 efun(2026-10-21,KB 06 §7.336)

巫师(管理员)把命令敲成不带参数时,驱动把 0 传给字符串参数;下面的命令第一句就把这个 0 交给 efun,运行时报 Bad argument 1 to ...,这条指令从来没有用过。用 scripts/lpcc_bare.py 不登录、在一次 lpcc 求值里以 0 调用 main() 逐个确认后,由 scripts/lpc_fix_bare_arg.py 在第一处使用参数的语句前加守卫:文件自带 int help(object me) 的,守卫是 if (!arg) return help(me);(help() 定义在 main() 之后的 1 个文件补了原型 int help(object me);,否则编译报 Undefined function help)。改动(1 个文件,括号里是接住 0 的 efun):/cmds/wiz/test.lpc(present)。加载检查 HEAD 与工作树对照,没有退步。

blockade / recover:data/keep 不存在,解封之后世界仍然锁着(2026-10-21,KB 06 §7.340)

管理员扫描里 blockade 报 Error copying file from /data/user/f/fluffos.o to /data/keep/fluffos, Error: No such file or directory,随后的 recover 在第一个 cp() 上死于 */data/keep/fluffos: lstat failed,来不及执行最后的 LOGIN_D->set_madlock(0):封锁解不开。data/keep/ 是运行时目录,git 克隆里没有空目录。blockade 在第一次写之前 if (file_size("/data/keep") != -2) mkdir("/data/keep");(mkdir() 失败答 0,不抛错),recover 对没有保存档的玩家 if (file_size(src) < 0) continue;,其余照常恢复、照常解锁(scripts/lpc_fix_keep_dir.py,hy5 上实测:没有保存档时 recover 答“时空解封完毕”,blockade 建出 data/keep 并保存,recover 取回并删除)。改动(2 个文件):/cmds/adm/recover.lpc、/cmds/arch/blockade.lpc。

注册到不存在函数的 add_action(2026-10-21,KB 06 §7.349)

add_action("函数", "动词") 绑定的函数,在对象本身、它包含的头文件、它继承的父类和继承它的子类里都没有定义(scripts/lpc_scan_callout_targets.py 静态解析,子类用传递闭包复核,scripts/lpc_verify_handlers.py 运行时 function_exists() 复核)。驱动从玩家最新的一条动作往回找,遇到这样的句子就报 *Function for verb 'drop' not found.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 15 条注册(15 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/mingjiao/npc/changjinpeng.lpc(do_join)、/d/mingjiao/npc/lengqian.lpc(do_join)、/d/mingjiao/npc/pengyingyu.lpc(do_join)、/d/mingjiao/npc/shuobude.lpc(do_join)、/d/mingjiao/npc/tangyang.lpc(do_join)、/d/mingjiao/npc/wensong.lpc(do_join)、/d/mingjiao/npc/yanyuan.lpc(do_join)、/d/mingjiao/npc/yinwufu.lpc(do_join)、/d/mingjiao/npc/yinwulu.lpc(do_join)、/d/mingjiao/npc/yinwushou.lpc(do_join) 等共 15 个文件。最多的函数名:do_join 13 处、do_list 1 处、do_qiecuo 1 处。

对象路径里的 ..:new(__DIR__ "../obj/x") 报 Read access denied(2026-10-22,KB 06 §7.351)

__DIR__ 是当前文件所在目录(带结尾斜杠),__DIR__ "../obj/x" 就是 /d/zhiye/npc/../obj/x;驱动的 legal_path() 不接受带 .. 的路径,new() / load_object() 直接抛 *Read access denied.(实测,find_object() 对它答 0)。走库自己的 carry_object() / 房间 objects 表的路径,库的基类会先规范化,能用;直接把路径交给 new() / clone_object() / load_object() / move() 的不行,调用到那一步才报错。改成规范化后的绝对路径(scripts/lpc_fix_dotdot_dir.py --direct):/d/wudang/npc/xiaocui.lpc(__DIR__ "../obj/nuerxiang" → "/d/wudang/obj/nuerxiang")。

汉字按字节切片的老写法没有换成按字符:改法名 "澄" + name[2..3]、辈分判断 name[0..1] == "澄" 等 21 处(2026-10-22,KB 06 §7.360)

原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字);各派掌门的辈分判断(name[0..1] == "澄")永远不成立。

本库 21 处(13 个文件):拼名字的赋值 13 处、与汉字常量比较 8 处。例:name[0..1] → name[0..0](/kungfu/class/emei/kong.lpc:134);name[2..3] → name[1..1](/kungfu/class/shaolin/cheng.h:157);name[0..1] == "慧" → name[0..0] == "慧"(/kungfu/class/shaolin/cheng.h:140)。scripts/lpc_fix_gbk_slice.py 把下标按字符重算(社区的 UTF-8 版 xkx100utf8 里就是同样的写法,作对照);没有改的:截断(name = name[0..7],现在最多 8 个字,只是放宽)、ASCII 的区间(fname[0..3] == "room"、ctime(time())[4..19])和上下文看不出是汉字的区间。证明(scripts/lpcc_eval.py,bixiecanyang;其余各库是同一份文本):让澄字辈掌门 attempt_apprentice() 一个名叫「张三」的访客,HEAD 的法名是 慧,改后是 慧三;抽样 28 个库的 HEAD / 工作树加载对比 0 退步。工具不幂等([0..3] 折半得 [0..1],又是对齐区间),改过的文件记在 scripts/gbk_slice_converted.txt,不要重跑。

黑木崖脚下的老者:init() 里留着调试语句,每个走进 /d/heimuya/road1 的人都触发运行时错误(2026-10-22,KB 06 §7.361)

d/heimuya/npc/laozhe.lpc 的 init() 在 ::init() 之后有 what = new(__DIR__ "obj/yancao"); sprintf("%s aaa\n", what);:调试遗留,%s 收字符串或数字、不收对象,于是 (s)printf(): Incorrect argument to type %s. (arg: 0) 每次有人进入乱石坡都抛一次,后面的 call_out("greeting", ...) 永远不执行,老者不再搭话(还每次白克隆一个烟草)。两行都注释掉(同系的 haiyang2 / hy / hy5 / cctx / fqyy2 早就注释了)。证明:scripts/lpc_reach_census.py xiakexing2017 --load --walk(一个普通 NPC 访客走进每个加载成功的房间):HEAD 336/337,改后 337/337。

长度检查按字节写,旁边的提示按汉字写:2 处(2026-10-22,KB 06 §7.362)

老档的 strlen() 数字节(一个汉字 2 字节),转成 UTF-8 后它数字符:提示写「必须是 2 到 6 个中文字」的检查还是 strlen(name) < 4 || strlen(name) > 12,于是两三个字的房名、帮会名、群名、聊天室名、称号被提示自己说允许的那句话拒绝,上限却是提示的两倍;留言板的标题检查 i < 4 || i > 24 || i % 2 还会拒绝所有奇数个字的标题(post 你好吗)。本库 2 处(1 个文件):if ((strlen(name) < 2) || (strlen(name) > 12)) {(/adm/daemons/logind.lpc:518)。scripts/lpc_fix_strlen_gate.py 以提示为准:条件里每个 strlen 门槛都恰是提示所说数字的两倍才折半,存长度的变量(i = strlen(arg);)的门槛和 || i % 2 奇偶项、其后循环的 i % 2 == 0 && 一并处理;提示里有英文 / 字母 / 字节的不动。证明(scripts/lpcc_eval.py,bxsj 的留言板,求值对象继承 NPC 并重写 edit() 记录调用,command("post 标题")):HEAD 拒绝「你好」和「你好吗」、放行 23 个字的标题;改后接受前两个、拒绝第三个;shenmo 的 named.lpc invalid_new_name("张三"):HEAD 回 名字只能是2-4个中文字。,改后回 0。抽样 24 + 15 个库的 HEAD / 工作树加载对比 0 退步。

按字节写的汉字循环、茶馆猜诗和同盟频道标题(2026-10-23,KB 06 §7.363)

老档按字节做「一个汉字」:while (i--) if (i % 2 == 0 && !is_chinese(name[i..<0])) 只检查偶数下标,奇数位上的拉丁字母(张a)会过;茶馆猜诗 mixup() 用 strlen(str)/2 再 j=j+j、str[k..k+1] 交换两字节;泥潭系 channeld 的 switch (strlen(fname)) case 4/6/8/10 和 strlen(family)==4/6 按字节长度给同盟 / 门派名加空格。本驱动的 strlen() 和区间都按字符算。scripts/lpc_fix_byte_char.py:去掉奇偶项、把区间改成 name[i..i],按字符重写 mixup() 并把 strlen(first)>=14 折成 7,频道标题的 case / 门派长度改成字符数(对照 aoxiangtianji)。本库 汉字循环 / is_chinese 区间 1 个文件。证明(scripts/lpcc_eval.py):sjplii 的 check_legal_name 在 HEAD 接受「张a」、改后拒绝,两边都接受「张三」;xkx100 的 clubpoem->mixup("离离原上草") 得到「离离原草上」(真交换汉字);nitan_san 两字同盟名得到 【 兄 弟 】。关键对象加载通过。

血条 / 表情标题按字节算显示宽度(2026-10-23,KB 06 §7.365)

老档把汉字当两字节:bar_string[0..(val * N / max) * 2 - 1] 把「格数」换成字节下标,半格血条在本驱动(按字符算区间)上会整条画满;巫师表情 edemote 的 repeat_string("─", 13 - (strlen(emotion) / 2)) 也会少减汉字宽度。scripts/lpc_fix_display_pad.py:去掉 CJK 血条 / 空白条上的 * 2(jings/qis 赋值和 blank_string3[0..(23-a)] 的 23→11 一并改),表情标题去掉 / 2;ASCII 空格的 none_string / empty_string 仍保留 * 2(一格两个空格)。本库 4 处(2 个文件)。证明(lpcc_eval.py hhsj):tribar_graph(6,9,12) 在 HEAD 画出整条 12 个■,改后为 6 个■+3 个□;麻将 strlen(tempB)/2 是两字符牌码,未改。

doc/build devil 丢分号/粘连 inherit(2026-10-27,KB 04 §6.25 Also)

doc/build/devil.lpc(建档示例倭寇)被格式化弄坏:inherit NPC void create() 粘在一起,语句之间的 ; 全丢,还有嵌套坏掉的 set("con", set("con", 10)。按原意重写成带 setup() 的完好 NPC(原 /obj/blade、/obj/cloth 在多数库不存在,示例里去掉携带以免 create 炸)。证明:HEAD 加载失败;改后加载成功。