Valheim
Install with App

Details

Latest version
1.1.12
Last Updated
First Uploaded
Downloads
514
Likes
1
Size
47KB
Dependants
ADDatHost Valheim hosting
€1

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_brainpiece_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_nameRenamePin: 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 就成了最后一块 "补丁去拦截/删除" 的遗留物。它做三件事:

  1. AutoPins.Place 前拦掉落钉(RuleInjection.PlacePrefix);
  2. 每 2 秒扫一遍地图,把该规则已有的图钉删掉(ChineseLabels.SkipExisting);
  3. 一个配置项 [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.PlacePrefixBetterMapFixPlugin.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. 不想要上游标记的巨人遗骸(上古之剑、上古盔甲、骨…)—— 如果上游配置文件里能直接关,就不需要补丁来拦截、删除
  2. 软组织上游没有标记 ⇒ 补丁该做的是新增一条规则帮它钉住软组织。

之前的做法(1.1.4~1.1.8):5 个活动部件,全是绕路

部件 干什么
Rule Prefab Overrides 劫持上游 Giant RemainsPrefabs,5 个骨剑换成 giant_brain
Drop Existing Pins For Overridden Rules 按名字旧图钉(85+ 枚)
一次性清理守卫 防止每 2 秒重删把新钉的也删掉
PinRecord.Wipe() 清掉「已钉过」记录(否则同位置的新规则钉不出来)
ForceResweep() 清节流让上游立刻重扫

这 5 个部件在 1.1.4→1.1.8 期间一共引出 4 个 bug(钉不出来 / 钉了又删 / 来不及重扫 / 译名不生效)。

本版做法:2 个部件,不碰上游规则

  1. 上游开关关掉(在上游自己的 cfg 里):

    [Auto Pins - Mistlands]
    Giant Remains = false
    

    上游就不再钉骨/剑了。零补丁代码,而且用户随时能改回来。

  2. 本补丁新增一条规则(用已有的注入机制,和 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 OverridesDrop Existing Pins For Overridden RulesApplyPrefabOverrides()_overriddenRuleNames_overrideCleanupDonePinRecordWipe()ForceResweep()MPinRecordWipeFNextSweep —— 约 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 名字不会变。两处要一起改:

  1. 第 85 行 Name Map = … 里的那条(真正生效的那处);
  2. 第 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;),来不及重新落钉。 所以"软组织没钉出来"这次没有得到有效验证

本版改动

  1. 清完 PinRecord 后主动触发一次重扫:把 AutoPins._nextSweep 清 0, 让 BetterMap 下一次 Tick 立刻扫一遍,不用干等节流间隔。 (⚠️ 不直接 Invoke 它的 Sweep() —— Tick 里在它之前还有 Build() 和三个索引的非空检查, 走它自己的路径最稳。)
  2. 新增诊断 [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(之前只映射了复数版,所以永远命不中)。 它来自生物通道CreatureTrackerLocalization.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 == falsePlayer.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) : null
  • CreatureTracker.AddNamed ? 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 MinesMorgen Holes —— 上游 1.0.5 已提供 (后者改叫 Putrid Holes)。依赖同步升到 xtavim-BetterMap-1.0.5, 装不上 1.0.5 就不会缺这两条。
  • 🔴 新增第二道判重:比 prefab 集合。 原来只比「规则名 + 群系」, 上游把规则改名(Morgen HolesPutrid Holes)就认不出来,会注入出 第二条同 prefab 的规则 ⇒ cfg 里两个开关管同一批位置。现在只要上游已有规则 盯着同一批 prefab,不管叫什么名字都算已覆盖、跳过。
  • 映射表调整
    • Infested Mines=腐疫矿区 保留 —— 上游那条没有 NameToken,图钉名还是英文规则名;
    • Morgen Holes=腐朽之穴 删除 —— 上游没有这个名字的规则了,留着永远命不中;
    • Putrid Holes=腐朽之穴 新增(保险)—— 上游那条有 NameToken,游戏自己会翻。
  • 兼容性:本补丁挂载的 6 个类型 / 38 个方法签名与 1.0.5 零差异,不存在挂失败风险。

仍然需要本补丁的(上游 1.0.5 依旧没有)

Half Burried CryptHildir 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.PlacePinNames.For 取名,其余五条各自算名字 (生物走 Character.m_name、传送门/死亡标记走自己的逻辑)。 1.1.1 只把替换挂在 PinNames.For 上 ⇒ 这五条路径一个都没覆盖到

    修法:在游戏侧 Minimap.AddPin 上再兜一层 postfix,对刚落的那枚图钉做同一张表的替换。 与走哪条落钉路径无关,六条全盖住。原有的 PinNames.For postfix 保留作双保险。

  • 旧图钉改名趟的挂载点不可靠。 原来挂在 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,落钉时 gonull)⇒ 必然走兜底;
    • 预制体类规则里、那些没有 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 CavesMountainCave02 / Mountain / Dungeon / 默认开),与本补丁 1.0.0 起注入的那条逐项同构
    其他 Obsidian Deposits 默认值 开 → 关。
  • 上游仍然缺的(本次补上)

    • Infested Mines(迷雾之地 / Mistlands_DvergrTownEntrance1Entrance2)—— 默认开。 官方简中译名「腐疫矿区」(location_dvergrtown = Infested Mine)。 ⚠️ 上游有一条 Dvergr Town 看着像它,实际是 CharredTowerRuins1_dvergr + Biome=AshLands, 落钉要过群系过滤 ⇒ 永远命中不了迷雾之地的入口。
    • Morgen Holes(灰烬之地 / MorgenHole123)—— 默认开,简中「腐朽之穴」。
    • 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.py 7 组用例全通过(含"拿你当前 cfg 预演 → 36 条、旧 33 条全保留")。
  • 新增交付前核验脚本(开发者侧)verify_bettermapfix.py:机器核对 ① 映射表英文键能否命中上游字符串堆或本补丁注入的规则名(键名打错只会静默不生效); ② 注入的地点 prefab 是否真在游戏地点表里、群系是否一致、是否 enable=True; ③ 规则名(会成为上游 cfg 的键)是否含 BepInEx 禁用字符; ④ 脚本内清单与实际 DLL 是否一致(防清单漂移)。 本版跑分:映射表 36/36 命中;9 个地点 prefab 全部存在且群系一致; 唯一 WARN 是已知的 HalfBurried_ForestCryptm_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.jsondescription 末尾加一句 Read the applicability notice first., 并压缩原有措辞以适配 Thunderstore 的 256 字符上限(实测值)。
  • 补丁本体未重新编译BetterMapFix.dll 29184 字节, sha256 c51accfbd6b9dbec0b904cd1459283ba508c6b707c15efe9ca520025614595e1 —— 与 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_ForestCryptm_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.BindArgumentException。 又因为 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 CavesMountainCave02,山脉)—— 冰霜洞穴入口。
  • 规则 Half Burried CryptHalfBurried_ForestCrypt,黑森林)。
  • 规则 Hildir's CaveHildir_cave,山脉)、Hildir's CryptHildir_crypt,黑森林)—— 默认关。
  • 规则 VegvisirsVegvisir_SeekerQueen,迷雾之地)—— 原模组每段都有 Vegvisirs,唯独这段没有。
  • 调试项:Dump Location Names(整表打印游戏地点名,用于核对)、Verbose Pin Log(逐钉日志)。
DatHost — Valheim server hosting, first month for one euro
DatHost — Valheim server hosting, first month for one euro
DatHost — Valheim server hosting, first month for one euro
DatHost — Valheim server hosting, first month for one euro
DatHost — Valheim server hosting, first month for one euro