task: task lists
- Source
docs/formats/task.md- SHA-256
924351c3…3eda7994
Header · Instructions · Rebuilding · Leads
Lists of task instructions keyed by a u16 ID: each task list sets game variables and game flags, changes the party, the goth, the day, loyalty, the shop level and the like, when the story reaches a step that names it (pgrs task_id, condition_task_id, menu_action_task_id). The same instruction encoding is used by invk, whose instructions are conditions. Found as a pakd entry with pakd id 3 in every event/chapter/chapter_*.dat, as three entries of event/event_data.dat, and as entries 20–28 of WarrenReport/WarrenReport.dat. Terms are as in the glossary.
| Based on | All 21 TASK entries: event/chapter/chapter_*.dat#2 (9), event/event_data.dat#8, #12, #17 and WarrenReport/WarrenReport.dat#20–#28; Gibbed's ScreenplayTaskFile.cs, BaseTaskFile.cs, Screenplay/TaskOpcode.cs and TaskOpcodeHelpers.cs (checked 2026-10-11); the unpacked executable, SHA-256 7b0230a11498f40396c9048345e7cfe0ef442cb7ca8faffc6f197920d2669599. |
| Checked | 2026-10-12 |
| Method | A single-file C# program parsing every entry with Gibbed's layout and operand sizes; static analysis with Ghidra 12.1.4 of the setup (0x14035a350), the runner (0x14035a0b0), the opcode table at 0x140694BD0 (view exe.task_opcodes) and every opcode's handler (R31 review, 2026-10-11); each handler's operand reads, with objdump -d where the decompile was unclear (R29, 2026-10-12). Checked against the game data, 2026-10-12: the round trip, tor round-trip local/game/Data/event/chapter local/game/Data/event/event_data.dat local/game/Data/WarrenReport/WarrenReport.dat --docs docs, rebuilt all 21 task entries from their decoded rows (every operand written from its field, no byte taken from the original instructions) and the 11 files byte for byte; tor read-back over the same files read the decoded data back to the same files. 2026-10-13 (R36): the round trip again, tor round-trip local/game/Data/event local/game/Data/WarrenReport --docs docs --quiet, 21/21 task entries byte-identical; the decode refuses an entry whose padding isn't FF, whose lists don't each start where the one before ended, with an empty list, or with an opcode not in Opcodes. DuckDB over the exported data (data/game/event/**/*.task.jsonl and data/game/WarrenReport/**/*.task.jsonl, the invk rows in *.invk/*.jsonl, and the entries' bytes in local/bin/, all from tor export) for the facts that cite it. No in-game tests yet. |
| Name from | The game's magic TASK. The game also says "task" for battle objectives (BATTLETASK_LC_BTL_TSK…, TurnEndBonusTaskUpdateJob), a different thing. |
Header
| Offset | Size | Type | Name | Meaning | Confidence | Source |
|---|---|---|---|---|---|---|
| 0x00 | 4 | raw[4] | magic |
TASK. The game doesn't check it; it finds the entry by pakd id. |
confirmed | The round trip (21/21). 0x140350880; data. Agrees with Gibbed. |
| 0x04 | 2 | u16 | list_count |
Number of task lists. | confirmed | The round trip (21/21). 0x14035a350 sizes the ID array by it; 0x14035a0b0 bounds the ID search. Gibbed count. |
| 0x06 | 2 | raw[2] | padding |
FF FF, to a 4-byte boundary. Never read. |
confirmed | The round trip (21/21; R36: the decode refuses other bytes). Agrees with Gibbed CheckAndSkipPadding. |
| 0x08 | 2 × list_count |
u16[] | list_ids |
Each list's ID. | confirmed | The round trip (21/21). 0x14035a0b0 compares the ID asked for with each. Gibbed ids. |
| next | 0 or 2 | raw | padding |
FF FF when list_count is odd, to a 4-byte boundary. |
confirmed | The round trip (21/21; R36: the decode refuses other bytes). Agrees with Gibbed. |
| next | 4 × list_count |
u32[] | list_offsets |
Each list's offset from the start of the entry. | confirmed | The round trip (21/21). 0x14035a350 (finds them at 8 + align4(2 × list_count)), 0x14035a0b0. Gibbed offsets. |
Then the lists, one after another in order: each is a run of instructions ending with FF FF.
- confirmed: every entry parses this way: every padding byte is
FF, and each list starts where the previous one ended. The bytes after the last list are notFFpadding but thepakdentry's zero padding,align16(end + 1)zeros as for other entries (containers.md). Source: the round trip (R36); the byte maps of the file pages (tor pages, 2026-10-13), where each entry with lists ends in a zero tail up to the next 16-byte boundary (event_data, WarrenReport, the chapter files). - confirmed: the entries with no lists,
event/event_data.dat#12and#17andWarrenReport/WarrenReport.dat#20–#22and#25–#28, areTASK,list_count0,FF FF, thenFF FFat 0x08 and 6 zeros: anFF FFthat no offset points at. Source: DuckDB over their bytes (R36). - confirmed: no list is empty and no ID appears twice in an entry. The lists aren't in ID order in the chapter entries; they are in
WarrenReport.dat#23and#24(event_data.dat#8has one list). Source: the round trip (R36: the decode refuses an empty list); DuckDB over the*.task.jsonl(each list's ID from its step-0 row, R36). - likely: the game finds a list by a linear search for the first matching ID, and runs it to its
FF FF. Source:0x14035a0b0.
Instructions
An instruction is a u16 opcode, then the opcode's operand bytes; FF FF ends the list. The game finds each opcode's operand size and handler in a table of 114 records of 16 bytes at 0x140694BD0: {u32 operand_size, u32, u64 handler} (view exe.task_opcodes). It calls the handler with the operands and moves on by size + 2; an opcode above 113 stops the list without an error (0x140470480).
- confirmed: each opcode's operand size, where its last row in Operands ends, is the game's table's for all 114 opcodes, and equals Gibbed's. Source: unit test
TaskOperandSizesAreTheGamesagainstexe.task_opcodes; the round trip; GibbedTaskOpcodeHelpers.cs. - likely: how each opcode's operand bytes split into fields, and each field's type and sign, as in Operands, read from its handler. The round trip writes every operand from its field, but would pass with any split that covers the same bytes. Source: the handlers.
- confirmed: walking every list in
taskandinvkentries with these sizes finds only opcodes 0–113, and every list ends exactly at itsFF FF. Source: the round trip (R36, and invk.md's): the decode walks every list with these sizes, refuses an opcode with no row in Opcodes, and finds each list where the one before ended. - Opcodes 0–40 are tasks (set a game variable or game flag, change the party or a value), 48–86 conditions (used in
invk), which set the runner's result at+0x1C, and 96–113 expressions over the conditions that follow them (0x140359f90). 24, 41–47 and 87–95 do nothing (one shared handler,0x14004f490, that returns 1). Source:exe.task_opcodes; the handlers.
Opcodes
Each opcode's name, from what its handler does (static analysis, R31 review); the guess names keep what Gibbed's name says where the code fits it but doesn't show it. Expression names list their groups in order: all_k (all of k conditions) and any_k (any of k). Offsets are into the unit record (+0x148 HP, +0x14A MP, +0x188 team, …) unless another object is named. The party is the object at 0x140ED3960 (code of Battle\Army.cpp); "TacosGame" is the game object.
| Opcode | Name | Confidence | What the handler does | Gibbed |
|---|---|---|---|---|
| 0 | nop_0 |
likely | 0x14004f490 returns 1; operand size 0 |
Invalid |
| 1 | set_game_var |
likely | 0x14046dba0 → 0x1402cf2a0(0x140D520A0, u16, u8), which writes by the definition in 0x140D68150 |
SetGlobalFlag |
| 2 | set_game_flag |
likely | 0x14046dbd0: sets or clears bit u16 of 0x140D52540 by u8; no bounds check |
SetLocalFlag |
| 3 | add_approval |
guess | 0x14046dc20 → 0x1403213b0: byte [u8] of the table at 0x140F223D8 (or battle object +0x1B88) += s8, clamped 0–100 |
ModifyApproval |
| 4 | set_worldmap_location |
likely | 0x14046dc60: 0x140D5263C = u8 (old → 0x140D52640), 0x140D5263E = 0; the world map (0x1403e2a20) matches 0x140D5263C with its location records |
MoveToStrongpoint |
| 5 | add_goth |
guess | 0x14046dcf0: s32 ≥ 0 → 0x14031ba50 adds to party +0x1AC80 (max 99,999,999), < 0 → 0x14031fec0 subtracts (min 0); records stat 0xF |
ModifyGoth |
| 6 | op_6 |
— | 0x14046dd50: 0x1402c4350(TacosGame +0x3190, u8), the day, and stat 0xE; the world map (0x1403e59d0) adds 1 to it per move |
ModifyDays |
| 7 | add_battle_loyalty_lawful |
guess | 0x14046dda0 → 0x140470510(1, s8): battle units of team 1 whose +0x186 bits 3–4 = 1 get 0x14032f460(unit, 0xB, s8) |
ModifyLoyaltyForBattlePartyLawfulUnits |
| 8 | add_battle_loyalty_neutral |
guess | 0x14046ddc0 → 0x140470510(3, s8, 1) |
…NeutralUnits |
| 9 | add_battle_loyalty_chaotic |
guess | 0x14046dde0 → 0x140470510(2, s8, 1) |
…ChaosUnits |
| 10 | add_party_loyalty_lawful |
guess | 0x14046de00: 0x140470510(1, s8, 0) + 0x1404704a0(1, s8) (party records, +0x8E bits 3–4) |
ModifyLoyaltyForBattlePartyAndRosterLawfulUnits |
| 11 | add_party_loyalty_neutral |
guess | 0x14046de30: group 3 |
…NeutralUnits |
| 12 | add_party_loyalty_chaotic |
guess | 0x14046de60: group 2 |
…ChaosUnits |
| 13 | set_hero_alignment |
guess | 0x14046de90: unit 1's +0x8E bits 3–4 (and battle +0x186) = table 0x1406952F0[min(u8, 2)] |
SetAlignmentForHero |
| 14 | clear_game_vars_by_mask_bit |
likely | 0x14046df20: for each definition in 0x140D68150 whose u16 at +2 has bit u8 set, sets that variable to 0 |
ResetGlobalFlagsInCategory |
| 15 | set_unit_clan |
guess | 0x14046dfa0: party unit (u16 ID) +0x8E bits 0–2 = u8, battle copy +0x186 too |
SetClanForPartyCharacter |
| 16 | add_clan_loyalty |
guess | 0x14046e020: party units with +0x8E bits 0–2 = u8: 0x14032f460(+0x40, 0xB, s8) |
ModifyLoyaltyForRosterUnitsInClan |
| 17 | set_unit_loyalty |
guess | 0x14046e090: party unit u16: 0x140334f70(+0x40, u8), battle copy too |
SetLoyaltyForPartyCharacter |
| 18 | set_worldmap_child_location |
guess | 0x14046dcb0: 0x140D5263E = u16 (old → 0x140D52630), 0x140D5263C = 0 |
MoveToStrongpointChild |
| 19 | set_hero_clan |
guess | 0x14046e100: unit 1 +0x8E bits 0–2 = u8 |
SetClanForHero |
| 20 | add_item |
guess | 0x14046e170 → 0x14031bc10(party, u16) → 0x1403205d0 on party +0x4EA60 |
AddItemToParty |
| 21 | set_unit_strategy |
guess | 0x14046e1a0: battle unit (u16) +0x1D8 = u8 |
SetActionStrategyForBattleUnit |
| 22 | suppress_death_scene |
guess | 0x14046e1e0 → 0x1402cb710: appends u16 to a 30-entry list at game vars +0x550 (count +0x54D) |
SuppressDeathSceneForBattleUnit |
| 23 | set_victory_condition_id |
guess | 0x14046e210: 0x140D525E0 = u8, 0x140D5C954 = 1 |
SetClearConditionMessageId |
| 24 | nop_24 |
likely | 0x14004f490 |
NoOperation24 |
| 25 | set_victory_condition_unit_id |
guess | 0x14046e230: 0x140D525E1 = u8, 0x140D5C954 = 1 |
SetClearConditionUnitId |
| 26 | op_26 |
— | 0x14046e250 → 0x1403772a0(u8) (region BattleGameOver) |
SetAllegianceForHeroAndParty |
| 27 | set_unit_team |
guess | 0x14046e270: battle unit (u16) +0x188 = u8 |
SetFactionForCharacterInBattle |
| 28 | set_extra_gameover_condition_id |
guess | 0x14046e2b0: 0x140D525EF = u8 (read in 0x140344fc0) |
SetExtraGameOverConditionTextId |
| 29 | set_dungeon_var |
guess | 0x14046e2c0 → 0x1402cf3a0: writes a variable of the second definition table (event_data #4, 0x140D68158, storage +0x4B0–0x4EF) |
SetDungeonFlag |
| 30 | reset_dungeon_vars |
guess | 0x14046e2f0: 0x1402cd6d0 clears +0x4B0–0x4EF; clears 0x140D5262E |
ResetDungeonFlags |
| 31 | set_unit_class |
guess | 0x14046e320: party unit: if +0x89 ≠ value, 0x140330fa0(unit, value) and refreshes |
SetPartyCharacterClass |
| 32 | remove_item |
guess | 0x14046e3c0: u16 ≠ 0: 0x14031ede0, 0x140337250/0x14032ea10 → 0x1403201e0; ID / 1000 == 1 → 0x140320160 |
RemoveItemFromParty |
| 33 | give_title |
guess | 0x14046e450 → 0x1402cf030(n − 1, 1): game var 0x6A6 + n − 1 = 2; notice 4 (0x140309dc0, MenuNoticeWindow) if it was 0 |
GiveAppellation |
| 34 | raise_shop_level |
likely | 0x14046e480: Shop_Level (0x1402cde80, game var 0x46B) < u8 → notice 6; 0x1402cf500 sets 0x46B only if higher (shop_stock) |
IncreaseShopLevel |
| 35 | set_unit_target |
guess | 0x14046e560: battle unit +0x18A |= 0x80 |
SetTargetFlagForCharacterInBattle |
| 36 | remove_battle_commodity |
guess | 0x14046e5a0: s16 ≠ 0 → 0x14039e910(battle +0x1B68, 1, s16) |
RemoveConsumableFromBattleParty |
| 37 | raise_union_level |
guess | 0x14046e4e0: raises game var 0x882 (0x1402cf480, clamped by 0x140D68280) to u8; notice 0 |
SetUnionLevel |
| 38 | raise_chariot_turn_limit |
guess | 0x14046e540 → 0x1402cf250: raises game var 0x884 (read clamped 2–50) to u8 |
SetChariotTurnLimit |
| 39 | set_system_save_flag |
likely | 0x14046e5f0 → 0x1402d69d0(SystemSaveData +0x254, u16, u8 ≠ 0), bits 0–127 |
SetSystemSaveFlag |
| 40 | raise_game_var |
likely | 0x14046e630: GameVar_Get(u16) < u8 → set it |
SetGlobalFlagIfLower |
| 41 | nop_41 |
likely | 0x14004f490 |
NoOperation41 |
| 42 | nop_42 |
likely | 0x14004f490 |
NoOperation42 |
| 43 | nop_43 |
likely | 0x14004f490 |
NoOperation43 |
| 44 | nop_44 |
likely | 0x14004f490 |
NoOperation44 |
| 45 | nop_45 |
likely | 0x14004f490 |
NoOperation45 |
| 46 | nop_46 |
likely | 0x14004f490 |
NoOperation46 |
| 47 | nop_47 |
likely | 0x14004f490 |
NoOperation47 |
| 48 | compare_game_var |
likely | 0x14046e680: GameVar_Get(u16) vs u8 by comparison u8 |
CompareGlobalFlag |
| 49 | compare_game_flag |
likely | 0x14046e6f0: bit u16 of 0x140D52540 |
CompareLocalFlag |
| 50 | compare_worldmap_location |
likely | 0x14046e760: 0x140D5263C vs u8 |
CompareStrongpoint |
| 51 | compare_active_character |
guess | 0x14046e7f0: 0x1403339f0(battle manager +0x58 unit +0x138) vs u16 |
CompareBattleActiveCharacter |
| 52 | compare_primary_enemy_count |
guess | 0x14046e870: 0x140345050() vs u8 |
ComparePrimaryEnemyCount |
| 53 | compare_primary_enemy_count_le_except |
guess | 0x14046e8c0: 0x140345050() (− 1 if unit u16 passes 0x1403427b0) ≤ u8 |
ComparePrimaryEnemyCountLessThanOrEqualExcept |
| 54 | compare_primary_enemy_count_ge_except |
guess | 0x14046e940: the same, ≥ |
…GreaterThanOrEqualExcept |
| 55 | compare_unit_hp_le |
guess | 0x14046e9c0: battle unit +0x148 (0 → −1) ≤ u8 |
CompareBattleCharacterHealthLessThanOrEqual |
| 56 | compare_unit_hp_percent_le |
guess | 0x14046ea20: +0x148 ≤ +0x266 × u8 / 100 |
…HealthPercentLessThanOrEqual |
| 57 | compare_unit_mp_le |
guess | 0x14046eaa0: +0x14A ≤ u8 |
…MagicLessThanOrEqual |
| 58 | compare_unit_mp_percent_le |
guess | 0x14046eb00: +0x14A ≤ +0x268 × u8 / 100 |
…MagicPercentLessThanOrEqual |
| 59 | compare_unit_hp_percent_in_range |
guess | 0x14046eb80: +0x266 × lo / 100 ≤ +0x148 ≤ +0x266 × hi / 100 |
…HealthPercentInRange |
| 60 | compare_unit_hp_ge |
guess | 0x14046ec80: +0x148 ≥ u8 |
…HealthGreaterThanOrEqual |
| 61 | compare_unit_hp_percent_ge |
guess | 0x14046ece0: +0x148 ≥ +0x266 × u8 / 100 |
…HealthPercentGreaterThanOrEqual |
| 62 | compare_worldmap_child_location |
guess | 0x14046e7a0: 0x140D5263E vs u16 |
CompareStrongpointChild |
| 63 | compare_party_one_unit |
guess | 0x14046ed60 → 0x1402cd3e0: game flag 0x1F set, 0x20–0x50 clear, 0x1402c7ed0(…) == 1 |
CompareBattlePartyIsOneUnit |
| 64 | compare_hero_unequipped |
guess | 0x14046edc0: 0x1403a1e60(unit 1 +0x94) |
CompareHeroIsNaked |
| 65 | compare_unit_at_position_is_player |
guess | 0x14046ee30: event_data #2 row u8 gives (x, y) at +0xC/+0xD; the unit there has +0x188 == 1 |
CompareBattleUnitAtPositionIsPlayerFaction |
| 66 | compare_unit_hearts_ge |
guess | 0x14046eea0: (battle unit +0x143 ≠ 0) ≥ u8 |
CompareBattleCharacterHeartCountGreaterThanOrEqual |
| 67 | compare_unit_hearts_le |
guess | 0x14046ef00: ≤ |
…HeartCountLessThanOrEqual |
| 68 | compare_unit_at_position_character |
guess | 0x14046ef60: the unit at the #2 row's position: 0x1403339f0 == s16 |
CompareBattleUnitAtPositionCharacterEqual |
| 69 | compare_dungeon_var |
guess | 0x14046eff0 → 0x1402cdd90 (the second definition table) |
CompareDungeonFlag |
| 70 | compare_weather |
guess | 0x14046f060: 0x140D5262D vs u8 |
CompareWeather |
| 71 | compare_unit_at_position_entry_unit |
guess | 0x14046f0a0: the unit at the #2 row's position: record (0x140333c00) byte +4 == u8 |
CompareBattleUnitAtPositionIsEntryUnit |
| 72 | compare_approval |
guess | 0x14046f130: byte [u8] of the 0x140F223D8 table vs u8 |
CompareApproval |
| 73 | compare_unit_loyalty_ge |
guess | 0x14046f1b0: party unit +0x85 (0 → −1) ≥ u8 |
CompareCharacterLoyaltyGreaterThanOrEqual |
| 74 | compare_unit_loyalty_le |
guess | 0x14046f220: ≤ |
…LessThanOrEqual |
| 75 | compare_unit_received_attack |
guess | 0x14046f290: unit +0x2FF's unit (0x140345570), +0x18A bit 5 clear, team +0x188 == 1 |
CompareBattleUnitReceivedAttack |
| 76 | compare_unit_received_support |
guess | 0x14046f320: the same with +0x300 |
CompareBattleUnitReceivedSupport |
| 77 | compare_party_three_units |
guess | 0x14046f3b0 → 0x1402cd340: game flags 0x1F–0x21 set, 0x22–0x50 clear, 0x1402c7ed0(…) == 3 |
CompareBattlePartyIsThreeUnits |
| 78 | compare_has_item |
guess | 0x14046f410: 0x140387bc0(party, u16) |
CompareHasItem |
| 79 | compare_shop |
likely | 0x14046f480: u8 vs 0x140D52077, which the world map (0x1403e59d0) sets to the shop number it passes to ShopMenu_Open (0x10 for Deneb's, via Shop_IsDenebHere) |
CompareCurrentShop |
| 80 | compare_allies_slain |
guess | 0x14046f4c0: TacosGame +0x31AC vs u16 |
CompareAlliesSlainCount |
| 81 | compare_allies_incapacitated |
guess | 0x14046f5b0: TacosGame +0x31A8 vs u16 |
CompareAlliesIncapacitatedCount |
| 82 | compare_chariot_and_retreat_unused |
guess | 0x14046f6a0: (+0x319C ≤ +0x31A0 and ≤ +0x31A4) compared with u8 |
CompareOverallChariotAndRetreatingNotUsed |
| 83 | compare_party_has_unit |
likely | 0x14046f850: 0x14031f7c0(party, u16) ≠ 0 |
CompareHasCharacter |
| 84 | op_84 |
— | 0x14046f8c0: 0x140D5262F (world map 0x1403e59d0 case 6: selection index + 1) |
Unknown54 |
| 85 | compare_system_save_flag |
likely | 0x14046f900 → 0x1402d6960(SystemSaveData +0x254, u16) |
CompareSystemSaveFlag |
| 86 | compare_platform |
guess | 0x14046f970: 0x14026ca70 returns 0x140CDE96C; no writer in the decompile |
CompareSystemPlatform |
| 87 | nop_87 |
likely | 0x14004f490 |
NoOperation87 |
| 88 | nop_88 |
likely | 0x14004f490 |
NoOperation88 |
| 89 | nop_89 |
likely | 0x14004f490 |
NoOperation89 |
| 90 | nop_90 |
likely | 0x14004f490 |
NoOperation90 |
| 91 | nop_91 |
likely | 0x14004f490 |
NoOperation91 |
| 92 | nop_92 |
likely | 0x14004f490 |
NoOperation92 |
| 93 | nop_93 |
likely | 0x14004f490 |
NoOperation93 |
| 94 | nop_94 |
likely | 0x14004f490 |
NoOperation94 |
| 95 | nop_95 |
likely | 0x14004f490 |
NoOperation95 |
| 96 | all_n |
likely | 0x14046f9c0: runs u8 conditions; 1 when all hold |
AndCount |
| 97 | all_1_any_2_all_1 |
likely | 0x14046fa60: count 4; A && (B or C) && D |
AndOrAnd |
| 98 | all_2_any_2_all_1 |
likely | 0x14046faf0: 5; A && B && (C or D) && E |
AndAndOrAnd |
| 99 | all_3_any_2 |
likely | 0x14046fb80: 5 |
AndAndAndOr |
| 100 | all_1_any_2 |
likely | 0x14046fc10: 3 |
AndOr |
| 101 | any_of_4_all_2 |
likely | 0x14046fc90: 8; (A && B) or (C && D) or (E && F) or (G && H) |
OrOrOrOrOrOrAnd, whose formula "A ‖ B ‖ … ‖ (G && H)" doesn't fit the code |
| 102 | all_of_4_any_2 |
likely | 0x14046fd40: 8; (A or B) && (C or D) && (E or F) && (G or H) |
OrAndOrAndOrAndOr |
| 103 | any_2 |
likely | 0x14046fdf0: 2 |
Or |
| 104 | any_3 |
likely | 0x14046fe40: 3 |
OrOr |
| 105 | all_4_any_3 |
likely | 0x14046fec0: 7 |
AndAndAndOrOr |
| 106 | all_6_any_2 |
likely | 0x14046ff60: 8 |
AndAndAndAndAndAndOr |
| 107 | all_11_any_2 |
likely | 0x140470000: 13 |
AndAndAndAndAndAndAndAndAndAndAndOr |
| 108 | all_9_any_2 |
likely | 0x1404700b0: 11 |
AndAndAndAndAndAndAndAndAndOr |
| 109 | all_3_any_4 |
likely | 0x140470150: 7 |
AndAndAndOrOrOr |
| 110 | all_5_any_2 |
likely | 0x1404701f0: 7 |
AndAndAndAndAndOr |
| 111 | all_6_any_2_all_1 |
likely | 0x140470280: 9 |
AndAndAndAndAndAndOrAnd |
| 112 | all_7_any_2 |
likely | 0x140470320: 9 |
AndAndAndAndAndAndAndOr |
| 113 | all_4_any_4 |
likely | 0x1404703d0: 8 |
AndAndAndAndOrOrOr |
Operands
Each opcode's operand bytes, after its u16 opcode; offsets are from the first operand byte. An opcode with no rows has no operands (0, 30, 41–47, 87–95). Each opcode's operand size is where its last row ends, and must equal the game's table's (exe.task_opcodes). Read from each handler (static analysis, R29, 2026-10-12): which bytes it reads, at what width and sign; objdump -d where the decompile was unclear (31, 78). A byte its handler never reads is unk_<offset>. comparison picks one of the comparison functions at 0x140D51950, the same ones as pgrs's; a condition with a fixed comparison calls entry 3 (0x140D51968, less or equal) or 5 (0x140D51978, greater or equal) itself.
| Opcode | Offset | Size | Type | Name | Meaning | Confidence | Source |
|---|---|---|---|---|---|---|---|
| 1 | 0x00 | 2 | u16 | game_var |
The game variable to set. | likely | 0x14046dba0 → 0x1402cf2a0. |
| 1 | 0x02 | 1 | u8 | value |
Its new value. | likely | 0x14046dba0. |
| 2 | 0x00 | 2 | u16 | game_flag |
The game flag: bit game_flag & 7 of byte game_flag >> 3. |
likely | 0x14046dbd0. |
| 2 | 0x02 | 1 | u8 | value |
0 clears it, anything else sets it. | likely | 0x14046dbd0. |
| 3 | 0x00 | 1 | u8 | approval_id |
Which byte of the table at 0x140F223D8 changes. |
likely | 0x14046dc20 → 0x1403213b0. |
| 3 | 0x01 | 1 | s8 | amount |
Added to it, clamped 0–100. | likely | 0x14046dc20, 0x1403213b0. |
| 4 | 0x00 | 1 | u8 | location_id |
The new world-map location. | likely | 0x14046dc60. |
| 5 | 0x00 | 4 | s32 | amount |
Added to the goth when ≥ 0, else subtracted. | likely | 0x14046dcf0. |
| 6 | 0x00 | 1 | u8 | value |
Passed to 0x1402c4350 with TacosGame +0x3190. |
likely | 0x14046dd50. |
| 7 | 0x00 | 1 | s8 | amount |
Loyalty change. | likely | 0x14046dda0 → 0x140470510. |
| 8 | 0x00 | 1 | s8 | amount |
Loyalty change. | likely | 0x14046ddc0. |
| 9 | 0x00 | 1 | s8 | amount |
Loyalty change. | likely | 0x14046dde0. |
| 10 | 0x00 | 1 | s8 | amount |
Loyalty change. | likely | 0x14046de00. |
| 11 | 0x00 | 1 | s8 | amount |
Loyalty change. | likely | 0x14046de30. |
| 12 | 0x00 | 1 | s8 | amount |
Loyalty change. | likely | 0x14046de60. |
| 13 | 0x00 | 1 | u8 | value |
Index into the table at 0x1406952F0; above 2 counts as 2. |
likely | 0x14046de90. |
| 14 | 0x00 | 1 | u8 | mask_bit |
The bit of each definition's u16 at +2 that picks the variables to clear. | likely | 0x14046df20. |
| 15 | 0x00 | 2 | u16 | unit_id |
The party unit. | likely | 0x14046dfa0 → 0x14031f7c0. |
| 15 | 0x02 | 1 | u8 | value |
Its new bits 0–2 of +0x8E. | likely | 0x14046dfa0. |
| 16 | 0x00 | 1 | u8 | clan_id |
Party units whose bits 0–2 of +0x8E equal it. | likely | 0x14046e020. |
| 16 | 0x01 | 1 | s8 | amount |
Their loyalty change. | likely | 0x14046e020. |
| 17 | 0x00 | 2 | u16 | unit_id |
The party unit. | likely | 0x14046e090. |
| 17 | 0x02 | 1 | u8 | value |
Its new loyalty. | likely | 0x14046e090 → 0x140334f70. |
| 18 | 0x00 | 2 | u16 | location_id |
The new world-map child location. | likely | 0x14046dcb0. |
| 19 | 0x00 | 1 | u8 | value |
Unit 1's new bits 0–2 of +0x8E. | likely | 0x14046e100. |
| 20 | 0x00 | 2 | u16 | item_id |
The item to add. | likely | 0x14046e170 → 0x14031bc10. |
| 21 | 0x00 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046e1a0 → 0x1403454b0. |
| 21 | 0x02 | 1 | u8 | value |
Its new byte +0x1D8. | likely | 0x14046e1a0. |
| 21 | 0x03 | 1 | u8 | unk_0x03 |
Never read; 0 in the data. | — | 0x14046e1a0; DuckDB over the rows of this opcode (R36). |
| 22 | 0x00 | 2 | u16 | unit_id |
The unit whose death scene is suppressed. | likely | 0x14046e1e0 → 0x1402cb710. |
| 23 | 0x00 | 1 | u8 | value |
Written to 0x140D525E0. |
likely | 0x14046e210. |
| 24 | 0x00 | 2 | raw[2] | unk_0x00 |
Never read (the handler does nothing). Not in the data. | — | 0x14004f490; exe.task_opcodes (size 2). |
| 25 | 0x00 | 1 | u8 | value |
Written to 0x140D525E1. |
likely | 0x14046e230. |
| 26 | 0x00 | 1 | u8 | value |
Passed to 0x1403772a0. |
likely | 0x14046e250. |
| 27 | 0x00 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046e270. |
| 27 | 0x02 | 1 | u8 | value |
Its new team (+0x188). | likely | 0x14046e270. |
| 28 | 0x00 | 1 | u8 | value |
Written to 0x140D525EF. |
likely | 0x14046e2b0. |
| 29 | 0x00 | 2 | u16 | dungeon_var |
The variable of the second definition table to set. | likely | 0x14046e2c0 → 0x1402cf3a0. |
| 29 | 0x02 | 1 | u8 | value |
Its new value. | likely | 0x14046e2c0. |
| 31 | 0x00 | 1 | u8 | unk_0x00 |
Never read. Not in the data. | — | 0x14046e320 (objdump -d). |
| 31 | 0x01 | 1 | u8 | unit_id_low |
The party unit's ID is this byte plus 256 × class_id: the handler reads the u16 at 0x01. |
likely | 0x14046e320 (objdump -d: movzbl 0x1(%rdx), movzbl 0x2(%rdi), shlw $0x8). |
| 31 | 0x02 | 1 | u8 | class_id |
The class to set, compared with +0x89; also the high byte of the unit ID. | likely | 0x14046e320. |
| 32 | 0x00 | 2 | u16 | item_id |
The item to remove; 0 does nothing. | likely | 0x14046e3c0. |
| 33 | 0x00 | 1 | u8 | value |
n: game variable 0x6A6 + n − 1 is set; 0 counts as 1. |
likely | 0x14046e450. |
| 34 | 0x00 | 1 | u8 | value |
The shop level to raise to. | likely | 0x14046e480. |
| 35 | 0x00 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046e560. |
| 36 | 0x00 | 2 | s16 | item_id |
The commodity to remove; 0 does nothing. | likely | 0x14046e5a0. |
| 37 | 0x00 | 1 | u8 | value |
The level to raise game variable 0x882 to. | likely | 0x14046e4e0. |
| 38 | 0x00 | 1 | u8 | value |
The limit to raise game variable 0x884 to. | likely | 0x14046e540 → 0x1402cf250. |
| 39 | 0x00 | 2 | u16 | system_save_flag |
The system save flag. | likely | 0x14046e5f0 → 0x1402d69d0. |
| 39 | 0x02 | 1 | u8 | value |
0 clears it, anything else sets it. | likely | 0x14046e5f0. |
| 40 | 0x00 | 2 | u16 | game_var |
The game variable. | likely | 0x14046e630. |
| 40 | 0x02 | 1 | u8 | value |
Set when the variable is below it. | likely | 0x14046e630. |
| 48 | 0x00 | 2 | u16 | game_var |
The game variable read. | likely | 0x14046e680 → GameVar_Get. |
| 48 | 0x02 | 1 | u8 | comparison |
How it compares with value. Names: Comparison. |
likely | 0x14046e680. |
| 48 | 0x03 | 1 | u8 | value |
The right-hand side. | likely | 0x14046e680. |
| 49 | 0x00 | 2 | u16 | game_flag |
The game flag read (0 or 1). | likely | 0x14046e6f0. |
| 49 | 0x02 | 1 | u8 | comparison |
How it compares with value. Names: Comparison. |
likely | 0x14046e6f0. |
| 49 | 0x03 | 1 | u8 | value |
The right-hand side. | likely | 0x14046e6f0. |
| 50 | 0x00 | 1 | u8 | comparison |
How the location compares with location_id. Names: Comparison. |
likely | 0x14046e760. |
| 50 | 0x01 | 1 | u8 | location_id |
The right-hand side. | likely | 0x14046e760. |
| 51 | 0x00 | 1 | u8 | comparison |
How the active unit's ID compares with unit_id. Names: Comparison. |
likely | 0x14046e7f0. |
| 51 | 0x01 | 2 | u16 | unit_id |
The right-hand side. | likely | 0x14046e7f0. |
| 52 | 0x00 | 1 | u8 | comparison |
How the count compares with value. Names: Comparison. |
likely | 0x14046e870. |
| 52 | 0x01 | 1 | u8 | value |
The right-hand side. | likely | 0x14046e870. |
| 53 | 0x00 | 1 | u8 | value |
The right-hand side (less or equal). | likely | 0x14046e8c0. |
| 53 | 0x01 | 1 | u8 | unk_0x01 |
Never read; 0 in the data. | — | 0x14046e8c0; DuckDB over the rows of this opcode (R36). |
| 53 | 0x02 | 2 | u16 | unit_id |
The battle unit not counted. | likely | 0x14046e8c0. |
| 54 | 0x00 | 1 | u8 | value |
The right-hand side (greater or equal). | likely | 0x14046e940. |
| 54 | 0x01 | 1 | u8 | unk_0x01 |
Never read; 0 in the data. | — | 0x14046e940; DuckDB over the rows of this opcode (R36). |
| 54 | 0x02 | 2 | u16 | unit_id |
The battle unit not counted. | likely | 0x14046e940. |
| 55 | 0x00 | 1 | u8 | value |
The right-hand side (less or equal). | likely | 0x14046e9c0. |
| 55 | 0x01 | 1 | u8 | unk_0x01 |
Never read; 0 in the data. | — | 0x14046e9c0; DuckDB over the rows of this opcode (R36). |
| 55 | 0x02 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046e9c0. |
| 56 | 0x00 | 1 | u8 | value |
The percentage (less or equal). | likely | 0x14046ea20. |
| 56 | 0x01 | 1 | u8 | unk_0x01 |
Never read; 0 in the data. | — | 0x14046ea20; DuckDB over the rows of this opcode (R36). |
| 56 | 0x02 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046ea20. |
| 57 | 0x00 | 1 | u8 | value |
The right-hand side (less or equal). | likely | 0x14046eaa0. |
| 57 | 0x01 | 1 | u8 | unk_0x01 |
Never read. Not in the data. | — | 0x14046eaa0. |
| 57 | 0x02 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046eaa0. |
| 58 | 0x00 | 1 | u8 | value |
The percentage (less or equal). | likely | 0x14046eb00. |
| 58 | 0x01 | 1 | u8 | unk_0x01 |
Never read. Not in the data. | — | 0x14046eb00. |
| 58 | 0x02 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046eb00. |
| 59 | 0x00 | 1 | u8 | low |
The lowest percentage. | likely | 0x14046eb80. |
| 59 | 0x01 | 1 | u8 | high |
The highest percentage. | likely | 0x14046eb80. |
| 59 | 0x02 | 1 | u8 | unk_0x02 |
Never read. Not in the data. | — | 0x14046eb80. |
| 59 | 0x03 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046eb80. |
| 60 | 0x00 | 1 | u8 | value |
The right-hand side (greater or equal). | likely | 0x14046ec80. |
| 60 | 0x01 | 1 | u8 | unk_0x01 |
Never read; 0 in the data. | — | 0x14046ec80; DuckDB over the rows of this opcode (R36). |
| 60 | 0x02 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046ec80. |
| 61 | 0x00 | 1 | u8 | value |
The percentage (greater or equal). | likely | 0x14046ece0. |
| 61 | 0x01 | 1 | u8 | unk_0x01 |
Never read; 0 in the data. | — | 0x14046ece0; DuckDB over the rows of this opcode (R36). |
| 61 | 0x02 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046ece0. |
| 62 | 0x00 | 1 | u8 | comparison |
How the child location compares with location_id. Names: Comparison. |
likely | 0x14046e7a0. |
| 62 | 0x01 | 2 | u16 | location_id |
The right-hand side. | likely | 0x14046e7a0. |
| 63 | 0x00 | 1 | u8 | comparison |
How the result (0 or 1) compares with value. Names: Comparison. |
likely | 0x14046ed60. |
| 63 | 0x01 | 1 | u8 | value |
The right-hand side. | likely | 0x14046ed60. |
| 64 | 0x00 | 1 | u8 | comparison |
How the result (0 or 1) compares with value. Names: Comparison. |
likely | 0x14046edc0. |
| 64 | 0x01 | 1 | u8 | value |
The right-hand side. | likely | 0x14046edc0. |
| 65 | 0x00 | 1 | u8 | unk_0x00 |
Never read; 0 in the data. | — | 0x14046ee30; DuckDB over the rows of this opcode (R36). |
| 65 | 0x01 | 1 | u8 | position_id |
The row of event/event_data.dat#2 whose x and y (+0xC, +0xD) are the position. |
likely | 0x14046ee30. |
| 66 | 0x00 | 1 | u8 | value |
The right-hand side (greater or equal). | likely | 0x14046eea0. |
| 66 | 0x01 | 1 | u8 | unk_0x01 |
Never read. Not in the data. | — | 0x14046eea0. |
| 66 | 0x02 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046eea0. |
| 67 | 0x00 | 1 | u8 | value |
The right-hand side (less or equal). | likely | 0x14046ef00. |
| 67 | 0x01 | 1 | u8 | unk_0x01 |
Never read. Not in the data. | — | 0x14046ef00. |
| 67 | 0x02 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046ef00. |
| 68 | 0x00 | 2 | s16 | unit_id |
The ID the unit there must have. | likely | 0x14046ef60. |
| 68 | 0x02 | 1 | u8 | unk_0x02 |
Never read; 0 in the data. | — | 0x14046ef60; DuckDB over the rows of this opcode (R36). |
| 68 | 0x03 | 1 | u8 | position_id |
The row of event/event_data.dat#2 that gives the position. |
likely | 0x14046ef60. |
| 69 | 0x00 | 2 | u16 | dungeon_var |
The variable of the second definition table read. | likely | 0x14046eff0 → 0x1402cdd90. |
| 69 | 0x02 | 1 | u8 | comparison |
How it compares with value. Names: Comparison. |
likely | 0x14046eff0. |
| 69 | 0x03 | 1 | u8 | value |
The right-hand side. | likely | 0x14046eff0. |
| 70 | 0x00 | 1 | u8 | comparison |
How the byte at 0x140D5262D compares with value. Names: Comparison. |
likely | 0x14046f060. |
| 70 | 0x01 | 1 | u8 | value |
The right-hand side. | likely | 0x14046f060. |
| 71 | 0x00 | 1 | s8 | value |
What byte +4 of the unit's record must equal. | likely | 0x14046f0a0. |
| 71 | 0x01 | 1 | u8 | unk_0x01 |
Never read; 0 in the data. | — | 0x14046f0a0; DuckDB over the rows of this opcode (R36). |
| 71 | 0x02 | 1 | u8 | position_id |
The row of event/event_data.dat#2 that gives the position. |
likely | 0x14046f0a0. |
| 72 | 0x00 | 1 | u8 | comparison |
How the approval byte compares with value. Names: Comparison. |
likely | 0x14046f130. |
| 72 | 0x01 | 1 | u8 | approval_id |
Which byte of the table at 0x140F223D8. |
likely | 0x14046f130. |
| 72 | 0x02 | 1 | u8 | value |
The right-hand side. | likely | 0x14046f130. |
| 73 | 0x00 | 1 | u8 | value |
The right-hand side (greater or equal). | likely | 0x14046f1b0. |
| 73 | 0x01 | 1 | u8 | unk_0x01 |
Never read; 0 in the data. | — | 0x14046f1b0; DuckDB over the rows of this opcode (R36). |
| 73 | 0x02 | 2 | u16 | unit_id |
The party unit. | likely | 0x14046f1b0. |
| 74 | 0x00 | 1 | u8 | value |
The right-hand side (less or equal). | likely | 0x14046f220. |
| 74 | 0x01 | 1 | u8 | unk_0x01 |
Never read. Not in the data. | — | 0x14046f220. |
| 74 | 0x02 | 2 | u16 | unit_id |
The party unit. | likely | 0x14046f220. |
| 75 | 0x00 | 1 | u8 | unk_0x00 |
Never read; 0 in the data. | — | 0x14046f290; DuckDB over the rows of this opcode (R36). |
| 75 | 0x01 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046f290. |
| 76 | 0x00 | 1 | u8 | unk_0x00 |
Never read; 0 in the data. | — | 0x14046f320; DuckDB over the rows of this opcode (R36). |
| 76 | 0x01 | 2 | u16 | unit_id |
The battle unit. | likely | 0x14046f320. |
| 77 | 0x00 | 1 | u8 | comparison |
How the result (0 or 1) compares with value. Names: Comparison. |
likely | 0x14046f3b0. |
| 77 | 0x01 | 1 | u8 | value |
The right-hand side. | likely | 0x14046f3b0. |
| 78 | 0x00 | 1 | u8 | comparison |
How the result (0 or 1) compares with 1. Names: Comparison. | likely | 0x14046f410 (objdump -d: the right-hand side is the constant 1). |
| 78 | 0x01 | 2 | u16 | item_id |
The item looked for. | likely | 0x14046f410 → 0x140387bc0. |
| 79 | 0x00 | 1 | u8 | comparison |
How value compares with the current shop. Names: Comparison. |
likely | 0x14046f480. |
| 79 | 0x01 | 1 | u8 | value |
The left-hand side: a shop number. | likely | 0x14046f480. |
| 80 | 0x00 | 1 | u8 | comparison |
How TacosGame +0x31AC compares with value. Names: Comparison. |
likely | 0x14046f4c0. |
| 80 | 0x01 | 2 | u16 | value |
The right-hand side. | likely | 0x14046f4c0. |
| 81 | 0x00 | 1 | u8 | comparison |
How TacosGame +0x31A8 compares with value. Names: Comparison. |
likely | 0x14046f5b0. |
| 81 | 0x01 | 2 | u16 | value |
The right-hand side. | likely | 0x14046f5b0. |
| 82 | 0x00 | 1 | u8 | comparison |
How value compares with the result (0 or 1). Names: Comparison. |
likely | 0x14046f6a0. |
| 82 | 0x01 | 1 | u8 | value |
The left-hand side. | likely | 0x14046f6a0. |
| 83 | 0x00 | 1 | u8 | comparison |
How 1 compares with the result (0 or 1). Names: Comparison. | likely | 0x14046f850. |
| 83 | 0x01 | 2 | u16 | unit_id |
The unit looked for in the party. | likely | 0x14046f850 → 0x14031f7c0. |
| 84 | 0x00 | 1 | u8 | comparison |
How the byte at 0x140D5262F compares with value. Names: Comparison. |
likely | 0x14046f8c0. |
| 84 | 0x01 | 1 | u8 | value |
The right-hand side. | likely | 0x14046f8c0. |
| 85 | 0x00 | 2 | u16 | system_save_flag |
The system save flag read (0 or 1). | likely | 0x14046f900 → 0x1402d6960. |
| 85 | 0x02 | 1 | u8 | comparison |
How it compares with value. Names: Comparison. |
likely | 0x14046f900. |
| 85 | 0x03 | 1 | u8 | value |
The right-hand side. | likely | 0x14046f900. |
| 86 | 0x00 | 1 | u8 | comparison |
How 0x14026ca70's result compares with value. Names: Comparison. |
likely | 0x14046f970. |
| 86 | 0x01 | 1 | u8 | value |
The right-hand side. | likely | 0x14046f970. |
| 96 | 0x00 | 1 | u8 | count |
The number of conditions that follow; any number. | likely | 0x14046f9c0. |
| 97 | 0x00 | 1 | u8 | count |
Must be 4; otherwise the handler runs no condition and returns 2. | likely | 0x14046fa60. |
| 98 | 0x00 | 1 | u8 | count |
Must be 5. | likely | 0x14046faf0. |
| 99 | 0x00 | 1 | u8 | count |
Must be 5. | likely | 0x14046fb80. |
| 100 | 0x00 | 1 | u8 | count |
Must be 3. | likely | 0x14046fc10. |
| 101 | 0x00 | 1 | u8 | count |
Must be 8. | likely | 0x14046fc90. |
| 102 | 0x00 | 1 | u8 | count |
Must be 8. | likely | 0x14046fd40. |
| 103 | 0x00 | 1 | u8 | count |
Must be 2. | likely | 0x14046fdf0. |
| 104 | 0x00 | 1 | u8 | count |
Must be 3. | likely | 0x14046fe40. |
| 105 | 0x00 | 1 | u8 | count |
Must be 7. | likely | 0x14046fec0. |
| 106 | 0x00 | 1 | u8 | count |
Must be 8. | likely | 0x14046ff60. |
| 107 | 0x00 | 1 | u8 | count |
Must be 13. | likely | 0x140470000. |
| 108 | 0x00 | 1 | u8 | count |
Must be 11. | likely | 0x1404700b0. |
| 109 | 0x00 | 1 | u8 | count |
Must be 7. | likely | 0x140470150. |
| 110 | 0x00 | 1 | u8 | count |
Must be 7. | likely | 0x1404701f0. |
| 111 | 0x00 | 1 | u8 | count |
Must be 9. | likely | 0x140470280. |
| 112 | 0x00 | 1 | u8 | count |
Must be 9. | likely | 0x140470320. |
| 113 | 0x00 | 1 | u8 | count |
Must be 8. | likely | 0x1404703d0. |
Rebuilding
How the toolchain writes a task entry from its lists. confirmed: these rules rebuild all 21 entries byte for byte from their decoded rows. Source: the round trip, 2026-10-12.
- The header:
TASK,list_count,FF FF, the list IDs in list order,FF FFwhenlist_countis odd, then each list's offset. - Each list: its instructions, each its u16 opcode and the operand bytes laid out in Operands, then
FF FF. An opcode with no row in Opcodes can't be written. - After the last list:
align16(end + 1)zeros when the original's bytes after its data were exactly that (containers.md); otherwise those bytes as they are, which keeps theFF FFof the entries with no lists.
Leads
- Opcode 31 (
set_unit_class) reads its unit ID from bytes 0x01–0x02, overlappingclass_id, and never reads byte 0x00; no list uses it, so an in-game test would show whether it works at all. - Positional opcodes: 6, 26 and 84 name only offsets; a doc of the unit record and of TacosGame's fields would let them, and many
guessnames, be confirmed (HP, MP, loyalty, clan, alignment, goth and approval are Gibbed's words). - The second variable table (event_data
#4, opcodes 29, 30, 69) needs a term of its own;dungeonis Gibbed's. - Game flags: task opcode 2 has no bounds check, so a bit index of 128 or more writes into the second variable store at game vars
+0x4B0.