# Objectives, campaign rules and scoring

[Gameplay overview](gameplay.md) · [World inventory](gameplay/theatres.md) ·
[Native tables](gameplay/tables.json) · [Campaign code](gameplay/campaign-disassembly.txt).

The normal Earth game and Other Worlds share much of the object machinery, but
**their campaign promotion rules differ**. The expansion's manual is not a complete
specification of the normal game's front end.

## How missions are represented

Theatre files contain placed objects and their flags, qualities, velocities and
timers. TWR text supplies airbase intelligence and optional `Sxyth` teleport starts.
There is no separate per-briefing mission-script interpreter in the recovered
objective path. A briefing's “destroy these targets”, “meet this spy” or “pass
through these portals” is implemented by those objects and their handlers.

The [catalogue](gameplay/theatres.json) lists all 25 pristine world variants,
their environments and starts, and **339 stored objective-bearing anchors**.
It deliberately does not assign an object to whichever briefing start happens
to be nearest: that would invent mission ownership absent from the files.
Optional/preset-complete anchors are preserved, not counted as newly earned goals.

The engine's generator-orb quality table is the original manual's `hqtbl`.
In normal 3.42 it lives at DS:`ef93`:

| Quality | Objective | Handler |
| --- | --- | --- |
| 1 | Destroy this orb | `bd3f` |
| 2 | Clear fuel dumps **and Tets** from the checked census | `bd6e` |
| 3 | Clear KLA launch sites and AAA batteries | `bda5` |
| 4 | Clear ground vehicles and their sites | `bdb2` |
| 5 | Destroy this otherwise weapon-proof orb through aircraft impact | `bd49` |
| 6 | Clear saucers and saucer sites | `bd98` |
| 7 | Clear mindbenders | `bd8d` |
| 8 | Clear aliens, after aliens have been observed active | `bd7b` |

The native code adds detail beyond the short manual labels. Quality 2 includes
Tets, and quality 8 checks both that an alien census has previously been nonzero
and that the current checked count is zero. A completely unactivated alien area
is not automatically treated as cleared.

`bdbf` is the shared completion operation. It sets **live flag `0200h`**, increments
the mission-objective statistic at DS:`022c`, and announces completion. It tests
the bit first, preventing repeated credit. Every quality's satisfied and blocked
paths, and repeated completion, are exercised in the native checks.

## Local census and delayed completion

`ba36` prepares counts from the preceding active-world pass and clears current
counters; object handlers rebuild them during the next pass. Counts include
separate KLA/AAA quality arrays and live fuel, vehicle, site, saucer, mindbender,
Tet, alien and generator totals. Some peak/previous totals are retained.

The orb's own update (`bce0–bd3e`) selects its quality row and calls the goal handler
only while its distance tier/key passes the eligibility test. The checked numbers
are therefore tied to the streamed active neighbourhood and previous/current
passes, not a global query over every stored object in the theatre.

This explains a documented and observable quirk: killing the targets does not
necessarily light the complete indicator immediately. A return flyby of the orb
or its wreck can allow its objective check to run. Destroying an orb also leaves
a type-43 object whose handler still performs the applicable checks. The orb is
not merely a target that disappears, taking its mission logic with it.

Forcefield shutdown similarly uses generator census. It is not implemented by
explicit parent pointers linking each field to an orb. Preset completion and
physical destruction are separate: a completed-but-intact orb can still power
fields.

## Other objective carriers and special bits

- A **spy** is a ground-crew object with `0800h` set. Approaching on foot within
  **2000 cm** changes its pose and invokes shared objective completion (`8db1`).
- A **mission-objective saucer** is type 53. Its damage handler invokes completion
  when the fatal branch is taken (`c14c`). Its ordinary saucer machinery remains.
- A **portal** may award completion in addition to its movement/damage action.
- Setting an anchor's complete bit in the builder makes it optional/already done;
  it does not create a new completion award during play.

The special bit is type-specific. For orbs it enables gravity attraction; for
mindbenders it requires a planted-bomb lineage; for vehicles it restricts accepted
attack types; for crew it identifies the spy. It is not one universal “stronger”
or “hostile” flag.

