Decode battle passive activation state

This commit is contained in:
gamer147
2026-07-24 08:58:32 -04:00
parent 2641a62106
commit c494b786bf
6 changed files with 172 additions and 5 deletions

View File

@@ -1,7 +1,7 @@
<!-- DO NOT EDIT -- generated from vm-map/globals.toml by tools/globals_build.py --build -->
# Global Variable Reference (generated)
4276 globals (428 curated, 3848 auto shape-inferred). Source of truth: `vm-map/globals.toml`.
4277 globals (430 curated, 3847 auto shape-inferred). Source of truth: `vm-map/globals.toml`.
## choice-output
@@ -288,6 +288,7 @@
| `0x152421` | card_definition_visual_asset_ids | high | investigation | Card-result visual selected by FIELD before applying and presenting the effect. All 81 authored rows resolve to MVS*.AGF assets, with related card families sharing artwork. |
| `0x152486` | card_generation_weight_schedules | high | investigation | CDINIT's selector-specific candidate weights, parallel to card_generation_card_ids. FIELD computes base_weight + floor(current_stage_turn / growth_interval_turns) * growth_weight, with a zero interval selecting the fixed base path. The shipped lists populate 11..75 one-based slots. Columns: 0=base_weight, 1=growth_interval_turns, 2=growth_weight. |
| `0x1525b2` | card_generation_card_ids | high | investigation | Card ids parallel to card_generation_weight_schedules. FIELD scans slots 0..99, filters each nonzero id through the CDINIT2 story-flag rows, and performs cumulative weighted random selection. CDINIT clears only the first 50 slots even though its largest authored list reaches slot 75. |
| `0x15261f` | battle_triggered_passive_skill_flags | high | investigation | Per-exchange passive-skill activation matrix. CALCDMG clears both 300-cell side rows, considers the four equipped skills on each battle entity, retains category-4 passives allowed for that actor/target role, rolls skill_proc_chance_percent, and applies species, boss, movement-skill, action-category, and mutual-exclusion filters. BTL consumes the surviving flags to draw triggered-passive icons, play each passive's battle animation, and execute Roar, Counter, Reflect, Absorb, Pierce, Shield, Parry, and Revive behavior. |
| `0x152877` | battle_entity_indices | high | investigation | The two runtime entity rows participating in BTL. battle_actor_side_index and battle_target_side_index select these cells before BTL and CALCDMG access HP, unit definitions, skills, animation state, and voice banks. Columns: 0=side_0, 1=side_1. |
| `0x152879` | battle_selected_skill_ids | high | investigation | Selected skill for each battle side. BTL displays a positive entry through the skill-name table and selects skill-use voice columns; CALCDMG uses the same id for skill parameters. A zero entry follows the equipped/default ordinary-attack path. Columns: 0=side_0, 1=side_1. |
| `0x15288e` | battle_animation_effect_ids | high | investigation | BTANINIT2's sparse battle-animation composition table. BTANINIT expands the selected row into six runtime effect work slots. BTL anchors requested effects in slots 0 and 1 to the actor and slots 2 through 5 to the target; shipped data never populates slot 4. Columns: 0=effect_slot_0, 1=effect_slot_1, 2=effect_slot_2, 3=effect_slot_3, 4=reserved_effect_slot_4, 5=effect_slot_5. |
@@ -3747,6 +3748,7 @@
| `0xab8fb` | class_change_state_work | high | investigation | Ten-cell working copy of the current unit's unit_class_change_state row. CCINIT sets the slot belonging to an awarded rule; CALCCC manages the block copy between this buffer and persistent per-unit state. Columns: 0=promotion_slot_1, 1=promotion_slot_2, 2=promotion_slot_3, 3=promotion_slot_4, 4=promotion_slot_5, 5=promotion_slot_6, 6=promotion_slot_7, 7=promotion_slot_8, 8=promotion_slot_9, 9=promotion_slot_10. |
| `0x15261c` | battle_hit_result | high | investigation | CALCDMG initializes this to miss, changes it to hit after the accuracy gate, and then to critical after the critical-chance gate. BTL uses the same result to choose presentation, cut-ins, and critical voice columns; -1 is written only by a synthetic pre-battle path. |
| `0x15261d` | battle_hp_delta | high | investigation | CALCDMG's signed HP result. BTL subtracts it from the target's current HP, so positive values deal damage and negative values heal; zero and result state gate damage reactions and defeat handling. |
| `0x15261e` | battle_actor_hp_recovery | high | investigation | CALCDMG's actor-side HP recovery output. Absorb (SKINIT skill 40) contributes half of positive battle_hp_delta with a minimum of one, and the target's HP-absorption condition can add its level-scaled amount capped at the damage dealt. BTL adds the result to the actor's current HP, renders the green recovery number, and clamps the resource through the ordinary post-battle path. |
| `0x84a` | — | high | auto-shape | unit-name-table |
| `0xc32` | — | high | auto-shape | unit-desc-table |
| `0x101a` | — | high | auto-shape | unit-desc1-table |
@@ -3919,7 +3921,6 @@
| `0x15235a` | — | low | auto-shape | cdinit2-field? |
| `0x1523be` | — | low | auto-shape | cdinit2-field? |
| `0x152422` | — | med | auto-shape | cdinit2-field |
| `0x15261f` | — | med | auto-shape | record-table[stride 300] |
| `0x152878` | — | low | auto-shape | index/counter? |
| `0x15288b` | — | low | auto-shape | index/counter? |
| `0x152894` | — | low | auto-shape | btaninit2-field? |

