The ShadowFlare.exe is a 532KB Win32 GUI application with ~1043 functions. It was compiled January 14, 2003 and uses custom DLLs (RK_FUNCTION, RKC_*) for most heavy lifting.
Developer: Missinglink
Publisher: Denyusha
Window Class: SHADOW_FLARE
Window Title: ShadowFlare for Window98/Me/2000
Registry Key: HKEY_LOCAL_MACHINE\SOFTWARE\Missinglink\ShadowFlare
| DLL | Import Count | Purpose |
|---|---|---|
| RKC_RPGSCRN | 88 | RPG screen rendering, objects, maps |
| RKC_NETWORK | 38 | Multiplayer networking |
| RKC_RPG_SCRIPT | 25 | Scenario scripting |
| RKC_RPG_AICONTROL | 20 | AI/NPC behavior |
| RKC_DBFCONTROL | 20 | Double-buffer control |
| RKC_UPDIB | 17 | Sprite rendering |
| RKC_DIB | 14 | Bitmap/graphics |
| RKC_RPG_TABLE | 9 | Data tables |
| RKC_DSOUND | 9 | DirectSound audio |
| RKC_MEMORY | 6 | Memory management |
| RKC_FILE | 6 | File I/O |
| RKC_FONTMAKER | 4 | Font rendering |
| RKC_WINDOW | 2 | Window management |
Total: 258 RKC DLL imports
| Address | Description |
|---|---|
| 0x00401550 | CheckSingleInstance - Mutex check for single instance |
| 0x004014a0 | ProcessCommandLine - Parse command line args |
| 0x00401eb0 | LoadConfig - Load SFlare.Cfg configuration |
| 0x004016b0 | InitGame - Initialize all game subsystems |
| 0x004022e0 | WinMain - Main entry point |
| 0x00402520 | CreateGameWindow - Window class and creation |
| 0x00402b30 | WndProc - Window procedure (1142 bytes) |
| 0x00402840 | KeyDownHandler - Keyboard input |
| 0x004026d0 | KeyUpHandler - Key release (stub) |
| 0x004028a0 | LeftClickHandler - Left mouse button |
| 0x00402900 | RightClickHandler - Right/middle mouse |
| 0x00402920 | PaintInitialLoadingScreen - cached loading page and overlay |
| 0x004030F0 | RenderWorld - ground, shadow, and depth-sorted object passes |
| 0x004050F0 | RenderQuestNotice - timed title and Mission List shortcut |
| 0x0040CEA0 | RenderMissionList - two pages, script state, and detail panel |
| 0x004023d0 | UpdateGameState - Game state machine (switches on state 0/1/2) |
| 0x00401b90 | Shutdown - Cleanup all subsystems |
| 0x0041d970 | BootstrapGameplay - first-frame scenario setup (2936 bytes) |
| 0x00420df0 | LeaveTitleState |
| 0x00421bf0 | LeaveCharacterSelectState |
| 0x0041d880 | LeaveGameplayState |
| 0x00417bd0 | RenderLoadingScreen - gameplay loading presentation |
| 0x0046863c | CRT Entry - Runtime startup |
The game uses separate globals, not one monolithic struct:
| Address | Type | Description |
|---|---|---|
| 0x00482770 | HINSTANCE | Application instance handle |
| 0x00482774 | LPARAM | Last lParam from WM_CREATE |
| 0x00482778 | SFWindow | Window object (~0xB100 bytes) |
| 0x00482D10 | RKC_UPDIB* | Sprite system |
| 0x00482D18 | RKC_DBFCONTROL* | Double buffer control |
| 0x00482D20 | RKC_RPGSCRN_CHARANIMBLOCK* | Character animation |
| 0x00482D24 | RKC_NETWORK* | Network system |
| 0x00482D28 | RKC_DSOUND* | Sound system |
| 0x00482DB0 | int | Game mode (0=SP, 1=Server, 2=Client) |
| 0x0048D71C | int | Screenshot requested flag |
| 0x0048D8B8 | int | Window style index |
| 0x0048D8CC | int | IME enabled flag |
| 0x0048D8D4 | int | Initial loading state (-1 hidden, 0/1 loading, 2 confirm, 3 accepted) |
The game uses a state machine with 3 states stored at SFWindow+0x59C:
| State | Handler | Description |
|---|---|---|
| 0 | 0x420df0 | Title screen / Main menu |
| 1 | 0x421bf0 | Character selection / save selection |
| 2 | 0x41d880 | Main gameplay |
State transitions:
- State 0→1: Open new-character or saved-game selection
- State 1→2: Confirm a single-player or network mode
- State 2→0: Return to menu
0x004239b0 copies each present save's plain 0x160-byte player record into the
selection catalog. 0x00421e10 displays more than the name and level: it reads
gender at +0x18, job at +0x1c, level at +0x24, current HP at +0x34,
current MP at +0x3c, and experience at +0xd8. The row labels these as
Level, Job, Sex, Name, HP, MP, and EXP. Job 9 is the only gendered job label
(Wizard for stored gender 1, Witch for 0); the other shipped labels are
Hunter, Warrior, and Mercenary. Every label and padded value is submitted as a
separate bitmap-font packet at the same origin; the leading spaces position
the value without adding the label width a second time. At full selection
brightness, labels use retail's gold (224, 192, 128) while values use pale
white (224, 224, 224). Unselected labels and values use the corresponding
half-intensity colors; an empty slot always uses gray (112, 112, 112).
The visible loading screen is a sub-state of gameplay rather than top-level
state 1. At application startup, the game decodes Waiting.njp patterns 0, 1,
3, and 2 into cached DIBs. Initial single-player entry paints the Episode 1
background and loading label through 0x00402920. Once scenario setup marks
the world ready, the label is replaced by a 16-pixel horizontally moving
arrow; Return or a click in its bottom-right rectangle continues into the
world.
0x00417bd0 is not the ordinary map loading screen. It is a later
story/briefing visual presenter: it selects the Epilogue artwork from
Waiting.njp pattern 4 or an alternate VisualNN.njp, fades it over 120
rendered frames, waits until frame 300 before accepting Return, Escape, or the
primary mouse button, and animates WaitIcon.njp. Its active ID also gates the
local-player update, freezing the current action without cancelling it. Script
opcode 64 at 0x00434001 owns the selector. All seven shipped calls are
reconstructed, including the two-page Visual02 resource. The portable
runtime does not call this routine after every map change.
The nearby screen-space particle owner is separate again. Opcode 65 at
0x0043403e stores an evaluated RGB triplet, count, and one-frame spawn flag.
0x0041fe20 consumes five shared rand() values per new particle, draws its
short DDA line with opacity 300 through 1,000, advances it from Y -30, and
removes it at Y 479. Its 22 shipped calls provide red, white, pale-red, and
blue falling ambience; two calculate density from a temporary distance value.
Retail's ordinary map transition remains black with the crossed-swords
Waiting.njp image and its LOADING plate only while loading is in progress.
The portable scenario transaction is currently synchronous and completes
between presented frames, so a fast transition goes directly to the new map
instead of manufacturing a fixed delay.
The initial scenario map is loaded by the large transition routine at
0x00426200. The f00_01.Lst indices are preserved across ground and object
records: NJP entries hold visible patterns and their following SDW entries hold
one-bit shadows. 0x004030f0 builds separate shadow and visible-object lists,
inserts both static scenery and the current dynamic actor block, then calls
RKC_RPGSCRN_OBJECTDISP::SortDisplayObject on each combined list.
Insertion first groups status classes and compares the projected
position + (judgement.left, judgement.top) point. The final sort cannot be
replaced by one Y key: it compares the absolute left, top, right, and bottom
edges of every remaining judgement rectangle. A candidate is held back when
another rectangle begins before its right and bottom edges and ends before at
least one of them; that other entry must be drawn first. All comparisons are
strict, so merely touching edges do not create a dependency. This is what puts
a character behind Remote Town's long walls and houses even when the visible
sprite covers a large screen area.
Status bits 0x100, 0x80, and 0x20 select classes one, two, and three;
class zero is the default. The packet sequence draws non-default shadows and
objects first, followed by default shadows and objects.
The portable RKC_RPGSCRN library now owns that exact ordering rule. The
renderer supplies decoded OBL bounds and the player/NPC judgement rectangles,
keeping DLL-derived behavior out of the executable-owned render code.
The MCT loader at 0x00427b50 first reads a 16-byte
MCED DATA v0000\x1a signature, two 260-byte paths, two unknown 32-bit
values, the music index, and a 256-byte title. Its variable entity section is
followed near EOF by a count and 16-byte entry records in key, world X, world
Y, direction order. 0x00427930 searches those records by key. The portable
loader now uses scenario 00000000's map path and entry key zero rather than
embedding f00_01, (89898, 2811), direction 3, and music index 0 in
WorldScene.
The item group near the end of that variable section constructs ordinary item
actors with initialization mode one and script identity
18000000 + local ID. 0x00458930 immediately turns that mode into the
settled state: zero height and vertical speed, bounce state two, runtime
judgement [-20,-20,19,19], and no landing sound. Definition offsets 0x30
and 0x34 select the Character/ITEM resource and either a CAF chart or, for
the static resource layout, a direct Pattern.njp/Pattern.sdw index.
Category-four definition-zero records are the sole branch which rolls an
inclusive MCT quantity; one source record still creates one actor.
The enemy branch creates character number 14000000 + local ID, resolves the
fixed 32-byte controller name through RKC_RPG_AICONTROL, and rearranges the
15 values before that name plus the 56 values after it into a 72-word
initializer consumed by 0x00458f40. That function copies the complete block
to runtime offset +0xc4, synchronizes the screen object, and initializes the
AI-list slot to -1. The default action selected by the constructor is action
seven; 0x0045b600 renders CAF chart zero using the MCT direction and advances
its frame counter on every active-map update.
The first four patrol bounds consumed by native AI action one come from
pre-controller values 1 through 4 and are added to the enemy's spawn
position. Pre-controller value 8 is intentionally duplicated by the loader:
it initializes both current life at +0xd4 and maximum life at +0xe4.
Post-controller value 54 becomes the movement-speed scale at +0x1dc.
Movement actions calculate AID parameter 3 * scale / 1000, using integer
arithmetic before passing the result to the shared movement controller.
0x004127d0 loads the global System/Game/Parameter/Control.aid catalog
before scenarios create enemies. Its version-one file contains 64 named lists,
18 event buckets per list, and 1,338 action records. The scenario loader uses
the DLL's exact-name lookup and stores the returned zero-based list number in
the enemy initializer. The portable AiControlDatabase follows that ownership:
it is loaded once by WorldScene, while scenario-local enemies retain a
non-owning list reference and its stable index.
Enemy update reaches the event selector at 0x0045c9f0. Parameter zero is
the candidate priority and parameter two is its random-selection weight.
Condition zero enables a current-life percentage test with the inclusive
minimum and maximum in conditions one and two; condition three enables the
same kind of inclusive range around target distance using conditions four and
five. A bound of -1 is open. The target query at 0x00459500 searches the
four player slots and then companion character numbers 16000000 through
16000003. A player is eligible only when its active state is exactly one,
it belongs to the enemy's scenario, and—when requested—its life is positive.
The nearest eligible player wins, with the lower slot winning a tie. The
companion pass only runs when no player qualified. It requires the actor's
script-active bit, positive life when requested, and zero in the owning
player's companion-mode field. Companions use the same strict-nearer tie rule.
All distance tests use the two judgement rectangles, not actor origins.
The default lookup at 0x004593f0 is deliberately separate. It still prefers
any eligible player over every companion, but accepts any nonzero player
active state and searches all type-five companion actors in scenario order.
Its companion path does not repeat the script-active-bit test, though it still
requires owner mode zero and optional positive life. The portable enemy target
selector keeps both entry points behind one typed query result so the event
evaluator and native movement/presentation actions cannot disagree about
target kind, identifier, measured distance, or the position used for facing.
The scenario loader's rearrangement is important for the six non-movement
presentations. Direct actions one through three read maximum target distance
from post-controller values 3 through 5, animation chart offsets from values
41 through 43, and speed-table indices from values 47 through 49. Retail adds
four to those authored chart offsets. Effect actions four through six use
post-controller values 9 through 11 as effect types, 15 through 17 as
subtypes, 12 through 14 as their direct parameters, and 18 through 20 as
additive values. Their chart offsets come from values 44 through 46 with a
retail base of seven, and values 50 through 52 select animation speed.
0x00458f40 sees these fields at runtime offsets +0x110 through +0x170;
the portable profile exposes the proven consumers while retaining both raw
MCT blocks.
0x00459290 dispatches presentation actions one through three to
0x0045a2f0 and actions four through six to 0x0045ac90. On entry, the
first family searches from distance zero through its authored maximum; the
second uses the default target query. Either one faces the selected actor,
starts at frame zero, and keeps that target for the presentation-side result.
The ten animation multipliers are exactly 0.3, 0.4, 0.6, 0.8, 1.0,
1.5, 2.0, 2.5, 3.0, and 4.0. A continuing update multiplies its
integer elapsed counter by the chosen value and truncates toward zero.
Both routines scan part zero for every newly crossed CAF frame. Status bit
0x40 is the impact point; bits 0x400, 0x800, and 0x1000 select the
three sound-marker slots consumed by 0x0045a2a0. A frame which jumps beyond
the end is not scanned before the draw frame is clamped, an odd but observable
retail edge case. Reaching the last frame draws it once, restores presentation
seven, and publishes events two through four for the direct family or five
through seven for the effect family only when the existing event is -1;
another event is never overwritten. A resource-less actor completes
immediately. The portable controller emits typed impact/effect and audio
marker results; damage resolution and effect construction remain separate
consumers rather than being hidden inside animation timing.
The sound helper first indexes DAT_00480a20 as
resource * 30 + marker * 10 + chart. It is exactly 25 enemy resources by
three marker slots by ten charts, with 59 non--1 overrides. When that cell
is -1, DAT_004809a8 supplies a marker-by-chart fallback. Only chart three
has one: sample 86 for every slot. The portable resolver preserves that
override-first order and hands the resolved sample back with the presentation
update, leaving actual playback to the world audio owner.
Damage itself does not belong to either presentation routine.
0x00412a40 is a shared packet-to-damage function called by the player
receiver at 0x00443cb0 and the enemy receiver at 0x00459690. Its first
argument is the same 77-word packet built by direct attacks and effects. Its
second argument is a 14-word receiver profile. Word zero selects the receiver
formula family, words three and four are the two defense values consumed by
ordinary and effect paths, words five onward provide the table-indexed
elemental modifiers, and word 13 is the receiver's native element.
Packet word 37 equal to one is an immediate override: word four is returned unchanged, even when it is below one. Every calculated branch clamps to one. Effect-family packet kind three uses table 11 with receiver family zero, or an element comparison and receiver word four with families one and two. The ordinary dispatch combines the low 16 bits of packet word zero and receiver word zero. Its four accepted pairs use table 7, table 11, or the shared elemental calculation; every other pair returns one without consuming random state.
The elemental calculation compares packet word 32 with the receiver's native
element and its opposing pair: Fire/Water, Earth/Thunder, Holy/Dark, and
Gel/Metal. The opposing element gets rand() % 4 + 10, the same element gets
rand() % 4 + 7, and any other element gets ten without that optional draw.
A second rand() % 3 + 9 supplies the base multiplier. The table-7 branch
instead draws its base factor first, asks the character owner to resolve
packet word two, and indexes the packet through the opposing element.
The source lookup does not change the arithmetic, but it is retained as a
typed request so the later live receiver can preserve the call.
The portable resolver keeps these lookups and draws in retail order. A missing table or unsafe index is reported as invalid instead of following the executable's unchecked pointer behavior. Receiver-owned barriers, life and mana changes, reaction state, equipment durability, status application, reflection, death, and drops stay outside this shared arithmetic boundary.
The enemy receiver at 0x00459690 is now reconstructed on top of that
boundary. The 72-word enemy initializer supplies native element, physical and
magical defense, hit-reaction chance and duration defense, and the flag that
always suppresses reaction displacement. Those values come from MCT pre-AI
words 6, 9, and 11 and post-AI words 38 through 40. They are decoded once
with the enemy rather than looked up again by the receiver.
The callback rejects an already defeated enemy and a negative source before doing anything. A non-positive packet base skips the shared damage formula but still reaches the later status, effect, and event paths. Damage is applied only when the packet's source slot matches the local player slot. In single-player and server mode, the amount credited to that slot is capped by the enemy's life before subtraction. A surviving hit asks the server path to broadcast its seven-field damage record. Client mode instead sends that record to the server and keeps its predicted local enemy at a minimum of one life. This branch is also the evidence that global mode one is server and mode two is client.
Visual enemies calculate hit reaction before life changes. Table 25 selects chance and duration from damage as a percentage of half maximum life, capped at row 49. Table 24 adds the packet's authored affinity grade for player attacks or row five for an opposing-element non-player packet. Effect-family packets replace chance, duration, and displacement suppression from their three element-indexed packet banks. The chance comparison consumes one draw even when its final value is zero. A reaction without movement is capped at 15 updates before packet word 76 is added; an enemy's always-suppress flag is applied after that cap. This ordering is intentional. The names matter here: a zero in the stored motion field permits the later action-ten impulse, while a one prevents it.
A started reaction selects presentation ten, records the impact angle, faces the source when motion is disabled, locks the action, and preserves packet reaction stages one and two. Stage one spawns effect 21015 through 21018 and plays sample 119. Reflection word 39 emits five effect-20013 requests with five independent spread draws and sample 61. Packet words 34/35 and 74/75 emit their two configured effects with null combat-packet pointers. Word 72 uses a strict 20-percent draw followed by separate packet-kind and 21011/21012 draws. The portable effect request now records whether the retail constructor received a packet pointer, so these paths cannot be confused with the packet-owning attack effects.
A lethal authoritative hit records whether the packet was effect-family, retains its full source character number, requests the retail kill-accounting path, and emits the local player's enabled on-kill statuses 7, 8, and 9. Presentation eleven and the action lock are selected only after reflection and packet effects have had their normal chance to run. Player direct hits now commit the returned life, attribution, reaction, event, and defeat state to the live enemy and forward its audio requests.
The live presentation consumer follows 0x0045bb20 for action ten. CAF chart
two is sampled across the authored reaction duration: the displayed frame is
counter * frame_count / duration, with the last update forced to the last
frame. Sound-status lookup deliberately uses counter % frame_count instead
of that displayed frame. When displacement is not suppressed and packet word
76's delay has elapsed, each update projects the enemy along the stored impact
angle by (duration - counter) * 120 / duration and passes the segment through
the ordinary map, object, and actor collision owner. Completion unlocks the
actor, restores presentation seven, and publishes event 16 only from the
native minus-one event state.
Action eleven follows 0x0045bec0. It selects chart three, switches to
direction eight only when that chart contains frames there, plays the separate
25-entry DAT_004815d8 death sample on update one, and emits effect 21010
with one rand() % 8 direction draw on entry. Once the CAF reaches its last
frame, the actor fades from strength 1,000 over 120 updates and is removed.
The chart-three marker fallback still supplies sample 86.
Receiver visuals now cross a world-owned effect boundary as well.
0x00429ec0 maps effects 21000 through 21014 to the exact OPTION resources
11000000, 11000001, 11000002, 11000009, and 11000017 through 11000027.
0x0042b860 creates the ordinary one-pass CAF owner at the source actor's
resolved position. The specialized 0x0042cba0 path used by 21010 through
21012 instead lasts 120 updates at initial strength 500 and fades during its
last 30. These visual actors participate in normal world depth sorting and do
not own damage. The four 21000 through 21003 variants are the ordinary
splatter selected by direct hit packets and play for both surviving and lethal
hits. Enemy death separately creates effect 21010. CAF frames advance once per
game update: that fixed-lifetime death effect reaches its last authored frame,
holds there, and only fades during the final 30 updates. Reflection,
staged-reaction, projectile, and spell effect
dispatchers remain separate follow-up branches. Network transport, experience
accounting, and drops remain outside this receiver/presentation boundary.
Eligible actions are copied into a temporary linked list at position zero. Finding a priority above the current maximum clears that list first, but a later lower-priority action is still inserted. Weighted traversal is therefore in reverse file order and may include those later lower-priority candidates. This is observable data behavior: 33 priority decreases exist inside the shipped event buckets. If no action is chosen, requested events 1 through 10, 16, and 17 retry event zero. The portable event evaluator preserves this path as a deterministic unit. Its target-condition callback now consumes the same typed result as the action dispatcher. Live enemy updates remain off until selected-action storage and the complete movement/presentation consumers can be connected without a partial behavior path.
The native dispatcher at 0x00459340 sends selected action zero to
0x0045c350 and action one to 0x0045c3c0. Action zero resets its elapsed
counter on entry, requests presentation action seven when necessary, uses AID
parameter one as an inclusive duration, and exposes event 11 until it returns
to event zero. Action one builds an absolute patrol rectangle by adding the
four MCT offsets to the spawn position. It starts movement-controller mode
three for AID parameter-four updates, waits parameter-five updates, and repeats
until parameter-one total updates have elapsed. Its holding event is 12 and
its completion event is one.
Movement-controller mode three at 0x00454310 chooses each X and Y coordinate
with a separate rand() % inclusive_size draw on its first active update.
With a zero update limit it exits before either draw. The passive portable
controller reproduces this event, counter, presentation, speed, and random
contract without yet changing a live enemy.
The next dispatcher group is intentionally small. Actions two through four
enter 0x0045c560 and request presentation actions one through three; actions
five through seven enter 0x0045c5a0 and request presentation actions four
through six. Both handlers clear the current presentation and reset the
action counter only on entry. Their presentation routines later restore idle
action seven and publish the matching event number when the animation ends.
Action eight at 0x0045c5e0 only records its own entry and resets the counter,
leaving the current presentation alone.
Control.aid contains 450 action-two records, 158 action-three records, 178
action-five records, 91 action-six records, and 42 action-seven records. It
contains no action-four or action-eight records, even though both native
paths exist. These dispatch transitions and their animation, targeting,
marker, typed-effect, and completion-event consumer are portable and tested.
They remain behind the live-enemy boundary until damage/effect construction,
audio playback, and movement can be connected as one complete update path.
Actions nine and ten at 0x0045c600 and 0x0045c780 are the target-movement
pair. If the selected AID condition enables a target range, they repeat the
retail target lookup with its minimum and maximum; otherwise they use the
enemy's default eligible target. A missing target immediately returns event
nine or ten. A valid target starts presentation action eight at parameter
three times the MCT speed scale divided by 1,000. Parameter seven controls
target refresh cadence and parameter eight is the random-turn chance.
Action nine retreats until the bounds distance reaches 10,000, using movement mode five for a player and mode two for a scenario actor. Event 14 holds it until parameter one's inclusive duration, after which it returns event nine. Action ten approaches to bounds contact with modes four and one respectively; event 15 holds it before event ten. Either action also returns its completion event if the walking presentation stops.
Action eleven at 0x0045c900 starts fixed-point movement mode zero toward the
enemy's cached walk point. Its speed is the AI list's WalkPointSpeed times
the MCT scale divided by 1,000, and its stop distance is 150. It keeps event
minus one through elapsed counter 90 and returns event zero at counter 91, or
as soon as the walking presentation stops. Retail Control.aid contains 61
action-nine and 205 action-ten records, but no action-eleven records. Of the
retreat records, 44 repeat an authored range lookup and 17 use the default
target. All approach records use range lookup; 30 enable the optional
random-turn and refresh pair.
Gameplay pointer selection starts at 0x0040ede0, which asks 0x004165d0 to
collect the current display objects inside the configured pointer square. A
candidate must have an opaque pixel from a visible NJP part in that inclusive
square; this is not a loose actor-bounds test. Turning the range option off
reduces the test to the exact cursor tip. Within one priority group, an exact
tip hit wins first and the nearest candidate wins otherwise. The candidates
also keep their normal world depth order and are grouped by the five
priorities stored in SFlare.Cfg. With the retail defaults, a ground item
wins over a person when both are in range, while type-two enemies use the
highest ENEM priority and win over both.
0x0040ee70 scans that result from front to back. For a person it projects the
actor's feet, subtracts the MCT label height, draws a half-transparent black
plate around the centered 6-by-12 name, then draws a black one-pixel shadow
and the actor's configured name color. Ground items use the name from
Item.Ibn; ordinary money is formatted as its quantity followed by Gold.
The selected object's visible RGB strengths each receive +300. Values above
1000 do not multiply the palette color: RKC_UPDIB moves each channel toward
white, which produces the pale hover tint seen in the retail game.
Enemies use that same nameplate and pale-tint path, with their MCT name color
and label height.
Message layout at 0x00456550 counts ASCII and Shift-JIS glyph widths, adds
an eight-pixel text-box inset, and positions actor messages above the same MCT
label anchor. 0x00456bb0 surrounds that box with the nine-pixel
Hukidasi.njp frame and draws its tail from
System\Game\Pattern\Hukidasi.njp, then places the 6-by-12 Font01.njp text
at a four-pixel inset. The tail overlaps the bottom four frame pixels before
extending into the world. The portable renderer follows this path for actor
messages instead of using a fixed screen-bottom dialogue box. Message events
retain their current script character, which also anchors Syria's initial
branch even though it does not run an explicit facing command.
The same layout function gives ~ a special meaning when message flag
0x40000000 is set. The tildes are removed from the displayed text and each
enclosed run is stored as a clickable character range with its line and
columns. The companion messages use opcode-2 mode one, pass a writable script
operand for the selected zero-based range, and supply an initial selection.
Dune's first menu starts on range three, QUIT. These bytes are UI control
markup, not part of the English message.
Mode one also carries companion follow-up text with initial range -1.
Those messages have no ~ spans and are acknowledged like ordinary speech;
the empty range list makes 0x00457fa0 write -1 through the supplied result
pointer before the status-one callback. Harley's Explanation branch uses
this form for messages 1000057 and 1000058, then reaches the same
status-one release chain. A non-negative initial range is therefore part of
the choice contract, not just a visual default.
Pointer handling at 0x00457fa0 replaces the current range only when
0x00457bb0 finds the pointer inside one of those spans. Moving away leaves
the last range selected. 0x00456bb0 draws that range in red
(255, 0, 0) and the other ranges in gray (96, 96, 96). The portable
conversation state now owns the same selection, so hover rendering and the
option returned to the interpreter use one set of range indices.
World interaction goes through 0x00449240. It measures the shortest gap
between the player and target judgement rectangles with 0x004143c0, rather
than comparing their center points. The player's initial interaction distance
at offset 0x3f4 is 0x9f (159 world units). A target outside that distance
starts movement-controller mode one and is followed as it moves. Once the
rectangle gap reaches 159, the player faces the actor and starts that actor's
status-zero script. This is why clicking a distant person in retail walks
toward them instead of opening a remote conversation or simply rejecting the
click. Type-three ground items use the same approach path. Once close enough,
0x004526a0 calls the local player's inventory insertion routine and removes
the scenario entity only when ownership has transferred successfully.
Type-two enemies split from the script-interaction path. A living, visible,
pointer-enabled enemy outside the same inclusive 0x9f rectangle-gap range
becomes movement-controller mode one's moving target. On reaching the range,
the player stops, faces the enemy, stores its character number, and requests
the currently selected ordinary attack. The SFlare.Cfg field mirrored at
0x0048d540 is the Attack While Moving gate used by this branch; retail
unconditionally restores it to enabled while loading the config. The portable
target controller now owns the approach-to-ready transition and cancels it if
the enemy disappears, dies, becomes hidden, or loses pointer status. CAF
attack startup and the impact marker are owned separately from that approach.
0x00450630 chooses the player attack action from the equipped main-hand
instance's subtype. No main hand, or a subtype outside the five explicit
branches, selects action 7. Subtype 0 selects action 8, subtype 3 selects
action 9, subtype 1 selects action 10, and subtypes 4 and 5 select the
separate actions 19 and 20.
Action 7 at 0x00439140 and actions 8 through 10 at 0x00435e60 share the
same authored timing contract. Action 7 and action 8 use CAF charts 5 then 6;
action 9 uses 15 then 16; action 10 uses 19 then 20. On entry the actor locks
movement, clears the displayed frame, sets the previously scanned frame to
minus one, and synchronizes the action. Action 7 calculates the displayed
frame before incrementing its counter, while 8 through 10 increment first.
This one-update distinction is preserved. Action 7 requests its swing sound at
counter five; 8 through 10 do so at counter six. A null or light main hand
uses sample 1, and weapon weight 60 or greater uses sample 2 through
0x00466110 selector four.
0x00450c60 derives the animation tier from Table 4 after capping the derived
Speed of Attack value at 255. The ten frame factors are exactly 0.6, 0.7, 0.8,
0.9, 1.0, 1.1, 1.2, 1.3, 1.4, and 1.5. Equipped weight greater than the
derived capacity forces tier zero. The action calls this calculation on every
update, so an equipment or weight change can affect an attack already in
progress. 0x00445630 counts equipped objects only
and omits the off hand when the main-hand classifier suppresses it; portable
weight and base-contribution accumulation now do the same.
The damage moment is not inferred from a frame count. The action scans part
zero of every newly crossed frame in its first chart and emits an impact when
CAF status bit 0x40 appears. It only scans while the global displayed frame
is still in that first chart, then subtracts the first chart's frame count,
clamps within the recovery chart, and unlocks on its final frame. The shipped
male and female chart-5 data both contain ten first frames with the marker at
frame 7 and nine chart-6 recovery frames. The portable world validates the
retained target again at the marker before publishing the typed impact event;
the receiver owner then handles packet construction and enemy mutation.
Actions 19 and 20 share 0x00437fe0. They use player CAF chart 10 without a
recovery chart, calculate the displayed frame before incrementing the action
counter, and use a separate factor table: 0.3, 0.4, 0.5, 0.6, 0.8,
1.0, 1.2, 1.4, 1.7, and 2.0. Both shipped male and female chart-10
fixtures contain 17 frames with the 0x40 launch marker on frame 3. Sample 3
is requested at counter 6 and the final frame ends the action. Retail has no
shipped subtype-four Item.Ibn record, so action 19's data-dependent projectile
branch remains without a real fixture rather than being guessed.
The ordinary action-20 path reads five adjacent fields from its equipped
category-zero record. Raw offsets 0xb8, 0xbc, 0xc0, and 0xc8 become
the generic effect selector, spread pattern, travel speed, and piercing flag;
the intervening 0xc4 field is zero in every shipped record and has no proven
consumer here. The effect selectors map 0..3 to generic actor types
1, 0, 4, 5. Selector zero rolls impact effect 21000..21003; the others
roll 21007..21009.
Spread values 0..8 create one straight shot, two parallel shots, one homing
shot, straight or homing 3-way fans, straight or homing 5-way fans, and
straight or homing 7-way fans. The 3-way angles are 0, -15, +15; the 5-way
angles are 0, -10, +10, -20, +20; and the 7-way angles are
0, -8, +8, -16, +16, -24, +24 degrees. Double Bowgun is deliberately
different: it computes two explicit origins 200 units from the player at
minus and plus eight degrees, but both actors keep the unmodified target
angle and therefore fly in parallel.
Every non-double request retains the player as owner, projects its origin 200
units from the player, targets enemies and scenario objects with mask 20,
uses 30-unit judgement bounds and display height 350, and enters the ordinary
category-50000000 actor list. Double shots use owner kind zero solely to keep
their explicit origins; their family-zero packet still identifies the local
player for receiver attribution. The generic actor performs physical evasion,
static and target collision, contact sample 20, first-target expiry, optional
target memory for piercing, and homing when requested. This is the same actor
owner used by reconstructed enemy effects, not a player-only damage shortcut.
The family-zero packet is built before the projectile exists.
0x00450f80 counts job-history bytes matching ranged job 5 through the
current level. A player currently in job 5 keeps 100 percent physical attack;
other jobs use jobLevel * 50 / 30 + 40 percent, capped at 90, with a minimum
result of one. Packet word 36 carries the derived hit rate consumed by the
effect actor. Equipment reflection consumes the first random draw and the
projectile's hit-effect choice consumes the next. The shot requires nonzero
current weapon durability and subtracts one durability after creating the
complete fan, not once per projectile.
0x00437fe0 can redirect action 20 into action 21 for a ranged-job player
while the retail increased-power state is active. That state and action 21
belong to the later skill/status slice; the ordinary action-20 path does not
invent a placeholder for them.
The portable first pickup checkpoint keeps that separation. WorldScene
owns the stable ground entity and pending approach, while PlayerInventory
owns the accepted category, definition ID, and quantity. Gold fills existing
stacks to 10,000 as 0x00449ef0 does. The retail 9-by-4 placement grid,
multi-cell item sizes, full-inventory failure, and inventory panel are still
part of the next inventory slice.
The category-four, definition-one branch in 0x00449ef0 is the exception to
ordinary ownership. Below player runtime maximum +0x2c0, it destroys the
concrete Mine item and increments the separate count at +0x328. At the
maximum it leaves the instance intact and returns failure, so the
single-player caller recreates the normal mode-zero world drop; it does not
try the backpack. 0x00408a80 draws Status.njp pattern 67 only while that
count is nonzero, then always draws count / maximum at the authored Mine
row. The color compares the live maximum against base value ten.
The Mine definition happens to carry generic weight value one. That value is
not part of the live encumbrance path: 0x00445630 reads the nine equipment
pointers only, and neither it nor its callers read +0x328. Consequently the
separate counter does not alter the inventory Weight readout or attack-speed
tier.
Inventory artwork goes through 0x00465cb0, separately from the world item
CAF path. After drawing an ordinary weapon or armor icon, it divides current
durability times 100 by maximum durability. Results from zero through nine
draw Status.njp pattern 16 at the lower-right of the complete multi-cell
footprint. Nonzero durability uses the low four bits of the gameplay update
counter for an eight-update-visible, eight-update-hidden cycle. Zero
durability skips that blink and leaves the warning visible. Backpack,
equipment, and pointer-held items all reach the same drawing rule. The nearby
pattern-17 branch depends on a different runtime item state and remains
unclassified rather than being folded into the condition model.
0x00454210 initializes the executable's shared movement controller and
0x00454930 advances it. It is not an A* route search. A direct collision
sweep is followed by stateful cardinal obstacle-edge steering. Controller
modes cover fixed points, scenario actors, other players, bounded wandering,
and related approach behavior. Calls from the player, PEOPLE actors, and enemy
actors all reach this controller. RKC_RPG_AICONTROL chooses enemy intent and
parameters; it does not contain a second enemy pathfinder.
The portable MovementController now keeps the same pair of cardinal
movement and wall directions between updates. 0x00414990 sweeps along the
dominant axis, interpolates the other coordinate with integer arithmetic, and
returns the last free point before contact. During cardinal edge movement it
also checks a one-pixel strip on the wall side; that small side step is what
lets an actor stay against an edge and turn its corner without an axis-slide
shortcut.
On the first blocked direct step, 0x00454930 compares the attempted quadrant
with the returned contact. If no movement was possible, it probes east, south,
west, or north in the quadrant-specific retail order. Those results select
the cardinal movement direction and the side occupied by the wall. A blocked
edge step swaps them by taking the opposite wall direction. Leaving an edge
is controlled by the signs of the remaining X/Y distance and the exact
movement/wall pair. It is not based on whether the Euclidean distance happened
to improve.
The portable implementation follows those tables directly and has no A*
fallback. Live town actors take part in the same judgement query. The selected
interaction actor remains solid; mode-one interaction ends at the 159-unit
rectangle gap before collision. Player and PEOPLE movement share this owner,
including facing during detours. The PEOPLE walk at 0x0045da25 passes
dynamic collision mask 0xffffffff, just like the player. Its own character
number is the only live entry excluded by 0x004145b0. The portable world
therefore keeps one live blocker set for the hero and all PEOPLE actors,
excludes only the actor currently moving, and refreshes positions as actors
are updated.
Remote Town fixtures cover the sacks beside Ostare, the Ostare-to-Malse approach with the town actors present, longer companion trips made from successive ordinary movement commands, and a wandering PEOPLE actor steering around another live actor. Per-actor retail type masks, enemy integration, and the remaining controller modes are still follow-up work.
The scenario entity manager updates every active-map entry through its virtual
update slot at 0x00429ce0; it does not first test the camera or render clip.
0x004298c0 inserts those entries in ascending character-number order. In the
gameplay frame at 0x0041e4f0, the player update runs before the scenario
manager at 0x004274f0. Portable Remote Town follows that player-first order,
then updates its seven PEOPLE records in their MCT order, which is ascending
by local ID. This preserves both offscreen wandering and the live blocker
positions seen by the next actor in the same update.
The controller's cardinal values are 1 = north, 2 = south, 3 = west,
and 4 = east. The initial one-pixel probes are ordered by the attempted
direction:
| Attempt | Probe order if contact equals the start | Selected movement / wall |
|---|---|---|
| southeast | east, south, west, north | east/south, south/east, west/south, north/east |
| northeast | east, north, west, south | east/north, north/east, west/north, south/east |
| southwest | west, south, east, north | west/south, south/west, east/south, north/west |
| northwest | west, north, east, south | west/north, north/west, east/north, south/west |
| south or north | east, then west | east/vertical, then west/vertical |
| east or west | south, then north | south/horizontal, then north/horizontal |
The edge-stop tests are equally specific. For a destination northwest of the actor, the stop pairs are south/east and east/south. Northeast uses south/west and west/south. Southwest uses north/east and east/north. Southeast uses north/west and west/north. On a shared axis, movement directly away from the destination stops the edge walk. These pairs explain why a generic “distance got smaller” test produces loops that the retail code does not.
Player movement calls 0x00454930 with dynamic collision enabled and mask
0xffffffff; the resolver uses its low byte. 0x004145b0 excludes only the
moving character number and interprets the mask like this:
| Mask bit | Dynamic character class |
|---|---|
0x01 |
local character numbers 0 through 3 |
0x02 |
type 0 |
0x04 |
type 1 |
0x08 |
type 2 |
0x10 |
type 4 |
0x20 |
type 3 |
0x40 |
type 5 |
That means the player query treats every relevant live actor as solid. It does not remove the NPC selected for interaction. The controller's mode-one range test is what prevents the player from trying to occupy that NPC's rectangle. The portable player currently supplies all loaded town people to the same query; preserving the individual class masks matters once enemies and other players join the portable world.
The character-selection object owns a 0x160-byte record beginning eight bytes
into its small wrapper. 0x00440f70 copies all 0x58 dwords into the gameplay
player at offset +0x10, then initializes the new-game defaults. The confirmed
record fields are name at 0x00, gender at 0x18, the save-menu job value at
0x1c, and level at 0x24.
New characters obtain thirteen values from Table.Tbd. The selected table is
0x385 - gender. The saved field uses zero for female and one for male, so
this selects table 901 for female and table 900 for male. The values
are stored in a slightly shuffled part of the record because current life and
current mana sit beside their base maxima. Runtime +0x40/+0x44 are base
maximum and current life, while +0x48/+0x4c are base maximum and current
mana. Both current values start at their maximum. 0x0044ea60 later builds
the separate derived values which include equipment and status modifiers.
The item panel keeps equipment separate from the backpack. Pointer classifier
0x00447290 returns main hand for x=480 through 543 and y=16 through 143.
0x00446320 accepts category-zero items there, compares the definition's
required-level field with player offset +0x34, and stores the equipped
instance at player offset +0x4f4. Clicking an occupied main hand with an
empty pointer unequips it; carrying another valid weapon swaps the two.
The decoded weapon record maps requirement level from field-block offset
0x94, player CAF part from 0xa8, and that part's RGB strengths from
0xac through 0xb4. Short Sword requires level one and selects part 12.
Its first two entries in the ten-value derived contribution block are 20 and
100. Retail runs both the derived-value refresh at 0x00450080 and appearance
refresh at 0x00444ca0 after changing equipment. The portable equipment owner
keeps those contributions available for combat, updates the visible 30 weight
immediately, and rebuilds the player part mask without making the item panel
own world rendering state.
The player receiver at 0x00443cb0 builds its 14-word defense profile just
before calling the shared damage routine. Word zero is the player family,
word one is the character number, words two through four are the already
derived attack, physical defense, and magical defense, and words five through
twelve are Fire, Water, Earth, Thunder, Holy, Dark, Gel, and Metal. Retail
does not write word 13 for this family, and none of its family-zero damage
branches read it. The portable snapshot clears it rather than carrying an
uninitialized stack value.
0x0044fba0 derives the eight base affinities from a two-dimensional player
value. Its anchors are (0,20000), (0,-20000), (-20000,0), (20000,0),
(14140,-14140), (-14140,14140), (-14140,-14140), and
(14140,14140). Each result is
(20000 - trunc(distance)) / 2000, with signed division toward zero.
0x0044fca0 then adds item contributions with 32-bit wrapping and clamps each
final value to -10..10.
0x0044fe30 supplies those item values from the main hand, helmet, body,
boots, optional off hand, and four accessories. Weapons and armor combine the
definition's eight base values with instance words 39 through 46. Accessories
use only those rolled instance words. An identified category-two backpack
item also contributes when its inventory width is not one; ordinary one-cell
accessories in the backpack do not. A main-hand weapon classified as
two-handed suppresses the off hand here as well as in player drawing.
0x004672f0 proves that classifier uses weapon subtype one or three and the
weapon field at raw offset 0xcc; the previously suspected field at 0xdc
is unrelated.
The third value in each serialized item header and runtime offset +0x1c is
the identified flag, not an item-quality tier. The same flag is mirrored in
instance word 48 for weapons and armor and word 47 for accessories. New
variant-one and variant-two definitions begin unidentified; the other
variants begin identified. Tooltip color comes from the definition's variant,
so loading an identified ordinary item cannot accidentally recolor its name.
The rest of 0x00443cb0 is now a passive portable receiver rather than an
early live-world shortcut. Increased Power multiplies physical defense by
12/10 and adds two to spell level before the normal 1..20 clamp, global
magic-level addition, and final 1..30 clamp. A local Energy Shield uses
table 17 row 9 to scale ordinary physical defense and sends ordinary damage
to mana while mana remains. Effect-family damage still reaches life.
Magic Shield only handles effect-family packets for the locally owned player. Table 17 row 18 supplies its reduction and a zero result is forced back to one damage. Damage of at least 20 requests spell training. The shield emits effect 21029 and sample 60, then charges table 16 at the currently selected magic row and Magic Shield's effective level. Equipped instance parameter 19 lowers that cost but cannot take it below one. Empty mana disables the shield. Counter Burst repeats that cost path with spell 19 after a valid reflection.
Lethal local damage searches the Special Item owner for category four,
definition 98000000. The first match is consumed, both life and mana return
to maximum, sample 17 plays, and effect 21020 is requested. Without it the
player enters presentation action five. Retail then checks helmet, body,
off hand, and boots for one durability point with 20, 30, 30, and 20 percent
chances. Only occupied slots consume draws. The off-hand draw happens before
the two-handed-main-hand test, and a break requests both equipment sync and a
derived-value refresh.
Packet word 38 enables player reflection. The equipment pass always consumes its chance draw and sums instance parameters 20 and 21 across active gear; Counter Burst adds table 17 row 19. Only a live type-two scenario source can receive the reflected immediate packet. It carries the player number, derived physical defense, level, a random effect number from 20015 through 20017, and at least one reflected damage. Source value 100 halves that damage. Equipment reflection creates effect 20014 toward the source, while Counter Burst creates 21030; both play sample 60.
Player hit reaction uses the packet element to select one of the eight defense
affinities. Negative values -10..-1 add table 26 chance and duration.
Table 25 supplies damage-scaled defaults, while effect packets select their
negative, neutral, or positive chance, duration, and motion banks. Instance
parameters 14 and 15 defend chance and duration, and parameter 16 forces
motion after the non-motion duration cap. Packet stages, configured effects,
the 20-percent random hit visual, sample 119, and event four follow in the
same order as retail. The result deliberately leaves live state mutation,
effect ownership, audio playback, and equipment synchronization to later
world owners.
The owned companion is created by 0x004501c0 as category 40000000 with
character number 16000000 + player slot. Table 60 supplies its name,
PARTNER resource number, and three draw strengths. 0x004136f0 then sums
each parameter column from zero through saved companion level - 1 in table
800 + companion type; it does not substitute the PEOPLE record for the
matching dog in town. The initializer uses judgement
[-80,-80,79,79], starts at the player's position, and retains the player as
its owner. The portable profile and actor preserve all six shipped companion
rows and all three PARTNER resource directories.
Script opcode 3 enters at 0x0043167d. It evaluates a companion type, stores
the second operand for the eventual message-close result, reads that type's
saved level from the Table 60-sized player array, and calls 0x00413830 plus
0x004136f0 to build the selected profile. The generated name and values are
shown through the ordinary actor speech path. Its level and experience lines
come from the active companion record at player offsets +0x154 and +0x158,
even when the requested profile belongs to another type.
The experience cap is min(player level / 3 + 2, 35). Reaching it prints
Experience Limit, or Experience Max when the cap is 35; otherwise the
active experience value is printed. The instruction stream has a retail UI
bug worth preserving: the M Defense value comes from profile offset +0x54
(magical hit rate), while M Evasion Rate comes from +0x44 (physical
defense). Six shipped calls cover types zero through five once each in three
scenarios, so the portable interpreter keeps the profile construction behind
a typed world hook instead of learning about tables or player storage.
The ordinary owner mode in 0x004622b0 measures judgement-bound distance to
the player. Below 160 the companion requests idle action two and refreshes a
five-update linger. From 160 through 599 it starts action three at
table row 1 / 5 after that linger. At 600 or farther it starts or promotes
the route to action four at table row 2 / 5. Distances of 4000 or more snap
the actor to the player position plus (200,200). Actions two, three, and
four render PARTNER charts zero, one, and two. The other opening branch
searches for a type-two target within 1200 and enters companion attack mode;
the portable actor now follows that handoff too.
That search only runs while the owning player's runtime field +0x15a0 is
zero. 0x00440f70 initializes the field to one, so the companion starts
inactive. Inactive does not mean stationary: the same distance bands and
follow actions still run, but autonomous acquisition is skipped. Enemy target
selection at 0x004593f0 and 0x00459500, as well as category-50000000
effect contact, also requires owner mode zero. An inactive companion remains
visible and solid and follows normally, without attacking, drawing enemy
attention, or receiving hostile effect contact.
The Space branch in 0x004429b0 and the HUD branch in 0x00445bd0 both write
1 - current to +0x15a0. Switching to inactive clears the companion's
pending command at +0x184; a presentation which is already locked can
finish. The HUD hit box is x 0..111, y 393..408, using strict vertical
comparisons. This is runtime state rather than part of the saved character
record, so scenario transitions retain it while a newly entered game starts
inactive.
Attack mode 0x00462610 drops back to ordinary owner mode when the companion
is more than 1499 judgement units from its player. Otherwise it repeats the
nearest living type-two search within 1200. A target beyond the fixed
159-unit attack range starts the common collision-aware run route at
table row 2 / 5; a target inside it is faced and requests presentation
action one.
Presentation action one is 0x0045fff0, which uses PARTNER chart five. Its
speed tier is signed table row 0 / 32, not parameter row 17, and the ten
frame factors are 0.2 through 1.1 in steps of 0.1. Each update scans
every newly crossed part-zero CAF cell. Status 0x400 plays sample 95;
status 0x40 repeats an exact-facing living type-two search inside 150
judgement units. Its presentation lock suppresses further AI decisions until
the chart reaches its last frame, even if the selected target disappears in
the meantime. The hit check compares companion row six with the enemy's
physical evasion. A miss creates the normal MISS actor. A hit sends the
family-one packet with the companion character number, row-five physical
attack, owner-stored companion level, native element, and a random effect
from 21000 through 21003, then plays sample 44. The live path hands that
packet to the existing enemy receiver, so reaction, attribution, effects,
death, player experience, and drops remain owned by the same systems as a
player hit.
The player's owned companion does not reuse either receiver. Its virtual
callback is 0x0045f9f0, selected from the type-five companion vtable at
0x00476e38. It rejects companions with no life and presentation actions
7, 8, and 10 before reading the packet or consuming randomness. The defense
profile is family one: character number, physical defense, magical defense,
and the companion's native element. A non-positive packet base again skips
the shared formula and becomes zero damage.
Only the client whose local player slot equals companion character number % 10 changes companion life. Lethal damage selects presentation action six,
resets its counter, and locks the action. Unlike the enemy receiver there is
no damage attribution array, source status 73, reflection, kill-status award,
or network request in this callback.
A surviving companion uses tables 24 and 25 like the enemy family, but has no separate reaction-defense or always-suppress-displacement fields. Player-family packets use the native element's opposing packet strength to select table 24; other packets use row five when packet element is opposing. Effect-family chance, duration, and displacement suppression come directly from the native-element banks. A reaction that permits displacement is capped to 15 updates before packet word 76 is added, then presentation action five stores the impact angle and optional facing.
Packet reaction stage one creates effect 21015 through 21018 with owner kind four and plays sample 119; stage two only changes the reaction stage. The two configured packet effects and the 20-percent random hit effect instead use owner kind two. That otherwise easy-to-miss constructor difference is preserved in the portable request. Death action six and default event four are selected after those common effects. The live world now applies the returned state, queues those effects through the common effect owner, plays the returned audio, and lets both direct enemy impacts and category-50000000 runtime actors target the companion.
0x004616d0 presents surviving hits with PARTNER chart three. It scales the
display frame over the receiver duration, holds frame zero for reaction stage
two, and applies the same diminishing collision-aware 120-unit impulse used by
the other actor families when displacement is not suppressed. The final
update releases action five back to idle action two.
0x00461990 presents death with PARTNER chart four in direction eight. Its
first update creates effect 21010 with a random direction and writes a
persistent respawn countdown: 900 gameplay updates normally, or 600 when the
backpack contains category-four definition 98000002. The item is checked but
not consumed. The final chart frame is held, then opacity fades over 60
updates. While the saved countdown is nonzero the actor remains invisible and
non-colliding.
When that countdown reaches zero, the owner restores companion life to its
table-backed maximum, places it at the player, and requests action eight.
0x004610b0 plays PARTNER chart seven in direction eight; reaching its last
frame releases the lock and returns the companion to ordinary owner AI.
Enemy death accounting at 0x004134a0 awards one companion experience point
when the defeat source belongs to the local slot and the companion is alive
below its cap. The cap is player level / 3 + 2, limited to 35. The point is
awarded before the player's own level threshold is applied. 0x00412e20 then
uses the possibly new player level for that cap and table 800 + companion type row 18 for each companion threshold. A gained level rebuilds the summed
profile and restores full companion life; experience is cleared at the cap.
The level and experience fields in the 0x160-byte record are only the active
companion's working copy. 0x00440f70 allocates one level array and one
experience array with the Table 60 row count, initializes every level to one,
and leaves every experience at zero. Opcode 45 reaches 0x00450500: it first
copies the active values into their current array row, selects the requested
row, loads that row's values back into the record, and clears the defeated
countdown. It then replaces character 16000000 + player slot at the hero and
fills its life to the rebuilt maximum. Selecting the already owned type still
runs the complete reset.
Before saving, retail synchronizes the active row again. It writes the Table 60 count, the complete level array, and the complete experience array directly after the magic block; the Land Mine count follows them. OpenShadowFlare now owns that exact section rather than only skipping it on the way to the Mine field, so inactive-companion progression survives swaps and save/load cycles.
The second table row is the value consumed by 0x00450d40. It is 128 for both
new characters, producing movement tier five. This is now read through the
portable RKC_RPG_TABLE boundary and owned by PlayerData; PlayerActor
receives only the resulting movement tier and no longer owns level or other
character data.
Retail saves begin with ShadowFlare0005 and the same plain 0x160-byte record.
The load menu reads this copy directly. The complete record is repeated inside
the obfuscated payload and restored by 0x0044cac0 along with dynamic objects
and world state.
0x0044b580 writes the payload size first, followed by one byte from the
Visual C++ rand() sequence and a four-byte signed-byte checksum. It XORs
each plain payload byte with that random byte, then replaces the result with
its index in the same 256-byte permutation used by the retail data path.
OpenShadowFlare now writes that envelope and performs the inverse operation
when preserving an existing save. It also follows the retail variable-sized
item stream to restore and rewrite the nine known equipment slots, backpack,
the belt, and the 9-by-10 Special Item owner. Grid positions, Gold quantities,
durability, identified state, and all still-unnamed instance bytes survive
the round trip. The extra equipment records and unknown trailing payload bytes remain
untouched. Loading also restores the following counted arrays in executable
order: operand-type 12 quest state, type 10 transport unlocks, and type 11
general script/conversation state. The 51 transport values are validated
against Table 40. The rest of the dynamic payload is still pending.
The three counted quest, transport, and script-state arrays are immediately
followed by the player's magic owner. Retail writes count 22, then 22
32-bit availability values from runtime +0x1440, 22 levels from +0x1498,
22 experience values from +0x14f0, and the eight global magic-bar spell IDs
at 0x0048d508. 0x00440f70 initializes those arrays to zero, one, and zero
respectively and leaves every bar slot at -1; only availability value three
is treated as learned. The portable save path now restores and replaces this
exact section while retaining all later unknown bytes.
Script opcodes 67 and 69 are the mutation and query paths for that array.
0x004340e7 evaluates a spell index and writes the exact learned value 3 at
player +0x1440 + index*4. 0x0043412b reads the same slot, compares it with
3, and writes the resulting boolean to its second operand. These handlers do
not touch spell level, practice experience, or the bar, and the portable
runtime does not let its temporary All Spells debug override affect either
script operation.
After the magic history, companion arrays, and Land Mine count, retail writes
three 32-bit world values. 0x0044b580 first copies DAT_0048ce80, the live
walk/run word. It then snapshots the current player actor's fields +0x60 and
+0x64 into player offsets +0x15a4/+0x15a8 and writes them as the scenario
ID and scenario entry value. The load routine reads those exact destinations
at 0x0044deae, 0x0044dec4, and 0x0044dee1; the front end subsequently
passes +0x15a4/+0x15a8 to 0x00426200.
This is entry persistence, not arbitrary-coordinate persistence. A saved hero
returns to the corresponding MCT entry, following the same entry-key lookup as
ordinary scenario travel; position and facing therefore come from that entry
rather than separate save words. OpenShadowFlare now restores and replaces all
three words through one world-state owner. A live regression saves in scenario
6 at entry 4 and reloads at (35105,-6156); original 0004.Ssv supplies the
corpus tuple running=1, scenario=0, entry=0 and rewrites byte for byte.
The literal page count ten, ten Giant Warehouse unlock values, and ten ordinary item containers follow. The selected page is not in the stream. OpenShadowFlare restores and replaces the flags and all ten containers while preserving all later unmapped values. A version-four portable tail carries the same owners when a newer save does not yet contain that later retail suffix; versions one through three remain readable and supply run/walk as a migration fallback.
The script operand reader and writer expose those same unlock values as type 13. The ten shipped operands address indices two through nine. Town of Antalusia sentence 55 provides the first end-to-end proof: after accepting the Sacred Wing, Berini writes one to index two while granting Giant Warehouse III. The portable interpreter callback now targets the saved ten-flag owner, not a transient script map.
Primary-button input has two retail behaviors. A press and release is a latched destination click. Keeping the button down continuously replaces the destination with the live pointer position, but releasing after that held state cancels movement immediately.
0x004562f0 makes the distinction explicit in each 28-byte input record:
offsets +0x04 and +0x08 are the previous and current down states, +0x10
is the press edge, +0x14 is the release edge, and +0x18 counts updates
while the button remains down. 0x00441c00 does not arm release cancellation
until that counter is greater than nine. A normal click can therefore span
several game updates and still latch its destination. The pointer target is
refreshed on every down update either way.
The portable gameplay state now preserves that ten-update threshold rather than classifying any click longer than one 30 Hz update as a hold. A press consumed by a speech bubble stays UI-owned until button release, even when selecting the option closes that bubble. It cannot be reinterpreted as a held ground command on the following update.
Portable gameplay still updates at the retail 30 Hz cadence, while the window is presented at 60 Hz. Rendering the current simulation snapshot twice made camera scrolling visibly step at a constant rate. The runtime now keeps the previous and current actor positions and interpolates only their render positions and the camera between updates. Collision, scripts, animation counters, and all other game state remain on the fixed 30 Hz clock.
The variable section at 0x324 begins with counted object, PEOPLE, and enemy
resource preload lists. Four counted runtime groups follow in a fixed order:
object, PEOPLE, enemy, and item. Their common record contains IDs, optional
names and colors, label height, position, judgement, direction, initial CAF
part overrides, optional fixed-capacity part/color arrays, and one preserved
unknown value.
Those initial three-channel values are not the only object state. Script
opcode 56 at 0x00433a78 finds an existing scenario entity by character
number, sets the override-present word at runtime +0xfc, then writes effective
visible, pointer, and judgement values at +0x100, +0x104, and +0x108.
The type-zero draw path at 0x0045ddd0 selects the override visibility when it
is present. 0x0045e080 independently selects its pointer and collision values.
Reads and writes through the 100-, 300-, and 200-million script keys continue
to address the underlying MCT-backed channels.
Near Remote Town's first periodic status checks persistent script flag 71. Its
two branches use opcode 56 to alternate objects 10001030 and 10001031,
leaving pointer and judgement disabled for both. The objects share almost the
same position, so treating the command as an ordinary state write would leave
the base script variables wrong when the opposite branch runs. A scan of the
shipped SCS catalog found 66 opcode-56 calls across 13 scenarios; every target
is a type-zero object, including later three-part visible/collidable swaps.
The object tail is 0x34 bytes. FUN_00429600 creates its type-zero
0x120-byte runtime class through 0x0045dca0; its character number is local
ID plus 10,000,000. The initializer at 0x0045dd00 copies the transformed
tail. Tail value zero chooses between a static Pattern.Njp index and a CAF
chart stored by values one and two. The remaining confirmed values feed draw
status bit 0x80, object height, draw flags and strength, and red/green/blue
strengths into 0x0045ddd0. Unknown values remain lossless in
ScenarioObject.
Remote Town contains seven of these dynamic objects, with local IDs 0,
200 through 204, and 300. The final one is the named Warehouse object.
They use object resources 8, 15, and 14 from the first preload list. These
records are separate from the map's static OBL scenery. Across all 209 retail
MCT files, the exact sequential decoder reaches 5,203 object and 163 PEOPLE
records without a resource-list mismatch.
The periodic script path also reaches the shared combat-effect owner. Opcode
30 enters its handler at 0x0043309b, evaluates fourteen operands, initializes
the same 77-word packet used by native attacks, and calls the common
22-argument request allocator at 0x0042fdc0. The handler converts operand
three from degrees with the retail radians constant, projects operands zero
and one by operand seven, and stores that result as an explicit owner-zero
origin. It chooses packet word 34 from 21000..21003 when the effect number is
nonzero or 21007..21009 when it is zero, consuming one value from the shared
Visual C++ random stream. Packet words 4, 37, 40, 41, 43, and 72 retain the
other authored values; hit value 9999, packet direction eight, target kind 19,
and constructor value 21 equal to 200 are fixed by the handler. The shipped
catalog contains 411 calls in 33 scenarios and every call has exactly fourteen
operands.
The neighboring packetless placement command is opcode 36 at 0x0043332d.
It evaluates operands in the retail order 1, 2, 5, 6, 4, 3, then 0 and sends
the same allocator a seven-value descriptor: effect number, explicit X/Y,
display height, chart direction, and right/bottom judgement bounds. Negative
directions are replaced with eight. Owner, source, target, speed, and angle
fields are zero, instance is -1, constructor value 21 is 200, and the packet
pointer is null. The judgement pointer contains {0, 0, right, bottom}.
The common one-pass handler at 0x0042b860 uses the explicit origin for owner
kind zero and turns the supplied lower-right bounds into the point judgement
{right+1,bottom+1,right+1,bottom+1}. It runs chart zero for the selected
direction's complete CAF lifetime. The
effect switch maps 20007, 20008, and 20009 to OPTION resources 11000005,
11000006, and 11000007. Those are the only opcode-36 effects in the shipped
catalog: 353 calls in 26 scenarios, all with exactly seven operands.
Object 200 and the Warehouse at local ID 300 are the first reconstructed
type-zero pointer actions. The normal world pointer tests opaque cells in
their static NJP or current CAF display, then uses the common judgement-box
distance before running status kind zero for script characters 10000200 and
10000300.
Object 200 reaches opcode 37 at 0x004334da. Its argument zero sets the
transport UI owner used by 0x0040c950. That renderer draws Status pattern 13,
compacts enabled Table 40 rows into ten entries per page, uses patterns 22
through 24 for rows and patterns 11/12 for paging, and plays sample 58 on
navigation or selection. Table 40 row zero is Remote Town, scenario zero,
entry 50. The same-scenario branch at 0x00426200 combines that entry value
with local player zero as entry * 4, selecting MCT entry key 200 at
(94685,-2756), direction 7.
Destinations are discovered by a separate periodic script path rather than by
clicking object 200. Each teleporter's status-kind-5 sentence runs opcode 34
at 0x004337b5. The handler resolves its first operand as a scenario character
and calls the common 0x004143c0 judgement-rectangle distance routine against
the local player. It writes that exact distance to the second operand. When it
is zero, the authored sentence assigns one to its type-10 operand, whose value
is the destination's Table 40 row. The portable interpreter now follows that
same path, and the existing save extension retains the resulting 51-row flag
array.
The same Remote Town periodic sentence owns the activation presentation.
Opcode 27 enters at 0x00432d05 and evaluates eight operands. It resolves
operand zero as a local-player slot when below four or as a scenario character
otherwise, projects that world position, and applies the evaluated X/Y
offsets. Its Shift-JIS-aware scan treats a double-byte character as two cells,
uses six pixels per cell and twelve pixels per line, centers the widest line,
and bottom-aligns the block. The update packet is a black rectangle extending
three pixels around the text at operand-seven opacity, followed by a black
+1,+1 shadow string and the operand-four-through-six RGB string. Remote
Town supplies object 10000200, offset {0,-160}, message 1000060, RGB
{224,224,224}, and opacity 1000.
Opcode 46 at 0x004336e0 resolves a type-zero object and writes its evaluated
second operand directly to runtime offset +0xf4, the draw-strength field.
Objects 10000203 and 10000204 receive temporary flag 1000039, which the
script raises or lowers by 50 per update. This gives the activation a
twenty-update fade in either direction and prevents the hidden animated object
from advancing at strength zero.
Opcode 38 at 0x00433544 is not another visual packet. It checks the active
transport UI selector and current scenario, and clears that service only when
the evaluated argument matches. Remote Town calls it with zero on the first
update outside object 10000202; the same branch resets the sample-80 latch.
An independently open right-side inventory remains active and the camera
returns to its right-panel anchor.
Opcodes 31 and 32 are the paired scenario-enemy registry searches at
0x00432762 and 0x004327c9. Both evaluate an inclusive start and end
character number, initialize their result to -1, and call the binary-search
lookup at 0x00430770 for each number in ascending order. Missing entries are
skipped. Opcode 31 stops at the first entry whose value at +4 is one; opcode
32 uses the first whose value is zero. The result is written through the third
operand. All 134 opcode-31 calls across 90 shipped scenarios and all 34
opcode-32 calls across 13 scenarios use the same type-one, type-one, type-four
shape.
The registry value is enemy lifecycle state, not current HP or an ordinary
SCS flag. Enemy activation at 0x0045a140 writes one through 0x00430750.
The death owner at 0x0045bec0 keeps that value during chart three and its
120-update fade, then writes zero as the enemy expires before invoking the
status-kind-four callback at 0x004309a0. The portable hook consequently
reports one for a zero-life enemy until EnemyActor::expired() becomes true;
only MCT enemies are registered, so an absent ID never behaves like an
inactive enemy.
Dusty Ruins scenario 00010004 supplies the first shipped mission fixture for
that exact boundary. Its periodic sentence scans 14000000..14000007; only
after all eight Garam Goblin slots have finished their complete death
presentation does it run the object-state sequence, positioned sample, and
opcode-62 completion of mission three. The corresponding Remote Town path is
also executable-owned: completed quest zero plus player level 30 lets Ostare
start mission three, and the completed return branch creates Table 30 row 4
once before persistent flag two suppresses later rewards.
Scenario 00010005 supplies the related fixed-item path. Stone Spike enemy
one selects Table 30 row 23, whose zero attempt count uses the active-player
count and whose ten choices all select Table 31 row 401. The result is fixed
category 4, definition 99000001: Syria's stolen Spirit Stone on automatic
page zero at (1,0). Remote Town removes that exact definition after message
1000045, completes mission two, and creates category 2 definition 1100001
from the 1000046 callback. Definition 98000001, despite sharing the Spirit
Stone display name, is a different page-two story item.
Operand type 3 reaches that same registry directly. The reader at
0x004346e2 adds 14000000 to the operand's local enemy number, calls
0x00430770, returns the entry value at +4, and returns -1 when lookup
fails. The shipped corpus has 160 type-three operands, all as the left side of
opcode-0 comparisons. This is why 04000003 operand 30000 addresses enemy
character 14030000; it is not a generic runtime flag.
Opcode 25 enters at 0x004326c9. It evaluates four operands and calls
0x0045a140 with the resolved enemy, {x,y}, and direction. The handler
ignores the native routine's zero result when the slot is already alive, so
that case remains a successful no-op. For an inactive slot, 0x0045a140
clears its old action, movement, reaction, attribution, and death fields,
selects action 7, restores maximum life and full draw strength, installs the
new position and direction, and writes registry value one. Its virtual screen
sync at 0x00459f60 does not rewrite the MCT spawn rectangle. All 34 shipped
calls across 13 scenarios have the exact operand shape {4,6,7,1}.
Opcode 28 enters at 0x00433022, evaluates one target character, and calls
0x004309f0. That helper finds status kind 6 and invokes its sentence inline
through 0x00430ab0; a missing status returns success without running
anything. There are 181 shipped calls across 32 scenarios, all with one
operand, and every target has a matching kind-six status. The portable frame
stack now retains character context per nested sentence, so a kind-six action
uses its target and the caller regains its own context afterward.
The shipped controller in 04000003 combines these paths. Sentence 164 uses
opcode 32 to find an inactive slot in 14030001..14030005. Sentence 165 waits
40 updates and invokes kind six on controller 30000, whose sentence 175
creates effects 20007 and 20008 and positional sample 27 at object 10030000.
Sentence 168 then activates the chosen slot at that position facing direction
7. Keeping expired enemy actors in their original scenario vector is therefore
a fidelity requirement, not merely a container implementation choice.
The complete 0x00426200 call takes player number, scenario ID, entry value,
an auxiliary transition flag, an optional explicit position, and an entry-key
player override. With a nonnegative entry value, both the same-scenario fast
path and the full load path query:
entry key = local player number + entry value * 4
An entry value of -1 uses the supplied world coordinates instead. A changed
scenario saves/releases the old scenario state, changes music when needed,
rebuilds map and dynamic resources, loads that scenario's Scenario.Njp, and
then applies the entry. The portable fresh-world loader now accepts the first
three values explicitly and resolves scenario directories with the retail
decimal %08d spelling. Scenario 6, entry value 4 is covered against MCT key
16.
Portable ownership now follows the teardown boundary visible in this routine.
ScenarioWorld owns the current MCT and SCS data, GND and OBL state, map
patterns and overview, exploration mask, scenario objects, PEOPLE actors, and
ground items. Player data, equipped and carried items, belt contents, Special
Items, quests, missions, transport flags, and common resources stay in
WorldScene. Types 10 through 13 script values live with that persistent
owner as the retail global and local-player arrays do. A scenario is prepared
as a temporary complete owner and only then handed to the interpreter runtime,
whose callbacks remain at a stable address.
The live path now uses the same transaction. Same-scenario travel resolves the
entry and relocates without rebuilding resources. A changed scenario commits
only after all data and visuals have loaded; failure keeps the old map, script,
player, items, progress, and music usable. A successful commit clears stale
pointer, interaction, ground-item, and pending-audio state, preserves the
player-owned and progress owners, adopts the new SCS, relocates to its entry,
switches BGM, and starts the later standard loading presentation. Explicit
coordinate entry -1, multiplayer ownership, and exact teardown ordering
remain. The alternate VisualNN presentation is now reached independently
through its scenario opcode rather than this transition path.
The local-player record and resolved entry are installed before the loader
runs scenario status kind 7. This ordering is shared by the changed-map path
at 0x0042642b and the same-scenario path at 0x00427474; the latter does not
skip initialization merely because it kept the current resources. Opcode 50
at 0x004321cb exposes that current entry through the common operand writer.
Dusty Ruins scenario 00010000 branches on it to retain either its B1F or
B2F caption during both cross-map arrival and its authored same-map floor
transition.
Opcode 49 enters at 0x0043389b. In the local single-player branch it resolves
its message operand through the current SCS, copies the raw text into
0x0048d5f8, and clears 0x0048d5f4. Direct references in the executable only
write those globals; no reader or renderer has been identified. The portable
script owner therefore keeps the latest message ID and text as evidence-backed
state without drawing an area banner that the known retail path does not show.
Opcode 39 starts at 0x00431c43. It evaluates operands zero and one in order,
calls the statically linked Visual C++ rand routine at 0x00467c6e, divides
that result by the wrapped signed span upper - lower + 1, adds the remainder
to the lower bound, and sends the result plus raw operand two to the common
writer at 0x00434920. The bounds are inclusive and exactly one random value
is consumed. All 611 shipped calls have three operands across 55 scenarios;
the authored corpus includes 285 0..1 choices and 41 20..40 ranges. The
portable interpreter receives the next value through its
host hook, which connects to the shared world random owner rather than
creating a private script generator.
The immediately preceding arithmetic handlers complete the same writable
operand family. Opcode 13 at 0x00431b53 keeps the low 32 bits from imul.
Opcode 14 at 0x00431b9b stores the signed idiv quotient, and opcode 15 at
0x00431bef stores its remainder. Both division handlers test the divisor
first and return through the successful command epilogue without writing when
it is zero. The shipped catalog contains 67, 126, and 195 calls respectively;
all have two operands and all destinations are temporary flags.
The first authored cross-map path is now traced end to end. During the
scenario update at 0x004305d0, status kind three resolves its character to a
live MCT entity and calls the inclusive rectangle test at 0x00414350
against the local player. This does not consult the entity's three ordinary
state channels. Remote Town object zero is consequently an invisible trigger
at (90124,4275) with bounds [-106,66,964,604], even though its visible and
judgement channels are zero.
Its sentence 219 calls opcode 17 at 0x00432162 with scenario 1 and entry
zero. The handler stores both values in the pending transition record, enables
the request, and resets the explicit-position selector to -1. The portable
world defers the actual transaction until the interpreter has returned, then
publishes one scenario-change event to the runtime. This keeps the SCS owner
valid during command execution. Once that synchronous transaction has
finished, the runtime resets map-local UI and camera state, changes music, and
presents the new world immediately.
Scenario 1 is Near the Remote Town, map f00_02, music track 1. Entry key
zero places the local player at (90581,5288), direction 7, with the camera
still anchored at (320,240). The MCT creates 48 objects and 127 enemies.
Its object-zero kind-three trigger calls the same opcode with {0,0}, landing
back at Remote Town's (89898,2811), direction 3, and music track 0.
The Warehouse reaches opcode 41 at 0x004335ac. Argument zero toggles runtime
flag 0x0048ce48, the same one-page Special Item owner handled by
0x00447970; it is not a separate warehouse container. Nonzero opcode-41
arguments instead toggle 0x0048ce4c and clear the first flag. Scanning every
shipped Scenario.Scs found one such call: scenario 99000013, sentence 10,
for object 10000900. The matching MCT names it Giant Warehouse on Tower of
Ordeal 12F.
0x00447ca0 handles that second owner. Player offsets +0x520 through
+0x544 hold ten independent containers, +0x55c through +0x580 hold ten
page-unlock values, and +0x558 is the transient selected page. New
characters enable page zero only. 0x00404760 draws Status pattern 73, then
pattern 74 for a disabled tab, 75 through 84 for enabled tabs, or 85 through
94 for the selected tab at (24 + page*24,41). Enabled tab clicks and the
close cell at (272..295,40..55) use sample 58.
The four player containers at +0x548 through +0x554 are neither Warehouse
variant. Category-four item instances return their authored page through
0x00466480 and their fixed cell through 0x00466490; those values come from
the last three words of the 100-byte Item.Ibn record. Ground pickup routes
an item with a non-negative page into that owner and rejects a duplicate
instead of placing it in the backpack.
Script opcode 58 at 0x00433b33 queries those four containers first, followed
by backpack +0x514, main hand +0x4f4, body +0x4ec, off hand +0x4f8,
head +0x4e8, legs +0x4f0, and accessories +0x4fc..+0x508. Opcode 59 at
0x00433ced removes the first match in the same order and runs both player
refresh paths after an equipment removal. Neither path checks the belt,
alternate arms, +0x51c Warehouse, or Giant Warehouse. Opcode 75 at
0x0043443c constructs the named definition, skips an existing item in its
authored page, copies its fixed cell, and inserts it into +0x548 + page*4.
The save writer serializes these four containers directly after all ten Giant
Warehouse pages.
The portable decoder also reads all seven Remote Town PEOPLE records and their
bounded-wander tails. The first value after the bounds is copied to runtime
offset +0xd4 and gates native action 21's target-facing branch. The next is
inverted by the loader and controls autonomous wandering at +0xd0. The
final value, -65 for all seven records, remains unnamed. Enemy and item
records are now structurally traversed at their retail 0x13c and 0x10 tail
sizes, so the entry table and three footer values are read in forward file
order. Their tail meanings and runtime actors are still open.
All seven people records are instantiated from that table. Resource lookup at
0x00455ee0 resolves each ID to its zero-padded Character\PEOPLE directory;
the four animals share resources 01000000 and 01000001 exactly as named by
the MCT. The first record creates Ostare through the type-one path constructed
at 0x0045d020. 0x0045d620 draws idle chart zero using MCT direction 7 and
advances its frame counter once per game update. After the tail's 30-update
pause, 0x0045d150 starts movement-controller mode three. That mode chooses an
inclusive random point inside the spawn-relative rectangle through the shared
selector at 0x00454310, while
0x0045d9f0 draws chart one and moves at 10 world units per update until
arrival or the tail's 30-update limit. The MCT's custom mask disables parts 4
and 5, leaving the shadow and two visible frame-zero cells rather than drawing
every CAF layer.
0x00454310 handles seven destination modes before the collision and stepping
code at 0x00454930:
- mode 0 keeps a fixed point;
- modes 1 and 4 approach a scenario actor or player;
- modes 2 and 5 retreat from a scenario actor or player;
- mode 3 chooses independent inclusive X and Y coordinates in a rectangle;
- mode 6 projects toward the edge of a rectangle after a 30-degree rotation.
The target modes compare judgement bounds rather than actor origins. A zero
refresh interval becomes one; otherwise the old destination is retained until
the authored interval. Every refresh consumes the percentage draw, including
a zero-percent turn chance. A successful turn consumes a second draw and
rotates by rand() % 2001 - 1000 steps of 0.0010471973333333333 radians.
Retreat selects a point one unit beyond the stop distance. Mode 2 then returns
no movement despite updating that stored point, a native quirk preserved by
the portable selector. Mode 6 uses signed floor midpoint rounding and native
truncate-to-zero projections. Destination selection remains separate from
collision, path stepping, and animation.
All seven Remote Town records use this type-one PEOPLE class, but the two tail flags produce different behavior. Only Ostare enables autonomous wandering. Ostare, Syria, and the four animals allow native action 21 to face its evaluated target; Malse deliberately ignores that turn request. Native action 18 separately stops the current walk and enters interaction state, while action 19 releases it. Keeping actions 18 and 21 separate is important: scripts may suspend an actor without changing its direction.
Scenario opcode 20 reaches the PEOPLE virtual handler at 0x0045d480 through
the executable switch case at 0x00431fc9. The handler stores the evaluated
action, clears the old action counters, and interprets its next three values as
repeat mode, restart frame, and end frame. A repeat value of -1 selects the
one-shot path. Other values enable repetition; restart -1 returns to frame
zero and end -1 uses the selected direction's final CAF frame. PEOPLE update
0x0045d850 handles actions 4 through 19 and draws chart action - 1. It
presents frame zero on entry, increments once per game update, keeps the final
one-shot frame for that update, then writes action one so the next update is
idle. The repeated path rewinds to restart - 1 after presenting its end
frame, allowing the normal increment to present the restart frame next.
Remote Town sentence 146 supplies {12000002,4,-1,-1,-1,-1}, so Syria plays
all 111 frames of resource 9 chart three once. The following opcode handlers
are separate from that presentation. 0x0043244d copies the local player's
derived maximum life at runtime +0x1a0 to current life at +0x1a4, then
looks up character 16000000 + local player number and fully restores it only
when its current life is positive. This heals a living owned companion without
reviving a defeated one. 0x004324cf copies derived maximum mana at +0x1a8
to current mana at +0x1ac. The script then plays its authored positioned
sample through opcode 16.
Malse's Repair branch also exposed the two otherwise hidden weapon-set
pointers. Opcode 52 at 0x004310d7 prices active and alternate main hands as
one Arms group and active and alternate off hands as one Shield group; the
other equipped selectors each own one slot. Selector -1 walks the backpack
but accepts only category-zero weapons and category-one armor. Opcode 9 at
0x0043234a mutates those same groups. The alternate pointers are serialized
as entries ten and eleven of the equipment save stream, after the five visible
gear and four accessory pointers.
The per-item helper at 0x004667a0 uses the Table 34 weighted item value,
divides it by ten, scales it by missing durability over maximum durability,
and clamps a nonzero repair to at least one Gold. 0x00466800 restores the
instance durability. The backpack sum and mutation loops at 0x00467180 and
0x00467140 deliberately ignore accessories, medicine, Gold, and belt items.
0x0044a240 gives category-three item use a strict target order. It first
tries player HP and MP, scaling both flat and maximum-percent definition values
by 100 plus the corresponding equipped base bonus. If neither changes, it
resolves character 16000000 + local player slot and applies the companion
flat/percent fields only while that actor is alive and below maximum life.
Only then does it reach the condition/timed-effect branch. The first changed
owner consumes the concrete backpack or belt item and plays sample 16; full or
dead targets leave it untouched.
The final branch is element alignment rather than a temporary status timer.
White Medicine writes zero to runtime offsets +0x74/+0x78 when needed.
0x0044fd10 moves those axes toward one of eight fixed element anchors by the
definition's 4,000-unit step, snapping at the final step and using x87
cosine/sine truncation otherwise. Those runtime fields are record offsets
0x64/0x68; the Status marker, affinity builder, combat packets, and ordinary
save stream already share them.
Player CAF parts are not independent actors that should all be drawn.
0x00444ca0 rebuilds an enable table on every appearance refresh: entries 0
and 1 are the base body and shadow, while equipped items select additional
armor and weapon entries. The new-player MCT record starts at direction 3, and
Animation00.Sdw supplies the corresponding one-bit player shadow.
The same MCT stores music index 0 for Remote Town. 0x004275e0 maps scenario
music indices to System\Game\Music\BGM%02d.Voc, loads the selected container
into voice slot 500, and resets its start counter. 0x004275a0 starts sample
zero looping on the following gameplay update with the configured BGM volume.
The fixed gameplay interface is drawn by 0x004039f0, after
0x004030f0 has submitted the camera-driven world. Gameplay resource loading
at 0x004127d0 assigns System\Game\Pattern\Bar.njp to UPD slot 5.
Patterns 7 and 8 form the fixed y=400 through y=479 bar, pattern 10 marks run
mode, pattern 11 marks walk mode, and pattern 15 frames the experience bar.
Patterns 19 through 28 are the level digits.
When the owned companion exists, pattern 30 draws its bottom-left frame.
Pattern 29 is clipped from the right with width
current life * 109 / maximum life; an exact full value forces width 109,
while a positive value which truncates to zero draws no fill. Below 30
percent, the fill uses RGB strength 1500 for two of every four HUD updates and
1000 for the other two. Pattern 31 labels owner mode zero as ACTIVE, while
pattern 32 labels mode one as INACTIVE.
Life uses patterns 0 through 2 at x=81, y=426. Mana uses patterns 3 through 5
at x=106, y=453. Both live fills are 206 pixels wide and use
current * 206 / maximum, preserving one pixel for a positive value which
would otherwise truncate to zero. Packet clip rectangles reveal the live
portion without scaling the artwork. The other patterns provide delayed
damage/healing colors, particles, conditions, and later values which still
need their gameplay owners.
The executable registers IDC_ARROW once in the window class and contains no
later SetCursor call. Hover, selection, and click state are therefore drawn
as world feedback; the pointer itself remains the normal platform arrow.
The three text buttons on the lower right are handled by 0x00445bd0, not by
the inventory window itself. Their inclusive rectangles are Menu
(589..639, 402..413), Status (537..577, 420..437), and Item
(583..636, 429..448). Each click is consumed by the interface before world
movement is considered. The same ownership continues through mouse release
when an item is dropped out of the inventory; otherwise the held click becomes
a movement command on the following update.
0x00416bb0 draws the ordinary gameplay feedback as four one-pixel lines.
The configured range selects half-sizes 0, 12, 16, 24, or 48; the
shipped default is the 16-pixel choice. Empty ground uses white at strength
100, any target raises it to 300, and a ground item changes it to
(224, 224, 0). The square is not submitted below y=407 and disappears while
a modal gameplay message owns input.
0x004103c0 draws the Escape menu with patterns 59 and 58 from Status.njp;
the first is the half-transparent fill and the second is the opaque frame.
Its six original boolean rows begin at y=86 and advance by 16 pixels. The
portable game deliberately leaves the screen-mode row at y=86 blank and always
uses an LWL window, so the first visible setting remains at the retail y=102.
ON occupies x=376 through 425 and OFF occupies x=426 through 463.
The five click-range values occupy 24-pixel cells beginning at x=317 and y=182. The priority row below them displays classes from priority four down to zero. Clicking a class shifts every lower class up by one and moves the selected class to priority zero, which is why it moves to the right-hand end rather than simply swapping with its neighbor.
The effect and BGM tracks use Status patterns 120 and 68 at y=223 and y=243.
The first 200 slider positions cover DirectSound values -3000 through zero;
the far-left position is the separate mute value -10000. BGM changes update
the playing world voice immediately. Escape closes the panel and ordinary
world input remains suspended for every frame owned by it.
The Help row at y=286 switches to 0x0040e710. Status pattern 10 is the
640-by-415 authored frame; pattern 66 supplies the 230-by-128 ground preview at
(64, 70), surrounded by the original half-opacity one-pixel outline. The
player is drawn at (152, 148) with CAF chart 7. The mouse and keyboard tables
are not rewritten descriptions: their original text, colors, 6-by-12 font
cells, two-pixel keyboard line spacing, and coordinates are preserved.
Opening Help from the settings row also starts the common modal-tab animator
at 0x004088b0. Status patterns 27 through 30 slide the CLOSE tab from
y=413 to y=393 and then pulse in the retail eight-phase order. Escape or a
click above y=412 closes Help. The H shortcut opens the same reference page
directly. The conditional companion branch now draws the real owned PARTNER
actor at (212,158) with chart seven and the same animation counter as the
player preview.
The Map row at y=270 and the N shortcut switch to 0x0040d4d0. The screen
keeps the right half of the world visible and clips its own content to
(32, 40) through (318, 374). Pattern 0 from the current scenario's
Scenario.Njp is positioned in one-tenth real-screen coordinates, with the
local player held at (160, 210). A separate map-sized mask begins black.
Gameplay clears a 68-by-46 rectangle in that mask around every visited
position, which reveals the overview without exposing unexplored territory.
MapIcon.njp patterns 0 and 1 form the local PLAYER. marker. It is visible
for 15 updates and hidden for five. Arrow keys change the map origin by 16
pixels horizontally or 10 vertically; Enter clears both offsets. Status
pattern 71 supplies the authored half-screen frame and pattern 118 pulses over
60 updates. The scenario title is drawn at (72, 50) over its half-opacity
label backing.
Unlike Help and the Mission List, the Map is not a pausing modal. Its active
UI flags are 0x11: the left 320 pixels belong to the Map while the world
keeps updating in the right 320-pixel viewport. The camera anchor moves from
(320, 240) to (480, 240), so the local player stays centered in the visible
half, and mouse picking uses the shifted projection. Clicks in the Map panel
do not become world commands. Escape or N closes it and restores the normal
camera anchor; the secondary-click handler at 0x0044ad80 also closes it for
clicks above the HUD. Retail loads and saves the exploration masks as
Save/M%08d%02d.msk; portable
per-save mask persistence and the three other online-player markers remain
to be connected.
The Save and Return row occupies y=302 through 313, and Save and Exit occupies
y=318 through 329. They enter confirmation states two and three. Both dialogs
draw their prompt at y=170, with YES at (336, 202) and NO at (384, 202).
Opening a dialog and accepting it use sample 56; NO uses sample 55 and returns
to the ordinary settings state. After YES, states one and four draw
Now saving the data for one update. A successful state-one save then
transitions to the title, while state four ends the game.
When Save Image at Game End is enabled, the render loop first draws world
layers 20, 19, and 18 into a separate preview surface. 0x0044e830 writes the
centered player view beside the save as Save\%04d.Bmp. The bitmap is 391 by
114 pixels, uncompressed 24-bit BGR, and does not include the HUD or Escape
menu. The load screen places it at (224, 60) without scaling.
+0x000: HWND hwnd
+0x004: HMENU hMenu
+0x008: char className[256] "SHADOW_FLARE"
+0x108: char windowTitle[256] "ShadowFlare for Window98/Me/2000"
+0x508: WNDCLASS wndClass (40 bytes)
+0x534: int windowFlags
+0x584: RKC_DIB cursorBitmap
+0x59C: int gameState (0, 1, or 2)
+0x5A0: RKC_UPDIB* pUpdib
+0x5A4: RKC_RPGSCRN* pRpgScrn
+0x5A8: RKC_DBFCONTROL* pDbfCtl
+0x5AC: void* pUnknownObj
+0x5B4: RKC_NETWORK* pNetwork
+0x5B8: RKC_DSOUND* pDsound
+0x5BC: State0Handler object
+0x620: State1Handler object
+0x684: State2Handler object
+0xB144: void* pUnknownObj2
+0xB154: HIMC immContext
- RKC_DBFCONTROL::StopAll()
- Wait for drawing complete (spin on drawing flag)
- State handler cleanup (at +0x5BC, +0x620, +0x684)
- RKC_DSOUND::Release()
- ImmReleaseContext()
- Destroy DSOUND, NETWORK, DBFCONTROL, RPGSCRN, UPDIB
- Destroy object at +0xB144
| Address | Function Name | Description |
|---|---|---|
| 0x004022e0 | WinMain | Entry point, message loop |
| 0x00401550 | CheckSingleInstance | Mutex-based single instance check |
| 0x00401eb0 | LoadConfig | Load SFlare.Cfg |
| 0x004014a0 | ProcessCommandLine | Parse command line args |
| 0x00402520 | CreateGameWindow | Register class, create window |
| 0x004016b0 | InitGame | Initialize all subsystems |
| Address | Function Name | Description |
|---|---|---|
| 0x004023d0 | UpdateGameState | State machine dispatcher |
| 0x00420c40 | State0_Init | Initialize title/menu state |
| 0x00420df0 | State0_Update | Update title/menu |
| 0x00421a00 | State1_Init | Initialize loading state |
| 0x00421bf0 | State1_Update | Update loading (cleanup) |
| 0x0041d3f0 | State2_Init | Initialize gameplay state |
| 0x0041d880 | State2_Update | Dispatch gameplay logic |
| 0x0041d970 | State2_Main | Main gameplay (2936 bytes!) |
| Address | Function Name | Description |
|---|---|---|
| 0x00402b30 | WndProc | Window message handler |
| 0x00402840 | KeyDownHandler | VK_RETURN, VK_SNAPSHOT |
| 0x004026d0 | KeyUpHandler | (stub) |
| 0x004028a0 | LeftClickHandler | Mouse left button |
| 0x00402900 | RightClickHandler | Mouse right/middle |
| Address | Function Name | Description |
|---|---|---|
| 0x00426200 | LoadScenario | Main scenario loader (4846 bytes) |
| 0x00426160 | LoadScenarioData | Helper for scenario loading |
| 0x004021b0 | FindSaveSlot | Find next free save slot |
| Address | Size (bytes) | Description |
|---|---|---|
| 0x00429ec0 | 20119 | CommandDispatcher - Command/Event dispatcher (huge switch) |
| 0x00430f80 | 13677 | ScriptInterpreter - opcode values 0x00 through 0x4b |
| 0x00462f80 | 9247 | LoadItemData - magic SFItemDataV0000 |
| 0x004103c0 | 7773 | OptionsMenu - Settings menu |
| 0x0044cac0 | 7527 | LoadGame - load save file, XOR decrypt |
| 0x00414990 | 7221 | ObjectNpcDisplay - via RKC_RPGSCRN_OBJECTDISP |
| 0x00427b50 | 6780 | LoadScenarioMct - magic MCED DATA v0000 |
| 0x0040aed0 | 6758 | ItemStatsDisplay - format item stat bonuses |
| 0x0044b580 | 5429 | SaveGame - write save file, XOR encrypt |
| 0x00409a60 | 5212 | StatusScreenDisplay |
| 0x00405750 | 4936 | CharacterStatusDisplay - element bonuses |
| 0x00426200 | 4846 | LoadScenario - main scenario loader |
| 0x00423ca0 | 4306 | ScenarioLoader_Phase2 - called from State1 |
| 0x00446320 | 3898 | (UI/input related - needs analysis) |
| 0x0041afc0 | 3680 | NetworkServerHandler - RKC_NETWORK packets |
| 0x00441c00 | 3462 | (needs analysis) |
| 0x004039f0 | 3437 | Gameplay HUD, bars, level, and status packets |
| 0x0041d970 | 2936 | State2Handler_Main - main gameplay update |
| 0x00421e10 | 2710 | CharacterCreation - class/gender select |
| 0x004239b0 | ~500 | LoadSaveSlotInfo - read save headers |
| 0x004021b0 | 129 | FindSaveSlot - find free save slot |
| Address | Function Name | Description |
|---|---|---|
| 0x00401b90 | Shutdown | Release all subsystems |
The CAF marker now enters the direct player-impact path from 0x00439140 and
0x00435e60. 0x00413e00 compares derived player hit rate at +0x1bc with
the enemy initializer's pre-AI word 10, clamps the difference to 20..98,
then consumes exactly one rand() % 100 roll. A miss stops there. A hit calls
0x00417e70, then fills the family-zero packet with the local player number,
derived physical attack and defense, eight affinities, the 17 persistent
words copied from runtime +0x1dc, level, reaction modifiers from main-hand
instance parameters 16, 14, and 15, and the weapon identifier.
The first hit-effect draw selects 21000..21003. 0x0044f990 then always
consumes the reflection roll, summing active equipment instance parameter 20
as its chance and parameter 21 as its returned percentage. Main-hand subtypes
8 and 9 consume one later draw and replace the effect with 21004..21006.
The target receiver runs next. Sample 6 is queued after it returns, and only
then does an occupied main hand consume its 30-percent durability roll.
Weapons with zero maximum durability still consume that roll but do not lose
condition.
The live owner commits the receiver's life, per-player attribution, reaction,
event, and defeat fields back to EnemyActor. It deliberately reuses
resolveEnemyDamage, including its tables and conditional random draws.
Broken category-zero and category-one equipment remains present and keeps its
weight, while 0x0044ea60 proves that its base derived contributions and
element strengths no longer participate. Hit/death CAF presentation,
reaction displacement, and ordinary configured effect-list ownership now run
at the live actor boundary described above. Specialized effect dispatch
families remain separate follow-up work.
Enemy effect presentations enqueue a controller request; they do not create a
projectile directly. 0x0042fdc0 stores the request in a 0x3b0-byte node,
including both actor identities, the direction, optional origin and source
judgement, the complete combat packet, the authored delay, and the remaining
constructor values. The list updater at 0x0042fd60 keeps that node until the
handler selected by 0x00429ec0 returns zero.
The low raw effect numbers do not enter the specialized controller families.
Dispatcher 0x00429ec0 sends values 0..7 to generic initializer
0x0042a300 with OPTION resources 0,1,2,3,0,0,4,0. This includes all
shipped direct-special values 0,1,4,5,6,7. The normal direct forms use
30-unit bounds, whereas type six uses [-160,-160,159,159] and contact
visual 21023. They project from the live owner by constructor field 21, carry
the packet, collide with scenery and the first target, and use contact sample
20. This makes the raw family a generic runtime actor, not a truncated form
of effect 10001.
All twelve nonnegative effect types found in the shipped enemy records have a
specialized branch. The dispatch covers types 1, 2, 3, 4, 5, 10, 11, 12, 13,
14, 16, and 21. This is separate from the much simpler 21000-series receiver
visuals already owned by CombatEffectActor.
Types 1 and 2 establish the controller/actor boundary. Their first controller
update creates source resources 10000012 and 11000027 respectively at the
source actor's then-current position. At constructor delay 12, the handler
resolves that position again and creates a second actor 180 world units along
the stored angle. The type-1 child uses resource 10000010 and positional
sample 19; type 2 uses resource 10000040 and sample 94. Both use
[-50,-50,50,50] judgement, retain the packet, use CAF chart zero, and expire
when their static environment sweep collides. The controller expires after
creating the child, independently of both visual actors.
Type 3 is the first staged area controller. 0x0042b540 reads row zero and
column subtype - 1 from Table 205; shipped Plasma Bat subtype 20 resolves to
five waves. Beginning at constructor delay 12, one wave is attempted every
four updates. Its position is projected from the stored impact origin, not
the enemy's later position, at radius wave * 200 + 250 along the stored
angle. A [-100,-100,100,100] judgement check includes map collision and
live type-zero scenario objects. The first failed placement sets a persistent
stop flag, suppressing that wave and every later wave without consuming a
random value.
Each clear wave consumes one rand() % 4 chart choice and creates resources
10000030, 10000031, and 10000032 at the same position. The first actor
uses that random chart, applies the copied packet to every overlapping target
only on its first update, and does not expire after the first target. The
other two use chart zero and are visual layers without an active collision
window. All three use direction eight, their selected chart's full lifetime,
and the same 100-unit judgement. Positional sample 21 is emitted once per
clear wave. The controller remains alive until delay + Table205Value * 4,
independently of the actors it has already created.
0x00429dd0 creates those children in the category-50000000 actor family.
0x0045e1a0 installs a 126-word descriptor, and 0x0045e1e0 owns their
common update. That update supports homing, free, and owner-attached movement;
static environment sweeps; an inclusive target-collision window; independent
player/companion/enemy target bits; exact target IDs; living and
current-scenario checks; optional previous-hit storage; physical or magical
evasion checks; receiver callbacks; and configured positional audio. It then
draws either the descriptor's static pattern or CAF chart and applies the
authored lifetime. The nearby action dispatcher at 0x0045f960 belongs to the
separate category-40000000 actor class; it is not part of this generic effect
actor.
For types 1 and 2, descriptor offset +0x1c gives the source actor one complete
chart-zero/direction-eight lifetime and leaves the traveling child unlimited.
+0x40 enables the static environment query, +0x44 makes only the child
expire on contact, +0x48 carries its distance per update, +0x54 carries
display height, and +0x58 selects chart zero. Target collision starts at
update zero for the child through +0x8c; the source uses -1 and never opens
that window. +0x94 enables drawing. These fields explain the two actors
without inventing a visual “action one.”
The portable implementation must preserve both lists. A controller is simulation state that can create several actors over time; a runtime actor is renderable state that can move, collide, and dispatch an impact. Treating an enemy request as one short CAF would make the picture plausible while moving the actual effect, sound, and damage to the wrong update.
EnemyEffectController now ports the complete controller half of types 1, 2,
3, 4, 5, 10, 11, 12, 13, and 14 without pretending that every specialized
family is finished. For types 1 and 2 it emits the source actor on update
zero, re-resolves the source at the exact authored delay, projects the second
actor with the retail Y-axis convention, copies the combat packet, and places
sample 19 or 94 at that second position. A zero delay creates both actors in
one update, a negative delay remains active, missing owners resolve from zero,
and owner kind zero leaves the child at its explicit origin without
projection. Omitted origin or judgement pointers do not leak stale values.
The same owner now keeps type 3's table-driven wave counter, fixed origin,
persistent placement stop, random damaging chart, two visual companions, and
sample 21. Passive timing and obstruction tests are paired with the shipped
Plasma Bat in scenario 00010001; the live case renders all three resources,
passes the first layer through the ordinary player receiver, and preserves
the player's item owners.
Type 4 (0x0042a860) follows the source rather than storing a fixed origin.
On update three it resolves the source position and creates resource
10000002 with the source judgement, chart zero, direction eight, and
additional display status 0x80. On the authored delay, which is ten for the
shipped request, it resolves the source again. Resource 10000000 is then
created at each opposite judgement corner with display height 200: the first
actor draws chart one, while the second draws chart zero but deliberately
uses chart one's frame count as its lifetime.
That delayed update also plays positional samples 29 and 23 and creates an
invisible one-update actor. Its judgement expands every source edge by 150,
its collision window is update zero only, and it passes the copied packet to
every eligible target while using the common bank-zero contact sample 20.
The controller expires immediately after those three actors have been handed
to the runtime list. When the local player is less than 3001 world units from
the resolved source, the same update requests mode zero camera shake:
duration eight and magnitude six. 0x00412690 stores the request and
0x00412720 alternates a zero/six vertical world offset until the eighth
update, after which the camera is steady again.
Type 5 (0x0042cd70) uses the animation data itself as its clock. On update
three it resolves the source, stores that position in the controller, and
creates resource 10000051 there with a point judgement at the source
rectangle's lower-right edge plus one. The handler asks that resource's chart
zero, direction eight for its maximum frame count on every update. It does
not use constructor delay ten for the later sequence.
At that frame count it creates resource 10000050 with the same point
judgement and display height 200. Four updates later it creates the invisible
one-update packet actor at the captured position, expanding the original
source judgement by 150. That actor uses contact sample 20 and the same nearby
eight-update, magnitude-six camera shake as type 4. At frame-count plus 15,
resource 10000052 appears at height 200 with additional display status
0x80.
Sample 22 plays at the captured position at frame-count offsets 6, 9, 12, 15,
18, and 21. The controller increments after that last pulse and expires when
its counter reaches frame-count plus 22. Resource 10000051, resource
10000050, resource 10000052, and the invisible packet all remain separate
runtime actors with their own lifetimes.
Type 10 (0x0042e7e0) reads its wave count from Table 206 row zero at
subtype - 1. Beginning at the authored delay, it attempts one wave every
eight updates. Positions are projected along the stored direction from the
stored impact origin at wave * 300 + 250 world units. Every placement uses
[-150,-150,150,150]; the first failed map or live-object query latches a
stop flag which suppresses that wave and all later waves without shortening
the controller's timeline.
Each clear wave creates resource 10000060 with chart zero, direction eight,
and that chart's complete animation lifetime. Its update-zero collision
window processes every overlapping target selected by the request's target
mask, uses target identifier zero, and passes along the copied combat packet.
Sample 22 plays at the wave position. A player at a strict distance below
3001 receives the same eight-update, magnitude-six camera shake as types 4
and 5. The controller expires at delay + Table206Value * 8. Shipped enemy
26 in Devil's Castle 2F uses type 10 subtype 20; its live regression also
proves that the fifth attempted wave is suppressed by the scenario's
placement data while the first four remain visible and active.
Type 11 (0x0042d6e0) creates the same resource-10000012 source visual as
type 1 on update zero. At the authored delay it reads Table 204 row zero at
subtype - 1, divides retail's 6.283184 full-circle constant by that count,
and emits one resource-10000010 actor for each angle
stored_angle - index * step. Nonzero owners are resolved again for every
child and place it 180 world units along that angle. Owner kind zero instead
uses the stored explicit origin for every child without the 180-unit
projection.
Each child uses homing mode one and a turn value of 20, carries the requested
target kind and identity, moves at constructor value six, and draws chart
zero while updating its direction from the current travel angle. Its bounds
are [-80,-80,79,79], its explicit lifetime is 90 updates, and it expires on
either static collision or its first target. The copied packet and bank-zero
sample 20 use a collision window beginning at update zero with no authored
end. After the loop, sample 19 plays once at the final child's initial
position and the controller expires. Table 204's shipped rows produce two
through eight children.
The common actor homing branch starts from the actor's current position each update. It resolves target kind one through the live player slots and other kinds through the scenario actor list, rejects missing or dead targets, and permanently disables steering after the first failed lookup. While the target has not been passed on both axes, the desired angle is compared with the current angle through retail's truncated full-circle constants. A difference inside 20 degrees snaps to the target; otherwise the actor turns 20 degrees along the shorter side. The CAF direction follows the resulting angle before drawing.
Type 12 (0x0042db10) starts with resource 11000027 at the resolved source
and reads Table 204 row zero at subtype - 1. Table 204 column 29 supplies
the spread divisor, which is eight in the shipped data. On update zero the
controller creates that many resource-10000080 warning actors. The total
span is count * 2.5132736 / divisor; odd counts are centered on the stored
direction, while retail applies an additional span / count / 2 offset to
even counts. Nonzero owners place each warning 150 world units along its
angle, while owner kind zero leaves every warning at the explicit origin.
Warnings are stationary, use [-50,-50,50,50], chart zero, their directional
CAF row, constructor display height, and the subtype itself as their explicit
lifetime.
When the counter reaches the authored delay, retail reads the same Table 204
values and resolves the source again. It emits the matching fan as
resource-10000081, now at radius 180 for nonzero owners. These children move
straight at constructor value six; they do not use the type-11 homing mode.
They have the same 50-unit bounds, a 90-update lifetime, static-contact and
first-target expiry, optional previous-target memory, and bank-zero sample 20.
In each copied packet, the controller replaces words 34/35 with effect
21021 and the child's retail direction, then words 74/75 with effect
21022 and that direction. Sample 94 plays once at the last projectile's
spawn position and the controller expires. A zero delay therefore emits the
source plus both complete fans on update zero. Dread Wisp 24 in North of The Remains of The Dead (03010003) provides the shipped subtype-ten live case.
Type 13 (0x0042e240) reads its radial count from Table 204 row zero at
subtype - 1. Beginning at the authored delay, it attempts four shells at
four-update intervals. Shell radii are wave * 200 + 350; each point uses
the angle stored_angle + index * (6.283184 / count) from the stored explicit
impact origin and placement bounds [-100,-100,100,100].
Retail stores one obstruction flag for each of the eight possible Table 204
points. A failed map or live-object placement permanently suppresses only that
ray in this and later shells. Every clear point consumes one rand() % 4 and
creates resources 10000030, 10000031, and 10000032 together, with the
same descriptors as type 3: only the random-chart first layer has an
update-zero all-target collision window, while the two chart-zero layers are
visual. Sample 21 plays once after every shell at the final radial position,
even if that point's obstruction flag is set. The controller increments its
shell radius after each attempt and expires exactly at delay + 16.
Lightning Gargoyle 11 in Ancient Ruins B1F (03140000) supplies the shipped
subtype-20 live case.
Type 14 (0x0042e5c0) creates no source or warning actor. When its counter
equals the authored delay, it resolves a nonzero owner and projects the launch
point 180 units along the stored angle. Owner kind zero instead uses the
explicit origin directly. Resource 10000070 moves at constructor value six,
uses constructor value seven as display height, and derives its chart-zero CAF
direction from the travel angle.
The projectile has [-50,-50,50,50] bounds, no explicit lifetime, static and
first-target expiry, the requested target mask and identifier, optional
previous-target memory, the copied packet, and bank-zero contact sample 20.
Sample 22 plays at the launch point and the controller returns zero on that
same update. Stone Wisp 2 in Ancient Ruins B1F (03140000) supplies the
shipped subtype-one live case.
Type 16 (0x0042ea50) launches resource 10000110 when its counter equals
the authored delay. Nonzero owners are resolved and projected 180 units along
the stored angle; owner kind zero uses the explicit origin directly. The
actor moves at constructor value six, uses constructor value seven as display
height, has [-80,-80,79,79] bounds, chart-zero directional drawing, static
and first-target expiry, optional previous-target memory, the copied packet,
and contact sample 20. Sample 19 plays at launch.
The controller keeps the returned category-50000000 actor identity plus its
latest x/y position. On every later update it looks that identity up and
refreshes the stored position. Once the actor no longer exists, resource
10000111 is created at the last recorded position with
[-240,-240,239,239] bounds, direction eight, and a complete chart-zero
lifetime. Its copied packet processes every eligible overlapping target only
on update five, with contact sample 20. Sample 22 plays when the explosion is
created. A local player at an inclusive distance of 3000 or less receives the
same eight-update, magnitude-six camera shake as the other area families, and
the controller then expires. Goliate's second effect variant in Goliate's
Mansion B3F (04050002) supplies the shipped subtype-ten live case.
Type 21 (0x0042eeb0) reads its ray count from Table 207 row zero at
subtype - 1; the shipped ranges produce one, three, or five rays. Update
zero creates source resource 11000210 at the resolved owner with the
source rectangle's lower-right point plus one as its judgement. When the
counter reaches the authored delay, resource 10000100 is launched once per
ray at stored angle - index * (6.283184 / count). Nonzero owners resolve
and project each launch point 180 units; owner kind zero uses the explicit
origin directly.
Each ray uses [-80,-80,79,79] bounds, constructor value six for travel
speed, constructor value seven for display height, the homing movement mode
with a twenty-degree turn, and animation speed travel speed * 1000 / 30.
Its lifetime is the complete chart-zero/direction-eight animation. Static
contact or the first eligible target expires it. The copied packet uses
contact sample 20, and sample 19 plays once at the final ray's launch point.
The controller stores every returned actor identity and refreshes each saved
position independently until that actor disappears.
A missing ray enters a four-stage sequence at its last position. One stage is
created every four controller updates with [-240,-240,239,239] bounds and a
complete chart-zero/direction-eight lifetime:
- stage one uses resource
12000000and rewrites packet words 32/34 to0/20000; - stage two uses resource
11000033, rewrites them to1/21013, and applies the packet to every overlapping target on update zero; - stage three uses resource
10000030and2/20005, with visual companions10000031and10000032; - stage four uses resource
10000060and3/21000, with the same update-zero all-target packet window as stage two.
Every primary stage plays sample 19. Stage four also plays sample 22 and, for
a local player no farther than 3000 units away, requests an eight-update,
magnitude-six camera shake. The controller expires only after every ray has
entered stage five. Arc Angel's third attack in scenario 99000036 is the
shipped subtype-30 five-ray live case.
RuntimeEffectActor now ports the next shared parts: chart-zero source
lifetime, free movement from the immutable spawn point, the zero-distance
first update, retail integer projection, static OBL/GND sweeping, the
special-environment exclusion bit, contact expiry, inclusive target-window
timing, CAF frame scaling, and interpolated render snapshots.
The target query is deliberately separate from the movement sweep. At the
start of an active collision-window update it intersects the actor's current
judgement rectangle with dynamic objects in display-query order. Mask bits
1, 2, 4, 8, and 16 select players, locally owned companions,
enemies, NPCs, and scenario objects. Exact identity, life, owner, active,
display, runtime-state, and self filters run before either every eligible
target is processed or the first nearest position wins a strict distance tie.
Players, companions, and enemies use packet word 1 to select physical or
magical evasion and packet word 36 as hit rating. The 20..98 chance consumes
one retail random value per processed target even without a receiver packet.
The optional repeat list records an identity before that roll, including a
miss, and stops growing at 500 entries. Typed packet/miss requests preserve
the receiver boundary. Target and object sound pairs share the original
once-per-update guard, including the distinct NPC multi-target spatial mode,
while static contact uses the actor's pre-movement position.
The live world owner now constructs target snapshots, applies typed receiver requests, queues positional sounds, and renders the runtime actor resources. Its type-4 support also keeps invisible packet actors outside the renderer, honors a lifetime chart separate from the displayed chart, carries additional display status into the retail sort classes, and applies the short camera shake without changing the portable presentation backend.
The lethal callback at 0x004134a0 uses each enemy's MCT pre-AI value 13 as
its experience reward. Per-player attributed damage selects Table 14 row
damage * 10 / maximum life, clamped to 0 through 10, and that percentage is
applied to the reward. A direct local-player kill increments the persistent
total at player-record offset 0xb0; ordinary main-hand subtypes select the
eight counters beginning at 0xb4, while effects and other sources use
0xd4. The experience total lives at 0xd8, so the existing 0x160-byte
player record already carries all of these values through save/load.
Table 13 supplies the threshold for level - 1. Reaching it discards overflow,
records the current job in the 100-byte history at 0xdc, applies the matching
gender-specific 900-series growth column to the 13 base parameters, refreshes
life and mana, and resets experience. New-character initialization fills that
history with novice job 0x10, matching 0x00440f70. The full later
skill-unlock branch still needs its own class-system slice.
The script-facing job selection is now mapped. Opcode 71 begins at
0x00434186 and reads runtime player offset +0x30, which is saved-record
offset 0x1c. It writes menu value zero for Mercenary job 16, one for
Warrior 6, two for Hunter 5, and three for Wizard/Witch 9. Opcode 70 at
0x004341da evaluates one operand and accepts only values one through three,
writing raw jobs 6, 5, and 9 respectively. It does not rebuild stats,
alter level history, or change any other record field. Scenario 03900003
sentences 366 and 381 contain the shipped change/query pair.
Opcode 72 enters at 0x00434259 and takes no operands. It clears the other
gameplay panels, sets DAT_0048ce60, and snapshots the equipped main hand,
off hand, and body color indices. The corresponding item getters read runtime
offset +0x348 for category zero and +0x320 for category one. Both serialize
as item-state word 49. The centered panel is Status.njp patterns 102 through
109 at (160,144) and offers the 16 triples beginning at 0x00475bf0.
Color clicks write the selected item immediately so the hero preview updates
live. OK clears the panel after broadcasting the changed player state. Cancel,
right click, and Escape call 0x00410360, restoring all three opening values
before closing. Only the primary CAF part uses the selected table triple; the
secondary weapon part continues to use its definition strengths. The three
shipped opcode-72 call sites are scenario 01000000 sentence 212,
02100000 sentence 357, and 03900002 sentence 102.
Script opcode 68 enters at 0x004342de. It ignores a level-100 player,
otherwise reads that same Table 13 threshold and adds
threshold * evaluated_argument / 100 to experience. The multiply and divide
use a signed 64-bit intermediate. Reaching the threshold calls
0x00412fb0, clears experience, rebuilds the player, and refreshes the display
exactly like an enemy-earned level. The portable interpreter keeps the
percentage in the script boundary and sends the award to the shared player
experience owner, so scripted and combat level-up notices and audio cannot
diverge.
Enemy death action 11 calls 0x0045a000 before 0x0045a030. The first uses
MCT pre-AI value 14 as a Table 30 row. Every attempt consumes the chance draw;
a success consumes a separate ten-slot profile draw, applies Table 31's
category, level, variant, and episode filters, then uses 0x00401520(9) for
the weighted item offset. Constructing the definition rolls 39 instance and
eight element triples. Successful objects are placed around the enemy at
radius 200.
West Ruins supplies the first quest-critical fixture. Black Hammer's MCT
record selects Table 30 row 6. Its attempt value zero uses the active-player
count, its chance is 100, and all ten slots select Table 31 row 400 with
variant digits 111. That profile fixes category 4, definition 99000000, so
one stolen gem is created at radius 200 in single-player without entering the
weighted equipment path. Remote Town sentence 37 then uses opcodes 58 and 59
to find and remove that automatic-owner item around Malse's completion
message.
The Gold callback reads MCT post-AI values 26 through 28. Equipped instance parameter 26 changes the 100-percent multiplier, the chance comparison is strict, and the inclusive amount draw occurs only after Gold construction. Its origin is the enemy position plus the judgement left/top corner and 100 world units on y. Both paths create complete owned item instances through the ordinary ground-item owner. The next PRNG draw then chooses death effect 21010's direction, and the first bounce produces the existing category landing sound.
0x00450fb0 creates the level-growth text as an auto-sized text owner with
four pixels of padding. Flag 0x80 centers its rectangle in the 640 by 416
play area; its background is black at opacity 250 and its text color is
224,224,224. 0x00451a40 draws a separate one-pixel white frame at opacity
500 and counts down the 900-update lifetime.
The notice stays centered through counter 840. During the next ten updates its
stored initial position is interpolated to x 640 - width, y 1, after which
it remains in the upper-right until expiry. 0x00451cb0 ignores notice clicks
for the first 30 updates. A later click inside the notice releases it and
consumes the input before ordinary world interaction.
Script opcode 73 enters at 0x004343b0. It closes the ordinary gameplay
panels, initializes the executable's Blackjack state, and gives that modal
exclusive input. Opcode 74 at 0x00434412 later writes the retained outcome:
zero for a draw, one for a player win, or two for a dealer win. Closing the
result starts scenario status kind 8. The shipped users are scenarios
99000018 and 99000023; their SCS sentences decide what happens after each
outcome.
The modal update at 0x00403560 deals one card every 15 gameplay updates.
The opening order is dealer, player, hidden dealer, player at counters 15,
30, 45, and 60. Ordinary draws use rand() % 52; only the hidden opening
card uses % 53, allowing the extra joker. A card already held by either hand
is rejected and rerolled. Sample 44 accompanies each completed deal.
Ranks use the standard 1,2,3,4,5,6,7,8,9,10,10,10,10 values. Aces and the
joker are flexible: each becomes 11 when the remaining hand can stay at or
below 21, otherwise one. A total above 21 is represented as -1. The dealer
draws through 16 and stands at 17. Equal totals draw except at 21, where a
two-card natural beats the same total made with more cards.
The primary-button rectangles are strictly inside (229,337)-(328,370) for
Hit and (335,337)-(434,370) for Stand, and an action is accepted only on a
15-update boundary. A bust reveals the dealer hand immediately. The result
stays up for 200 updates; sample 64 marks a player win, sample 65 a dealer
win, and a draw is silent. A click after the first result update shortens the
remaining display to one update.
0x0040da90 draws the complete modal from Card.Njp: board pattern 65 at
(32,40), seven stacked card backs, Status pattern 119, the title pair 55/56,
the fixed player and companion previews, and patterns 59/60 for Hit and
Stand. Dealer cards begin at (182,70) and player cards at (158,210), with
an 80-pixel spread below five cards and a compressed 240-pixel span
afterward. Pattern 54 is the dim card/button shadow. Patterns 57 and 58 mark
a natural or bust; patterns 61, 62, and 66 are win, lose, and draw.
The portable state, renderer, audio requests, and script hook preserve those separate owners. Deterministic tests cover score rules, unique timed dealing, input phases, dealer completion, dismissal, Card.Njp placement, and the real scenario opcode pair.
0x00407a60 draws the Magic half-panel only while its active flag is set.
Status pattern 6 supplies the complete 320-by-416 frame. The page value at
application offset +0xa93c selects six consecutive spells, with icon rows
starting at y=59 and advancing by 48. Status pattern 32 draws each empty
32-by-32 well at x=24, y=row - 3. Availability value 3 draws
MagicIcon.njp pattern spell + 2 at x=27; value 1 draws the same authored
icon dimly. Only odd availability states receive the name and table-backed
level, experience, MP, and effect text.
The displayed spell level comes from 0x00451e60: Increased Power adds two,
the stored value is clamped to 1 through 20, the derived magic-level modifier
is added, and the result is clamped to 1 through 30. Selector 2 of
0x00417410 reads MP from Table 16, after which equipped instance parameter
19 lowers the result to a minimum of one. Selector 0 reads the displayed
effect from Table 17. Table 27 supplies the next experience threshold; stored
level 20 prints Max. Hovering x=60..227 over the name line concatenates
Table 600 + spell into the pointer-centered help owner.
Status patterns 69 and 70 are the previous/next arrows. Their pointer
rectangles are x=16..48 and x=270..304 at y=335..351. The panel's eight
large-icon slots begin at x=32, y=359, while input uses the encompassing
32-pixel cells beginning at x=29, y=356. 0x00447790 picks learned page and
bar entries with sample 57. On release, 0x00404e40 removes every existing
copy of that spell, assigns the destination slot, and plays sample 58.
0x00404ee0 draws the always-available gameplay bar. Its start is x=224 in
the full view, x=344 beside a left panel, and x=124 beside a right panel.
A four-pixel gap precedes slots zero and four. Empty and ordinary entries use
16-by-16 MagicBarIcon patterns at y=392; the selected spell expands to its
26/27-pixel MagicIcon at y=382, which also shifts later entries. The final
icon selects normal attack targeting. 0x00447570 applies the same dynamic
rectangles, selects a learned spell with sample 58, and makes spell selection
and normal targeting mutually exclusive.
Gameplay bootstrap 0x0041d970 writes one to player field +0x1288 after
both the new-player and saved-player initialization paths. The active spell at
+0x127c is consequently left at -1: retail enters gameplay with the final
normal-attack icon selected instead of restoring an active spell selection.
This is runtime command state, separate from the saved availability, level,
experience, and bar arrays. WorldScene::loadInitialScenario now applies the
same bootstrap rule after either portable initialization path.
The portable GameplayMagic state mirrors those page, hitbox, hover, and drag
rules but does not own spell data. It emits assignment and selection intent
to the runtime boundary, which mutates PlayerMagic; this keeps persistent
state and casting outside the UI layer. The Status half of the tab remains a
separate GameplayStatus interaction state but shares this left-panel owner.
0x00405750 draws Status.njp pattern 5. It overlays job and name at x=22 and
x=92, level and experience on the right, then current/maximum HP, weight,
physical attack/defense, hit/evasion, walking/attack speed, current/maximum MP,
and the four magical values at the executable's eight-pixel right-aligned
coordinates. Derived values are neutral grey when unchanged, red when below
base, and gold when above it. 0x0044fca0 combines the saved elemental point
with equipped and carried-item strengths, clamps all eight affinities to
-10..10, and selects Status patterns 36..56. Pattern 57 places the marker from
the saved x/y axes. S, the HUD Status label, and the top tabs switch the
live window without pausing the right-hand world view.
The normal-target icon sets application field +0x1288 instead of selecting
a spell. In the secondary-click branch of 0x00441c00, the local player stores
the clicked world angle and position and asks 0x00450630 for the equipped
ordinary attack. Weapon actions 8, 9, and 10 become actions 11, 12, and 13.
There is no retained enemy target: each impact marker asks 0x004417f0 for all
currently valid enemies inside the ordinary melee judgement range.
Actions 11 and 12 begin the one-handed and two-handed three-stage chains.
0x004364e0 runs CAF chart 5 or 15, then hands off to action 14 or 15.
0x00436c20 runs chart 7 or 17 and hands off to action 17 or 18.
0x004372b0 runs chart 8 or 18 before returning to idle. All three stages use
the Table-4 attack-speed tier with factors 0.8 through 1.7. Before its impact,
each stage attempts short collision-aware forward movement in ten-unit
increments up to the retail 61-unit cutoff. Counter six plays the equipped
weapon sound.
A new Mercenary starts with raw attack speed 100. Table 4 maps that to tier five, whose three-stage combo factor is 1.3. Ostare's Short Sword contributes zero to the attack-speed field, so the fairly quick opening combo already is the normal character baseline. The Dagger is a separate, modest improvement with an authored contribution of 50. The portable timing regression round-trips a Short-Sword-equipped hero through a real save and then through the same-entry revival transition; all three paths complete the combo in the same number of simulation updates.
The three impact markers play consecutive Voice00 samples. Raw gender one
uses 96, 97, and 98; raw gender zero uses 99, 100, and 101. This is independent
of whether an enemy is close enough to receive the hit. Action 13 is the
separate heavy-weapon spin path and remains outside the portable three-stage
controller for now.
Spell zero enters action 22 through the same secondary-click command, using
CAF charts 11 and 12 and Table 20 row zero. 0x0043a260 compares the dominant
axis of the stored click direction and tries one of four cardinal portal
positions exactly 500 world units from the player. If that corridor is
blocked, it tries the remaining directions in order zero through three. The
four exact corridor rectangles are [-80,-580,79,79],
[-80,-80,79,579], [-580,-80,79,79], and
[-80,-80,579,79].
0x00420020 rejects a portal in the linked town scenario, stores the field
position, and pairs it with scenario footer value zero and entry key
400 + local player. The resulting entry value is 100. The endpoint uses
resource 10000020 and an [-80,-80,79,79] contact rectangle. Four static
Pattern.njp layers begin seven updates apart; each falls from height 300 to
zero in steps of 50 while its strength rises from zero to 1000 in steps of
200. The central chart-zero direction-eight animation begins after the fourth
layer lands, waits a random 1..90 updates initially, then waits 30..89 updates
between loops. Sample 79 starts the presentation and sample 51 starts a center
loop.
0x00420970 requires leaving an endpoint before it can trigger again. Entering
the field endpoint travels to the player's Remote Town entry. Entering the
town endpoint restores the exact saved field position and consumes the pair.
The portable single-player path follows those owners; multiplayer replication
and the endpoint owner-name hover remain pending.
0x00449a40 handles a targeted player spell command. Fire Ball must have
availability value three and a valid pointed enemy. 0x00451e60 supplies its
effective level; Table 16 supplies the MP cost, equipped instance parameter 19
reduces it, and the result is clamped to at least one. A selected enemy
consumes the pointer command even when MP is insufficient. On success the
function faces the target, selects action spell + 22, and deducts MP before
the action runs.
0x00439730 dispatches Fire Ball as action 23. It selects player CAF chart 13
then chart 14 and scales Table 20 row one by the attack-speed tier factors
0.6, 0.7, 0.8, 1.0, 1.15, 1.3, 1.45, 1.6, 1.75, 1.9. The first chart-13
frame carrying status 0x40 becomes the effect delay after division by that
speed. The displayed frame is the truncated action counter times speed, and
the completion allowance is at least one and otherwise truncated 7 / speed.
Chart 14 remains on frame zero until that threshold is reached.
The action creates effect controller 10001 immediately with the marker delay; that owner later creates the visible resource-10000010 projectile, plays sample 19 at launch and sample 20 at impact, performs collision, and delivers the packet. The family-zero packet combines Table 17 with magical attack, Table 18 with magical hit rate, Table 19's element type, the player element and state words, and the three-column groups from Tables 70 through 78. Packet word 73 retains spell number one.
Practice belongs to the receiver rather than the cast command.
0x00459690 calls 0x0044f6f0 when a family-zero or family-one packet with a
valid spell number reaches the enemy. The latter function requires an odd
availability value and a stored level below 20, excludes companion spells
seven through nine in ordinary mode, adds one experience point, and compares
against Table 27. Reaching the threshold subtracts it and raises the stored
level once. Consequently a cancelled or missed Fire Ball does not gain
practice, while a successful projectile contact does.
Ice Bolt proves which parts of Fire Ball are common and which remain authored
spell data. 0x00449a40 gives spell two action 24 after the same learned,
pointed-enemy, and mana checks. 0x0043ae10 uses CAF charts 13 and 14 and the
same ten speed-tier factors, but it reads Table 20 row two and builds effect
10002 rather than effect 10001.
Its family-zero packet carries subtype one in word 3, presentation 21013 in word 34, and spell two in word 73. All level-dependent damage, hit, type, movement, and element-bank values are read using row two. The controller arguments otherwise preserve the pointed target, source judgement, 200 display height, Table 35 travel speed, marker-derived delay, and Table 21 target-memory value.
Effect 10002 creates source resource 11000027 on update zero. At the delayed
marker it resolves the hero again, projects the launch point 180 world units
along the stored angle, and creates resource 10000040 with
[-50,-50,50,50] collision bounds, Table 35 travel speed, environment and
first-target expiry, and the copied packet. Sample 94 plays at launch and
sample 20 at contact. The portable player-spell builder now selects these
retail descriptors while the already shared effect owner retains projectile
timing, movement, audio, and collision.
Plasma remains in 0x00449a40's pointed-enemy whitelist and enters action 25.
0x0043a840 reads Table 20 row three, but unlike Fire Ball and Ice Bolt it
uses CAF charts 11 and 12. The first chart's status-0x40 frame still supplies
the effect delay through the common truncated counter and speed calculation.
Its family-zero packet sets word 3 to zero, word 34 to presentation 20005, and word 73 to spell three. Packet word 5 uses the player's derived physical defense rather than magical defense. The effect request uses target mask four, keeps the pointed enemy identity and angle, supplies the hero's current position as an explicit origin, leaves travel speed and display height zero, and places the effective spell level in constructor argument 17.
Effect 10003 reads Table 205 row zero at effective level - 1. Beginning at
the marker delay, it attempts one wave every four updates at distance
250 + wave * 200 along the stored angle. Placement uses
[-100,-100,100,100] against the map and solid scenario objects. One failed
placement latches an obstruction flag and suppresses all later waves.
A clear wave consumes one random value for the primary chart and creates resources 10000030, 10000031, and 10000032. Only the first layer has an update-zero all-target collision window; the other two are visual. Sample 21 plays at the wave position. The portable player action now constructs this request, while the existing effect-10003 owner continues to own all wave placement, random ordering, rendering, audio, contact, and expiry.
The ordinary secondary-click branch in 0x00441c00 handles spells which do
not require a pointed character. It performs the same learned-state,
restriction, effective-level, and MP-cost checks as the targeted command, but
stores the clicked world angle and position, enters action spell + 22 with
character target -1, faces the click, and deducts MP. The portable command
keeps that aim point in the action event even though Hell Fire itself does not
use it; later ground spells can therefore reconstruct their own placement
without recovering cursor state from the UI.
0x00439d10 dispatches Hell Fire as action 26. It uses player CAF charts 13
and 14, Table 20 row four, the common ten casting-speed factors, and the first
status-0x40 marker for effect delay. Its family-zero packet uses subtype
zero, magical defense, presentation 20001, spell four, and the row-four
values from Tables 17 through 19 and 70 through 78.
Effect request 10004 has owner kind one, target mask four, target -1, zero
direction, travel speed, and display height, no explicit origin, and the
player judgement rectangle as its source area. The existing effect owner
shows warning resource 10000002, then creates the two resource-10000000
layers and plays samples 29 and 23 at the marker delay. Its invisible area
expands the source judgement by 150 units, applies the packet to every valid
target, plays sample 20 on contact, and shakes the camera for eight updates.
Practice remains receiver-owned, exactly like the targeted spells.
Ice Blast continues the same targetless secondary-click command as Hell Fire.
The clicked world point controls the hero's facing only; 0x0043b3f0 does not
pass the aim position, a direction, a character target, or an explicit effect
origin. Action 27 uses CAF charts 11 and 12, Table 20 row five, and the common
speed-tier and status-0x40 marker timing.
Its family-zero packet uses subtype one, magical defense, presentation 21013,
spell five, and the row-five values from Tables 17 through 19 and 70 through
78. Effect request 10005 has owner kind one, target mask four, target -1,
the player's judgement rectangle, zero travel and direction values, and Table
21 row five in its final constructor value.
The already reconstructed 0x0042cd70 owner captures the hero's current
position on update three and creates resource 10000051 there. That resource's
chart-zero frame count schedules resource 10000050, then an invisible area
packet four updates later. The contact area expands the player judgement by
150 units, plays sample 20 per target, and shakes a nearby camera for eight
updates. Resource 10000052 follows at frame-count plus 15 with display status
0x80; sample 22 pulses six times before the controller expires at
frame-count plus 22. Successful packet receivers award Ice Blast practice.
Heal remains on the targetless secondary-click command and enters action 28,
but 0x0043ca60 does not build a family-zero combat packet. It uses CAF
charts 11 and 12 and Table 20 row six. Unlike the five attack spells, the
action scans every newly displayed chart-11 frame and resolves the cast only
when it crosses status 0x40; no delayed controller is queued at command
time.
At the marker it always creates simple effect 21020 with owner kind one, source judgement, packet direction eight, and no combat packet. The common one-pass owner maps that effect to Character OPTION resource 11000060 and holds it at the hero for one CAF pass.
If current HP differs from maximum HP, the action restores Table 17 row six percent of maximum HP, capped at the missing amount. It then calls the normal spell-training function and plays sample 17. At full HP the command still spends MP and the marker still creates the visual, but it restores nothing, plays no sample 17, and awards no practice. The portable Heal resolver owns those restorative rules separately from the attack-spell packet builder.
0x0043d290 dispatches spell seven as action 29 with CAF charts 11 and 12,
Table 20 row seven, and the common casting-speed factors. At each newly crossed
chart-11 status-0x40 marker it toggles runtime rate +0x15e4: activation
stores the effective level at +0x15e8 and reads Table 200 row zero, while a
second cast writes zero. The companion profile rebuild then applies Table 200
rows 1 through 13. Resource 11000040 is rendered at the living companion.
The kill paths at 0x00440860, 0x00441e20, and 0x00459690 do not require
the companion itself to deal the last hit. While Moon is active, any defeat
source whose character number modulo ten equals the local player slot calls
the companion-mode practice function for spell seven.
0x0043ceb0 dispatches spell eight as action 30. It uses CAF charts 11 and 12,
Table 20 row eight, the ordinary targetless command and MP cost, and scans all
new chart-11 frames for status 0x40. At the marker an inactive cast stores
the effective level at runtime +0x15e0 and Table 201 row zero at +0x15dc;
an active cast clears that rate. Both branches call 0x00450080 and
0x00452910 to rebuild the player and companion runtime values.
0x0044ea60 applies Table 201 rows 1 through 12 after base and equipment
contributions. They modify attack speed, walking speed, maximum HP, maximum
MP, physical attack, physical defense, hit rate, physical evasion, magical
attack, magical defense, magical hit rate, and magical evasion. Speed fields
are clamped to 0..255 and the remaining values to at least one. The shipped
table leaves both maximum-pool rows at zero, boosts speed and offense, and
reduces physical defense and evasion.
0x0044f2f0 proves that sustained spells share one resource owner rather than
running independent timers. Equipped instance parameter 17 supplies the life
rate and parameter 18 supplies the mana rate; special-item definitions
98000003 and 98000004 add five to the respective rate. Moon +0x15e4 and
Berserker +0x15dc are then added to the mana side. Every third global update
the function scales maximum HP and MP, retaining separate signed fractional
remainders at +0x1638 and +0x163c. Life recovery only runs for a living
hero and never lowers HP below one; mana may reach zero. The player update
then clears both spell rates at zero MP and rebuilds both profiles. Neither
rate nor its effective level is part of the persistent 0x160-byte character
record.
0x00444960 renders player common animation block 500, which the retail data
list maps to Player/Common/Powerup.Caf and .Njp. It uses chart zero,
direction eight, the player position, runtime frame +0x15f4, full opacity,
and RGB strengths 1000/200/200. The frame advances on every active update.
The same locally owned kill test used by Moon trains spell eight while
Berserker is active.
0x0043d670 dispatches spell nine as action 31. It uses CAF charts 11 and 12,
Table 20 row nine, and the normal targetless command-time MP cost. Like Moon
and Berserker, it scans every newly displayed chart-11 frame for status
0x40. At the marker, runtime flag +0x15ec toggles off when already set. An
inactive flag is set only if current MP at +0x1ac is nonzero, which matters
when the up-front cast cost consumed the last point.
Energy Shield has no Table 202 and does not store an effective level. The
player damage receiver at 0x00443cb0 resolves spell nine's effective level
when a packet arrives. For a locally owned player, an active shield and a
non-effect-family packet scale physical defense by Table 17 row nine before
the common damage calculation. The resulting damage is subtracted from MP
instead of HP while MP remains. It does not spill through when damage exceeds
the remaining MP; the next ordinary packet reaches HP. Family-three effect
packets always bypass Energy Shield.
0x00443490 clears +0x15ec whenever MP is zero. The same local kill-owner
checks used by Moon and Berserker call spell-nine companion-mode practice
while the flag is active. 0x00444be0 draws animation block 500, mapped to
Player/Common/Powerup.Caf and .Njp, after the Berserker pass. It uses chart
zero, direction eight, runtime frame +0x15f8, full opacity, and RGB strengths
1000/1000/300.
0x00440180 dispatches spell eighteen as action 40 on CAF charts 11 and 12
with Table 20 row eighteen. The targetless input branch validates the learned
spell and pays its normal Table 16 cost before entering the action. A newly
crossed first-chart status-0x40 marker toggles runtime flag +0x1628, resets
aura frame +0x162c, clears Counter Burst flag +0x1630, rebuilds derived
values, and sends the multiplayer state update. The inactive counterpart's
frame is not reset.
There is no action-entry visual or sample. Unlike Energy Shield, this marker
does not reject activation after an exact-cost command leaves MP at zero;
0x00443490 clears the flag at the start of the following player update.
The player receiver at 0x00443cb0 only applies Magic Shield to family-three
effect packets owned by the local player. Table 17 row eighteen parameter zero
reduces the resolved damage, which is forced to at least one. Post-reduction
damage of at least 20 trains spell eighteen. Every intercepted packet also
creates effect 21029/resource 11000241 and plays sample 60.
The receiver's MP charge has a retail quirk: it reads parameter two from the
currently selected magic row, but at Magic Shield's effective level. Equipped
instance parameter 19 reduces that value and the result is clamped to at least
one. If the charge leaves less than one MP, the receiver clamps MP to zero and
clears +0x1628 immediately. Non-effect packet families bypass all of this.
0x00444a20 draws resource 11000240 at the player using chart zero, direction
eight, runtime frame +0x162c, full opacity, and RGB strengths
1000/1000/1000. The state remains live across ordinary scenario relocation,
is absent from the disk character record, and is cleared with the other player
powerups on death. Multiplayer live-state packets do include the runtime flag.
0x00440530 dispatches spell nineteen as action 41 on CAF charts 11 and 12
with Table 20 row nineteen. The targetless command pays its ordinary Table 16
cost. At a newly crossed first-chart status-0x40 marker, runtime flag
+0x1630 toggles, its own aura frame +0x1634 resets, and Magic Shield flag
+0x1628 clears without changing Magic Shield's old frame. The action has no
entry effect or sample. Like Magic Shield, an exact-cost activation lasts
through its marker update and is cleared by 0x00443490 at the start of the
next player update.
The reflection branch in 0x00443cb0 runs after local damage and revival but
before hit reaction. Packet word 38 must enable reflection, packet word zero
must identify a type-two source, and that living source must still resolve in
the current scenario. Counter Burst adds Table 17 row nineteen parameter zero
to a successful equipment reflection percentage. The returned immediate
packet uses the post-resolution incoming damage times that combined percent,
halves it when the source's runtime value is 100, and clamps it to at least
one. It carries the local player identity, physical defense, level, one random
presentation from 20015 through 20017, and the retail receiver flags.
An active Counter Burst uses effect 21030/resource 11000251 and sample 60.
Incoming damage of at least 20 trains spell nineteen. Its hit-time MP cost
reads parameter two from the currently selected magic row at Counter Burst's
effective level, subtracts equipped instance parameter 19, and clamps to at
least one. Empty MP clears +0x1630 immediately. Without a valid live source,
none of the reflection, effect, training, or MP-charge path runs.
0x00444b00 loops resource 11000250 at the player with chart zero, direction
eight, runtime frame +0x1634, full opacity, and RGB 1000/1000/1000. It is
drawn directly after Magic Shield, before the later Berserker and Energy
Shield passes. The live state survives scenario relocation, is absent from
the disk save, is present in multiplayer state packets, and clears on death.
0x0043fcc0 dispatches spell twenty as player action 42 on CAF charts 11 and
12 with Table 20 row twenty. The targetless secondary-click path stores the
clicked world point, pays the ordinary Table 16 MP cost, and faces it. A newly
crossed chart-11 status-0x40 marker looks up companion character
16000000 + local player slot. A missing, defeated, or special-presentation
7, 9, or 10 companion leaves the already-paid cast as a harmless no-op. The
same is true when the companion's full judgement rectangle cannot stand at
the clicked point. An ordinary companion attack or hit presentation is not
cancelled; owner mode six waits and takes over when that presentation ends.
A valid handoff sets companion owner mode six. 0x004627d0 turns that into
presentation action ten and immediately clears the owner mode. The special
presentation at 0x00461c40 plays PARTNER chart six in direction eight at the
old position, moves the companion to the stored point when chart seven begins,
then plays chart seven in direction eight. Only a newly crossed chart-seven
status-0x40 marker creates the blast. Finishing chart seven releases the
presentation lock and returns the companion to its ordinary AI.
The marker creates special effect 21031 through 0x0042f890. That effect
places two resource-10000000 actors at the companion, using charts one and
zero with RGB strengths 500/500/1200, plays samples 29 and 23, and requests
camera shake 8/6 while the local observer is nearer than 3001 world units.
The chart-six/chart-seven boundary itself submits positional sample 45 twice,
and the impact marker submits sample 46 before effect 21031's two samples.
Enemy judgement rectangles are tested against a 640-by-640 box centered on
the companion. Each living target then uses the companion's hit rate against
the enemy's physical evasion; failed rolls show the ordinary MISS
presentation.
The shared family-zero subtype-three packet deliberately does not follow the normal player-spell builder. Its source is the companion, word four uses the owner player's magical-defense field, and word five stays zero. Spell twenty still owns the Table 17/18/19 and Tables 70 through 78 rows and word 73, so a successful receiver hit trains Explosion. One retail quirk is especially easy to lose: the effective level used to index those rows comes from spell 21, Elemental Strike, rather than Explosion. The randomized ordinary 21000-through-21003 impact is also drawn once for the shared packet before the per-enemy hit rolls.
0x0043e000 dispatches spell ten as action 32. It requires the pointed living
character selected by the secondary-click path, uses CAF charts 11 and 12 and
Table 20 row ten, and pays the ordinary Table 16 MP cost. The action stores the
hero position as an explicit origin and the angle to the selected target, but
passes zero travel speed because effect 10010 owns a fixed wave line rather
than a projectile.
The family-zero packet has subtype three. Its damage is Table 17 row ten plus
the player's magical attack, word five carries physical defense, its hit value
is the Table 17 value plus magical hit rate, word 72 is one, and word 73 is
spell ten. The impact presentation consumes one rand() draw and selects
21000 through 21003. The effect request retains target mask four, constructor
effective level, and the action marker delay.
The existing controller at 0x0042e7e0 reads the wave count from Table 206.
Every eight updates it projects a placement from the fixed origin at
wave * 300 + 250, creates resource 10000060 with a 150-unit area packet,
plays sample 22, and requests eight updates of magnitude-six camera shake when
nearby. A blocked first placement suppresses every later wave; a blocked later
placement ends the line after its already-created waves. Packet contact uses
the normal receiver-time practice path.
0x0043beb0 dispatches spell eleven as action 33 on CAF charts 13 and 14 with
Table 20 row eleven. The pointed command supplies a living enemy character.
On action entry the cast computes its marker delay, resolves the direction to
that character, and creates effect 10011 with owner kind one, target mask
0x14, the selected identity, Table 17 parameter three as travel speed,
display height 200, the player judgement, effective level, and Table 17
parameter four in constructor field 22.
The family-zero subtype-zero packet adds Table 17 parameter zero to magical attack and parameter one to magical hit rate. Word five carries magical defense, word 34 is presentation 20000, word 72 is zero, and word 73 identifies spell eleven. No random draw occurs while building the cast.
The existing effect-10011 handler at 0x0042d6e0 creates source resource
10000012 on update zero. At the marker delay it reads Table 204 for the
effective-level projectile count and divides retail's 6.283184 full circle
between them. Resource-10000010 children start 180 units from the live hero,
home toward the selected target with turn value 20, use 80-unit bounds and a
90-update lifetime, and expire on scenery or first contact. Sample 19 comes
from the last spawn and sample 20 from contact. The ordinary packet receiver
awards practice only after a successful hit.
0x0043c490 dispatches spell twelve as action 34 on CAF charts 13 and 14 with
Table 20 row twelve. Its pointed command and effect-10012 constructor match
Flame Strike's selected target, direction, Table 17 travel speed, height 200,
player judgement, marker delay, effective level, and constructor field 22.
The packet is family zero, subtype one. Table 17 parameter zero is added to magical attack, parameter one to magical hit rate, word five carries magical defense, word 34 begins as 21013, word 72 is zero, and word 73 is spell twelve.
The handler at 0x0042db10 creates source resource 11000027 and a
Table-204-sized resource-10000080 warning fan on update zero. Table 204 column
29 supplies the spread divisor for the retail count * 2.5132736 / divisor
calculation, including the even-count half-step. At the marker delay it emits
resource-10000081 projectiles 180 units from the live hero. They travel
straight with 50-unit bounds, a 90-update lifetime, scenery and first-target
expiry, and optional hit memory from constructor field 22. Their packet
replaces words 34/35 and 74/75 with directional presentations 21021 and 21022.
Sample 94 plays at the final launch; sample 20 and practice belong to contact.
0x0043b950 dispatches spell thirteen as action 35 on CAF charts 11 and 12
with Table 20 row thirteen. The retail pointed-spell switch requires a living
enemy and stores its angle, but the effect-10013 request passes source identity
-1, target mask four, target identity -1, zero travel values, the stored
angle, an explicit pointer to the hero position, no source judgement, the
marker delay, effective level, and Table 17 parameter four.
The copied packet retains the real player source. It is family zero, subtype zero, adds Table 17 parameter zero to magical attack and parameter one to magical hit rate, carries physical defense in word five, presentation 20005 in word 34, zero in word 72, and spell thirteen in word 73.
0x0042e240 reads the effective-level ray count from Table 204. It attempts
four radial shells, four updates apart, at 350 + shell * 200 from the fixed
origin. Each ray independently latches failed placement, consumes one retail
random draw for the resource-10000030 chart while clear, and also creates
resources 10000031 and 10000032. Only the first resource processes every
target in its 100-unit update-zero area. Sample 21 plays once per shell at the
last radial position, even if that final ray is blocked. The controller ends
at marker delay plus 16; packet contact owns spell practice.
0x0043da20 dispatches spell fourteen as action 36 on CAF charts 13 and 14
with Table 20 row fourteen. The pointed command resolves the selected living
enemy and sends effect 10014 owner kind one, the player source, target mask
0x14, target identity, Table 17 parameter-three travel speed, height 200,
target direction, player judgement, marker delay, and Table 17 parameter four.
Constructor effective level remains zero.
The family-zero packet has subtype two. It adds Table 17 parameter zero to magical attack and parameter one to magical hit rate, uses magical defense in word five, presentation 21019 in word 34, zero in word 72, and spell fourteen in word 73.
The effect-10014 handler at 0x0042e5c0 has no source visual. At the marker
delay it re-resolves the live hero and places resource 10000070 180 units along
the stored direction. That actor travels straight with 80-unit bounds and
expires on scenery or first contact. Sample 22 is emitted at launch; the
actor's bank-zero sample 20 and spell practice occur only on contact.
0x0043e5e0 dispatches spell fifteen as action 37, but it does not use the
ordinary Table 20 casting timeline. 0x00449a40 first requires an equipped
main-hand item whose subtype is zero, three, or one. Invalid and empty hands
consume the pointed command without deducting MP or entering the action. The
three accepted subtypes select CAF pairs 5/6, 15/16, and 19/20, and
0x00450c60 supplies the same ten attack-speed tiers as weapon attacks.
Action entry creates effect 21025 with owner kind one, the player source,
player judgement, direction eight, no packet, and constructor field 21 set to
200. The common one-pass handler maps it to resource 11000100. The action then
scans every newly crossed first-chart frame for status 0x40. While a weapon
still exists, a marker re-resolves the selected enemy angle, plays sample 154,
and constructs effect 10015 with target mask 0x14, Table 17 parameter-three
speed, height 200, player judgement, hard-coded delay one, and Table 17
parameter four in constructor field 22. Action counter six independently
plays selector-four weapon audio. Recovery runs to the final frame of the
selected second chart.
The family-zero packet uses physical type zero and subtype zero. Damage is
Table17[15, level, 0] * physical attack / 100, clamped to one, while word
five uses physical defense. Word 32 is Table 17 parameter five, word 34 is
21024, word 72 is one, and word 73 is fifteen. The accuracy field preserves a
retail oddity: Table 17 parameter one is added to magical hit rate even though
the packet is physical.
Effect 10015 enters the generic actor initializer with resource 10000090. A
live owner projects its origin 200 units along the stored angle. The actor
uses [-80,-80,79,79] bounds, display height 155, a fixed seven-update
lifetime, directional chart zero, straight Table 17 travel, scenery and
first-target expiry, and bank-zero contact sample 20. Its copied packet enters
the ordinary receiver-time damage and spell-practice path.
0x0043ecf0 dispatches spell sixteen as action 38 on CAF charts 13 and 14
with Table 20 row sixteen. The pointed command requires a living enemy but no
particular weapon. Action entry sends effect 10016 the player source, target
mask 0x14, selected target, Table 17 parameter-three travel speed, height
200, target angle, player judgement, the chart-marker delay, and Table 17
parameter four. Constructor effective level remains zero.
The family-zero packet has magical type three and subtype three. Table 17 parameter zero is added to magical attack, parameter one to magical hit rate, word five carries magical defense, word 32 carries parameter five, and word 34 uses one retail random draw to select 21000 through 21003. Word 72 is zero, word 73 is sixteen, and the normal Table 70 through 78 banks are copied.
The existing effect handler at 0x0042ea50 launches resource 10000110 when
the marker delay expires, with 80-unit bounds, packet contact, and sample 19.
It tracks the projectile's runtime identity and last position until removal,
then creates resource 10000111 there. On that burst's fifth update the packet
is applied to every target in its 240-unit area, sample 22 plays, nearby camera
shake is requested, and the controller ends. Receiver contact owns spell
practice.
0x0043f8d0 dispatches spell seventeen as action 39 on CAF charts 11 and 12
with Table 20 row seventeen. Action entry creates one-pass effect 21028 with
owner kind one, the local player source and judgement, no target or packet,
packet kind eight, instance minus one, and constructor field 21 set to 200.
The common effect path maps it to resource 11000230.
At each newly crossed first-chart status-0x40 marker, the local player sets
the Identify input flag and requests the right-side Inventory panel. A second
Identify command while that flag is set is consumed before MP is charged.
0x004087b0 draws System/Common/Pattern/System.njp pattern zero for the
normal pointer and pattern one at the same pointer coordinates for Identify.
The Identify branch in 0x00446320 only accepts an unidentified backpack
item while no item is held. It sets the instance's identified flag, calls
0x0044f6f0 for one spell-seventeen practice event, and clears the input flag
without closing Inventory. Already identified items leave the mode active.
Equipment, belt, and special-item storage never enter this branch. Secondary
click or closing the panel clears the mode without changing an item.
The item database name is the identified display name, while its description is the base name shown before identification. Unidentified information hides all values. The saved instance mirrors the identified value at raw word 48 for category-zero and category-one items, and word 47 for category-two items.
Operand type nine in 0x004346b0 is the interaction gate used by the switch
scripts. It first resolves the local player and requires its scenario to match
the running script. Player action +0x3f8/+8 must be one. The target must
resolve through the current scenario and appear in the object-display list;
the common judgement-distance routine then compares it with the player's live
range at +0x3f4. The ordinary value is 0x9f, so the portable query returns
one only for a displayed actor within 159 judgement units of an idle hero.
Opcode 26 reaches 0x00432b02. Its first operand accepts player slots zero
through three or a scenario character number. Missing sources and absent
remote player slots are successful no-ops. The handler evaluates the other
six operands, formats operand three with %d, centers the result using a
six-pixel character width, and draws it at the projected actor position plus
the supplied offsets and fixed -12 baseline. Black text at (1,1) is the
shadow; the requested RGB text is drawn over it without a backing box.
Opcode 60 at 0x00433edf writes its evaluated value to local-player field
+0x159c. The player update clears that field before periodic scenario status
kind five runs. 0x00434ef0 checks it after the base player pass and draws
common player animation 502, Player/Common/UnlockSW, at chart zero,
direction eight and full RGB strengths using the current player frame counter.
Opcode 29 at 0x00433056 does not own local switch state. It evaluates one
value and asks 0x00419050 to send scenario event kind six in packet 0x22
only in network-client mode. Shipped scripts place this behind the play-mode
branch; single player uses opcode 28. The catalog contains 60 type-nine gates,
60 opcode-26 labels, and 60 opcode-60 markers across the same 22 scenarios.
Opcode 29 has 61 calls across 23 scenarios.