NOTE

逆向方法与数据支撑:本文结论来自三类证据的交叉:① 32 位客户端 Torchlight2.exe(ImageBase 0x400000,文内 sub_XXXXXX 均为绝对虚地址)中加载器与任务状态函数的反编译体;② 原版全量数据资产的普查——MEDIA/RESURRECT/ 3 份 DAT、MEDIA/QUESTS/ 184 份 DAT、MEDIA/INVENTORY/ 37 份槽位 DAT 与 MEDIA/INVENTORY/CONTAINERS/ 30 份容器 DAT,以及对全 MEDIA 16,084 份 DAT 的键名扫描;③ 对 exe 数据段的 UTF-16LE 宽字符串检索。全部结论已收敛至真值账本(Ledger C-0175 / C-0176 / C-0177,状态均为 guarded、级别 must-agree)。源材料中标为“没逆 / 未定 / 移植选择”的部分,本文一律如实标注,不作补全。

核心工程结论:三个子系统都是数据驱动的——在哪复活、复活扣多少、任务对白在完成前后各说什么、背包与装备栏各有几格,全部写在 DAT 里而不在代码里。但有三处极容易“按键名想当然”:XP_PENALTY 这个键存在,发行数据里却全是 0;ICONABOVEHEAD 这个键存在,发行数据里却全是 false;技能栏容器 SPELLSLOTS 只有 4 格,而不是界面上那排 10 个数字键。


0. 核心结论速览 ​

  1. 原版死亡只扣金币,不扣经验、不扣声望。MEDIA/RESURRECT/ 三条选项的 XP_PENALTY 与 FAME_PENALTY 全部为 0。加载器确实读这两个键(字段偏移 +96 / +100),但发行数据没给它们任何非零值。若仅依字面预设并实现死亡扣除经验,将偏离原版实际运行规则。
  2. 复活是发行数据里的三选一:INPLACE(INDEX 0 / WHERE CURRENT / GOLD_PENALTY 30 / ALLOW_IN_NON_PORTAL_LEVELS:false / DIFFICULTIESBIN:3)、INTOWN(1 / TOWN / 0)、LEVELENTRANCE(2 / LEVEL_START / 10)。一个 DAT 文件一条选项,INDEX 是面板顺序,WEIGHT 三条都是 1。
  3. 复活选项的加载器是 sub_651DF0,而非难度加载器。早期语料曾将其误标为难度加载,其实际读取 WHERE 与 ALLOW_IN_NON_PORTAL_LEVELS,推测系因字段重合导致的误判。
  4. GOLD_PENALTY 是百分比还是定额,尚待定位。加载器将其作为整数存入 +92;读取 +92 并真正扣款的消费函数尚未完全闭环。
  5. 任务的两态就在同一个对话块里:DIALOG(未完成)与 DIALOG_COMPLETE(已完成)是两条独立文本;[DUNGEONS_WHEN_COMPLETE] / [DUNGEONS_WHEN_NOT_COMPLETE] 是同一个两态在关卡侧的投影。编辑器导出只有 EditorSetEditorActiveQuests / EditorSetEditorCompletedQuests 两个动作——两态坐实,没有第三态。
  6. 发行任务普查常见六种 [DIALOG] 子块:INTRO / DETAILS / COMPLETE / RETURN / HUDDETAILS / PASSIVE。后续代码逆向还确认引擎支持第七种 MOREDETAILS;各自触发时机见任务完成、目标计数与对话状态机。
  7. ICONABOVEHEAD 在全 MEDIA 16,084 份 DAT 里出现 445 处,取值全部是 false,另有 424 个对话块根本没写它。而它的 UTF-16LE 宽字符串在 exe 里命中 1 处——引擎确实读它,不是死键。图标“激活”态源自缺键时的默认值,试图通过显式配置该键开启头顶叹号无法达成预期。
  8. 任务完成条件已有后续补完。通用谓词、DEFEAT 计数与存档、[DEFEATCOUNT] 替换、对话状态机和 GLOBAL_TRILLBOT 特判均已钉住;[REWARD] 中 GOLDMINPCT 等百分比口径仍未查。见任务完成、目标计数与对话状态机。
  9. 玩家的三个面板是三个真容器:MAIN BAG 24 格(单槽 BAG)、BODY 13 槽、SPELLSLOTS 4 格(单槽 SPELL)。格数、槽位名、顺序全是数据,不是界面布局的选择。
  10. 技能栏是 4 格,不是 10。原版界面上那排 1–9/0 是技能快捷键,与 SPELLSLOTS 容器不是一回事。将其视为 10 格属于由前端 UI 表现对底层数据产生的误读。
  11. BODY 的第 13 槽是 POINTER(拿在手上那件)。它在容器定义里就属于 BODY,不是复刻时加的。