View File

@@ -709,8 +709,10 @@ The v1 map labels *shapes and tables*; the next increments add *meaning*, cheape
story gates, map/minimap geometry, rewards, and 174 resolved entry/clear/failure transitions. With
RTINIT's used-provider surface closed, ITMES/SKMES/VIMES/EIMES/CIMES/MAMES joined, INFOMES/MES
classified, STINIT2's engine-dead authoring tier retained at medium confidence, and the
309-script ADV primary/alternate/transition surface-slot registry named, continue with
consumer-led tables that still have shipped readers;
309-script ADV primary/alternate/transition surface-slot registry named, the selected
movement-route grid reconstructed, and CALCDMG/BTL's two-side-by-300 triggered-passive
matrix plus actor HP-recovery output closed, continue with consumer-led tables that still
have shipped readers;
never assign one universal meaning to a parameter bank whose meaning varies by provider selector.
2. **Label remaining 2D record tables by their readers** — cross-reference which scripts read each
`rec[sN]` table and infer purpose from context, preserving reserved rows and sparse cells. RECOVER's

View File

@@ -3267,3 +3267,34 @@ Evidence regressions protect the exact corpus xrefs and clear geometry, all seve
readers, both SETROUTE flood-score copies, the INIT2 cardinal vectors, and every canonical name.
**Next:** investigate the remaining unnamed stride-300 table at `G[0x15261f]`.
## Data-semantics sidebar: battle triggered-passive matrix (2026-07-24)
The former stride-300 auto table at `G[0x15261f]` is the reserved `2 × 300`
`battle_triggered_passive_skill_flags` work matrix. Its first dimension is BTL's two battle sides,
and its second dimension is the SKINIT skill id. The complete corpus surface is compact and exact:
BTL and CALCDMG are its only readers/writers, with 37 two-dimensional lookups and three full
600-cell clears.
CALCDMG rebuilds the matrix for each exchange. A local eligibility surface first assigns the
actor/target roles of shipped passive ids 28..50 (with Seal/id 44 absent from this activation path).
The script then scans the four equipped skills on both battle entities, accepts category-4 passives,
applies the configured `skill_proc_chance_percent`, and filters surviving cells through the concrete
combat contract: species and boss slayers, anti-air/anti-sub movement skills, ordinary-versus-special
attack categories, role-specific defensive/offensive passives, and mutually exclusive reactions.
The result is an activation record for this exchange rather than the entity's persistent skill
ownership; `entity_skill_flags` remains the latter source.
BTL proves the output side. It indexes each participant's four equipped skills through the matrix
to draw the triggered-passive icons, and repeats the dynamic join for actor- and target-side passive
battle animations. Direct column tests then implement Roar, Counter, Reflect, Absorb, Pierce, Shield,
Parry, and Revive behavior. The adjacent `G[0x15261e]` is now
`battle_actor_hp_recovery`: CALCDMG adds Absorb's half-damage return and any level-scaled HP-absorption
condition amount, while BTL adds the result to the actor's HP and renders the green recovery number.
Evidence regressions protect the exact two-script/40-reference surface, all three `2 × 300` clears,
the passive-id eligibility range, category gate and dynamic filters, BTL's three skill-indexed
presentation consumers, and the actor-recovery producer/consumer pair.
**Next:** investigate the highest-use remaining auto-shaped row table, the stride-14 table at
`G[0x6f70]` (27 references across ADDEXP, EVOLVE, GAMECLEAR, GAMESTART, TRAIN, and UNITECH).

