TO:R Data Map

Elemental resistance in HP damage

Source
docs/executable/elemental-resistance.md
SHA-256
6dfc436e…51b2e03b

How equipment's elemental defense enters the HP damage formula: its opposing-element lookup, target-affinity conditions and limits. This covers the elemental part of R42-I10 (../issues/R42-I10-elemental-resistance.md); no in-game behavior has been verified.

Based on The unpacked executable, SHA-256 7b0230a11498f40396c9048345e7cfe0ef442cb7ca8faffc6f197920d2669599; battle/battle_data_release.dat, entries #11, #16 and #19; localize/LocalizeText_en-us.dat, entries #37 and #54.
Checked 2026-10-15
Method Ghidra 12.1.4 through REA: decompile 0x140388bb0, 0x14038bff0, 0x14038c220 and 0x140451230; disassemble 0x14038bff0; read the lookup tables at 0x140689BA8 and 0x140689B40. Cross-check against saved Ghidra decompilation and objdump -d; DuckDB queries below. Code interpretations are likely, not runtime observations.

Which equipment value is read

Likely: 0x140388bb0 passes the attacker, defender, action and the paired HP formula rows to 0x14038bff0. For each elemental component, that function calculates an offensive contribution and calls 0x14038c220 with the defender and defender formula row. Its fifth argument is passed on the stack at 0x14038c1ca; the decompiler omits it from the displayed call, but the assembly preserves it.

Likely: the helper maps the incoming element to the element that beats it, sums that column of arms_data.element_resist over the defender's equipment slots 1–6, and multiplies by the corresponding defender hp_damage_data.element_def weight. Equipment slot 0 is excluded. These are signed equipment bytes and signed 16-bit formula weights.

Incoming element Equipment defense column read Zero-based array index
Air Ice 5
Earth Air 0
Lightning Earth 1
Water Lightning 2
Fire Water 3
Ice Fire 4
Light Dark 7
Dark Light 6

Confirmed: the executable bytes at 0x140689BA8 (file offset 0x6881A8) decode as ten little-endian 32-bit values: 0, 6, 1, 2, 3, 4, 5, 8, 7, 0. The eight named element IDs are 1–8. The lookup therefore agrees with the game's own help: SELECTHELP_LC_SHTXT_ELE02_DEF_TEXT through ELE09_DEF_TEXT describe strength in one element providing resistance to the next weaker element. In particular, FIRE strength resists ICE, and WATER strength resists FIRE. This mapping alone does not demonstrate a defect.

When that value affects damage

Likely: the helper also checks the incoming element against the defender's affinity (unit byte +0x185), using the 10-by-10 byte matrix at 0x140689B40 (file offset 0x688140). Let U be the offensive contribution for this component, and R be the weighted equipment sum described above. It returns:

Incoming element versus defender affinity Matrix value Contribution returned
Incoming element beats the defender's affinity 1 max(U - R, 0)
Defender's affinity beats the incoming element 2 min(-R, 0)
Same element, neutral relationship, or no mapped element 0 0

Likely: for the six-element cycle, only two defender affinities can produce a nonzero contribution for any incoming element. For ICE these are AIR (first branch) and FIRE (second branch). LIGHT against DARK and DARK against LIGHT both use the first branch; the matrix has no value-2 branch for these two elements. A missing defender also returns zero. These conditions are executable logic; changing formula weights alone cannot make the neutral branch use resistance.

Likely: U is the action's elemental bonus amount times the attacker's element_atk weight, plus the battlefield element times field_element. 0x14038bff0 also handles elemental buff-card contributions; each component goes through the same helper. In the spell action builder 0x140451230, the action's bonus amount is the larger of the effect slot's byte +0x42 and the selected weapon's element_bonus. It is separate from the spell's ordinary amount.

Likely: the helper's result joins the percentage term in 0x140388bb0, alongside the separate attacker-affinity versus defender-affinity bonus and other modifiers. The combined term is capped by tuning_values rows 79 and 80 before multiplying the stat-derived part of damage; the ATK/DEF contribution is added separately. Equipment resistance is therefore not a flat percentage of final damage.

Likely: with nonnegative equipment values and weights, the first branch can cancel U but cannot make this component negative. If U is zero, adding resistance in that branch changes nothing, even though the attack has elemental advantage. The second branch can supply a negative contribution without an offensive bonus. This distinction and the neutral branch can explain a reported lack of effect, but neither establishes developer intent or an elemental bug without a controlled observation.

Decoded-data checks

Run these SQL statements with DuckDB from the repository root. The file pages in Based on bind them to the source game-file hashes.

SELECT _row, _name_id, element_bonus,
       "element_resist[3]" AS water_defense,
       "element_resist[4]" AS fire_defense,
       "element_resist[5]" AS ice_defense
FROM read_json_auto('data/game/battle/battle_data_release.dat/11.arms_data.jsonl')
WHERE _row IN (250, 465, 466);

SELECT _row, _name_id, "effect_kind[0]", "amount_type[0]",
       "formula_id[0]", "element[0]", "unk_0x42[0]"
FROM read_json_auto('data/game/battle/battle_data_release.dat/16.effect_data.jsonl')
WHERE _row = 91;

SELECT _row, "element_atk[5]", "element_def[4]", "field_element[5]"
FROM read_json_auto('data/game/battle/battle_data_release.dat/19.hp_damage_data.jsonl')
WHERE _row IN (21, 22);

SELECT key, text
FROM read_json_auto('data/game/localize/LocalizeText_en-us.dat/54.txte.jsonl')
WHERE key SIMILAR TO 'SELECTHELP_LC_SHTXT_ELE0[2-9]_DEF_TEXT';

Confirmed: Mage Staff (arms row 250) has element_bonus 0. Robes of the Inferno (465) has FIRE defense 10; Robes of Black Ice (466) has ICE defense 10. Iceblast (effect row 91) has HP effect kind 1, amount type 3, formula 21, element 6 (ICE), and byte +0x42 zero. Formula row 21 has ICE attack weight 100 and ICE field weight 50; defender row 22 has FIRE defense weight 100. The arrays are populated and the relevant defense weight is nonzero. These decoded values do not prove their in-game effect.

Evidence and limits

REA Evidence IDs from this inspection:

Observation Evidence ID
Elemental helper decompilation ev_f94f6f07d7cfbd84488e25e1f72162a50854071c3599880c4573f7721c342c33
Elemental component caller decompilation ev_f1e2a0032d12a3dc695399b3f1d231df1bb03ec2382053ff5785e820e2e064cf
HP formula decompilation ev_eba0ea3b7fd043abd5aab9254f61c3e2ffbe7df4673645047fab0f15ac4b2492
Spell action builder decompilation ev_f1aed27a59f4911c3c2ff163a773a9fa36ca32f986af7bf0266355e699910e97
Caller assembly, including fifth argument ev_62696f6d095983faabfe35326f6988b3cd2b94fcfd513e859ebab4574456345c
Opposing-element table bytes ev_b7bc5bf94b897bd36dc978cf101c526aaec5ee81a06f885446efbaa452b90fe1
Affinity matrix bytes ev_1bc8c4babaa1edbc2c53722f1003fe3ed6f6ae00482d52835ebc9364ab834006

This is one executable's static HP damage path. It does not establish PSP behavior, developer intent, another build's behavior, or the result of an in-game attack. HP restoration has a separate consumer (0x14038aaf0), documented in hp_restore_data; the opposing-element rule above must not be applied to that path without tracing it. The controlled tests and unresolved questions are in the issue report (../issues/R42-I10-elemental-resistance.md).