## Portals

The original `xpqtbl` is at DS:`ee29`. Each row has colour, flash colour, action bits
and a signed damage/repair selector. A collision is acted on only for the
player-aircraft sentinel and while the player is not on foot (`c7ba`).

| Quality | Actual stored action |
| --- | --- |
| 1 | Return to the current base point |
| 2 | Random relocation |
| 3 | Return to base and apply 75 aircraft damage events |
| 4 | Fatal event |
| 5 | Return to base and complete objective |
| 6 | Return to base **and apply damage**, with no extra flash bit in the colour word |
| 7 | Repair without relocation |
| 8 | Complete objective without relocation |

Quality 6 deserves care: the manual's prose calls it a no-flash return, but its
stored fourth word is **1**, which the shared routine `c78e` handles as damage,
just like quality 3. The table and consumer agree; the prose description is incomplete.

Relocation at `217b` uses DS:`02a9` for the base point, or randomized horizontal
coordinates for the random action, and sets a new altitude. Do not interpret a
portal as directly containing arbitrary destination coordinates.

## Persistent worlds, aircraft and recovery

CORNCOB.BAT coordinates the menu and flight programs: menu selects state, flight
runs, menu resumes with results. World modifications are repacked to compact
records and saved with the campaign. Destroyed scenery, lost aircraft and remaining
aircraft form the persistent campaign, rather than each sortie recreating its map.
The already decoded `.THT` files show these changed worlds directly.