View File

@@ -71,8 +71,9 @@
- [x] **2.3n — Extract `STINIT2`'s stage-definition registry.** Name/string-matrix specialization corrects the generic 321-record view to 74 sparse stages plus 296 strings in six pre/post-clear description slots, classifying all 1,634 instructions. FORT, FIELD, SELSTAGE, and STAGECLEAR prove main/EX flags, seven-column story gates, unlock groups, numbered/event presentation, map/minimap geometry, base point and three-coin rewards, and the loader contract. All 174 entry/clear/failure decisions resolve through SCINIT and all 74 loader ids resolve to STINIT. The remaining `0xedc4d` array has no shipped reader and is retained at medium confidence as the authoring-only stage difficulty tier: values 1..8 correlate with challenge/reward progression but have no runtime effect.
- [x] **2.3o — Name the ADV layer surface-slot registry.** `G[0x3239]` is an eight-by-three primary/alternate/transition surface bank initialized by 143 scripts and read across 309. The common CG loader proves double-buffer selection and transition staging; INIT2 supplies the paired retained-object handles, and the companion resource, layer-index, live-slot, and handle globals are curated.
- [x] **2.3p — Name the selected movement-route grid.** `G[0xc6077]` is a reserved 1000-by-27 work grid carrying the one-cell-wide route copied from MVSEEK's remaining-step scores. SETROUTE proves the cardinal backtrace, FIELD proves route rendering and execution, SELACT proves move-before-action use, and RTN_M002..018 prove candidate-endpoint testing. The shared map target coordinates and INIT2 cardinal vectors are curated with it.
- [x] **2.3q — Name the triggered passive-skill matrix.** `G[0x15261f]` is a two-side-by-300 SKINIT-indexed activation matrix rebuilt for each CALCDMG exchange. CALCDMG gates owned category-4 passives by actor/target role, proc chance, species/boss/movement/action conditions, and pairwise exclusions; BTL uses the surviving flags for passive icons, reaction animations, and passive combat behavior. The adjacent actor HP-recovery output is curated with it.
- [x] **2.4 — Partial global-var map BUILT + wired into the disassembler.** `tools/global_map.py``build/global-var-map.{json,md}` (16,354/49,435 globals labelled: string tables, `*INIT` field arrays, 122 record tables w/ strides, current-entity index pointers). `sys4load` renders the labels inline (`=rec[s30]`, `=current-entity-index?`). See `docs/name-resolution.md`.
- [ ] **2.5 — Grow the global-var map (future, incremental).** Static first: RTINIT is closed at 1,043/1,043 movement steps; the RECOVER/ILINIT condition ABI, ALINIT recipes, AFINIT affinity/progression tables, CTINIT name palette, CVINIT character-voice registry, LAINIT terrain definitions, MPINIT stage-terrain atlas, SPINIT H-scene gallery, TRINIT training actions, CDINIT/CDINIT2 card selection/effects, BTANINIT/BTANINIT2 battle animations, STINIT2's complete stage surface, the 309-script ADV layer surface-slot registry, and the selected movement-route grid are closed; ITMES/SKMES/VIMES/EIMES/CIMES/MAMES are joined to their definitions; and the non-table INFOMES/MES ABIs are classified. Next, continue remaining consumer-led 2D table naming. Full detail: `docs/name-resolution.md` → "Future step — growing the map". Packed `call-script` ids, SCJUMP decision-to-scene dispatch, the shipped RTINIT movement-provider join, and the six completed message joins are resolved.
- [ ] **2.5 — Grow the global-var map (future, incremental).** Static first: RTINIT is closed at 1,043/1,043 movement steps; the RECOVER/ILINIT condition ABI, ALINIT recipes, AFINIT affinity/progression tables, CTINIT name palette, CVINIT character-voice registry, LAINIT terrain definitions, MPINIT stage-terrain atlas, SPINIT H-scene gallery, TRINIT training actions, CDINIT/CDINIT2 card selection/effects, BTANINIT/BTANINIT2 battle animations, STINIT2's complete stage surface, the 309-script ADV layer surface-slot registry, the selected movement-route grid, and the battle triggered-passive matrix are closed; ITMES/SKMES/VIMES/EIMES/CIMES/MAMES are joined to their definitions; and the non-table INFOMES/MES ABIs are classified. Next, continue remaining consumer-led 2D table naming. Full detail: `docs/name-resolution.md` → "Future step — growing the map". Packed `call-script` ids, SCJUMP decision-to-scene dispatch, the shipped RTINIT movement-provider join, and the six completed message joins are resolved.
## Phase 3 — Name the unnamed opcodes *(top ~20 BEFORE Phase 4; the rest on demand)*

