BetterMapFix
[AI-generated] Patch for BetterMap: pins mistlands vegvisir stones, adds missing dungeon entrances (infested mines, morgen holes, hildir crypts), renames pin labels to Chinese. Client-only. Read the applicability notice first.Changelog
首次公开发布 = 1.0.2。下面的 1.0.0 / 1.0.1 是开发期迭代,没在 Thunderstore 上发布过 (列在这里是为了说明代码里那几处防护是怎么来的)。
[1.1.12] — 修 Ash Pot / Greydwarf Nest 两枚图钉显示英文(NameToken 与规则名单复数不匹配)
问题
灰烬之地的红陶罐图钉在地图上显示英文 Ash Pot(用户报告:"map 里标记的 ash pot 是什么")。
根因(不是漏翻,是键名单复数不匹配)
PinNames.For(Rule, GameObject) 的取名优先级(IL 实证):
① rule.NameToken 非空 ⇒ 返回它 ← Ash Pots 走的就是这条,返回 "Ash Pot"
② 否则 For(go):Piece / Pickable / MineRock 的名字
③ 兜底返回 rule.Name(英文)
上游有两条规则的 NameToken 是裸英文(不是 $ 本地化键,游戏不翻),
且与规则名差一个字母(规则名复数、钉名单数):
| 规则名(= cfg 键名) | NameToken(= 图钉名) | 旧内置表键 | 结果 |
|---|---|---|---|
Ash Pots |
Ash Pot |
Ash Pots=灰烬陶罐 |
❌ 命不中 |
Greydwarf Nests |
Greydwarf Nest |
Greydwarf Nests=灰矮人巢穴 |
❌ 命不中 |
而替换是整串精确匹配 ⇒ 只写复数规则名,单数钉名永远命中不了。
(Giant Remains / Leviathan 两组两种写法都齐,所以没事。)
改动
- 内置表补两条:
Ash Pot=灰烬陶罐、Greydwarf Nest=灰矮人巢穴(69 → 71 条) - README 的条数说明与「NameToken 变体」⚠️ 注记同步更新
- 源码里补了注释,说明这两条为什么必须收(避免后来人当"重复条目"删掉)
备注
- 本机 cfg 的
Name Map不需要动 —— 内置表与用户表做并集,重启即生效。 - 已落地的旧图钉由
Rename Existing Pins那趟 2 秒低频重命名兜底改名。
[1.1.11] — 改回 Giant Remains=巨人遗骸(跨工程交叉评审发现的错)
问题
由 EasyMap 工程(BetterMap + 本补丁成果的内联版)评审时发现:
Giant Remains=软组织 ← 错
上游 Giant Remains 规则管的是骨与盔甲武器,它自己的 Description 就写着
(IL 实证,原文照抄):
The bones and armour of the fallen giants. Black marble, and iron and copper scrap.
Prefabs = giant_ribs, giant_helmet1, giant_helmet2, giant_sword1, giant_sword2。
而软组织是另一个东西(giant_brain,piece_giant_brain = Soft Tissue),
1.1.9 起已由本补丁单列一条 Soft Tissue 规则 + Soft Tissue=软组织 映射。
⇒ 同一个「软组织」挂在两条语义不同的规则上,是矛盾的。
为什么会出现(不是"笔误",是遗留物)
- 1.1.8:用户要求把当时的显示名改成「软组织」。那时上游这条规则被本补丁劫持成
只认
giant_brain⇒ 标「软组织」在当时是对的。 - 1.1.9:改成「关上游 + 新增
Soft Tissue规则」,但把这条映射留在了内置表里。 当时的理由是"地图上已有的软组织钉存的m_name就是Giant Remains,删了会变回英文"。 - ⚠️ 这个理由只对发布者那一台机器的存量图钉成立,不该写进发布默认值。
任何没关掉上游
Giant Remains的用户(或将来重新打开的), 骨/剑图钉都会被错标成「软组织」。
改动
- 内置表:
Giant Remains=软组织→Giant Remains=巨人遗骸 - 同步修正本机 cfg 里那条 1.1.8 时期写下的陈旧值(
Name Map值 +# Default value:注释两处)
对已有图钉无影响
改名趟是就地覆写 pin.m_name(RenamePin: pin.m_name = zh),
所以之前被改名成中文「软组织」的那批钉,存的已经是中文;
改回默认值后查表查不到,不会再变。软组织仍由 Soft Tissue=软组织 负责。
与 EasyMap 的一致性
两个工程的 ChinesePins 表各有 71 条,本次修正后完全一致。
(EasyMap 那边本来就是对的,无需改动。)
[1.1.10] — 同样的思路也用在「女王洞穴入口」上:删掉 Skip Rules
[1.1.10] — 同样的思路也用在「女王洞穴入口」上:删掉 Skip Rules
背景
1.1.9 把「巨人遗骸」改成「上游 cfg 关掉 + 补丁新增规则」之后,Skip Rules 就成了最后一块
"补丁去拦截/删除" 的遗留物。它做三件事:
- 在
AutoPins.Place前拦掉落钉(RuleInjection.PlacePrefix); - 每 2 秒扫一遍地图,把该规则已有的图钉删掉(
ChineseLabels.SkipExisting); - 一个配置项
[3 - Caves] Skip Rules指定要跳过的规则名。
而上游 cfg 里本来就有这个开关:
[Auto Pins - Mistlands] Entrance to the Queen。⇒ 照 1.1.9 的模式简化掉。
改动
- 上游开关关掉(在上游自己的 cfg 里):
[Auto Pins - Mistlands] Entrance to the Queen = false - 删掉
Skip Rules整套:SkipRules配置项、RuleInjection.PlacePrefix、BetterMapFixPlugin.SkipRules(含启动日志那行)、ChineseLabels.SkipExisting()、_skippedTotal、以及AutoPins.Place上那条 prefix 挂载。
DLL 43,008 → 40,960 字节。
保留
映射 Entrance to the Queen=女王巢穴入口 —— 万一将来重新打开上游那个开关,
图钉名仍是中文。
现在的整体形态
补丁只做加法:
| 补丁做的事 | 说明 |
|---|---|
| 注入 6 条上游没有的规则 | 半埋墓穴 / 希尔迪 ×3 / [Mistlands] Vegvisirs / 软组织 |
| 图钉名中文化 | 上游的规则名是普通英文串(不是 $ 键),游戏不会翻 |
| 指引石兜底扫描 | 挂在它自己的 Sweep 之后 |
不再修改上游规则、不再删除任何图钉。
[1.1.9] — 换思路重做「巨人遗骸」:关上游 + 新增一条规则(删掉全部拦截机器)
[1.1.9] — 换思路重做「巨人遗骸」:关上游 + 新增一条规则(删掉全部拦截机器)
用户重新梳理后的诉求(本版的依据)
- 不想要上游标记的巨人遗骸(上古之剑、上古盔甲、骨…)—— 如果上游配置文件里能直接关,就不需要补丁来拦截、删除;
- 软组织上游没有标记 ⇒ 补丁该做的是新增一条规则帮它钉住软组织。
之前的做法(1.1.4~1.1.8):5 个活动部件,全是绕路
| 部件 | 干什么 |
|---|---|
Rule Prefab Overrides |
劫持上游 Giant Remains 的 Prefabs,5 个骨剑换成 giant_brain |
Drop Existing Pins For Overridden Rules |
按名字删旧图钉(85+ 枚) |
| 一次性清理守卫 | 防止每 2 秒重删把新钉的也删掉 |
PinRecord.Wipe() |
清掉「已钉过」记录(否则同位置的新规则钉不出来) |
ForceResweep() |
清节流让上游立刻重扫 |
这 5 个部件在 1.1.4→1.1.8 期间一共引出 4 个 bug(钉不出来 / 钉了又删 / 来不及重扫 / 译名不生效)。
本版做法:2 个部件,不碰上游规则
-
上游开关关掉(在上游自己的 cfg 里):
[Auto Pins - Mistlands] Giant Remains = false上游就不再钉骨/剑了。零补丁代码,而且用户随时能改回来。
-
本补丁新增一条规则(用已有的注入机制,和 5 条洞穴规则同一套):
Name = Soft Tissue Prefabs = [giant_brain] Biome = Mistlands Category = Ore DefaultOn = 1 IsLocation = 0- 映射表
Soft Tissue=软组织。
- 映射表
🔑 为什么这样能"零清理"
反汇编 PinRecord.Has(category, position, within)(IL 实证):
ldfld entry.Category
ldarg.0 ; 查询用的 category
bne.un → 跳过 ; 分类不同就跳过
去重记录是带分类比较的。 所以新规则只要沿用同一分类 Ore:
- 老位置(原来骨/剑图钉占过的)→ 记录还在 → 新规则不重复钉; 但那儿本来就有图钉(且显示「软组织」)⇒ 无需删、也不会重复
- 新位置 → 没记录 → 新规则正常落钉 ✅
⇒ 不用删图钉、不用清记录、不用强制重扫。
删掉的代码
Rule Prefab Overrides、Drop Existing Pins For Overridden Rules、
ApplyPrefabOverrides()、_overriddenRuleNames、_overrideCleanupDone、
PinRecordWipe()、ForceResweep()、MPinRecordWipe、FNextSweep —— 约 150 行。
DLL 46,080 → 43,008 字节。
保留的
- 映射
Giant Remains=软组织—— 地图上已有的那批软组织图钉存的m_name就是Giant Remains(当时的规则名),删了它们会变回英文。 Skip Rules(女王洞穴入口)—— 那是另一件事,本次不动。 (不过同样的思路也适用于它:上游 cfg 里本来就有[Auto Pins - Mistlands] Entrance to the Queen,将来想简化也可以照这个模式来。)
[1.1.8] — 「巨人遗骸」改名「软组织」
[1.1.8] — 「巨人遗骸」改名「软组织」
背景
1.1.7 之后软组织正常钉出来了,但图钉名显示「巨人遗骸」—— 那是规则名
(Giant Remains → 映射表译名),而这条规则现在只认软组织,标签该跟着变。
改动
- 内置映射表:
Giant Remains=巨人遗骸→Giant Remains=软组织。 Rule Prefab Overrides的说明文字改用英文规则名(Giant Remains)指代, 不再用中文译名 —— 译名会变,拿它解释配置容易再次对不上。
⚠️ 重要:老用户必须手动改 cfg,否则不生效
Name Map 是可配置项,而 BepInEx 的 cfg 里已存在的键不会用新默认值覆盖。
实测你这台机器的 cfg 里存着 1.1.0 时期写下的旧值:
Name Map = …Giant Remains=巨人遗骸…
它会盖过内置表,所以光升级 DLL 名字不会变。两处要一起改:
- 第 85 行
Name Map = …里的那条(真正生效的那处); - 第 84 行
# Default value: …的注释(只是展示用,顺手保持一致)。
(1.1.8 的交付已替你改好,并留了 bettermapfix.client.only.cfg.bak-<时间戳> 备份。)
已有图钉会一起改名
改名趟是按 PinData.m_name 查表的,而旧图钉存的 m_name 就是 Giant Remains
⇒ 下一轮扫描会自动改成「软组织」,不用删了重钉。
[1.1.7] — 清完去重记录后立刻重扫;加 ZNetScene 名字核查诊断
[1.1.7] — 清完去重记录后立刻重扫;加 ZNetScene 名字核查诊断
背景:1.1.6 的测试时间上不够,不能算软组织失败
1.1.6 那次跑的日志时间线:
12:53:50 世界加载(覆写生效)
12:54:48 玩家在移动
12:55:05 删除旧图钉 + PinRecord.wiped ← 清理实际发生在这一刻
12:55:08 Stopping build thread
12:55:11 Net scene destroyed(游戏关闭)
清理后只过了 6 秒就关游戏了 —— BetterMap 的 AutoPins.Tick 有节流
(if (Time.time < _nextSweep) return;),来不及重新落钉。
所以"软组织没钉出来"这次没有得到有效验证。
本版改动
- 清完
PinRecord后主动触发一次重扫:把AutoPins._nextSweep清 0, 让 BetterMap 下一次Tick立刻扫一遍,不用干等节流间隔。 (⚠️ 不直接Invoke它的Sweep()——Tick里在它之前还有Build()和三个索引的非空检查, 走它自己的路径最稳。) - 新增诊断
[9 - Debug] Dump ZNet Names(默认giant):把ZNetScene里名字含关键词的 已注册预制体与世界里活着的实例打进日志,并单独核查配置里点名的名字是否注册。 回答两个问题:- 规则里写的预制体名到底注册没有(没注册 ⇒ 规则永远匹配不到,属于"静默失效");
- 世界里那个东西的实际名字是什么。
[1.1.6] — 修「新钉出来的软组织立刻又被删」;Leviathan 真名查到了
[1.1.6] — 修「新钉出来的软组织立刻又被删」;Leviathan 真名查到了
用户实测反馈(1.1.5)
① 软组织被标记之后立刻又被删掉;② Leviathan 那边仍不中文。
① 死循环根因:清理是每 2 秒重复的,而目标规则还在继续落钉
真机日志(同一位置出现两次删除):
已删除图钉 «巨人遗骸»(位置 (-5949.3, 28.4, -2799.9),剩 355 枚)。
[BetterMap] PinRecord: wiped
BetterMapFix: 已清空 BetterMap 的「已钉过」记录(PinRecord.wiped)
已删除图钉 «巨人遗骸»(位置 (-5949.3, 28.4, -2799.9),剩 354 枚)。 ← 新钉出来的又被删
链条是:删旧钉 → PinRecord.Wipe() → BetterMap 按新规则重新钉出 giant_brain
→ 但它的图钉名仍是规则名 Giant Remains(⇒ 翻成「巨人遗骸」)
→ 名字跟旧的一模一样,又被那趟清理删掉,循环往复。
修法:覆写规则的清理改成每会话只做一次(_overrideCleanupDone 标记)。
清掉"改规则之前留下的"那些就够,之后新落的一律放过。
(Skip Rules 的删除不受影响 —— 那些规则永远不会落钉,重复删也无害。)
② Leviathan 的真名:诊断开关一次就查到了
日志实证:
[诊断] 未翻译的图钉名 #21: "Leviathan"
是单数 Leviathan,不是规则名 Leviathans(之前只映射了复数版,所以永远命不中)。
它来自生物通道:CreatureTracker 走 Localization.Localize(Character.m_name),
而 m_name 是普通字符串 "Leviathan"(不是 $ 键)⇒ Localize 原样返回 ⇒ 地图上显示英文。
修法:映射表补上 Leviathan=甲壳素(保留 Leviathans=甲壳素,两条都留)。
顺带收紧了诊断过滤器:原来把 $item_copperore / $enemy_dragon 这类本地化键
也记了,实测 60 个名额大半被它们占掉 —— 而那些键 Minimap.PinNameData.SetTextAndGameObject
里游戏自己会 Localization.Localize,根本不用我们管。现在只记纯 ASCII 的英文名。
[1.1.5] — 修「软组织钉不出来」;加未翻译图钉名诊断
[1.1.5] — 修「软组织钉不出来」;加未翻译图钉名诊断
用户实测反馈(1.1.4)
① 覆写生效了、旧图钉也删掉了(日志实证:覆写 1 行、删除 85 枚),但软组织没被钉出来; ② Leviathan(甲壳素)那枚还是没翻成中文。
① 软组织钉不出来的根因:PinRecord 去重记录没清
反汇编 PinRecord(IL 实证):
| 方法 | 行为 |
|---|---|
Load() |
_loaded == false 时从 Player.m_customData["BetterMap.pinned"] 读回记录 |
Forget() |
只清内存缓存、不落盘 ⇒ 下次 Load() 又把数据读回来 |
Wipe() |
清空并 Flush() 落盘,打 PinRecord: wiped |
1.1.4 只删了 Minimap 上的图钉,没清去重记录 ⇒ 那些位置仍算"已钉过",
于是盯着同一位置的 giant_brain 被 dedup 挡掉,永远钉不出来。
(日志里没有 PinRecord: wiped,证实从没被清过。)
修法:删掉"被覆写规则"的旧图钉后,调一次 PinRecord.Wipe()(每会话只做一次)。
副作用:BetterMap 会按当前规则重新评估一遍该钉什么 —— 这正是想要的效果。
② Leviathan 名字对不上:静态分析查不到,改用真机诊断
已排除的可能(都查过):
CreatureTracker.Describe的名字是Named ? (given 为空 ? Localize(Character.m_name) : given) : nullCreatureTracker.Add用Named ? Label : ""作为图钉名- 本地化表里没有
enemy_leviathan(只有成就项ach_grindleviathan) - UnityPy 读
Leviathan预制体:7 个组件里一个字符串都没有 ⇒ 看不到Character.m_name
⇒ 加诊断开关 [4 - Labels] Log Unknown Pin Names(默认开):
把映射表没命中、且看起来需要翻译(纯 ASCII 或以 $ 开头)的图钉名打进日志,
去重、最多 60 个、满就停。看到那个英文名是什么,加进 Name Map 就能翻。
[1.1.4] — 「巨人遗骸」改为只认软组织
[1.1.4] — 「巨人遗骸」改为只认软组织
需求
用户:BetterMap 自带的巨人遗骸会识别很多种要素(上古之剑、上古护甲、肋骨…),只想让它认软组织。
根因(IL + 资产表 + 本地化表三重核对)
上游 Giant Remains 规则盯着 5 个 prefab:
| prefab | 游戏内名称 |
|---|---|
giant_ribs |
肋骨(本地化表无 piece_giant_ribs) |
giant_helmet1 / giant_helmet2 |
上古护甲 |
giant_sword1 / giant_sword2 |
上古之剑 |
而 giant_brain(= 软组织)在整个 BetterMap 1.0.5 的 DLL 里一次都没出现 ——
上游根本没识别它。⇒ 所以这不是"过滤掉别的",而是把整份列表换成 giant_brain。
(附带确认:这 4 个预制体的组件结构完全一样,都没有 Piece、没有任何 $ 串
⇒ PinNames.For(go) 返回 null ⇒ 图钉名就是规则名 Giant Remains,
映射表里已有 Giant Remains=巨人遗骸,不用改映射。)
本版改动
- 新增
[5 - Rules] Rule Prefab Overrides(默认Giant Remains=giant_brain): 覆写某条规则盯哪些预制体。格式规则名=prefab1,prefab2,多条用;隔开。 只改Prefabs,不动规则名 ⇒ BetterMap 的 cfg 键名不变,开关还在原地。 - 新增
[5 - Rules] Drop Existing Pins For Overridden Rules(默认开): 被覆写过的规则,地图上已有的旧图钉按规则名(含中文译名)删掉;重访后按新规则重新落钉。 (改规则只影响新落钉,旧的那些得单独清。) SkipExisting的限流门拆开:清理不再被Rename Existing Pins开关挡住 ("改名"和"清理"是两件事),且清理排在改名之后(删除集合同时认英文与中文译名)。- 通用性:这套覆写机制不限于巨人遗骸,以后想改任何规则的盯捕范围都直接改配置。
[1.1.3] — 适配 BetterMap 1.0.5;去掉已被上游覆盖的两条注入
[1.1.3] — 适配 BetterMap 1.0.5;去掉已被上游覆盖的两条注入
上游 1.0.5 做了什么(IL 实证,不是看 CHANGELOG 猜的)
拆 PinRules..cctor 重建规则表,并用已知良好的 1.0.4 表做归一化逐条校验后才下结论:
规则数 73 → 75,只多两条。
| 新规则 | prefabs | Biome | Category | DefOn | IsLoc | NameToken |
|---|---|---|---|---|---|---|
Infested Mines |
Mistlands_DvergrTownEntrance1/2 |
Mistlands | Dungeon | 1 | 1 | 无 |
Putrid Holes |
MorgenHole1/2/3 |
AshLands | Dungeon | 1 | 1 | $location_morgenhole |
另修了三项:没有指引石的遗迹不再出指引石图钉(AutoPins.Inside 组件扫描改了)、
图钉图标不再与 TargetPortal 等自带图标模组冲突、同会话二次读世界不再反复报错。
本版改动
- 删除两条注入:
Infested Mines、Morgen Holes—— 上游 1.0.5 已提供 (后者改叫Putrid Holes)。依赖同步升到xtavim-BetterMap-1.0.5, 装不上 1.0.5 就不会缺这两条。 - 🔴 新增第二道判重:比 prefab 集合。 原来只比「规则名 + 群系」,
上游把规则改名(
Morgen Holes→Putrid Holes)就认不出来,会注入出 第二条同 prefab 的规则 ⇒ cfg 里两个开关管同一批位置。现在只要上游已有规则 盯着同一批 prefab,不管叫什么名字都算已覆盖、跳过。 - 映射表调整:
Infested Mines=腐疫矿区保留 —— 上游那条没有 NameToken,图钉名还是英文规则名;Morgen Holes=腐朽之穴删除 —— 上游没有这个名字的规则了,留着永远命不中;Putrid Holes=腐朽之穴新增(保险)—— 上游那条有 NameToken,游戏自己会翻。
- 兼容性:本补丁挂载的 6 个类型 / 38 个方法签名与 1.0.5 零差异,不存在挂失败风险。
仍然需要本补丁的(上游 1.0.5 依旧没有)
Half Burried Crypt、Hildir Cave / Hildir Crypt / Hildir Fortress、
迷雾之地的 Vegvisirs(上游 7 条 Vegvisirs 仍无 Mistlands)、
Entrance to the Queen 的跳过、以及整套图钉名中文化。
[1.1.2] — 修「非规则路径的图钉名不中文」;女王洞穴入口不再自动标记
[1.1.2] — 修「非规则路径的图钉名不中文」;女王洞穴入口不再自动标记
修复
-
🔴
Leviathans(以及所有非规则路径落的钉)名字没被翻成中文。 用户实测 1.1.1 后反馈"leviathan 没有被正确显示中文"。根因(全量反汇编 BetterMap 1.0.4 才发现):
Minimap.AddPin一共有 6 个调用方 ——AutoPins.Place/CreatureTracker.Add/NamedPins.Retag/DeathMarkers.Rebuild/VehicleTracker.Reconcile/AutoPins.Portal。 而其中只有AutoPins.Place走PinNames.For取名,其余五条各自算名字 (生物走Character.m_name、传送门/死亡标记走自己的逻辑)。 1.1.1 只把替换挂在PinNames.For上 ⇒ 这五条路径一个都没覆盖到。修法:在游戏侧
Minimap.AddPin上再兜一层 postfix,对刚落的那枚图钉做同一张表的替换。 与走哪条落钉路径无关,六条全盖住。原有的PinNames.Forpostfix 保留作双保险。 -
旧图钉改名趟的挂载点不可靠。 原来挂在
Minimap.UpdatePins上,但 IL 实证Minimap.Update()里是if (m_pinUpdateRequired) { m_pinUpdateRequired = false; UpdatePins(); }⇒ 只在校验位置位时才跑,不是每帧。改挂每帧都跑的Minimap.Update(内部仍 2 秒限频)。 (这就是"地图上已有的英文图钉没被改掉"的原因。)
变更
Leviathans的译名按你的要求改为「甲壳素」(原为「利维坦」)。- 新增配置
[3 - Caves] Skip Rules(默认Entrance to the Queen): 不自动落钉的规则名,逗号分隔,填 BetterMap cfg 里的规则名(键名)。 默认填女王洞穴入口 —— 按游戏流程,迷雾之地的女王巢穴是由女王指引石标记的, 不该再额外出一枚入口图钉(会变成双重标记)。 · 只挡新落钉(挂在AutoPins.Place前面,那是它所有规则落钉的唯一入口); · 地图上已有的那枚会被自动删掉,每删一枚打一条日志(同时认英文原名和中文译名, 避免"先被翻成中文就删不掉"); · 想恢复自动落钉就把这里清空。 · 🔴 我们的 prefix 整段 try/catch,出错时return true放行 —— 绝不因为补丁自己的异常把 BetterMap 的落钉搞停摆。
兼容性
- 与 1.1.1 相比只改了挂载点与内置译名表;依赖仍为
xtavim-BetterMap-1.0.4。
[1.1.1] — 图钉中文名全覆盖(69 条规则名)
[1.1.1] — 图钉中文名全覆盖(69 条规则名)
修复
-
有些图钉名仍是英文。 用户实测发现
Leviathans(海洋利维坦)没被翻成中文。根因(全部 IL 实证):图钉名由
PinNames.For(Rule, GameObject)决定,三个分支 —— ①rule.NameToken非空 ⇒ 返回本地化键(如$piece_vegvisir),游戏自己翻; ② 否则For(go):Piece.m_name/Pickable掉落物名 /MineRock掉落物名… 也都是本地化键; ③ 都拿不到 ⇒ 兜底返回rule.Name(英文)。 而AutoPins.Place里没有任何Localization调用 ⇒ 落到第 ③ 支的图钉就是英文。会落到第 ③ 支的有两类:
- 地点类规则(
IsLocation=true,落钉时go传null)⇒ 必然走兜底; - 预制体类规则里、那些没有 Piece / Pickable / MineRock 组件的预制体 ——
Leviathans正是这种。
修法:不再逐个判断哪些预制体缺组件,直接把 BetterMap 全部 69 条规则名一次覆盖。 匹配是整串精确匹配,多余条目永不被命中、无副作用;代价只是 cfg 里那串稍长(1353 字符)。
- 地点类规则(
-
内置映射表 36 → 69 条。新增的 33 条(按上一条的性质,其中地点类 5 条是"必然英文"):
规则名 中文 规则名 中文 Leviathans利维坦 Beehives蜂房 Sulfur Arches硫磺拱 Thistle蓟草 Frozen Trolls冰冻巨魔遗骸 Mushrooms蘑菇 Boar Runestones鬓猪符文石 Dandelions蒲公英 Draugr Runestones尸鬼符文石 Flint燧石 Greydwarf Runestones灰矮人符文石 Copper / Tin / Silver Deposits铜 / 锡 / 银矿源 Ash Pots灰烬陶罐 Obsidian Deposits黑曜石矿源 Unstable Lava Rock不稳定熔岩 Flametal焰金属 Vegvisirs导向符文石 Magecap法师帽菇 Greydwarf Nests灰矮人巢穴 Jotun Puffs巨人泡泡 Fuling Totems丑地精图腾 Smoke Puffs余烬泡泡 Raspberry / Blueberry / Cloudberry / Lingonberry Bushes覆盆子 / 蓝莓 / 云莓 / 越橘丛 Vineberries酒莓 Carrot / Turnip / Kale Seeds胡萝卜 / 芜菁 / 羽衣甘蓝种子 Snowballs雪球 译名优先取游戏自己的本地化表(
resources.assets的简中列),所以与你在游戏里看到的名词一致; 游戏里没有对应名词的才自拟(Sulfur Arches/Frozen Trolls/Ash Pots/Unstable Lava Rock/Leviathans)。注:
鬓猪是游戏官方简中的写法(enemy_boar),照抄是为了与游戏内一致 —— 不习惯可以自己改。 -
顺带修正了三处与游戏官方译名不一致的旧译法(之前是按字面直译的):
Copper/Silver/Obsidian Deposits由 矿脉/矿 → 矿源(游戏里叫Copper Deposit=铜矿源),Thistle蓟 → 蓟草,Fuling Totems地精图腾 → 丑地精图腾(item_goblintotem)。 其余 30 条原有译名一字未改(已用脚本逐条比对确认),你地图上已有的中文图钉不会变样。 -
配置项
Name Map的注释同步改写:原先给的"示例串"如今都成了内置项, 改成说明「内置表已覆盖全部 69 条规则名,正常不用动这里」+ 如何覆盖某条内置译名。
兼容性
- 与 1.1.0 相比只改了内置译名表与文案,注入规则、指引石链路、依赖声明均未动。
- 依赖仍为
xtavim-BetterMap-1.0.4。
[1.1.0] — 适配 BetterMap 1.0.4;补上腐疫矿区 / 腐朽之穴
背景:上游 BetterMap 在 1.0.4(2026-09-19 发布)里自己修好了「Boss 指引石不落钉」, 并补上了「冰霜洞穴」。本版据此重新核对了两边,把补丁收缩到上游仍然缺的那部分。
变更
-
依赖版本升到
xtavim-BetterMap-1.0.4。补丁同时兼容 1.0.2 / 1.0.3 / 1.0.4, 但依赖里写 1.0.2 会让 r2modman 把你已经升上去的 BetterMap 要求降回 1.0.2。 -
上游 1.0.4 的改动(逐条核对结果),规则表 diff 只有三处:
变化 细节 指引石机制 7 条 Vegvisirs规则由Prefabs=[Vegvisir_*](这条路天生走不通,指引石是 Location 预制体的子物体、没有自己的 ZDO)改为新字段Component=typeof(Vegvisir);新增AutoPins.Spawned(LocationProxy)/Inside(...),挂在 Location 生成事件上扫内部组件 —— 比本补丁「按 Exploration Radius 扫玩家附近」更彻底。冰霜洞穴 新增 Frost Caves(MountainCave02/ Mountain / Dungeon / 默认开),与本补丁 1.0.0 起注入的那条逐项同构。其他 Obsidian Deposits默认值 开 → 关。 -
上游仍然缺的(本次补上):
Infested Mines(迷雾之地 /Mistlands_DvergrTownEntrance1、Entrance2)—— 默认开。 官方简中译名「腐疫矿区」(location_dvergrtown= Infested Mine)。 ⚠️ 上游有一条Dvergr Town看着像它,实际是CharredTowerRuins1_dvergr+Biome=AshLands, 落钉要过群系过滤 ⇒ 永远命中不了迷雾之地的入口。Morgen Holes(灰烬之地 /MorgenHole1、2、3)—— 默认开,简中「腐朽之穴」。Hildir Fortress(平原 /Hildir_plainsfortress)—— 默认关,与另两个希尔迪地牢一致。
-
迷雾之地 Vegvisirs 改为「上游优先」:检测到
Rule.Component字段存在(1.0.4+)时, 本补丁也照上游的写法把这条规则填成Component=typeof(Vegvisir),交给它自己的新机制。 本补丁自己的扫描器保留作兜底:上游先落钉并写进PinRecord,本补丁随后被去重挡住, 不会产生重复图钉;万一上游没有接管这条规则,女王指引石仍然标得出来。 -
中文映射表新增
Infested Mines=腐疫矿区、Morgen Holes=腐朽之穴、Hildir Fortress=希尔迪平原要塞(共 36 条)。 -
修复:升级后新增的内置译名对老用户不生效(补上三条译名之后立刻验收发现)。 症状:腐疫矿区图钉能标出来了、但名字还是英文
Infested Mines。 根因:BepInEx 的ConfigEntry在 cfg 文件里已存在某个键时,不会用新的默认值覆盖文件里的旧值 —— cfg 里早就写着 1.0.3 那张 33 条的Name Map,所以 1.1.0 新增的三条根本没进内存 (日志里中文名映射已载入 33 条就是证据)。 修法:新增[4 - Labels] Merge Default Map(默认开)—— 内置默认表先铺一层,再用你的Name Map覆盖(你的条目优先),以后再加内置译名就不会重犯。- 想完全自己掌控映射表(只认你写的那张):关掉本项。
- 想删掉某条内置译名:在你的表里写
英文名=(值留空 = 显式删除)。 - 日志改为打出「内置 N 条 ∪ 用户表(覆盖 X / 用户新增 Y / 显式删除 Z)→ 生效 K 条」, 这类"静默不生效"的问题看数字比读代码快得多。
- 离线核验:
verify_merge_default_map.py7 组用例全通过(含"拿你当前 cfg 预演 → 36 条、旧 33 条全保留")。
-
新增交付前核验脚本(开发者侧)
verify_bettermapfix.py:机器核对 ① 映射表英文键能否命中上游字符串堆或本补丁注入的规则名(键名打错只会静默不生效); ② 注入的地点 prefab 是否真在游戏地点表里、群系是否一致、是否enable=True; ③ 规则名(会成为上游 cfg 的键)是否含 BepInEx 禁用字符; ④ 脚本内清单与实际 DLL 是否一致(防清单漂移)。 本版跑分:映射表 36/36 命中;9 个地点 prefab 全部存在且群系一致; 唯一 WARN 是已知的HalfBurried_ForestCrypt(m_enable=False,默认关)。
兼容性
- 逐项核对过上游两次改动:
Plugin.BindPinRules/AutoPins.Sweep|Build|Place(Rule, ZNetView, Vector3)/PinRecord.Has/PinNames.For(Rule, GameObject)/Rule原有 9 个字段 + 无参构造 —— 1.0.2 → 1.0.4 全部保留、签名未变,只多了一个Component。 所以接口自检在两种版本上都会通过,功能按版本自动选择通道。
[1.0.3]
仅文档 —— DLL 一字未改
- README 顶部新增中英双语「适用性声明 / Applicability Notice」:本补丁是在本机 (BepInEx 5.4.2350 / 游戏 1.0.15 / 30 个启用模组)那套环境下做出来的,下面描述的症状 有可能是我这套模组组合互相冲突造成的,并不一定是原模组本身的 bug,可能不适用于他人的环境; 如遇问题请先向原模组作者反馈。
manifest.json:description末尾加一句Read the applicability notice first., 并压缩原有措辞以适配 Thunderstore 的 256 字符上限(实测值)。- 补丁本体未重新编译:
BetterMapFix.dll29184 字节, sha256c51accfbd6b9dbec0b904cd1459283ba508c6b707c15efe9ca520025614595e1—— 与 1.0.2 完全一致,已装 1.0.2 的不需要换文件。
[1.0.2]
新增:图钉名字中文化
- 打开
[4 - Labels] Chinese Labels(默认开)后,地图上图钉的名字会显示中文 (洞穴、要塞、祭坛那类地点图钉)。做法是给原模组算图钉名的PinNames.For(Rule, GameObject)做一个精确整串替换 —— 不动rule.Name(那是它 cfg 的键名)、不动规则表、图标与去重记录。 Name Map(默认 33 条,英文=中文逗号分隔)可自行增删;键名照抄 BetterMap cfg 里的键即可, 想连资源图钉也中文就往后面加(配置注释里给了现成一串)。Rename Existing Pins(默认开):已经放在地图上的旧图钉也一并改名。 名字是落钉那一刻写进地图数据的,所以单独跑一趟低频重命名(2 秒一次), 只动名字精确命中映射表的图钉,直接改显示文本、不重建 UI 元素。- 交付前校验脚本新增「映射表英文键必须存在于 BetterMap.dll」检查(33/33 通过)—— 键名打错一个字母只会静默不生效,必须机器核。
修正
Half Burried Crypt默认值改为关:实测游戏地点表里HalfBurried_ForestCrypt的m_enable = False,原版永远不会放置它,这条规则放着也不会落钉。
[1.0.1]
修复(重要 —— 1.0.0 会让 BetterMap 的自动标记整体停摆)
- 规则名里的撇号踩了 BepInEx 的 cfg 键字符限制。
Hildir's Cave/Hildir's Crypt里的'属于 BepInEx 禁用字符 (ConfigDefinition.CheckInvalidConfigChars:= \n \t \ " ' [ ]), 原模组把rule.Name直接当 cfg 键用 ⇒Config.Bind抛ArgumentException。 又因为 Harmony 的 prefix 抛异常会让原方法整体不执行,AutoPins.Build每轮都在开头炸掉, 连带 BetterMap 自己的资源标记全部失效(日志里每 10 秒一条ArgumentException)。- 规则改名为
Hildir Cave/Hildir Crypt(去掉撇号)。 - 新增注入前名字校验:含禁用字符的规则直接跳过并报错,绝不让它进原模组的规则表。
- 规则改名为
- 补上异常熔断:两条 prefix 全程
try/catch,逐条BindRule也各自try/catch—— 以后任何意外都只记一条日志,绝不会再波及原模组的Build。
说明
- 因为 BepInEx 的补丁是内存里的,1.0.0 造成的停摆需要重启游戏才会随 1.0.1 一起恢复。
[1.0.0]
首个版本,针对 BetterMap 1.0.2。
修复
-
Boss 指引石从不被标记。根因:BetterMap 的自动标记只遍历
ZNetScene.m_instances(只有网络对象 ZDO),而指引石是 Location 预制体的子物体、没有 ZDO,也不在 ZNetScene 的名字表里(GetPrefab返回 null ⇒IsPrefabZDOValid永远不过)。7 种 Boss 指引石全是这个下场。 做法:挂在 BetterMap 自己的AutoPins.Sweep之后补扫玩家附近的Vegvisir组件, 再调用它自己的AutoPins.Place(rule, null, pos)落钉 —— 类别 / 图标 / 标签 / 去重记账 全部沿用原模组,连"删掉就不再出现"都一致。 -
洞穴条目不全。原模组只在黑森林补了墓穴 / 巨魔洞 / 熊洞,山脉段没有洞穴条目。 做法:在
Plugin.BindPinRules之前往PinRules.All注入规则,剩下的交给原模组 (建 cfg 开关、进图例、进_byLocation地点表、去重)。
新增
- 规则
Frost Caves(MountainCave02,山脉)—— 冰霜洞穴入口。 - 规则
Half Burried Crypt(HalfBurried_ForestCrypt,黑森林)。 - 规则
Hildir's Cave(Hildir_cave,山脉)、Hildir's Crypt(Hildir_crypt,黑森林)—— 默认关。 - 规则
Vegvisirs(Vegvisir_SeekerQueen,迷雾之地)—— 原模组每段都有 Vegvisirs,唯独这段没有。 - 调试项:
Dump Location Names(整表打印游戏地点名,用于核对)、Verbose Pin Log(逐钉日志)。