`3UNIV.DAT` is the shared working world passed between the DOS programs, not a
master scenario definition. The front end associates the continued world with a
pilot's numbered `THT/*.THT` campaign file; opening a fresh scenario or another
saved theatre replaces the working world. Direct engine use can change the
working file without updating `PILOT.SCR`, but this does not make its changes
universal across every pilot and future fresh scenario.
The [save-file relationship and handoff sequence](state-formats.md#how-the-save-files-fit-together)
explain how `PILOT.SCR`, `.THT` and `3UNIV.DAT` fit together.

The writer serializes 64 tile headers, the surviving compact object records and
a twelve-byte starting-position trailer. Persistent changes can therefore include:

- Destroyed or removed scenery and enemies, wreck-type replacements and craters.
- Completed-objective bits, including the flags set by generator, spy and portal rules.
- Parked/abandoned aircraft records, their locations and persistent variant/damage flags.
- Positions, coarse orientations, flags, velocities and timers of saved world objects.
- Mission-builder edits, enabled `UNIVBIN.DAT` imports and the `-e` world-copy tool.
- Sky/ground colours committed with H+1/H+2, and changes to the stored starting point.

This is not a complete memory snapshot. `encodeobj` (`F3DVEC.ASM:6997`) writes
position, three angle high bytes, flags, three velocity words, one timer word and
type. It does not write the ordinary live hit-point byte at +27, so nonfatal
object hit-point loss alone is not preserved by this format. Explicit wreck,
removal, damage-flag and objective-state changes can be preserved. The flush path
also excludes object types whose extended-property bit 0 is set: for example
explosion, puff and projectile effects. Palette hallucination offsets themselves
are not serialized; H+1/H+2 explicitly pack the selected base colours into the
world header.

The normal engine saves on a clean campaign exit (`3.ASM:11434`), and also before
the detailed-intelligence handoff (`3.ASM:3157`). H+1/H+2 update the in-memory
header immediately but do not themselves call DOS file writing. See the
[palette setter and save-path trace](gameplay.md#undocumented-controls-confirmed-in-the-shipped-342-engines).

Walking into another parked aircraft transfers its state into the player flight
state (`1e6a`) and preserves a previous aircraft if necessary. Damaged/crashed
planes and black experimental planes receive different ground-crew handling;
ordinary spare planes are not equivalent to an unlimited respawn counter.

The control-tower proximity helper `a610` uses the native distance helper's 1/64
scale, with tests at **5200** and **4200** units, about **3.328 km** and **2.688 km**.
These update near-base and ground-crew/recovery-related state. Final landing/home
status also depends on other flags and contact checks; being near any arbitrary
radar model is not the complete return-home rule.

Entering a tower doorway on foot within **2000 cm** raises the tower/report event
(`9887`). The return-from-report path can restore the retained aircraft and repair
state. A rescue van uses a separate close-range handoff and sets rescue status.
This is why returning on foot, returning with the aircraft, a low flyby, landing,
and rescue yield different outcomes and scores.

## Exact score formula

The native score calculator is **`ISCORE.EXE`, file offsets `4698–484f`**, normal
retail 3.42. It accepts a pointer to the same 130-byte statistics block used by
saved records. Original code, including Borland 32-bit multiplication/division,
is executed by 72 controlled score probes.

Let quality indices `q=1..8` refer to the displayed quality, and define:

```
primary = 15 * completed_objectives + fuel_or_tet_kills
          + sum(AAA_kills[q] + site_kills[q] + orb_kills[q])

danger = 10 + sum(q * (peak_AAA_count[q] + peak_KLA_count[q]))

raw = integer_divide(primary * danger * 5, 10)
      + sum(q * (KLA_kills[q] + vehicle_kills[q]))

score = integer_divide(raw * present_status_factor * sortie_status_factor,
                       planes_lost + 1)
```

The peak defence arrays are captured when weighted local defence totals exceed
previous maxima. They are not numbers of kills. The score therefore rewards doing
primary work in the presence of a more dangerous defence mix; secondary KLA/vehicle
kills are quality-weighted and added afterward.

The native status-factor tests have priority, not just independent contributions:

| Present-status test, in order | Factor |
| --- | ---: |
| Killed (`0002h`) | 2 |
| Captured (`0008h`) | 1 |
| Home (`0010h`) | 5 |
| Rescued (`0100h`) | 4 |
| Otherwise | 1 |

| Sortie-status test, in order | Factor |
| --- | ---: |
| Crash away from home (`0040h`) | 1 |
| Airborne ejection (`0020h`) | 1 |
| Crash at home (`0200h`) | 3 |
| Successful landing (`0004h`) | 5 |
| Otherwise | 1 |

Abort (`4000h`) returns score zero. The routine preserves legacy signed word
intermediates and 32-bit arithmetic; the formula is a readable explanation for
ordinary non-overflowing statistics, not permission to substitute unlimited-precision
math and assume every corrupted/extreme record will behave the same.

Important statistics offsets relative to this 130-byte block:

| Offset | Contents |
| --- | --- |
| `00` | Sortie/result flags |
| `02` | Relevant contacting/killing object type |
| `04` | Aircraft lost |
| `06` | Fuel/Tet kills |
| `08` | Eight KLA kill counts |
| `18` | Eight AAA kill counts |
| `28` | Eight vehicle kill counts |
| `38` | Eight site kill counts |
| `48` | Completed objectives |
| `4a` | Friendly losses |
| `4c` | Maximum aircraft damage |
| `4e` | Eight peak AAA counts |
| `5e` | Eight peak KLA counts |
| `72` | Eight orb kill counts |

Offsets here are hexadecimal. Site categories share some counters, so the display's
“Other Sites” does not imply a unique counter for each visual type.

## Medals: actual boundaries

Normal MOAG checks these at `1534a–15411`; Other Worlds does so at
`17d35–17dfc`. Both supplied builds have the same tested score boundaries:

| Award | Native condition |
| --- | --- |
| Bronze Star | **Score > 10,000**, through 20,000 |
| Silver Star | **Score > 20,000**, through 50,000 |
| Gold Star | **Score > 50,000**, through 100,000 |
| Stellar Cluster | **Score > 100,000** |
| Purple Heart | At least **27** maximum damage, home, no aircraft lost, no away-crash flag |
| Distinguished Service Cross | At least **five** objectives during the sortie |
| Service Award | Career sorties reach `(previous service awards + 1) * 10`, subject to survival |

The manual says 10,000/20,000/50,000/100,000 begin the score awards, and says “more
than 27” for the Purple Heart. The native comparisons differ at those exact
boundaries. Eighteen score-boundary probes exercise both editions. Theatre Combat,
Distinguished Flying Cross and Combat Master are checked by the preceding campaign
completion scan, not these score bands.

## Promotions differ between editions

Native probes isolate the promotion decision after campaign totals have been
prepared. They do not substitute a fictitious filesystem into the theatre scanner.

| Rank | Normal retail 3.42 | Other Worlds |
| --- | --- | --- |
| 2nd Lieutenant | Initial | Initial |
| 1st Lieutenant | At least one career objective | At least one career objective |
| Captain | At least **seven** career objectives | At least **seven** career objectives |
| Major | Earth-saved result flag `0400h` | At least 20 career objectives |
| Lt. Colonel | No promotion path in this decision | At least two theatres reaching 20 objectives |
| Colonel | No promotion path in this decision | At least three theatres reaching 20 objectives |
| General | No promotion path in this decision | All required theatres completed, with at least three required |

Existing higher ranks are not demoted by this decision. Unused higher-rank text
in the normal executable is therefore not proof that its normal promotion path
awards those ranks. Both builds promote to Captain at seven, despite the manual
listing eight. Other Worlds grants General in the all-required-theatres branch;
there is no additional separate secret-mission flag test in that branch. Any
prerequisite embodied in the required-theatre definitions is a separate question.

The underlying scan distinguishes required theatres, completed theatre records,
and newly completed campaigns for medal reporting. It also preserves resurrection
and pilot status. Every record field and all restrictions in that scanner are not
yet independently decoded.

## Ending, experimental aircraft and SRATS

The previously untraced **SRATS.EXE** has a confirmed gameplay call site.
At `41ff–424b`, the flight loop requires:

1. Aircraft flag **`2000h`**, the black experimental variant.
2. The height word at DS:`0e94` to reach the threshold at DS:`027c`.
3. The enabling flag at DS:`0aec`.

The theatre reader sets that threshold to **three fifths of atmosphere height**;
the height comparison uses the position word spanning bits 8..23, approximately
`Z >> 8` in the ordinary positive altitude range. This is an altitude-based ending
trigger, not a standard “all enemies dead” check.

After tearing down the normal display/timing state, the code calls `0d3b`.
That helper passes the literal filename **`srats.exe`** at DS:`0d13` to the DOS
program-launch wrapper. When it returns, the engine sets result bit **`0400h`**
and exits the sortie. Outcome selector `de9d` chooses panel **14**, “You have saved
Earth from alien attack!” That same result bit is what promotes the normal-game
pilot to Major. This connects the experimental-plane ascent, starfield sequence,
ending artwork and campaign promotion in actual code.

The command-line `-f` handler sets DS:`0aec` (`d7a4`). **The same flag also
unlocks throttle increases in the experimental aircraft** (`479c–47fe`). With
`2000h` set and `0aec` clear, the throttle-up input is ignored. Merely walking
into the aircraft therefore does not grant permission to fly it.

The normal menu supplies `f` through its conditional argument builder at
`112f2–11307`. The condition is the pilot's **Combat Master medal count** at
record offset `25eh`, read by `15e5d–15e6e`. That award is granted by the required-
theatre completion scan. Once it has been earned, the ordinary throttle-up key
works; there is no separate cockpit ignition sequence. Six native throttle probes
confirm the gate, including unaffected normal and reserve aircraft.

The menu also contains the test-flight orders: report to White Sands, climb as
high as possible, use the emergency rocket booster, then press Enter. This is
stored briefing text; the native ending condition above is the actual trigger.
The additional Enter press is not tested by that altitude branch.

Other ending panels are selected from flight status, not randomly: safe landing,
rescue, crash landing, fatal crash, missing, capture, friendly-fire discipline and
hazard-specific death. The handler at `de59` distinguishes AA shell (type 22), KLA
(type 24), crater (type 32) and other fatal contact for the corresponding panels.
Friendly losses can add the military-police panel after a returned sortie.
