Challenger Continent · Weapon UNIT Bench

武器数值设计台The Forge Bench

Torchlight2.exe 1.25.9.5 的逐指令实现复算 —— float32 舍入、截断方向、ceil 与 trunc 的每一处差别都照抄。 改一个键,立刻看到面板会写什么、DPS 数字是多少、以及每一刀真正掉在什么区间。

工作台The Bench

左边填 UNIT BLOCK 的键,右边是引擎会算出来的东西。所有数字实时联动。

基础档与品质档 inherited

武器基础档
品质档
改这两项只会填表,不会自动覆盖你手动改过的数字。

基础伤害 the dimension

LEVEL · 曲线自变量
MINDAMAGE
MAXDAMAGE
曲线系数 · 覆写曲线时用
这两个键的绝对值不进结果:MAXDAMAGE 决定量纲总闸, 两者的比值决定下限系数 +0x3B8MAXDAMAGE:0 会让整条链不跑。

伤害分配 percent of base

DAMAGE_PHYSICAL
FIRE 火
ICE 冰
ELECTRIC 电
POISON 毒
1% 值
全部是 base 的整数百分比。不勾「写入」= 键不存在(默认 −1), 此时 MaxDmg = base;勾上并填 0生效的,物理基础伤害会真的归零。 元素键填 0 等于没写。右下角「1% 值」= 当前调参粒度。

乘数与攻速 multipliers

SPEED · 越小越快
SPEED_DMG_MOD
RARITY_DMG_MOD
DEVIATION_30FPS
SPEED 只进 DPS 分母、对伤害零影响SPEED_DMG_MOD 只进伤害、 对攻速零影响。两者同步改是完全对冲的(只改节奏不改强度)。

附加伤害 · 武器自身 baked into the table

武器 DAT 的内联 [EFFECT] TYPE:DAMAGE[AFFIXES] roll 出的词缀、附魔师加的, 在物品创建 / 存盘时被 bakeDamageEffectsIntoTable 一次性搬进伤害表、effect 本体随后被删除 —— 所以扫 effect 列表永远看不到它们。入桶取值是 (int)ceil(效果值), 按 flags & 0x400 分流:附魔师进桶B(祛魔会整桶清零),其余进桶A。

插槽 · 已镶宝石 scanned per socket, not baked

SOCKETS
MAX_SOCKETS
宝石不是 WEAPON,它的 52 / 10 effect 不会被烤sumDamageMax/MinOfType 每次求和都现扫每个插槽里宝石的 0 号 effect 链表, 读原始 +0xD4(float,不 ceil),而且每加一条截断一次。 每颗宝石各自不同,所以按插槽分别填。

烘焙链 loadCombatStatsFromDat @0x58F0C0

面板预览 CItemTooltip::build

近似排版 · 文案取自 MANDARIN/TRANSLATION.DAT

一刀实况 CCharacter::resolveStrikeHit

伤害包下限上限均值占比
暴伤加成 % 填 0 即「暴击 = 全部取上限」这一已验证行为
每个类型是独立伤害包,各自 rand_int_between(min,max) 掷一次、 各自过 applyArmorSoakAndAbsorb —— 物理吃护甲,元素吃对应抗性。 暴击不掷点,全部取上限再叠暴伤。

随等级变化 the curve

面板 DPS 真实均值 DPS 105 级(曲线末点)
◆ ◆ ◆

体检The Assay

按引擎实际分支给出的判定,不是经验法则。

致命 · 会让数值链失效 警告 · 结果与直觉不符 提示 · 机制说明 良好
◆ ◆ ◆

DAT 出入In & Out

左边填身份字段,右边直接拿走 UNIT BLOCK;也可以把现成的 .DAT 丢进来反填。

身份字段 identity

NAME
UNIT_GUID
UNITTYPE
BASEFILE
DISPLAYNAME
名称颜色
ICON
RARITY · 掉落权重
MINLEVEL
MAXLEVEL

从 .DAT 导入 read back

拖入 .DAT,或点击选择文件
只读取粘进来的那一层。BASEFILE 继承来的键(多数武器的 DEVIATION_FROM_30FPSRARITY_DMG_MOD)请用上面的基础档 / 品质档补。

UNIT BLOCK copy & paste

下载的文件是 UTF-16LE + BOM + CRLF,可直接放进 MEDIA 树,不含任何注释行
公式与出处 · 每一行都能在二进制里指到

基址 0x400000,Torchlight2.exe 1.25.9.5。烘焙只在物品创建 / 读档 / 祛魔时发生一次, 之后所有消费端只读烤好的三个东西:+0x3A4 基础物理、+0x3B8 下限比值、 +0x3EC 桶C(DAT 基础元素伤害)。

ratio  = f32(MINDAMAGE / MAXDAMAGE)                          @0x58F17C  fild/fidiv/fstp
pctArg = trunc(MAXDAMAGE × f32(SPEED_DMG_MOD×0.01f)
                        × f32(RARITY_DMG_MOD×0.01f))
base   = ceil(BASE_WEAPON_DAMAGE(LEVEL) × pctArg × 0.01f)     @0x57C840  仅 MAXDAMAGE≠0 才调用
v150   = f32(base × 0.01f)
元素k  = trunc(DAMAGE_k × v150)      k: FIRE=2 ICE=3 ELEC=4 POIS=5      → 桶C @0x58E460
MaxDmg = DAMAGE_PHYSICAL > -1 ? trunc(DAMAGE_PHYSICAL × v150) : base   → +0x3A4
MinDmg = trunc(MaxDmg × ratio)                                          → +0x3A0

面板 DPS  = ceil((D + ratio×D) × 0.5 / T)                     @0x5803E0
   T = DEVIATION_FROM_30FPS × SPEED/100
   D = +0x3A4 + Σ(k=0..6) sumDamageMaxOfType(k)
       + Σ(每颗宝石){ 宝石+0x3A4 + sumDamageMaxOfType(this,6) + Σ(k) sumDamageMaxOfType(宝石,k) }
   sumDamageMaxOfType(k) @0x580120 = Σ(桶A + 桶B + 桶C)  原值
   sumDamageMinOfType(k) @0x580260 = Σ(桶A + 桶B + 桶C×ratio)

一刀      = 逐类型 rand_int_between(min, max),各自 applyArmorSoakAndAbsorb  @0x548850
暴击      = max + trunc(暴伤% × 0.01 × max)     ← 不掷点
地址函数作用
0x58F0C0CEquipment::loadCombatStatsFromDat读全部 UNIT 级战斗 descriptor
0x57C840CEquipment::setBaseWeaponDamage等级曲线缩放 → +0x3A0/+0x3A4/+0x3B0
0x58E460CEquipment::addDatBaseDamageOfType写桶C(4 个调用点 = 四种元素)
0x5803E0CEquipment::computeDPS面板 DPS,只被 tooltip 与编辑器调用
0x580120 / 0x580260sumDamageMax/MinOfType逐类型求和;只有桶C 乘 ratio
0x548850CCharacter::resolveStrikeHit实战:7 类型循环 + 掷点 + 暴击
0x5182E0CAttackDescription::ctor+0x30[0]=MaxDmg,+0x4C[0]=MinDmg
0x577E70getAttackSpeedLabel档位阈值 {130,110,90,80,0} @0x2173A78

两处已知的原版口径问题本工具都照实复现:① 插槽循环里 sumDamageMaxOfType(this,6) 传的是武器本体且类型写死 6, 每颗宝石重复计一次;② ratio 被乘在整个 D 上,平坦加成因此被低估。