1. 复活系统(MEDIA/RESURRECT/) ​

1.1 三条 authored 选项全表 ​

MEDIA/RESURRECT/ 下一个 DAT 文件对应一条复活选项。三份文件的全部有效键值如下:

源文件NAMETITLEWHEREGOLD_PENALTYXP_PENALTYFAME_PENALTYINDEXWEIGHTDIFFICULTIESBINALLOW_IN_NON_PORTAL_LEVELS
RES_IN_PLACE.DATINPLACEResurrectCURRENT3000013false
RES_IN_TOWN.DATINTOWNTownTOWN00011(未写)(未写)
RES_IN_DUNGEON.DATLEVELENTRANCEEntranceLEVEL_START100021(未写)(未写)

三条的 DESCRIPTION 原文:

INPLACE        Resurrect here, but lose [GOLD] Gold.
INTOWN         Resurrect in town and lose nothing.
LEVELENTRANCE  Resurrect at the entrance to this area, but lose [GOLD] Gold.

三条的 IMAGE 都是 NONE;REQUIRES_ITEM / TAKE_ITEM / EFFECT / BLOCKING_EFFECT 四个键在三份文件里一条都没用(全为空)。DESCRIPTION 里的 [GOLD] 是占位符,其替换逻辑未查。

三点观察:

  • XP_PENALTY 与 FAME_PENALTY 三条全为 0。这两个键加载器是读的(见 §1.2),所以它们不是死键——只是 Runic 从没给过非零值。原版“死亡惩罚”的全部内容就是 30 或 10 金币。
  • INDEX 0 / 1 / 2 对应面板上的顺序;WEIGHT 三条都是 1,它参与什么计算没查。
  • DIFFICULTIESBIN:3 与 ALLOW_IN_NON_PORTAL_LEVELS:false 只出现在 INPLACE 这一条上。两个键的运行时语义都没逆,本文只报数据。

1.2 加载器 sub_651DF0:键表与字段偏移 ​

sub_651DF0 读取一份复活 DAT 并填充一个对象。它读的键共 16 个:

ALLOW_IN_NON_PORTAL_LEVELS  NAME  TITLE  DESCRIPTION
GOLD_PENALTY  XP_PENALTY  FAME_PENALTY  INDEX
REQUIRES_ITEM  TAKE_ITEM  EFFECT  BLOCKING_EFFECT
IMAGE  WEIGHT  DIFFICULTIESBIN  WHERE

从反编译体读出的字段偏移(对象基址起,字节):

DAT 键名字段偏移
NAME+8
GOLD_PENALTY+92
XP_PENALTY+96
FAME_PENALTY+100
WHERE+104
WEIGHT+136
REQUIRES_ITEM+144
TAKE_ITEM+152
ALLOW_IN_NON_PORTAL_LEVELS+160
DIFFICULTIESBIN+164
INDEX+212
TITLE / DESCRIPTION / EFFECT / BLOCKING_EFFECT / IMAGE—

加载器用到的三个取值例程是引擎通用的 DAT 读法:sub_677F00 取字符串、sub_677F30 取 bool、sub_677F80 取 int。

NOTE

表中“—”表示源逆向材料未记录该键的专属偏移,并非该键不存在。留下偏移表的目的只有一个:下次去找 +92 的消费端时不必再逆一遍加载器。

1.3 墓碑:语料曾把 sub_651DF0 标成“难度 DAT 加载” ​

早期的全函数语料(ALLFUNCS/_FINDINGS.md)把 sub_651DF0 标成了难度 DAT 加载器。这个标签是错的,判据很简单:

  • 它读 WHERE 与 ALLOW_IN_NON_PORTAL_LEVELS——难度 DAT 里没有这两个键;
  • GOLD_PENALTY / XP_PENALTY / FAME_PENALTY 三个键在难度 DAT 与复活 DAT 里都有,大概就是因此被标错的。

该历史误判标签的影响在于误导逆向方向:若按“难度加载”追溯 GOLD_PENALTY 的消费逻辑,会误入难度系统,而复活惩罚逻辑并不在该处。记录此处纠错历史,有助于避免后续分析产生偏差。

1.4 未闭环的机制细节 ​