View File

@@ -170,6 +170,11 @@ def test_load_and_lint():
and entries[0xccc0a]["name"] == "map_target_tile_x"
and entries[0xccc0b]["name"] == "map_target_tile_y",
"selected movement route grid and coordinate helpers are curated")
check(entries[0x15261e]["name"] == "battle_actor_hp_recovery"
and entries[0x15261f]["name"]
== "battle_triggered_passive_skill_flags"
and entries[0x15261f]["type"] == "int[2][300]",
"battle recovery output and triggered-passive matrix are curated")
# lint clean against a permissive address universe (curated addrs are self-consistent)
errors, warnings = G.lint(entries, set(entries))
check(errors == [], f"globals.toml lints clean (errors={errors})")
@@ -308,6 +313,110 @@ def test_selected_movement_route_grid_evidence():
0xccbd4: [0, 1, 0, -1, 0],
}, "INIT2 defines the no-move plus four-cardinal-neighbor vectors")
def test_battle_triggered_passive_skill_flags_evidence():
target = 0x15261f
recovery = 0x15261e
refs = []
recovery_refs = []
loaded = {}
for name, path in paths.scripts().items():
if path.read_bytes()[:8] != b"SYS4422 ":
continue
script = sys4load.load(path)
loaded[name] = script
for index, instruction in enumerate(script.instructions):
if (3, target) in instruction.args:
refs.append((name, script, index, instruction))
if (3, recovery) in instruction.args:
recovery_refs.append((name, instruction))
check({name for name, _, _, _ in refs}
== {"BTL.BIN", "CALCDMG.BIN"}
and len(refs) == 40
and sum(instruction.opcode == 0x12c
for _, _, _, instruction in refs) == 37
and sum(instruction.opcode == 0x6c
for _, _, _, instruction in refs) == 3,
"triggered-passive matrix has exactly 37 lookups and three clears in BTL/CALCDMG")
clears_cover_both_rows = True
for _, script, index, instruction in refs:
if instruction.opcode != 0x6c:
continue
previous = script.instructions[index - 1]
clears_cover_both_rows &= (
previous.label == "mul"
and previous.args[1:] == [(0, 2), (0, 300)]
and instruction.args[1] == previous.args[0]
)
check(clears_cover_both_rows,
"every triggered-passive clear covers both reserved 300-skill side rows")
calcdmg = loaded["CALCDMG.BIN"].instructions
eligibility_columns = {
instruction.args[-1][1]
for index, instruction in enumerate(calcdmg[:-1])
if 0x1f1 <= instruction.offset <= 0x3b9
and instruction.opcode == 0x12c
and instruction.args[1] == (9, 0xf)
and instruction.args[3] == (0, 300)
and calcdmg[index + 1].label == "mov"
and calcdmg[index + 1].args[1] == (0, 1)
}
check(eligibility_columns == set(range(28, 51)) - {44},
"CALCDMG seeds the actor/target eligibility surface for shipped passive skill ids")
category_gate = [
instruction for instruction in calcdmg
if instruction.offset == 0x4d6
]
dynamic_writes = {
instruction.offset
for instruction in calcdmg
if instruction.opcode == 0x12c
and instruction.args[1] == (3, target)
and instruction.args[3] == (0, 300)
and instruction.args[-1] == (9, 8)
}
check(len(category_gate) == 1
and category_gate[0].args == [
(12, 0), (3, 0xa6f86), (9, 8)
]
and dynamic_writes == {
0x52e, 0x57c, 0x5d3, 0x612, 0x62a, 0x65b,
0x688, 0x6bd, 0x725, 0x799, 0x7b5, 0x7fb,
0x817, 0x85d,
},
"CALCDMG gates category-4 skills then filters the side/skill activation cells")
btl_dynamic_consumers = {
instruction.offset
for instruction in loaded["BTL.BIN"].instructions
if instruction.opcode == 0x12c
and instruction.args[1] == (3, target)
and instruction.args[3] == (0, 300)
and instruction.args[-1] == (9, 0x12)
}
check(btl_dynamic_consumers == {0x1b8c, 0x2270, 0x2319},
"BTL indexes triggered flags by equipped skill for icons and both-side animations")
check({name for name, _ in recovery_refs}
== {"BTL.BIN", "CALCDMG.BIN"}
and len(recovery_refs) == 20
and any(instruction.offset == 0xa3f
and instruction.label == "div"
and instruction.args == [
(3, recovery), (3, 0x15261d), (0, 2)
]
for name, instruction in recovery_refs
if name == "CALCDMG.BIN")
and any(instruction.offset == 0x943
and instruction.label == "add"
and instruction.args[-1] == (3, recovery)
for name, instruction in recovery_refs
if name == "BTL.BIN"),
"CALCDMG computes actor recovery and BTL adds it to the acting entity's HP")
def test_merge_precedence():
curated, _ = G.load_toml(paths.VM_MAP / "globals.toml")
auto = G.load_auto(paths.BUILD / "global-var-map.json")
@@ -372,6 +481,7 @@ if __name__ == "__main__":
test_lint_catches_bad_vocab()
test_adv_layer_surface_slot_evidence()
test_selected_movement_route_grid_evidence()
test_battle_triggered_passive_skill_flags_evidence()
test_merge_precedence()
test_sys4load_labels_from_registry()
test_miner_finds_known_flags()

