Xenon 2

Autopilot · write-up

Regenerating embedded autopilot data

xenondoc/AUTOPILOT_GENERATED_ASSETS.MD · 6 KB · updated 2026-10-03

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.