项现状
GOLD_PENALTY 是百分比还是定额未定。加载器将其存入 +92 偏移;读取 +92 并结算扣款的消费函数尚未定位。
复活后恢复 HP 比例三份 DAT 中均未配置该键,加载器亦不读取,属于引擎内建逻辑。
WHERE 除 CURRENT / TOWN / LEVEL_START 外的可选值引擎是否支持其他语义未验证。
ALLOW_IN_NON_PORTAL_LEVELS 判定数据中 INPLACE 配置为 false,关卡是否为 portal level 的内部判定逻辑未逆向。
DIFFICULTIESBIN仅在 INPLACE 中出现,值为 3;具体位掩码语义未逆向。
EFFECT / BLOCKING_EFFECT / REQUIRES_ITEM / TAKE_ITEM发行数据中均未使用,具体行为未验证。
WEIGHT均为 1,参与公式未定位。

复刻实现的工程处理:复活动作交由状态重置处理,不额外消耗随机数以保证联机同步一致性;状态重置为满生命值并触发对应事件;未接入经济与难度限制系统时,惩罚字段暂不结算。实测验证表明,原地复活 (INPLACE) 配置的高代价与城镇/关卡起点 (TOWN / LEVEL_START) 的零代价机制,与官方资源的设计意图一致。

1.5 MOD 制作建议 ​

  • 复活属于数据驱动机制。修改消耗代价、复活位置或界面排序时,直接调整 MEDIA/RESURRECT/ 下的三份 DAT。如需新增第四项复活选项,需增加新 DAT 文件并分配新的 INDEX;建议复活位置继续使用验证过的 CURRENT / TOWN / LEVEL_START。
  • 死亡掉落机制注意点:XP_PENALTY / FAME_PENALTY 虽被加载器读取,但官方默认选项未配置扣除经验与声望,配置非零值的实际结算机制尚需游戏内实测确认。
  • GOLD_PENALTY 计算口径:当前尚未明确数值为百分比还是固定数值,修改前建议通过游戏内实测对照。
  • REQUIRES_ITEM / TAKE_ITEM / EFFECT / BLOCKING_EFFECT 在引擎中有字段解析,但官方未启用;制作“消耗特定物品复活”等机制时可优先尝试这组接口。

2. 任务系统(MEDIA/QUESTS/,184 个) ​

2.1 一个任务 DAT 的骨架 ​

以 A1-ICEDEEP-TAKINGNOTES.DAT(任务 a1-IceDeep-TakingNotes,显示名 Taking Notes)为例,一份任务 DAT 由顶层键与若干块组成。顶层键有 NAME / DISPLAYNAME / QUEST_GUID / QUESTRANKMIN / QUESTRANKMAX / DIALOGPRIORITY / SHOWTIPS / CANABANDON / SHOWQUESTCOMPLETE(这些键的运行时语义本文没逆,只列出)。块有:

块内容语料覆盖
[DIALOG]六种子块(见 §2.2),每个子块里是 UNITNAME / DIALOG / DIALOG_COMPLETE / ICONABOVEHEAD 等键131 个任务至少有一个对话子块
[DUNGEONS_WHEN_COMPLETE] / [DUNGEONS_WHEN_NOT_COMPLETE]各列一组 dungeon 名77 个任务带这一对
[REQUIREMENTS] / [QUESTSCOMPLETE]前置任务名列表85 个任务带前置
[REWARD]GOLDMINPCT / GOLDMAXPCT / XPMINPCT / XPMAXPCT / FAMEMINPCT / FAMEMAXPCT / TREASURECLASS123 个任务带奖励块

Taking Notes 的关卡两分与前置:[DUNGEONS_WHEN_COMPLETE] = FROSTEDHILLS,[DUNGEONS_WHEN_NOT_COMPLETE] = ICEDEEPCAVERNS,[QUESTSCOMPLETE] 为空。Runic 自己的模板任务 A1-ACQUIRETEMPLATE.DAT 则前置于 A1-GOTOTOWN_TALKTOREGENT,关卡两分为 ESTHERIANCITY / THETEMPLESTEPPES。

2.2 [DIALOG] 下的六种子块 ​

六种子块的名字:INTRO / DETAILS / COMPLETE / RETURN / HUDDETAILS / PASSIVE。

[!UPDATE] 本节原始普查只确认发行数据中的六种常见子块。后续逆向已补全触发时机,并确认引擎还支持 MOREDETAILS;见任务完成、目标计数与对话状态机。下面的模板文字仍仅作为当时的数据侧线索保留。

INTRO       Quest introduction text
DETAILS     Obtain the [item] from the [place], in the [zone].
            (DIALOG_COMPLETE) Return to the [questNPC] in the [zone] for your reward.
COMPLETE    Quest completion text
RETURN      Return to Quest NPC text
HUDDETAILS  - Obtain [item] in [zone]
            (DIALOG_COMPLETE) - Return Armor Schematics to Vanquisher Scout

模板里没有 PASSIVE。PASSIVE 与任务状态的关系没查;能确定的只有两件事:它一个任务里能有多个(全库 449 个 PASSIVE 块 vs 184 个任务),以及 ICONABOVEHEAD 主要就写在它里面(445 处里的 441 处,见 §2.4)。

