-
Notifications
You must be signed in to change notification settings - Fork 0
Event Script Opcodes
Navigation: Home - Data formats - Event Script Opcodes
This note decodes the bytecode dispatcher used by the event.dat script section.
[TOC]
Derived from:
- dispatch loop at
0x172CA/0x1750C - opcode jump table at
0x17494 - handler stubs at
0x172D8..0x17484
- Bytecode opcodes are in range
0x00..0x32. -
0xFFterminates a script block. - Invalid opcodes branch to
0x1748Cand set abort flagA4-$8616. - After each opcode, control returns to
0x1750Cto fetch next byte.
The following were re-confirmed directly from mm2.capstone.asm:
-
Tile-event scanner
0x1754A: triplet table is 3-byte groups(pos, event, cond)read from offset 0, terminated by the first00 00 00group. String-table offset =(terminator+3) + str_rel_word; script PC start =terminator+5. This matchesdecode_event.decode_locationexactly. -
Position byte =
(row<<4)|coli.e.(y<<4)|x(0x17608-0x17618), confirming the(y,x)trigger order. -
Cond byte is AND-masked with the direction/context byte and the event is
skipped when the result is zero (
0x17682and.b+beq). -
Event id selects a record in one
0xFF-delimited pool (skip loop at0x17262). The pool mixes opcode scripts and plain-text records. -
Dispatch
0x172CA-> table0x17494; valid opcode range0x00..0x32(cmp.l #$33at0x174FA). -
argcfix:OP_03reads 1 byte (was previously logged as 0). -
Special records: locations 63, 65, 68 contain no
00 00 00terminator and are not processed by the tile-event scanner (castle / Hall-of-Spells style data); the decoder now flags these as unreliable.
argc is inferred from read_u8/read_u16/read_u24 helper usage in each handler.
| Op | Handler | argc | Notes |
|---|---|---|---|
00 |
0x1748C |
0 | invalid/abort |
01 |
0x15924 |
1 | show text; u8 string index (via 0x15884->0x155BE) |
02 |
0x15942 |
1 | show text block; u8 string index (stub also pushes const 0x13) |
03 |
0x159CE |
1 | show text; calls 0x15942 (const 0x11) which reads u8 string index |
04 |
0x159F4 |
1 | show text above door; u8 string index |
05 |
0x15A46 |
1 | show text popup A; u8 string index |
06 |
0x15AEE |
1 | show text popup B; u8 string index |
07 |
0x15CE6 |
0 | wait-loop until SPACE key, then continue |
08 |
0x15D26 |
0 | wait for key (sets cursor mode 0xFD, calls wait helper 0x15CE6) |
09 |
0x15D3C |
0 | y/n prompt, writes result into condition flag |
0A |
0x15D9A |
0 | y/n prompt variant (mode 0xFD, calls 0x15D3C) |
0B |
0x15DB0 |
2 | open service/title window (u8 string via 0x15756, u8 position) |
0C |
0x15E12 |
2 | map transition to (u8 dest, u8)
|
0D |
0x15EC4 |
1 |
u8 -> engine thunk -$7E42 (generic engine call) |
0E |
0x160C2 |
1 | selector dispatch (see selector table below) |
0F |
0x162A6 |
0 |
end/return script (sets stop flag, calls cleanup 0x171AC) |
10 |
0x162B8 |
var | IF(cond) skip N token-stream entries |
11 |
0x162DC |
var | IF(!cond) skip N token-stream entries |
12 |
0x16300 |
12 | encounter/combat setup: 10-byte block + 2 bytes (dispatch pushes 0) |
13 |
0x16386 |
10 | encounter setup variant (calls 0x16300 with flag=1, 10-byte block) |
14 |
0x16398 |
0 | clear current tile event high-bit / clear runtime event flag |
15 |
0x16426 |
3 | apply state to party members (u8 count, u8, u8; 4th byte only when flag=1) |
16 |
0x16520 |
2 | scan up to 6 monster slots; set cond if field +0x3A/+0x28 matches arg2 |
17 |
0x165A4 |
2 | load condition byte from resolved variable pointer |
18 |
0x165C6 |
4 |
OP_15 handler with flag=1 (u8 count, set, and, or) -> masked party apply |
19 |
0x165D8 |
4 | add entity to party/roster (writes fields +0x3A/+0x40/+0x46) |
1A |
0x166F8 |
2 | store u8 into resolved variable pointer |
1B |
0x16724 |
1 | clear cond unless cond >= u8 (threshold gate) |
1C |
0x16742 |
1 | engine query -$7BB4(1, u8) -> cond |
1D |
0x16762 |
1 | engine call indexed by u8*7+1 (-$7E84) |
1E |
0x16780 |
1 | timed wait/delay loop for u8 iterations (-$7BC0/-$7BD2) |
1F |
0x1690E |
6 | apply effect to party members (selector + values via 0x167B0) |
20 |
0x16A22 |
6 | party-effect variant (calls 0x1690E with mode=1) |
21 |
0x16A34 |
3 | set tile data at (y,x) (arg1 nibbles) into arrays -$55BA/-$54BA
|
22 |
0x16A9E |
2 | set condition if current era (-$79B5, low byte of era word) is in [lo, hi] — era/time-range gate |
23 |
0x16ADA |
2 | reads day-of-year (-$79DE[era]) and range-checks it (calendar/day gate) |
24 |
0x16B54 |
2 | check u16 gold amount -> condition flag (strong evidence) |
25 |
0x16B82 |
2 | check non-gold 16-bit code -> condition flag (hi,lo bytes) |
26 |
0x16BC0 |
0 | prompt to select a party member (ESC 0x1B ends script) |
27 |
0x16BC0 |
0 | select-party-member variant (input mode 1) |
28 |
0x16C86 |
2 | consume item id (arg1) from party inventory -> condition flag |
29 |
0x16D08 |
0 | force abort/exit flag |
2A |
0x16D16 |
14 | set treasure/reward block: u24 gold/exp, u16 gems, 3x(id,?,?)
|
2B |
0x16D74 |
var |
u8 + token-skip walk |
2C |
0x16D98 |
1 | add u8 to state var -$79B8, flag redraw |
2D |
0x16DBA |
2 | check party-member attribute (arg1 bits select fields +0xC/+0xE/+0xF) -> cond |
2E |
0x16F50 |
2 | set attribute bit (or arg2) on matching party class -> field +0x51
|
2F |
0x16FEA |
0 | clear/space-fill input buffer -$5C50
|
30 |
0x17034 |
10 |
answer/password check: 10 bytes vs player input; char = 0x11A - byte (0xFA=space) |
31 |
0x170BC |
3 | iterate target party members and call engine op |
32 |
0x17190 |
1 | load condition from script variable id |
Opcodes 0x10, 0x11, and 0x2B call token skip helper 0x157FC.
That helper:
- Reads a count byte.
- Repeats
counttimes:- reads current token byte from script stream,
- looks up token length delta in table at
A4-$6CC8, - advances script pointer by that delta.
So these opcodes are deterministic but variable-length and need token table support for full static disassembly.
All six read a u8 string index via 0x15884 → 0x155BE, copy the resolved
string into the draw buffer at A4-$5D3C via 0x158C8 (which maps @ / 0x40
to newline 0x0A for the renderer), then paint. Font: 8×8 bitmap font 8
(EXTRACTED/fonts/mm2/8); atlas export editor/assets/fonts/mm2_8.png /
mm2_8.json via tools/export_mm2_bitmap_font.py.
| Op | Handler | Typical use (from event.dat usage) |
|---|---|---|
01 |
0x15924 |
Basic one-shot message; sets exit-flag bit 0 (A4-$7950); draws via -$7EC0
|
02 |
0x15942 |
Multi-line text block; sets exit-flag bit 1; positions with -$7F62/-$7BFC, draws glyphs via -$7C62 until 0x0A or 0xFF
|
03 |
0x159CE |
Prep (-$7F5C, -$7ED8) then delegates to OP_02 with window height const 0x11
|
04 |
0x159F4 |
Door label — centered caption above a door tile; single -$7BE4 text draw after width centering |
05 |
0x15A46 |
Popup A — large plain overlay (see below) |
06 |
0x15AEE |
Popup B — framed sign popup (see below) |
Both handlers skip all drawing when A4-$79E1 (can't-see / darkness overlay)
is non-zero — same gate as OP_04.
OP_05 @ 0x15A46 — plain overlay (Popup A)
- Allocates a text window via
-$7C74(4, 3, 24, 13)→(x, y, w, h)in character cells (large, borderless). - One
-$7BE4text draw of the full string into that window. - Vertical position is adjusted by party facing: calls
0x1582e(counts wrapped text lines from0x0Anewlines and 14-char rows), halves the count, and if the result is< 5, shifts the window up by(4 − lines)via-$7BFC. - Observed content: longer narrative plaques — missing-person posters, quest hints, arena notices, radiation warnings (~40 uses in locations 0–59).
OP_06 @ 0x15AEE — framed sign (Popup B)
-
Preprocesses the string in place: every
-(0x2D) is rewritten to{(0x7B, full-width horizontal bar glyph) before draw. - Smaller window
-$7C74(8, 8, 18, 9). - Draws a UI border by blitting font control glyphs through
-$7C62:0x10/0x11top corners,0x0E×11 top bar,0x14/0x15sides with-$7BE4text column between,0x7Efill tiles,0x12/0x13bottom corners,0x0F×11 bottom bar (see control-glyph table below). -
Observed content: compact directional signs and town labels with arrows
and
$bullet markers (~110 uses) — e.g."< Shortcut\nPinehurst","Woodhaven$\n<Tundara","Dead Zone\nRadiation!".
OP_04 vs popups in practice: OP_04 is the shop/door nameplate fixed
above a door (dominant in town maps — often paired with OP_0B service window
and OP_0E shop selector). Use OP_05 for long read-once lore/warning
text without a frame; OP_06 for short outdoor signposts that need a
visible border and decorative rules/arrows; OP_04 strictly for door
labels on building entrances.
Reference atlas: editor/assets/fonts/mm2_8.png (128×64, 16×8 cells, codepoints
0x00–0x7F). Strings in event.dat are 0xFF-terminated on disk.
The copy helper 0x158C8 converts @ (0x40) → 0x0A for rendering; decoded
tool output may show either form — see 06-event-dat-format.md.
Control glyphs (0x00–0x1F) — UI chrome (drawn via -$7C62, especially
Popup B border builder @ 0x15AEE):
| Code | Char | Role |
|---|---|---|
0x0A |
(LF) | Full-frame box tile (also newline in text streams) |
0x0E |
(SO) | Top horizontal bar |
0x0F |
(SI) | Bottom horizontal bar |
0x10 |
(DLE) | Top-left corner |
0x11 |
(DC1) | Top-right corner |
0x12 |
(DC2) | Bottom-left corner |
0x13 |
(DC3) | Bottom-right corner |
0x14 |
(DC4) | Left vertical side |
0x15 |
(NAK) | Right vertical side / interior column pad |
Other control codes in 0x00–0x0F are present in the atlas but are not used
by the Popup B border prologue; they may appear in other UI thunks (OP_02
glyph loop, service windows).
Printable format characters in event strings (especially OP_06 signs):
| Code | Char | Visual / behaviour |
|---|---|---|
0x2D |
- |
Thin dash in source; OP_06 only rewrites to 0x7B before draw |
0x7B |
{ |
Full-width horizontal bar (decorative rule) |
0x5B |
[ |
Left arrow (same glyph as <) |
0x3C |
< |
Left arrow |
0x5D |
] |
Right arrow (same glyph as >) |
0x3E |
> |
Right arrow |
0x24 |
$ |
Diamond / bullet marker |
0x7E |
~ |
Solid block (also used as border fill in Popup B) |
0x5F |
_ |
Underline bar |
0x40 |
@ |
Line break (stored in strings; renderer sees 0x0A) |
-
0x22(0x16A9E) range-checks current era (A4-$79B5low byte) against script[lo, hi]args and writes boolean to condition flagA4-$7951. -
0x10/0x11branch via token-skip helper0x157FC, gated byA4-$7951. -
0x14clears current tile's visited/event bit inA4-$54BAand clears runtime flag byteA4-$55D6bit 7. -
0x29sets abort flagA4-$79EA = 1. -
0x32copies a variable byte (via engine thunk-$7F2C) into condition flagA4-$7951.
OP_0E reads one selector byte and runs a chained-subtraction switch:
-
Explicit cases call dedicated engine thunks:
-
1tavern/food,2..8other town buildings (each a distinct-$7Dxxthunk), -
100 (0x64),126..131 (0x7E..0x83),201..207 (0xC9..0xCF),226 (0xE2),253 (0xFD)are further specials (travel/portal/quest handlers).
-
-
Default (any other selector) ->
0x15EDC, which bins the selector into a service category using engine range-check-$7E9Cand calls town handler-$7DFA:selector range category byte adjusted index base 0x09..0x100x3C-80x11..0x370x3D-0x100x38..0x4B0x3E-0x370x4C..0x540x3F-0x4B0x56..0x5B0x40-0x550x5C..0x5E0x41-0x5B0x65..0x690x42-0x640x6A..0x7C0x43-0x690x97..0x980x44-0x960xE3..0xF30x45-0xE20xF4..0xFB0x46-0xF3
Gameplay-observed selector meanings (shops/temple/training/guilds) live in the
SELECTOR_NAMES map in tools/decode_event.py. Per-town tiles, strings,
donation quest, blacksmith specials, and training HP formula:
28-town-services.md.
OP_30 stores a 10-byte expected answer inline, encoded as byte = 0x11A - ascii
(0xFA = trailing space). Decoded answers found in event.dat:
-
"MEENU","KEYS"(matching user-reported answer words), and numeric combination codes"46","23","64","32".
After the argc corrections (OP_12=12, OP_13=10, OP_15=3, OP_18=4,
OP_30=10), a linear walk of every real script segment now consumes each
segment exactly: 0 overruns, 0 underruns (excluding token-skip and text
records). Previously there were 35 underruns from the wrong arg counts.
-
Shop/sign tiles (
Carriage Inn,Drewnhald Ironworks,Boar's Tongue Tavern, etc.):- Behavior: text only, retriggers when stepping onto tile again.
- No immediate movement/state side effect observed.
-
Arena gate:
- Without ticket: message shown (
"Sorry, but you must have a ticket to compete in these games."). - With ticket: immediate combat starts.
- Post-fight: returned to same gate area.
- Retrigger rule: step off and back onto trigger tile.
- Ticket is consumed per fight.
- Script chain in this location uses item-check style ops (
0x24/0x25) around gate logic, consistent with ticket gating.
- Without ticket: message shown (
-
Olympic Trial:
- Current observed behavior: text only (
"Hurl the spear..."), no immediate combat/transition.
- Current observed behavior: text only (
- There are 4 ticket items in
items.dat(color-coded):- green
- yellow
- red
- black
- There are 4 key items (town-purchased), used to free bishops in castles.
- Arena world model:
- Arenas exist in Middlegate, Atlantium, Sandsobar.
- Tundra has no tickets.
- Volcania uses the red ticket line.
-
OP_24is strongly indicated as a gold check opcode (not item-id):- observed args include
10,25,50,200,500, matching common payment gates.
- observed args include
-
OP_25is a non-gold 16-bit predicate path (small observed codes0/1/2so far). - Arena gate blocks likely follow:
- predicate (
OP_24/OP_25) -> condition flag - conditional skip (
OP_10/OP_11) - combat/arena transition call
- ticket consumption side effect
- predicate (
- Castle bishop gates should provide additional traces for key-specific checks using the same opcode family.
Extracted OP_24 arguments across locations:
-
0x000A(10) — Middlegate flow -
0x0019(25) — Atlantium flow -
0x0032(50) — Tundra/related flow -
0x00C8(200) — Sandsobar/related flow -
0x01F4(500) — Atlantium travel/training flow
These values align with payment-style prompts and are unlikely to be item IDs.
Observed OP_24 contexts (decoded segments):
-
0x000A(10): Middlegate mapper/travel style prompt flow. -
0x0019(25): Atlantium branch in arena/shop event chain. -
0x0032(50): Atlantium/Vulcania travel prompt family. -
0x00C8(200): training/skill purchase flow. -
0x01F4(500): larger payment gate.
Observed OP_25 contexts:
- decoded values now include
0x0000,0x0001, and0x0002(after endian correction). - remaining
Nonehits are in segments where token alignment is still uncertain after variable-length skip-token control flow.
Current best model:
-
OP_24: gold predicate (amount check to condition flag). -
OP_25: non-gold code predicate (likely word/flag/class/item-code family). -
OP_28: consume-item path; maps cleanly to multiple quest items/keys/discs.
User-verified gate behavior supporting this model:
- bishop key gate: missing key -> blocked with message
- bishop key gate: with key -> allowed and key consumed
- arena tickets: consumed per fight
Use the decoder's predicate extraction mode:
python tools/decode_event.py --predicates EXTRACTED/event.dat--predicates now runs a path-aware, heuristic walk across OP_10/11/2B skip-token
branches so predicate checks behind those branches are surfaced for review.
Use consume-item extraction mode:
python tools/decode_event.py --consumed EXTRACTED/event.dat --items items.dat --answers druids meenu keysThis reports:
-
OP_28consume hits with mapped item IDs/names - per-location context strings
- string hits for candidate word answers (
druids,meenu,keys)
This prints every parsed OP_24/OP_25 hit with:
- location + segment id
- decoded argument value (if recoverable)
- nearby opcode context
- nearby strings for gameplay correlation
Classification: Verified = dispatch + core behaviour confirmed from ASM (and often gameplay); Partial = structure/argc known, gameplay semantics incomplete; Unknown = behaviour not traced.
| Op | Status | Gap summary |
|---|---|---|
00 |
Verified | Invalid opcode → abort |
01 |
Partial | Basic text draw (-$7EC0); when used vs 02/03 in scripts unclear |
02 |
Partial | Multi-line glyph loop via -$7C62; window mode const 0x13
|
03 |
Partial | Engine prep then OP_02; exact prep thunks unmapped |
04 |
Verified | Door label above door; skips if can't-see |
05 |
Verified | Popup A — plain overlay, facing-adjusted |
06 |
Verified | Popup B — framed sign, -→{ preprocess |
07 |
Verified | Wait for SPACE |
08 |
Partial | Wait for key; sets cursor mode 0xFD
|
09 |
Verified | Y/N prompt → cond_flag
|
0A |
Partial | Y/N variant (mode 0xFD wrapper) |
0B |
Partial | Service/title window (-$7FBC/-$7FC2); arg2 position byte semantics |
0C |
Partial | Map transition (dest screen + packed entry coord); bit6 remap path |
0D |
Unknown |
u8 index → generic thunk -$7E42; per-index effects untraced |
0E |
Partial | Selectors 1–8 = town services; 0x64/0xC9–0xCF/0xFD specials; default range bins 0x09–0xFB → category table only |
0F |
Verified | End script / cleanup 0x171AC
|
10/11
|
Partial | Conditional token skip verified; static disasm needs full -$6CC8 length table |
12 |
Partial | 10× monster slot bytes + 2 tail bytes → combat; tail meaning unknown |
13 |
Partial | Same as 12 with internal flag=1 (10-byte block only) |
14 |
Verified | Clear tile event / visited bit7 |
15 |
Partial | Apply byte to N party members via 0x163CA; op/val/mask semantics |
16 |
Partial | Scan ≤6 roster slots; match field +0x3A or +0x28
|
17 |
Partial | Load script variable group/index → cond |
18 |
Partial | Masked party apply (set/and/or bytes) |
19 |
Partial | Add entity to party (+0x3A/+0x40/+0x46) |
1A |
Partial | Store u8 into script variable |
1B |
Partial | Clear cond unless cond >= threshold
|
1C |
Partial | Engine query -$7BB4(1, u8) → cond |
1D |
Partial | Engine call table -$7E84 indexed by u8×7+1
|
1E |
Partial | Timed delay loop (-$7BC0/-$7BD2) |
1F/20
|
Partial | Party effect dispatcher 0x167B0; 6-byte payload; mode flag on 20
|
21 |
Partial | Write (y,x) tile bytes into -$55BA / visited -$54BA
|
22 |
Verified | Era range gate (-$79B5) |
23 |
Partial | Day-of-year gate via -$79DE[era]; special cases at args 0xB5/0xB6
|
24 |
Partial | Gold predicate via 0x155DA + -$7E66; amounts 10/25/50/200/500 observed; exact compare op untested |
25 |
Partial | Non-gold u16 code via -$7E66; decoded values 0/1/2 only |
26/27
|
Partial | Party-member select; 27 uses alternate input path when arg mode=1 |
28 |
Partial | Consume item if present → cond; keys/tickets user-verified |
29 |
Verified | Force script abort |
2A |
Partial | 14-byte treasure/reward block (gold/exp, gems, 3× item triplets) |
2B |
Partial | Token skip only when A4-$77BD set (combat-related flag) |
2C |
Partial | Add to global counter -$79B8; sets redraw exit flag |
2D |
Partial | Check member attribute fields +0x0C/+0x0E/+0x0F
|
2E |
Partial | OR attribute bits onto matching class field +0x51
|
2F |
Verified | Clear input buffer -$5C50
|
30 |
Verified | 10-byte encoded password check |
31 |
Partial | Iterate party members matching mask; per-member engine call |
32 |
Verified | Load cond from script variable id |
Overlay locations 60–70 (see event_location_inventory.json): most are
string_bank records (no tile scripts) referenced by index from other code;
63/65/68 are castle_blob (no 00 00 00 terminator); 67 is a
mixed_pool with non-standard opcodes (0x41+) that are outside the
0x00–0x32 dispatcher — runtime trigger path for these banks is still
partially traced (queued dispatch @ 0x176B6 vs standard scanner).
Answer key for the RE team — fill in Your answer: as you play-test. Options are mutually exclusive unless noted.
Scenario: You step on an outdoor arrow sign vs a building nameplate vs a long quest plaque.
-
A.
OP_06= framed signposts with arrows/rules;OP_05= long borderless lore/warnings;OP_04= door/shop labels only (never dungeon wall fluff). -
B.
OP_05andOP_06are interchangeable; only string length differs. -
C.
OP_04is used for both doors and outdoor signs;OP_06is dungeons only. -
D.
OP_05is the default signpost; you have seen it with arrow glyphs where we expectOP_06.
RE guess: A — event.dat usage (~110 OP_06 sign strings vs ~40 OP_05 plaques; OP_04 clusters on town service tiles).
Your answer: ___
Scenario: Nordon/Nordonna intro dialogue vs a one-line “Travel (y/n)?” prompt vs a multi-paragraph NPC speech box.
-
A.
OP_01= single-line / simple prompts;OP_02= multi-line scroll block;OP_03= same block asOP_02after extra window prep. -
B. Nordon intro uses
OP_01; all y/n gates useOP_02. - C. All three look identical on screen — opcode choice is arbitrary.
-
D.
OP_03is the only opcode that can show more than one paragraph.
RE guess: A — loc 60 Nordon strings are referenced as blocks; town y/n tiles commonly decompile to OP_01 + prompt_yes_no.
Your answer: ___
Scenario: A tile runs script bytes but never opens a shop window (OP_0E).
-
A. Never —
OP_0Dis always followed by visible text you remember. -
B. Yes — e.g. cruise/arena return:
engine_call(0x09)thenmap_transition, with no shop selector. -
C. Yes — chest/treasure UI opens directly from
OP_0Dalone. -
D.
OP_0Donly appears in overlay banks 60–70, not walkable maps.
RE guess: B — multiple overworld scripts call engine_call(0x09) paired with map_transition (e.g. cruise ride loc 14, Corak's Cave door loc 22); per-index table still Unknown.
Your answer: ___
Scenario: Same inn/blacksmith tile, different party facing or roster size.
- A. Title box always appears in the same screen slot; arg2 is a mode/id, not facing math.
-
B. Box shifts vertically based on party facing (like
OP_05popups). - C. Box shifts when you have more than four live members.
- D. You have never noticed the service title box at all — only the shop UI.
RE guess: A — arg2 is passed to -$7FBC as a mode byte; no facing hook traced in the OP_0B handler (Partial).
Your answer: ___
Scenario: Arena entrance, Monster Bowl, guild enrollment beyond mage guild (0x0D).
-
A. Arena combat is not
OP_0E— entirely separate combat trigger; selectors0x09–0x10are misc shops only. -
B. Town arena tiles call unnamed selectors (e.g. Middlegate
0x10, Sandsobar0x49) that enter combat/ticket flow inside the engine thunk. -
C. Every selector
0x09–0xFBopens the same generic shop UI with different inventory tables only. -
D. Arena entry uses selector
0x64(portal travel).
RE guess: B — arena entrance tiles decompile to exec_selector(0x10) / 0x49 / etc., not OP_12 in the visible script (Partial).
Your answer: ___
Scenario: Mystic portal, quest milestones, endgame.
-
A.
0x64= long-distance portal travel;0xC9–0xCF= quest-specific handlers;0xFD= late-game / unused in normal play. - B. All of these are just renamed blacksmith shops.
-
C.
0xE2= arena only;0x64= boat ferry. -
D. You fire
0xFDregularly in early game.
RE guess: A — names in SELECTOR_NAMES + portal tiles using high selectors; 0xFD seen rarely (Partial).
Your answer: ___
Scenario: Same monster IDs, different fight feel (arena backdrop, boss tone, no escape).
- A. Tail byte 1 = terrain/backdrop id; tail byte 2 = flags (escape, arena, difficulty band).
-
B. Both bytes are fixed
0x00in all real fights — purely padding. - C. Tail bytes set party level / scaled HP only.
- D. Tail byte 1 = gold reward multiplier; byte 2 unused.
RE guess: A — scripts emit varied non-zero tails (06 02, 11 5C, 05 F0, …) with stable 10-byte monster blocks (Partial, tail table not dumped).
Your answer: ___
Scenario: Olympic Trial skill purchase, hire restrictions, quest progress bits.
- A. Sets per-member flag/field bytes (skills owned, class gates, quest bits) — not direct HP damage.
-
B. Always deducts gold when
OP_15runs (same opcode as payment). - C. Only used for alignment/reputation changes you see in a stat panel.
- D. You have never noticed any effect from these ops in play.
RE guess: A — Atlantium Olympic Trial uses apply_party / apply_party_masked on field 0x6D before party_effect skill grant (Partial).
Your answer: ___
Scenario: Fountain heals, temple cure, trainer spell/skill grant, stat shrine.
-
A. Selector byte chooses effect class (heal, cure, teach skill/spell, buff);
OP_20is a variant mode of the same dispatcher. - B. Only restores HP — poison/cure uses a different opcode family entirely.
- C. Purely cosmetic — no stat or HP change.
- D. Always grants a random item.
RE guess: A — party_effect(sel=0x09, …) follows skill prompts; party_effect(sel=0x80, …) follows shrine/fountain chains (Partial).
Your answer: ___
Scenario: Barrier drops, door opens, wall becomes floor — still there after leaving the tile.
-
A. Yes — e.g. Corak's Cave barriers lowered with visible map change (
set_tileon adjacent cells). -
B. Only clears the event bit (
OP_14); graphics never change. - C. Visual change is session-only — resets on town exit.
- D. You recall permanent changes but they were cut-scene transitions, not tile writes.
RE guess: A — loc 22 Hero's Tomb script writes collision/visual bytes via OP_21 then OP_14 (Partial).
Your answer: ___
Scenario: Our decoder labels flag_clear / flag_set / flag_alt.
- A. Generic script/state flags (quest done, already paid, etc.) — not item IDs.
- B. Directly encode ticket colors (0=green … 2=black).
- C. Bishop freed / arena won / goblet returned map 1:1 to codes 0/1/2.
-
D. You have never hit a gate you would attribute to
OP_25— likely wrong decode.
RE guess: A — --predicates finds zero recoverable OP_25 hits in event.dat; small integer codes fit flag words, not items (Partial, high uncertainty).
Your answer: ___
Scenario: Arena ticket turn-in vs bishop cage key.
-
A. Tickets:
OP_25check then separateOP_28consume in the same script step. -
B. Tickets: handled inside
OP_0Earena selectors (engine inventory logic); keys: explicitOP_28in castle scripts. -
C. Both tickets and keys use only
OP_25;OP_28is for quest discs/rings. - D. Keys check without consume — you keep the key after freeing a bishop.
RE guess: B — OP_28 consume hits map to keys/discs/rings; no ticket item IDs (208–211) appear in OP_28 contexts; arena tiles call exec_selector instead (Partial).
Your answer: ___
Scenario: “Travel for 25 gold (y/n)?” — you accept and have enough gold.
-
A.
OP_24is check only (gold ≥ amount); a later op/engine call deducts if you proceed. -
B.
OP_24both checks and subtracts gold immediately when true. -
C.
OP_24checks individual member gold, not party total. - D. Failed check still deducts partial gold.
RE guess: A — scripts pair check_gold_at_least(N) with if not cond: skip then map_transition / party_effect; no subtract in the OP_24 handler (Partial).
Your answer: ___
Scenario: When does the skip walk actually run? (A4-$77BD must be set.)
-
A. Primarily after combat victory / arena return cleanup (flag set @
0x12438and related combat paths). - B. Any time you skip a dialog with ESC.
- C. Whenever a y/n prompt appears.
-
D. You have noticed
OP_2Bbranches firing outside combat — e.g. shops, travel.
RE guess: A — $77BD is set on victory (combat_victory_rewards) and cleared on many map/event exits; OP_2B no-ops when clear (Partial).
Your answer: ___
Scenario: NPCs or tiles that only work on certain days or moon phases.
-
A. Yes — obvious day-locked quests you can name (matches
OP_23/ day-of-year table). -
B. Only subtle — benefits “once per moon phase” feel calendar-driven but may use vars, not
OP_23. -
C. Era gates (
OP_22) cover all time travel;OP_23never matters in normal play. - D. Unsure — never noticed any day-specific behavior.
RE guess: B — moon-phase shrine text uses load_var8 + store_var8, not OP_23; OP_23 reads -$79DE[era] (Partial).
Your answer: ___
Scenario: Corak prologue / Nordon / Nordonna dialogue.
- A. Shown only during the opening castle/tutorial sequence — not re-triggerable from a town tile later.
- B. Re-accessible anytime by returning to Middlegate castle tiles.
- C. Random encounter text anywhere in the world.
- D. Visible only in the Hall of Spells UI.
RE guess: A — loc 60 is a string_bank; most event segments are missing/trigger-only placeholders, not normal map scripts (Partial).
Your answer: ___
Scenario: % / letter garbage vs normal prose.
- A. Never seen in gameplay — metadata for shop/HoS/hire UI only.
- B. Shows as garbled text if you walk the wrong tile in a town.
- C. Appears during spell purchase windows as formatting codes.
- D. Same strings as location 70, duplicated for testing.
RE guess: A — loc 61 decodes as encoded tables; no readable quest prose in the string list (Partial).
Your answer: ___
Scenario: Spell purchase / class check scripts vs dead data.
- A. Dead — superseded entirely by loc 70 strings.
-
B. Still executed via queued dispatch (
0x176B6) for HoS/class flows; contains opcodes outside0x00–0x32. -
C. Only runs during endgame (
OP_22era gate). - D. You have bought spells from a tile whose script clearly lives in loc 67.
RE guess: B — loc 67 has runnable segments (prompt_yes_no + map_transition) plus non-standard opcodes; overlay path differs from tile scanner (Partial).
Your answer: ___
Scenario: Bishop cage text, “Yellow Interleave” puzzle, Triple Crown hints.
- A. Pulled during castle/bishop events; all four bishops use the same key-check/consume pattern (color-matched key).
- B. Bishops use tickets; keys are Sandsobar-only.
-
C. Puzzle text is shown as a normal
OP_06sign in Pinehurst overworld only. - D. Loc 70 is never read at runtime — editor artifact.
RE guess: A — loc 70 strings include Green Bishop + Yellow Interleave; castle overlays consume Yellow/Red/Black keys via OP_28 matching bishop color (Partial).
Your answer: ___
Scenario: Runtime behavior vs opaque static data.
- A. Normal tile events fire from these records today (bishop cages, sieges).
-
B. Partial — engine reads blob bytes directly; not via standard triplet scanner (
00 00 00missing). - C. Completely unused on Amiga retail — dead padding.
-
D. Only used if you hack
event.dat— vanilla never touches them.
RE guess: B — flagged castle_blob in inventory; queued dispatch likely consumes bytes without normal init (Partial).
Your answer: ___
Scenario: Text-only today — ever combat later?
-
A. Permanently non-combat — pays 500 gp to teach Athlete skill via
apply_party/party_effect. - B. Starts combat if you refuse the y/n prompt.
- C. Becomes a combat arena after a quest flag or era change.
- D. Was combat in an earlier retail build — patch changed it.
RE guess: A — loc 01 event 27 decompiles to skill purchase, not encounter_setup; user already reported text-only (Partial).
Your answer: ___
Scenario: Which colored ticket item gates each town arena? (items.dat: Green 208, Yellow 209, Red 210, Black 211 — note typo “Yellow Tickt”.)
- A. Middlegate=Green, Atlantium=Yellow, Sandsobar=Black, Vulcania=Red.
- B. Middlegate=Yellow, Atlantium=Green, Sandsobar=Red, Vulcania=Black.
- C. All four towns accept any ticket color.
- D. Tundara sells tickets but has no arena — ignore for pairing.
RE guess: A — Vulcania=Red is user-verified; four ticket items match four arena towns; Sandsobar Triple Crown text highlights Black ticket for cross-arena quest (Partial — exact shop→color mapping not in decoded scripts).
Your answer: ___
After you fill this in, please note any wrong RE guess lines inline — that is the highest-value feedback for the opcode gap table.
Interactive quiz: python tools/mm2_event_quiz.py (saves answers to EXTRACTED/event_quiz_answers.json).
tools/decode_event.py now includes:
- opcode map for
0x00..0x32 - raw script preview
- marker-based disassembly pass starting at opcode
0x22candidates
- Docs Wiki Hub ← full doc index
- Getting Started ← read this first
- Overview
- Workspace Notes
- Open Questions
- Full Analysis
- Game Remake
- Startup and Init
- Runtime Memory Map
- Main Loop and Map
- Exploration Input and Options
- Party and Session
- Game State Struct
- Data Hunk mm2 data 00
- Time Era Calendar
- GFX Loading
- ANM TV Format
- PC DOS graphics
- SNES graphics
- 3D View and Game Screen
- Amiga 3D Render Process
- Scripted Scene Graphics
- Event Graphics Opcodes
- Title Screen Assets
- Title Screen Animation
- Format inventory
- items.dat
- monsters.dat
- roster.dat
- attrib.dat
- map.dat
- spells & item use
- event.dat
- Events by location ← 71 maps
- Events hub (numbered)
- Event Script Opcodes
-
Event Script DSL ←
.mm2evtauthoring - Event Text Rendering
- Event Graphics Opcodes
- Combat Overview
- Combat System
- Encounter Tables
- Spell Cast ASM
- monsters.dat abilities
- Spells and item use
- Amiga audio in exe ← Paula / DATA / remake
- Controls: Sounds / Walk Beep
- MM2 music format (superseded)
- Title/death audio paths (superseded)
- Known songs catalog (superseded)
- Town Services
- Spell Sources
- Character Mechanics
- Skills and Hirelings
- Commerce Formulas
- Commerce World Services
- Mount Farview Class Quest
- Class Quest HP Bug
- Event Runtime
- Event to String Path
- Embedded Exe Strings
- Copy Protection
- Time Era Calendar
- Game State Struct
- MM1 Overview ← hub + decode status
- MM1 MAZEDATA format
- MM1 to MM2 outdoor
- MM1 WALLPIX by sector
- MM1 art & graphics
- MM1 items / monsters (status)
- MM1 map walker ↗
- MM1 2D maps gallery
- MM1 WALLPIX gallery