View File

@@ -2036,6 +2036,28 @@ usage = "CALCDMG's signed HP result. BTL subtracts it from the target's current
source = "investigation"
confidence = "high"
[[global]]
address = "0x15261e"
name = "battle_actor_hp_recovery"
category = "unknown"
type = "int"
value_domain = "nonnegative HP restored to the acting side"
usage = "CALCDMG's actor-side HP recovery output. Absorb (SKINIT skill 40) contributes half of positive battle_hp_delta with a minimum of one, and the target's HP-absorption condition can add its level-scaled amount capped at the damage dealt. BTL adds the result to the actor's current HP, renders the green recovery number, and clamps the resource through the ordinary post-battle path."
source = "investigation"
confidence = "high"
depends_on = ["0x4e085", "0x52383", "0x15261d"]
[[global]]
address = "0x15261f"
name = "battle_triggered_passive_skill_flags"
category = "data-table"
type = "int[2][300]"
value_domain = "{0,1}; rows are battle side, columns are SKINIT skill ids"
usage = "Per-exchange passive-skill activation matrix. CALCDMG clears both 300-cell side rows, considers the four equipped skills on each battle entity, retains category-4 passives allowed for that actor/target role, rolls skill_proc_chance_percent, and applies species, boss, movement-skill, action-category, and mutual-exclusion filters. BTL consumes the surviving flags to draw triggered-passive icons, play each passive's battle animation, and execute Roar, Counter, Reflect, Absorb, Pierce, Shield, Parry, and Revive behavior."
source = "investigation"
confidence = "high"
depends_on = ["0x4e693", "0x5212b", "0xa6f86", "0xaa8f2", "0xaaa1e", "0x15261a", "0x15261b", "0x152877"]
[[global]]
address = "0x152877"
name = "battle_entity_indices"