子块里的键:UNITNAME(说这段话的 NPC)、DIALOG、DIALOG_COMPLETE、ICONABOVEHEAD,另有几个菜单 / 暂停 / 看向玩家性质的键,本文不展开。RETURN 这类子块同一任务里也可以有多个——Taking Notes 的 RETURN 就有 4 块:1 块 UNITNAME 为 A1-JADOK,3 块分别是 A1-JADOK_NOTE1/2/3_ICEDEEP-TAKINGNOTES(三本日志各一段独白,ICONABOVEHEAD:false)。所以每种子块都要当列表存,不能当单条。

2.3 两态:DIALOG / DIALOG_COMPLETE,以及它在关卡侧的投影 ​

同一个对话子块里,DIALOG 是任务未完成时的文本,DIALOG_COMPLETE 是已完成时的文本,两条独立。Taking Notes 的 HUDDETAILS 块:

DIALOG           - Acquire |cFFD1FF7A[DEFEATCOMPLETECOUNT]/[DEFEATCOUNT] Journals|u in the |cFFD1FF7AIcedeep Caverns|u
DIALOG_COMPLETE  - Return to |cFFD1FF7AJadok|u in the |cFFD1FF7AFrosted Hills|u

(|cFFD1FF7A…|u 是文本里原样存在的标记,本文不展开其语义。)

全库 184 个任务里 68 个带两态文本;77 个带 [DUNGEONS_WHEN_COMPLETE] / [DUNGEONS_WHEN_NOT_COMPLETE]——那是同一个两态在关卡侧的投影;85 个带 [QUESTSCOMPLETE] 前置。

引擎侧把两态坐实了(来自全函数语料 ALLFUNCS/J_quests.funcs.json):

项事实
任务对象 +152任务名
任务对象 +45664 位 GUID
sub_6FB460按名查任务
sub_6F97C0状态推进;+8 是“已处理”标志;内含 GLOBAL_TRILLBOT 的具名硬编特判
编辑器导出EditorSetEditorActiveQuests / EditorSetEditorCompletedQuests——只有 active / completed 两个动作

所以没有“进行中”这个第三态:发行数据每个块只给两条文本,编辑器也只有两个动作。

后续进展:任务完成谓词、objective 计数、[DEFEATCOUNT] / [DEFEATCOMPLETECOUNT] 替换与 GLOBAL_TRILLBOT 特判已经补完,见任务完成、目标计数与对话状态机。[item] / [zone] / [place] / [questNPC] 的替换,以及两个 [DUNGEONS_WHEN_*] 块在关卡侧具体驱动什么,仍未查。

2.4 ICONABOVEHEAD:全库取值均为 false ​

该字段的分析过程与结论如下:

普查统计:全 MEDIA 目录下 16,084 份 DAT 中,ICONABOVEHEAD 仅出现在 QUESTS/ 目录下,共 445 处,取值全部为 false;其中 441 处位于 QUEST/DIALOG/PASSIVE 子块中;另有 424 个对话块未声明该键。按任务分布,共 32 个任务包含该字段。该键属于对话子块内部的布尔配置,与 UNITNAME 关联,属于局部配置而非全局开关。

原语判定:

  1. 若为冗余失效键,则引擎内部不解析该字符串。
  2. 若引擎读取该键,则缺省时具备默认值,官方仅在需要关闭头顶图标的子块中显式声明 false。

通过在 Torchlight2.exe 二进制中检索 ICONABOVEHEAD 的 UTF-16LE 宽字符常量,命中 1 处调用点,证实引擎明确解析该键。

结论:ICONABOVEHEAD 无法直接用于正向激活头顶感叹号。若仅根据该键的字面值绑定逻辑,图标将全部处于隐藏状态;官方数据中不存在配置为 true 的前例。

WARNING

键名字面含义容易引发误判。ICONABOVEHEAD 在数据中实际扮演“抑制”开关(显式置 false 屏蔽图标),常规任务状态下的图标激活由状态机解析时的缺省逻辑驱动。

复刻实现的工程处理:由任务状态(未完成显示、已完成隐藏)直接驱动图标可见性,避免受全 false 数据干扰。

2.5 解析注意事项:多重子块与对账校验 ​

在脚本解析任务数据时,若未将 PASSIVE 纳入对话子块枚举,会导致 445 处 ICONABOVEHEAD 中仅能捕获到 1 处(其余 441 处均位于 PASSIVE 块内)。

