Decode STINIT tagged object payloads
This commit is contained in:
@@ -564,8 +564,9 @@ def attach_stage_object_placements(records: list[dict]) -> None:
|
||||
"tile_x": "0xe7325",
|
||||
"tile_y": "0xe7357",
|
||||
"difficulty_mask": "0xe7483",
|
||||
"reinforcement_interval_turns": "0xe741f",
|
||||
"reinforcement_spawn_limit": "0xe7451",
|
||||
}
|
||||
unknown_bases = ("0xe73bb", "0xe73ed", "0xe741f", "0xe7451")
|
||||
for record in records:
|
||||
fields = record.get("array_fields", {})
|
||||
objects = []
|
||||
@@ -593,11 +594,27 @@ def attach_stage_object_placements(records: list[dict]) -> None:
|
||||
obj["required_story_flags"] = required
|
||||
if forbidden:
|
||||
obj["forbidden_story_flags"] = forbidden
|
||||
unknown = {
|
||||
base: fields[f"{base}/{slot}"]
|
||||
for base in unknown_bases
|
||||
if f"{base}/{slot}" in fields
|
||||
payload = {
|
||||
base: fields[key]
|
||||
for base in ("0xe73bb", "0xe73ed")
|
||||
if (key := f"{base}/{slot}") in fields
|
||||
}
|
||||
type_id = obj["type_id"]
|
||||
if type_id in (1, 2, 3, 4) and "0xe73bb" in payload:
|
||||
obj["initial_faction_id"] = payload.pop("0xe73bb")
|
||||
elif type_id in (6, 36):
|
||||
if "0xe73bb" in payload:
|
||||
obj["destination_tile_x"] = payload.pop("0xe73bb")
|
||||
if "0xe73ed" in payload:
|
||||
obj["destination_tile_y"] = payload.pop("0xe73ed")
|
||||
elif type_id in (7, 8):
|
||||
if "0xe73bb" in payload:
|
||||
obj["item_id"] = payload.pop("0xe73bb")
|
||||
if "0xe73ed" in payload:
|
||||
obj["item_quantity"] = payload.pop("0xe73ed")
|
||||
elif type_id == 28 and "0xe73bb" in payload:
|
||||
obj["card_generation_list_id"] = payload.pop("0xe73bb")
|
||||
unknown = payload
|
||||
if unknown:
|
||||
obj["unknown_fields"] = unknown
|
||||
objects.append(obj)
|
||||
@@ -624,7 +641,6 @@ def attach_stage_enemy_spawns(records: list[dict]) -> None:
|
||||
"movement_routine_set_ids": ("0xe7889", 3),
|
||||
"battle_routine_set_ids": ("0xe78e3", 3),
|
||||
}
|
||||
unknown_bases = ("0xe77d5",)
|
||||
for record in records:
|
||||
fields = record.get("array_fields", {})
|
||||
footer_arrays = record.get("footer_arrays", {})
|
||||
@@ -665,11 +681,13 @@ def attach_stage_enemy_spawns(records: list[dict]) -> None:
|
||||
spawn["required_story_flags"] = required
|
||||
if forbidden:
|
||||
spawn["forbidden_story_flags"] = forbidden
|
||||
unknown = {
|
||||
base: fields[f"{base}/{slot}"]
|
||||
for base in unknown_bases
|
||||
if f"{base}/{slot}" in fields
|
||||
}
|
||||
unknown = {}
|
||||
if (mode_key := f"0xe77d5/{slot}") in fields:
|
||||
mode = fields[mode_key]
|
||||
if mode == 2:
|
||||
spawn["first_clear_only"] = True
|
||||
else:
|
||||
unknown["0xe77d5"] = mode
|
||||
if unknown:
|
||||
spawn["unknown_fields"] = unknown
|
||||
spawns.append(spawn)
|
||||
|
||||
@@ -113,13 +113,41 @@ def test_real_mixed_table() -> None:
|
||||
"tile_x": 13,
|
||||
"tile_y": 1,
|
||||
"difficulty_mask": 7,
|
||||
"unknown_fields": {"0xe73bb": 1},
|
||||
"initial_faction_id": 1,
|
||||
}, "STINIT joins confirmed object buffers into one placement record")
|
||||
stage1_slot6 = next(
|
||||
obj for obj in records[0]["object_placements"] if obj["slot"] == 6
|
||||
)
|
||||
check(stage1_slot6["item_id"] == 222
|
||||
and stage1_slot6["item_quantity"] == 1,
|
||||
"STINIT object payloads decode by object type")
|
||||
stage1_slot5 = next(
|
||||
obj for obj in records[0]["object_placements"] if obj["slot"] == 5
|
||||
)
|
||||
check(stage1_slot5["reinforcement_interval_turns"] == 10
|
||||
and stage1_slot5["reinforcement_spawn_limit"] == 3,
|
||||
"STINIT object placements expose reinforcement schedules")
|
||||
stage1_slot8 = next(
|
||||
obj for obj in records[0]["object_placements"] if obj["slot"] == 8
|
||||
)
|
||||
check(stage1_slot8["card_generation_list_id"] == 1,
|
||||
"STINIT card objects expose their generation-list id")
|
||||
stage1_slot9 = next(
|
||||
obj for obj in records[0]["object_placements"] if obj["slot"] == 9
|
||||
)
|
||||
check(stage1_slot9["destination_tile_x"] == 13
|
||||
and stage1_slot9["destination_tile_y"] == 7,
|
||||
"STINIT teleport payloads expose destination coordinates")
|
||||
stage2_slot3 = next(
|
||||
obj for obj in records[1]["object_placements"] if obj["slot"] == 3
|
||||
)
|
||||
check(stage2_slot3["required_story_flags"] == [902],
|
||||
"STINIT object placements join positive story prerequisites")
|
||||
all_objects = [
|
||||
obj for record in records for obj in record["object_placements"]
|
||||
]
|
||||
check(sum("unknown_fields" in obj for obj in all_objects) == 185,
|
||||
"STINIT preserves unresolved tagged object payloads as raw evidence")
|
||||
extract_init.attach_stage_enemy_spawns(records)
|
||||
first_spawn = records[0]["enemy_spawns"][0]
|
||||
check(first_spawn == {
|
||||
@@ -133,7 +161,7 @@ def test_real_mixed_table() -> None:
|
||||
"object_slot": 2,
|
||||
"movement_routine_set_ids": [1, 1, 1],
|
||||
"forbidden_story_flags": [11],
|
||||
"unknown_fields": {"0xe77d5": 2},
|
||||
"first_clear_only": True,
|
||||
}, "STINIT joins confirmed enemy buffers into one spawn record")
|
||||
stage1_slot2 = records[0]["enemy_spawns"][1]
|
||||
check(stage1_slot2["random_selection_weight"] == 1
|
||||
@@ -141,6 +169,11 @@ def test_real_mixed_table() -> None:
|
||||
"STINIT preserves weighted object-linked enemy alternatives")
|
||||
check(sum(len(record["enemy_spawns"]) for record in records) == 1378,
|
||||
"STINIT assembles every populated enemy spawn slot")
|
||||
check(sum(
|
||||
spawn.get("first_clear_only", False)
|
||||
for record in records
|
||||
for spawn in record["enemy_spawns"]
|
||||
) == 485, "STINIT exposes every first-clear-only enemy gate")
|
||||
|
||||
|
||||
def test_real_message_tables() -> None:
|
||||
|
||||
Reference in New Issue
Block a user