Gameplay and engine investigation
The flight engine is substantially mapped: its update loop, all 61 object-handler entries, thirteen eight-quality tuning tables, aircraft force calculation, weapons, damage, enemy state machines and objective checks are identified. The campaign front end also supplies an exact scoring formula and different promotion rules for the normal game and Other Worlds.
- Flight dynamics, controls, walking and ejection
- Weapons, collisions and enemy behaviour
- Objectives, campaign persistence, scoring, ranks and endings
- World-by-world objective inventory: 25 original world variants and 339 stored objective anchors, with coordinates and qualities.
- Native tables, flight/gameplay disassembly, campaign disassembly and verification results.
Evidence and scope
Unless stated otherwise, addresses are file offsets in
installs/deluxe_dnk/3.EXE, the normal-game 3.42 flight engine. DS:xxxx
means a data-segment offset, not an executable file offset. The data segment is
1129h relative to the loaded module; its file base is 11490h. Normal near-code
pointers become file offsets by adding the 512-byte MZ header. Campaign helpers
have different headers and segments, recorded separately.
The explanations below distinguish traced code, execution of isolated original
routines, and claims from the supplied manuals. Descriptive variable names are
research labels, not recovered assembler symbols. MBUILDER.DOC actually does
preserve original symbols and tables; those are identified as such.
520 controlled native cases pass, with eight additional recorded flight-force scenarios. These cover collision boundaries, damage, all quality selectors, aircraft position scaling, weapon restrictions, saucer states, all eight orb objectives, scoring, medal boundaries in both editions, promotion differences, and experimental-aircraft throttle permission. The force scenarios retain original vector transforms, trigonometry and arithmetic; only HUD and sound calls are stubbed. These checks do not amount to a full-game emulator or an exhaustive campaign playthrough.
The newer flight engine's addresses must not be applied to the older engines. The pristine-world catalogue uses the 3.42 rules to describe their object fields; it does not establish that every older executable behaves identically.
Runtime organisation
The top-level frame loop begins at 280c. Much of the player's physics is inline
in this loop rather than behind one convenient flight-update function.
| Phase | Principal code | Work |
|---|---|---|
| Frame timing and pause/help | 280c–2a1c |
Read timer, clamp elapsed step, apply held T, handle pause/help |
| View and scenery setup | 2a1c–2cc7 |
Camera transforms, player geometry, ground/sky, stars and sun |
| Distant-object pass | 7ef2, called at 2cc7 |
Inspect distant records and promote objects becoming near enough |
| Prepare local census | ba36, called at 2e26 |
Preserve/reset defence and objective counters, update effect state |
| Particle and moving-object integration | 0b17, a187 |
Lifetimes, velocity, gravity, drag and position |
| Active world pass | 7c8c, called at 2e42 |
Object handlers, collisions, drawing, walking surfaces and hazards |
| Portal/autopilot relocation and streaming | 217b, 0e5c, 7dc8, 8362 |
Relocate player; load/offload scenery |
| Aircraft rotation and forces | 31a7–3c0c |
Control response, damage biases, stall limiter, thrust, drag and lift |
| Ground contact and movement | 3c0c–3fbc |
Landing, aircraft position, walking, jumping, parachute and remote aircraft |
| Effects, instruments and input | remainder through 4c5a |
Weapon recharge/fire, booster, views, ejection, status and exit |
| Finish sortie | 4c5a–4de6 |
Serialize state/results, update world and present outcome |
Rendering and simulation are interleaved. Distance sorting, collision eligibility and scenery streaming affect which handlers run. A faithful reconstruction cannot simply draw all objects and update every distant enemy indefinitely.
Object storage and dispatch
A live object occupies 74 bytes. Important recovered fields:
| Byte offset | Representation | Meaning |
|---|---|---|
00,04,08 |
signed 32-bit | World position X/Y/Z |
0c,0e,10 |
16-bit turns | Orientation |
12 |
byte | Distance tier; ff when detached from lists |
13 |
byte | Collision group/instance tag; also overloaded by effects |
14 |
32-bit | Squared distance key; scale depends on tier |
18 |
word | Live flags, quality and type-specific state |
1a |
byte | Rotation/detail-related selector, used by generic integration |
1b |
byte | Hit points for damageable scenery/enemies |
1c,20,24 |
three 32-bit values | Squared distance thresholds used by drawing/object logic |
28,2a,2c |
three near data pointers | Near/middle/far model records |
2e,30,32 |
signed words | Velocity components |
34 |
word | Lifetime/countdown, age or other type-specific timer |
36,38,3a |
signed words | Generic angular increments |
3c,3e |
words | Random motion/drag inputs for generic integration |
40,41 |
bytes | Gravity and effect/type-specific flags |
42,44,46 |
words | Collision half-extents |
48 |
word | Object type |
The compact world record's four raw parameters expand into the three velocities
and timer (7f85–8056). Model pointers are data pointers, not executable callbacks.
The separate code-pointer table is at DS:2c50, indexed by type * 2.
A handler entered with carry clear performs ordinary updates; carry set
notifies a collision. For player/aircraft contact, DI=ffff is a sentinel rather
than another object pointer. Several handlers give this sentinel special treatment.
The extended type-properties table at DS:2d44 governs walking, runway,
forcefield, capture and other contact rules. It is distinct from live flags.
Live flag meanings are context-dependent, but the common uses include:
| Mask | Common use |
|---|---|
0001 |
Object allocated/present |
0006 |
State bits, including saucer movement states |
0008 |
Receive object collision callbacks |
0010 |
Run per-frame handler |
0020 |
Participate in player contact checks |
0040 |
Six-hit attacking object |
0080 |
Twenty-five-hit attacking object |
0100 |
Select box-collision path; also damaged-aircraft state |
0200 |
Objective complete, for applicable types |
0800 |
Type-specific special option |
7000 |
Quality index 0–7; builder displays 1–8 |
8000 |
Newly allocated record awaiting list insertion |
A flag cannot be renamed globally from one example: crew use 1000 for their
shot pose, aircraft use the quality bits for variant selection, and saucers and
vehicles use low state bits differently.
The main moving pool has 320 slots, starting at DS:8fa6, allocated at
9fac. Guns have a separate 32-slot pool at DS:7732 (08a5). Objects need
both zero flags and a detached tier before their slot is reusable. World loading
also uses a separate distant record store; the working tile neighbourhood contains
four tiles. The original builder documentation's warnings about far-object editing
and crowded tile boundaries follow directly from these finite stores.
Clocks and reproducibility
The PIT interrupt at 1732 increments the fast clock and frame counter. Normal
programming uses a divisor of 1024; every 64 fast interrupts chains the normal
BIOS timer. The game therefore has a fast timer, a BIOS-rate timer and a rendered
frame count, rather than one universal tick.
Frame step is fast_frame_ticks >> 3, clamped to 2..35. The common time-scaling
denominator at DS:1b7f is 5. Holding T replaces the effective step with 50,
while preserving the normal step separately. It is a large simulation step per
rendered frame, not a request for one fixed multiplier independent of frame rate.
Many operations multiply by step / 5; others decrement once per frame or use
the hardware timers. The five-frame explosion is one example of a frame-based
lifetime. Damage and AI also consume the shared random stream. Frame cadence,
active objects and execution order consequently matter to reproduction; recording
only player input would not guarantee identical outcomes under a different loop.
The assembly PRNG updates a 32-bit state by multiplication with 278dde6dh,
wrapping at 32 bits (ea60). The bounded wrapper at ea51 uses the high part
of a product. This is separate from the Borland PRNG used to mask pilot saves.
It affects gameplay as well as cosmetic effects.
Undocumented controls confirmed in the shipped 3.42 engines
These refer to the DOS originals. The native reconstruction notes describe how those behaviours are carried into the unified executable; deliberate Redux changes are separate from the findings here.
| Input | Behaviour and scope |
|---|---|
| Alt-I | Enables invulnerability and clears current aircraft damage. Normal 3.42 forces the flag on; Other Worlds toggles it on/off. |
| Main-row 9 | Latches the tester cheat flag. The sortie-exit code then skips marking the result as invulnerable (2000h). This is a result-marker bypass, not proof that every other career restriction disappears. |
| Hold H with R, G or B | Changes the corresponding hallucination/palette counter. The source calls the routine takedrugs. Use from the cockpit: H also has an on-foot handler. |
| H+1 / H+2 | Copies the currently altered sky/ground colours into the base palette and packs them into the world header; normal world saving can persist them in 3UNIV.DAT. |
| U | Other Worlds: starts/stops recording the current flight to 3DEMO.DAT. Normal 3.42 omits this recorder. |
| L | Toggles cockpit/full-view rendering; omitted from the main manual's key lists. |
| F10 | Queues the mission-objective congratulations message. It does not complete an objective or increment the objective counter. |
The main-row 9 branch is at normal-engine file 0x45e9..0x45f5 and expansion
0x4af4..0x4b00. Its flags are DS:0ae7 and DS:0bb0. The corresponding
result exclusion tests start at 0x4c6a and 0x51da. Isolated execution of both
unchanged binaries verifies that an invulnerable sortie sets 2000h without the
latch and leaves that bit clear with it. No whole-career scoring claim is implied.
The H key calls the normal palette routine at file 0x227b from 0x4264;
Other Worlds calls its equivalent at 0x22c1 from 0x472d. Original keyboard-bit
reads and each R/G/B counter update were executed independently in both binaries.
With a five-unit step and rate 310, each key changes only its own counter by 310.
A private DOSBox run also confirms the visible H+R palette change in Other Worlds.
H+1/H+2 do more than change display registers. In normal 3.42 their setters
at file 0x22e1 and 0x22fa both call packbits at 0x643a. This packs the
sky and ground RGB components into the five colorbits bytes of the 16-byte
world header. writeuniv writes that header to the working world file,
3UNIV.DAT; normal exit writes it when readfileflag is set and no critical
error occurred. Resuming that saved world can therefore retain the colours.
The shortcuts themselves make no immediate DOS file-write call. They do not
rewrite the distributed theatre templates or install a global preference.
This persistence path is established by source and native setter disassembly;
a separate DOSBox save/reload experiment has not been performed.
A second private DOSBox check presses U twice in Other Worlds. It writes a
530-byte full/delta recording with the original 37-byte terminator. The recording
is made in a disposable install, not in the retained source installation.
The command-line -b playback path reads 3DEMO.DAT in the current directory;
it must accompany the appropriate world state.
The normal engine also retains a developer world-copy operation gated by -e.
At file 0x2e4e, it checks DS:0ae5, then the Right Shift release scan b6h,
flushes pending tile changes and calls copyuniv (0x84a6). The preserved
F3DVEC.ASM copies the current four-tile neighbourhood to eight preset locations:
(2,2), (2,4), (2,6), (4,2), (4,6), (6,2), (6,4), (6,6). This changes world
object records; it is a construction tool, not an ordinary flight difficulty
setting. The dispatch and call are confirmed in the shipped normal executable;
the resulting world rewrite has not been exercised in DOSBox.
TOWER's keyboard tables add J/K, U/D, G/Shift-G, several control-key navigation aliases, and F1's boss-screen round trip. Those are reader functions, not a second hidden tower menu. The early sun-face trigger is confirmed in 1.20/2.00, while the later engines retain its geometry without a confirmed selection trigger.
Compile-time variants in the preserved source
These are assembly-time branches, not additional command-line cheats. The
preserved KA.INC actually selects slalom=1, guns=0, saucey=0 and
normaltet=0. That is not the configuration of the ordinary retained 3.42
executables. The source collection contains historical and experimental paths;
this inspection does not establish that every switch combination still builds
or that a slalom edition was released.
Slalom and the alternate scenery
The training path in 3.ASM:9850 follows six ordered crossings, numbered 0..5.
It takes the high word of X divided by two as the checkpoint index, requires
movement toward increasing X, and alternates the required side of the line
Y=2*65536 cm. An out-of-sequence crossing, wrong side or ground contact away
from home resets the six cockpit progress indicators. The final crossing calls
the normal incondest mission-objective routine. No separate race timer is
established by this path; it should not be described as a proven timed race.
DEFOBJS.ASM:4861 constructs two rows of six Tet markers at different heights.
normaltet=0 adds a long stem to their geometry and slalom enlarges their
collision bounds. The same alternate layout places fifty trees and a farm
building: type 49's shell geometry becomes a tree (DEFOBJS.ASM:3166), while
type 50's saucer geometry is replaced by FARM.INC (DEFOBJS.ASM:3420). The
alternate type-51 template is another propeller aircraft. Several handler paths
change too: mortar/saucer updates feed radar and nearest-object detection,
forcefields omit their ordinary shutdown paths, and the training checkpoint
code is replaced by a proximity-warning display when a world file is loaded.
guns=0 independently omits the player's gun, missile and bomb control/update
blocks. It is broader than merely hiding the gun sight. slalom also disables
the map-builder variant even if mbflag is selected. These observations are from
source inspection; no alternate executable has been assembled or play-tested.
The blue ground polygon reuses crtrobj, but slalom sets its span to 16,000 cm
and its colour to blue. A pond is the likely visual intent; the retained symbol
name identifies the reused geometry, not a documented crater in the course.
For standalone generation, 3.ASM initializes naas, nvest and ndbst to
zero. INITPERMS can still place AAA, ground-vehicle and KLA sites when those
counts are explicitly supplied, but the ordinary menu's combat-training defaults
must not be assumed for this source-only standalone run. The three forcefield
placements precede the saucey layout branch and remain part of the course.
Finishing the six crossings calls incondest, which queues the ordinary
cmpltrmsg congratulations popup and its airstrip/F2 advice; no separate slalom
completion text was found in this path.
The training generator also places two coincident objsurv markers at (0,0,0)
and three more completing the corners of a 20.97152-kilometre square. These
are survey markers, not established world boundaries. survobj is a 20-metre yellow
horizontal square with a black cross: the origin marker becomes conspicuous when
the usual generator is omitted. It has no generator/objective role.
The slalom farm's bmrtim delegates collision handling to gmdest, which replaces
it with the ordinary fuel-dump wreck (gmobj2/gmobj3, type 33) and emits twelve
fragments. It does not use the saucer's roll-on-hit or tumbling death behaviour.
Slalom's tree type 49 selects donothing instead of shelltim, so hitting a tree
must not unpack a concealed enemy. Type 51's slalom bmrstim handles hits and
moves the three placed aircraft straight along their fixed attitude with
followangles; it does not run the normal enemy-launch countdown. Their placement
height is 131,072 cm, and their type-51 property entry is zero, unlike boardable
player aircraft. Their precise intended gameplay role is not documented; traffic
or a proximity-warning experiment is an inference from these routines.
Other significant switches
| Switch | Source-level effect |
|---|---|
shareflag |
Shareware/registered feature gates, including helper access, invulnerability toggle behaviour, demo machinery and world-type encoding choices. The makefile has explicit 3s/3r targets. |
mbflag |
Includes the in-game mission-builder machinery; registered target selects it, shareware target omits it. Slalom overrides its map-builder selection. |
demover, nodemo, demorec |
Distinct demo-only, playback-availability and recording branches. A demo-only build omits ordinary configuration/tower/report paths. |
qrocket |
Includes the rechargeable rear rocket booster and its thrust/HUD/input code. |
protection |
Old registration/trial checks and bouncing-ball interruption machinery; disabled in this snapshot. The 2.00 readme explicitly says all protection was removed. |
testerflag |
Includes the main-row 9 tester latch and its invulnerable-result-marker bypass. Enabled in the snapshot and confirmed in both retained 3.42 engines. |
MVPCD |
CD packaging path: separate writable-directory prefix for generated files, and changed argument parsing. |
getunivbin |
Includes UNIVBIN.DAT object importing/merging into the saved world. |
vgmode |
Legacy 640x350 and 320x200 colour and Hercules-related rendering branches. Their presence does not establish a working alternative build on modern or original hardware. |
normaltet, saucey |
Alternative Tet geometry and alternative procedural training/scenery layouts. |
soundflag, fadeflag |
Sound-code and fade branches; sound is enabled when soundflag=0. |
simpflag |
Includes the beginner-aerodynamics pitch-assistance branch. |
avgthrot |
Smooths the engine sound's frequency value, rather than smoothing physical throttle input. |
stestflag |
Replaces normal startup flow with an interactive sound test. |
larryflag |
Startup test-point messages addressed to Larry; the separate ADL module's setting suppresses hardware port writes. |
printflag, diagflag, jdiag, polydiag, copydiag, chopchop, finalscreen |
Assorted state dumps, joystick/renderer/copy diagnostics, test paths and end-of-run reporting. |
The protection code is unusually personal. DEFOBJS.ASM:5443 has a hotshot
branch described as detecting players too good for a trial. It checks roll and
attack-evasion progress, then branches to hosehim, with the comment "hose this
guy, he's too good!". That changes a survey object's collision-radius field to
-2. The later collision path explicitly treats negative radii as protection
signals: -1 sets critical error 97, -2 sets 98, and a negative radius on a type
other than survey marker 31 sets error 59. It also forces the ejected flag.
The main loop exits on a nonzero critical error and skips the normal world save.
Eight isolated native trap checks confirm these
outcomes in both 1.20 and normal 3.42. The 1.20 code contains both negative-radius
producers (0xc8d9, 0xc910); normal 3.42 contains neither producer even though
the trap consumer remains. These checks execute the unchanged trap prefix only,
not a complete trial-expiry session or all front-end handling.
Recovered resources and runnable experiments
The model browser now includes the source-only farm,
tree, unused foliage-only tree helper, tall Tet marker and large blue crater.
They appear beside normal models and under the source variants edition.
tools/extract_source_models.py reads the original DW/EQU/DUP data, preserving
all eight painter lists; it does not build or execute source instructions.
Its normalized data addresses start at 0xf800 and are not historical executable
offsets. The six stored detail models occupy 935 bytes. Source-file hashes are
recorded in their metadata.
The C++ reconstruction bundles these data and provides --slalom. It launches
the recovered training-layout/checkpoint experiment with player weapons disabled,
six progress cells and local objective completion; it does not save this run into
pilot careers. The selected slalom aircraft reuse the ordinary aircraft's stored
middle/far models, rather than adding another hidden aircraft mesh. Their
placements use Z=131072 cm and zero angles: STRTPERM copies the nine KBUF words
over the template, including the high word of Z and all three angles. The tree
object uses its full model at near/middle detail and its line-trunk helper at far
detail; the foliage-only helper is stored but unreferenced by that template.
--graphics-mode=ega-low and --graphics-mode=hercules make the source's 320x200
colour and 720x348 monochrome scene profiles available alongside default
640x350 EGA. World projection/rasterization runs at the selected resolution.
Hercules uses the recovered BKCLRS fill masks and HGCLINE solid-line mapping.
The port adapts the preserved cockpit/instruments and retains the original
helper display modes: there are no verified alternative cockpit bitmaps, some
source helpers assume 640 pixels, and the alternate branches omit the usual
full-view definitions. These are source-based reconstructions with documented
adaptations, not pixel comparisons against a retained released alternate build.
Was a Hercules edition released?
Publication remains unconfirmed after the 17 October 2026 web/archive search. No identified Hercules distribution, contemporary announcement or authenticated Hercules screenshot was found. This is a negative search result, not proof that no such build ever circulated.
The source evidence is stronger than an unused resolution constant:
3M.LNK and
3DEMO.LNK explicitly link hgcpixad and hgcline.
Together with the CRTC setup, raster routines and 720x348 projection branch,
these establish intended Hercules rendering support. The linker inputs do not
prove that a complete Hercules executable was built or distributed; the snapshot
also contains incomplete alternative paths and lacks verified alternate cockpit art.
Evidence from released versions points to VGA as the supported public target. The retained 1.20 CORNCOB.DOC says “Corncob absolutely requires a VGA adapter” and separately mentions that a mono-VGA system may or may not work. Its FAQ explains the dependency on VGA write mode 3 and palette effects. A monochrome VGA monitor is not a Hercules adapter. Later retained manuals require colour VGA. The June 1993 Brisbug catalogue lists version 2.0 as VGA: contemporary newsletter.
The working hypothesis is an experimental or older development configuration, possibly predating the documented releases. Its age and publication history cannot currently be established. The C++ Hercules option is a reconstruction from those source paths, not evidence of a recovered historical executable.
MVP CD packaging switch
MVPCD is set to zero in the snapshot. Its branches in 3.ASM and CMDLINE.ASM
redirect generated files through a writable-directory prefix, instead of trying
to write beside an executable on read-only media. A CD-enabled build accepts
-p<path> (also the -z dispatch alias) and prefixes 3.CMD, 3.RES,
3UNIV.DAT, 3D.CFG and 3DEMO.DAT; helper invocations also receive the path.
It suppresses the ordinary registered-copy banner. The branch does not introduce
CD audio, new models or a separate campaign-content loader.
The name and publisher identify this as MVP Software CD packaging, but no retained disc executable has been compared. CD distribution itself is documented: the Centre for Computing History's physical 1993 MVP Software's Game Jamboree contents transcription lists Corncob Other Worlds Campaign. That establishes a CD compilation appearance; it does not establish an enhanced CD edition or which source configuration that particular disc used.
Early protection behaviour
The 1.20 registration help offers a $15
machine-bound economy reply code or a $25 supplied unprotected edition with the
builder. Its menu executable contains trial-expiry, incorrect-reply-code and
incorrect-system-date messages. These establish the trial/registration system
independently of the later source's protection condition.
The protected source's welcomewagon interrupts play with IMG help/welcome pages:
the first immediately, others after a delay, an airspeed or altitude threshold,
and later fifteen-second intervals. bounceimg silences effects and makes the
player wait for a bouncing ball before Space resumes. A hidden Enter + keypad 5
- A check skips the current ball wait; it is established here from source only, not from a keyboard experiment in the protected executable.
The hotshot branch can end a trial flight based on roll/attack-evasion progress,
not merely elapsed calendar time. It deliberately reaches the error-98/ejection
trap described above. The normal critical-error path bypasses world saving;
this is not a hidden reward or a more difficult enemy. The 2.00 readme explicitly
announces removal of all protection. None of this enforcement is enabled in the
C++ reconstruction.
Cockpit frame and airflow indicator
In the unchanged deluxe 3.42 3.EXE, EPAGE (file offsets 0x561f..0x570e)
subtracts one byte from the main window's first-byte offset, then adds bpl-1
to select the right strip. At 80 bytes per row this is column 79, pixels
632–639; using byte 78 incorrectly lets the scenery overwrite the frame.
DRAWMDS (0xcd51..0xcdd2, through its DRAWDisc call) computes a six-pixel
airflow indicator using body velocity and passes win0, the radar clipping
rectangle (496,259)–(615,327). At zero velocity the computed centre is
(638,243), outside this rectangle: the original writes no pixels. Drawing
it against the whole screen instead causes a stray red dot beside the cockpit.
Forward velocity (1000,0,0) gives a visible ball at (559,298); lateral and
vertical samples independently confirm clipping at the right and top.
tools/verify_cockpit.py reproduces these findings in isolated original routines
without stubs, and retains the source hash, addresses and results in
analysis/gameplay/cockpit-checks.json. VGA memory writes establish pixel coverage;
this probe does not emulate four-plane colour composition or a complete frame.
Reproduce the investigation
From the repository root, using the same Python environment as resource extraction:
python3 tools/analyse_gameplay.py
python3 tools/verify_gameplay.py
python3 tools/catalogue_mission_rules.py
python3 tools/extract_source_models.py
python3 tools/verify_source_switches.py
python3 tools/verify_cockpit.py
The first tool needs Capstone and the existing extraction dependencies. Native checks additionally require Unicorn. An arbitrary extra range can be inspected without rewriting reports:
python3 tools/analyse_gameplay.py --show 0x37d0:0x3c0c
Original installations are read-only inputs. The generated reports contain source hashes and the native checks state their stubs and limits.
Scope and subsequent reconstruction
This report records the initial disassembly and native probes. The later C++ reconstruction adds independent native comparisons for fast-projectile contacts, landing/suspension, autopilot feedback, actor updates and campaign transitions. Those are no longer unimplemented items merely because this earlier report describes a smaller set of checks.
The limits of the evidence still matter. We have not compared every gameplay branch across all seven flight binaries, nor established whole-session equivalence against long DOS recordings with identical random-number consumption. A routine matching its native fixtures does not by itself prove every possible interaction in a complete campaign. The reconstruction notes list the coverage and remaining qualifications for that work.