为避免此类数据遗漏,解析工具应当采用两套独立机制进行校验对账:一套通过结构化语法树提取字段统计,另一套基于裸文本全局匹配文件名列表,两者结果严格一致方可验证解析完备性。此外,单个任务内支持声明多个 PASSIVE 块(全库 449 个子块对应 184 个任务),解析时必须保存为列表结构,不能使用单键字典直接覆盖。

2.6 [REWARD] 与占位符规范 ​

以 Taking Notes 的 [REWARD] 为例:

GOLDMINPCT 2000   GOLDMAXPCT 2000
XPMINPCT   1000   XPMAXPCT   1000
FAMEMINPCT  500   FAMEMAXPCT  500
TREASURECLASS QUESTREWARD_SIDEQUEST

模板任务 A1-ACQUIRETEMPLATE.DAT 的对应值为金币 0 / 经验 0 / 声望 400,TREASURECLASS 同为 QUESTREWARD_SIDEQUEST。

键名带有 PCT 后缀,但其具体百分比计算基数与缩放口径尚未反编译完全。TREASURECLASS 的掉落池抽取逻辑同属未完全逆向项。

2.7 MOD 制作建议 ​

  • 新增完成态 NPC 对话:直接在对话块内添加 DIALOG_COMPLETE 字段即可生效。官方 184 个任务中有 68 个定义了双态文本,其余 116 个未配置 DIALOG_COMPLETE。
  • 任务头顶感叹号配置:官方全库均为 false,不建议依赖 ICONABOVEHEAD:true 实现图标显示;官方资源中让图标正常显示的常规做法是不显式声明该键。
  • 对话多重块结构:PASSIVE 块在单个任务中通常存在多个实例,编写工具或解析脚本时须将其视为数组或列表进行处理。
  • 前置依赖链:[QUESTSCOMPLETE] 为前置任务链声明入口(85 个任务使用);[DUNGEONS_WHEN_COMPLETE] / [DUNGEONS_WHEN_NOT_COMPLETE] 负责在任务完成前后动态切换关联地下城(77 个任务使用)。
  • [REWARD] 奖励百分比与 TREASURECLASS 在修改前建议结合实际数值进行实测校准。
  • GLOBAL_TRILLBOT 在状态推进逻辑中具备特判逻辑,修改相关任务时须注意行为差异。

3. 容器系统(MEDIA/INVENTORY/CONTAINERS/,30 个) ​

3.1 两层定义:槽位 DAT 与容器 DAT ​

