Autopilot · write-up
Regenerating embedded autopilot data
Run manually from the repository root:
python xenon_tools/generate_native_assets.py
python xenon_tools/generate_native_assets.py --check
The first command regenerates all six outputs. The second reports stale files
without writing anything. --output-dir PATH writes a review copy elsewhere.
Paths default relative to the script, so invocation from another directory works.
Generation is deliberately not connected to CMake or runtime initialization.
Commit the generated files together with changes to their sources.
To regenerate the source tile-map JSON from captured RAM, run
python xenon_tools/level_tile_maps.py. Its default capture directory is
level_capture beside that script; pass another capture directory as the
positional argument when working elsewhere. Then run the native generator
above. The Level 4 first-boss wall is authored as 128 cells at columns 6–13,
rows 143–158. Captures immediately before and after its defeat show those
words changing to zero together. The C map session opens this group when the
main head enters its destruction procedure, and RAM restore also recognizes
an already-cleared wall.
| Authoritative source | Generated file in src/autopilot |
|---|---|
| assets/xenon_level_maps.json | xenon_autopilot_map_data.c |
| assets/xenon_level2_strategy.json | xenon_autopilot_level2_data.h |
| xenon_tools/xenon_symbols.py composite animation records | xenon_autopilot_envelope_data.h |
| xenon_tools/xenon_symbols.py Level 2 homing records | xenon_autopilot_homing_tables.h |
| Game $9C80 add-half/truncate sine formula, mirrored in autoplay._sine_table_value | xenon_autopilot_sine_data.h |
| assets/spriteatlas_shared and spriteatlas_level_01 through 05 (.raw/.idx) | xenon_autopilot_collision_data.h |
The Level 2 include contains the backbone, phase boundaries, screen bands and homing gates. It preserves waypoint order, including deliberate backtracking. The generator rejects unsupported phase counts and boss-eye order changes rather than silently pretending the native state machine supports them. Narrative JSON objectives and replan descriptions remain documentation, not executable policies.
Existing generate_native_level_maps.py and generate_native_envelope_data.py
remain usable individually; the aggregate command reuses their renderers.
These generators need checked-in JSON/Python data and renderer atlas pairs, not a running game or ReVa.
Sine generation preserves the asymmetric negative half and does not rely on
runtime C math-library rounding.
Embedded-data audit
The large map blob, animation/homing records, sine table and JSON-derived Level 2 mission tables now have repeatable generators. Small authored algorithm tables remain readable C: Level 1's six corridor waypoints, Level 3's named gate order, eight-direction vectors, player hulls, and candidate masks/search offsets. They are policy/algorithm definitions rather than copies of external asset blobs. They should be edited as code, with behavioral validation; no generator reads C back into itself as a supposed source of truth.
The native environment now uses generated collision masks and assembles mutable wall rasters/navigation graphs from C map state. Future embedded assets must join this manual command with explicit source paths. See AUTOPILOT_NATIVE_ENVIRONMENT.MD.
Replay page explanations from C comments
python xenon_tools/generate_replay_descriptions.py
python xenon_tools/generate_replay_descriptions.py --check
This separate generator is the one that reads C. Its output is documentation, not
data the autopilot uses, so it does not contradict the audit above. The replay page
(/replay/) explains each field of the Level mission card, the Configuration card
and each driver stage. Those texts are written once, as marked comments (/*! ... */)
on the struct members and enumerators they explain:
| Source | What it explains |
|---|---|
XapLevel1MissionState to XapLevel5MissionState (xenon_autopilot_mission.h) |
Level mission card |
XapSessionConfig (xenon_autopilot_session.h), XapDecisionConfig (xenon_autopilot_decision.h) |
Configuration card |
XapArbitrationStage (xenon_autopilot_driver.h) |
the Decision card's "→ stage" rows |
It writes src/autopilot/xenon_autopilot_replay_fields.h, which
xenon_autopilot_replay.c includes:
- the JSON writers for those cards' fields;
- name functions for the enums they use, plus the hazard kinds, tactics and weapons;
- the texts, returned by
xrp_describe().
So a field, its JSON key and its explanation are written in one place. Tags in a marked comment set the JSON key and how the value is written or shown; the script's docstring lists them.
Every member of those structs and every driver stage needs a marked comment
(/*! @hidden */ keeps a member off the page). The generator stops otherwise. The
generated header also copies each described struct's layout, so after a struct
changes the build fails in xenon_autopilot_replay.c until the generator runs:
size of array 'xrp_regenerate_replay_fields_<struct>' is negative.
test_replay_descriptions.py checks that the output is current.
A text never copies a measurement from the code; it names it in braces, and the
generator writes in the value:
* an integer enum constant or #define in src/autopilot, e.g. {WALL_ATTACK_BUDGET};
* an element of a numeric static const table, e.g. {boundaries[0]} or
{backbone[-1][1]};
* a count, {len(backbone)};
* sums and differences of these.
A name defined differently in two files is qualified with its file ({level5_tank.LEFT_GUARD}).
An unknown or ambiguous name stops the generator, and a changed constant makes the
output stale, so the test fails until the generator runs again. A value the code writes
inline gets a name in its level file first; for example, Level 3's intercept rows became
LEVEL3_INTERCEPT_MIN_SCREEN_Y and _MAX_SCREEN_Y. Only meanings stay written as digits:
state values, "-1: none", level numbers.
The replay overlay's zones, lines and points follow the same rule. Each level's file
lists its own in xap_levelN_geometry(), from the same constants its mission code uses
(Level 5 from its main, tank and final-ship files), and the replay only calls
xap_level_geometry().
Keep the texts true when the behaviour changes. No tool can notice that a text has gone wrong.