容器系统在 MEDIA/INVENTORY/ 下分两层:

  • 槽位(slot):INVENTORY/*.DAT,共 37 个。一个槽位定义一种“格子”能放什么——允许的 unittype、不允许的 unittype、是否可装备、是否隐藏等。
  • 容器(container):INVENTORY/CONTAINERS/*.DAT,共 30 个。一个容器是一组 (槽位名, 数量) 的列表;容器的总格数就是各槽位数量之和。

37 个槽位名:

ANY  ARMOR  BAG  BAG_ARMS_SLOT  BAG_CONSUMABLES_SLOT  BAG_SPELL_SLOT  BELT  BOOTS
COLLAR  ENCHANTER_SLOT  FISH  GLOVES  HEAD  ITEMCOMBINER_SLOT  ITEMCOMBINER_SLOT2
ITEMCOMBINER_SLOT3  ITEMCOMBINER_SLOT4  LEFTHAND  LEFTHAND_HIDDEN  LEVEL ITEM
MERCHANT ARMOR  MERCHANT ITEM  MERCHANT WEAPON  NECKLACE  PANTS  POINTER  RIGHTHAND
RIGHTHAND_HIDDEN  RING1  RING2  SHOULDERS  SPELL  STUD  STUD2  TORSO  TRADE_SLOT  WEAPON

3.2 玩家的三个面板 = 三个真容器 ​

容器名源文件格数槽位构成
MAIN BAGINVENTORY/CONTAINERS/MAINBAG.DAT24BAG × 24
BODYINVENTORY/CONTAINERS/BODY.DAT1313 个不同槽位各 1(见 §3.3)
SPELLSLOTSINVENTORY/CONTAINERS/SPELLSLOTS.DAT4SPELL × 4

BAG 槽位(INVENTORY/BAG.DAT)允许 TAKEABLE 与 QUESTITEM,不可装备;SPELL 槽位(INVENTORY/SPELL.DAT)允许 SPELL,可装备。

复刻侧按容器表自绘三个面板并实跑 dump 校验,读出的格数就是 24 / 13 / 4,槽位名与顺序与 BODY.DAT 逐一相同。格数是现读容器表比对的,不写死在测试里——写死的话“界面摆了 24 个”与“容器表说 24 格”两件事就分不开了。

3.3 BODY 的 13 个槽位 ​

#槽位名槽位源文件允许的 unittype不允许备注
1HEADINVENTORY/HEAD.DATHELMET—
2LEFTHANDINVENTORY/LEFTHAND.DATWEAPON, SHIELDRIGHT HANDED
3RIGHTHANDINVENTORY/RIGHTHAND.DATWEAPONBOW, LEFT HANDED
4BOOTSINVENTORY/BOOTS.DATBOOTS—
5BELTINVENTORY/BELT.DATBELT—
6PANTSINVENTORY/PANTS.DATPANTS—
7TORSOINVENTORY/TORSO.DATCHEST ARMOR—
8SHOULDERSINVENTORY/SHOULDERS.DATSHOULDER ARMOR—
9GLOVESINVENTORY/GLOVES.DATGLOVES—
10NECKLACEINVENTORY/NECKLACE.DATNECKLACECOLLAR
11RING1INVENTORY/RING1.DATRINGSTUD
12RING2INVENTORY/RING2.DATRINGSTUD
13POINTERINVENTORY/POINTER.DATTAKEABLE—隐藏槽,不可装备;“拿在手上那件”

第 13 槽 POINTER 在 BODY.DAT 的容器定义里就有,是数据的一部分。MERCHANTBODY 与 BODY 的 13 个槽位完全相同;MONSTERBODY 是 12 槽——没有 NECKLACE / RING1 / RING2,多出 LEVEL ITEM × 2。

3.4 技能栏是 4 格,不是 10 ​

SPELLSLOTS.DAT 只有 SPELL × 4。原版界面底部那排 1–9/0 是技能快捷键——那是键位映射,不是容器。两件事名字都带“技能”,但一个在 MEDIA/INVENTORY/CONTAINERS/,一个在 MEDIA/KEYMAPPING/,互不相干。

把技能栏做成 10 格,是把界面印象当成了数据。发行数据说 4。

3.5 30 个容器全表 ​

容器名源文件(INVENTORY/CONTAINERS/)格数槽位构成
BODYBODY.DAT13见 §3.3
MAIN BAGMAINBAG.DAT24BAG × 24
SPELLSLOTSSPELLSLOTS.DAT4SPELL × 4
POINTERPOINTER.DAT1POINTER × 1
WEAPONSWAPWEAPONSWAP.DAT2LEFTHAND_HIDDEN, RIGHTHAND_HIDDEN
PLAYER_BAG_ARMSPLAYER_BAG_ARMS.DAT32BAG_ARMS_SLOT × 32
PLAYER_BAG_CONSUMABLESPLAYER_BAG_CONSUMABLES.DAT32BAG_CONSUMABLES_SLOT × 32
PLAYER_BAG_SPELLSPLAYER_BAG_SPELLS.DAT24BAG_SPELL_SLOT × 24
STASHSTASHBAG.DAT20BAG × 20
PLAYER_STASH_BAG_ARMSPLAYER_STASH_BAG_ARMS.DAT40BAG_ARMS_SLOT × 40
PLAYER_STASH_BAG_CONSUMABLESPLAYER_STASH_BAG_CONSUMABLES.DAT40BAG_CONSUMABLES_SLOT × 40
PLAYER_STASH_BAG_SPELLSPLAYER_STASH_BAG_SPELLS.DAT40BAG_SPELL_SLOT × 40
SHARED_STASH_BAG_ARMSSHARED_STASH_BAG_ARMS.DAT40BAG_ARMS_SLOT × 40
SHARED_STASH_BAG_CONSUMABLESSHARED_STASH_BAG_CONSUMABLES.DAT40BAG_CONSUMABLES_SLOT × 40
SHARED_STASH_BAG_SPELLSSHARED_STASH_BAG_SPELLS.DAT40BAG_SPELL_SLOT × 40
PLAYER_TRADEPLAYER_TRADE.DAT6TRADE_SLOT × 6
PET BAGPETBAG.DAT24BAG × 24
PETBODYPETBODY.DAT3COLLAR, STUD, STUD2
MERCHANTBODYMERCHANTBODY.DAT13与 BODY 相同
MERCHANT BAGMERCHANTBAG.DAT35BAG × 35
MERCHANT BUY BACK BAGMERCHANTBUYBACKBAG.DAT40BAG × 40
MERCHANT ARMORSMERCHANTARMOR.DAT40MERCHANT ARMOR × 40
MERCHANT WEAPONSMERCHANTWEAPON.DAT40MERCHANT WEAPON × 40
MERCHANTITEMMERCHANTITEM.DAT40MERCHANT ITEM × 40
MONSTERBODYMONSTERBAG.DAT12HEAD, RIGHTHAND, LEFTHAND, BOOTS, BELT, PANTS, TORSO, SHOULDERS, GLOVES, LEVEL ITEM × 2, POINTER
NPCBODYNPCBODY.DAT2RIGHTHAND, LEFTHAND
LEVELBAGLEVELBAG.DAT10LEVEL ITEM × 10
QUESTREWARDSQUESTREWARDS.DAT3BAG × 3
ITEMCOMBINERBAGITEMCOMBINERBAG.DAT4ITEMCOMBINER_SLOT ×1, _SLOT2, _SLOT3, _SLOT4
ENCHANTER BAGENCHANGERBAG.DAT1ENCHANTER_SLOT × 1

两处文件名与容器名对不上的地方是发行数据本身如此:ENCHANTER BAG 的文件叫 ENCHANGERBAG.DAT(少了 T 多了 G),MONSTERBODY 的文件叫 MONSTERBAG.DAT。按文件名找容器时要留意。

3.6 顺带:面板键位来自 MEDIA/KEYMAPPING/ ​

复刻侧的面板开关绑的是从原版 MEDIA/KEYMAPPING/ 生成的 action:i = InventoryMenu(背包)、c = StatMenu(角色面板)。同一份键位表里还有 s = SkillMenu、w = WeaponSwap、a = AutoTarget——任何想在 TL2 系上加 WASD 移动的方案,先得处理这三条冲突。数字键 1–9/0 在原版键位表里就是放技能的。

3.7 这对做 MOD 意味着什么 ​

  • 改背包格数、改装备槽,改的是 INVENTORY/CONTAINERS/*.DAT 里的槽位数量,不是界面文件。MAIN BAG 的 24 就是 BAG 槽的 count。
  • 想给技能栏加格子,改 SPELLSLOTS.DAT 的 SPELL count——它现在是 4,与那排 10 个数字键无关。改了容器格数之后界面能不能显示出来是另一层的事,本文没查。
  • 一个槽能放什么由槽位 DAT 决定(INVENTORY/<槽名>.DAT 的允许 / 不允许 unittype 列表)。RIGHTHAND 不收 BOW 与 LEFT HANDED,NECKLACE 不收 COLLAR,RING1 / RING2 不收 STUD——这是数据,改它们比改物品的 unittype 更直接。
  • BODY 有 13 槽而不是 12,第 13 个是隐藏的 POINTER。处理装备存档、写自己的装备栏脚本时把它算上,否则会差一个。
  • MERCHANTBODY 与 BODY 完全同构,MONSTERBODY 少三个首饰槽、多两个 LEVEL ITEM——给商人或怪物做装备相关 MOD 时,别拿玩家的 13 槽套用。
  • 玩家仓库分三类(ARMS / CONSUMABLES / SPELLS)各 40 格、共享仓库同样三类各 40 格;商人侧 MERCHANT BAG 35 格,回购 / 护甲 / 武器 / 杂物四个容器各 40 格。这些数字全在表里,改它们不需要碰代码。

4. 给 MOD 开发者的速查 ​

4.1 键名会骗人:直觉与事实的对照 ​

键 / 数字直觉事实
XP_PENALTY / FAME_PENALTY死亡掉经验、掉声望加载器读,但三条选项全为 0——原版只扣金币
GOLD_PENALTY: 30扣 30 金币(或 30%)百分比还是定额未定;消费端没找到
sub_651DF0难度 DAT 加载器(旧标签)复活选项加载器;读 WHERE 与 ALLOW_IN_NON_PORTAL_LEVELS
ICONABOVEHEAD写 true 让 NPC 头顶亮图标全库 445 处全 false,引擎读它但缺键默认值没逆;写 true 无先例
任务“进行中”任务有三态数据与编辑器都只有两态:未完成 / 已完成
五种对话子块INTRO / DETAILS / COMPLETE / RETURN / HUDDETAILS六种,还有 PASSIVE;且 441/445 个 ICONABOVEHEAD 在它里面
技能栏 10 格界面上那排 1–9/0SPELLSLOTS 容器是 4 格;数字键是键位映射
BODY 12 槽头、双手、四件甲、腰带、鞋、项链、两戒13 槽,第 13 个是隐藏的 POINTER
ENCHANGERBAG.DAT文件名打错了发行数据就叫这个名字,ENCHANTER BAG 容器的源文件

4.2 直接可用的数据入口 ​

想做的事改哪里注意
改复活代价 / 地点 / 面板顺序MEDIA/RESURRECT/RES_IN_*.DAT 的 GOLD_PENALTY / WHERE / INDEXGOLD_PENALTY 口径未定,改完实测
让 NPC 交任务后换台词同一对话块里加 DIALOG_COMPLETE116 个原版任务还没用两态文本
给任务加前置[REQUIREMENTS] / [QUESTSCOMPLETE]85 个原版任务的写法可照抄
改背包 / 仓库 / 商人格数INVENTORY/CONTAINERS/*.DAT 的槽位 count界面能否跟着长没查
改一个装备槽能放什么INVENTORY/<槽名>.DAT 的允许 / 不允许 unittype比改物品 unittype 更直接

5. 未解与尚未闭环事项清单 ​

待解项现状
GOLD_PENALTY 的应用口径(百分比 vs 定额)读取 +92 并施加扣款的消费函数尚未定位
复活后 HP 回多少DAT 里没有对应键,属引擎行为,没逆
WHERE 是否认得 CURRENT / TOWN / LEVEL_START 以外的值没查
ALLOW_IN_NON_PORTAL_LEVELS 的判定逻辑、DIFFICULTIESBIN 的语义、WEIGHT 参与的计算没逆
EFFECT / BLOCKING_EFFECT / REQUIRES_ITEM / TAKE_ITEM发行数据里一条都没用,语义没查
任务完成条件([REQUIREMENTS]、objective 计数)已在后续报告补完;见 ../wiki/quest-completion-objectives-dialog-state-machine.md
[DEFEATCOUNT] / [item] / [zone] 等占位符的替换[DEFEATCOUNT] / [DEFEATCOMPLETECOUNT] 已补完;其余占位符未查
对话子块各自的触发时机已在后续报告补完,并确认引擎支持第七种 MOREDETAILS
ICONABOVEHEAD 缺键时的默认值没逆——需要 sub_677F30 在任务加载器里的调用点;这才是图标“亮”的来源
写 ICONABOVEHEAD:true 的实际效果发行数据无先例,没人验证
[REWARD] 的 *PCT 口径与 TREASURECLASS 的抽取方式没查
[DUNGEONS_WHEN_COMPLETE] / [DUNGEONS_WHEN_NOT_COMPLETE] 在关卡侧具体驱动什么没逆
sub_6F97C0 里 GLOBAL_TRILLBOT 特判的具体分支已补完:解锁内部成就 TRILLBOTASSEMBLED(成就表第 113 项)
任务 DAT 顶层键(QUESTRANKMIN/MAX、DIALOGPRIORITY、SHOWTIPS、CANABANDON、SHOWQUESTCOMPLETE)的语义本文只列出,没逆
改容器格数后界面是否跟着变没查
容器 DAT 里格数之外的键(优先级、自动放置、回购等)本文未展开

附录 A:符号、偏移与数字速查 ​

项值
复活选项加载器sub_651DF0(Torchlight2.exe,ImageBase 0x400000)
DAT 通用读法sub_677F00(字符串)/ sub_677F30(bool)/ sub_677F80(int)
复活对象字段偏移NAME +8 / GOLD_PENALTY +92 / XP_PENALTY +96 / FAME_PENALTY +100 / WHERE +104 / WEIGHT +136 / REQUIRES_ITEM +144 / TAKE_ITEM +152 / ALLOW_IN_NON_PORTAL_LEVELS +160 / DIFFICULTIESBIN +164 / INDEX +212
任务对象字段偏移+152 任务名 / +456 64 位 GUID / +8 已处理标志
任务按名查找 / 状态推进sub_6FB460 / sub_6F97C0(含 GLOBAL_TRILLBOT 特判)
编辑器任务状态导出EditorSetEditorActiveQuests / EditorSetEditorCompletedQuests
任务语料184 个任务;68 带两态文本 / 32 写过 ICONABOVEHEAD / 77 带关卡两分 / 85 带前置;PASSIVE 块 449 个
ICONABOVEHEAD 普查全 MEDIA 16,084 份 DAT;445 处全 false(441 在 PASSIVE);424 个块缺键;exe 内 UTF-16LE 命中 1 处
容器语料37 个槽位 DAT / 30 个容器 DAT;MAIN BAG 24 / BODY 13 / SPELLSLOTS 4

附录 B:支撑源材料与逆向结论映射 ​

  • 逆向验证项:
    • 复活机制:复活三选一机制、sub_651DF0 字段偏移及调用分析;
    • 容器机制:UI 面板格数与槽位映射规范、技能栏容器结构;
    • 任务机制:任务双态 DIALOG / DIALOG_COMPLETE 文本规范、ICONABOVEHEAD 字段全库分布与缺省特性。
  • 数据结构与配置清单:resurrect_and_saveload.json、quest_two_state.json、ui_three_panels.json。
  • 语料解析产物:quests.json(184 个任务的结构化解析)、containers.json(37 槽位 + 30 容器)。
  • 分析工具:tools/f2_resurrect.py、tools/f2_quests.py。