Make RustRaptor the repository root and track its playable bundle
This commit is contained in:
1 parent
4b1edefeec
commit
8f459796b1
9860 files changed
+21084
-61019
No files matched your search
@@ -1,52 +0,0 @@
|
||||
# RaptorReplica
|
||||
|
||||
RustRaptor is the default game in `../RustRaptor/`; see
|
||||
[Rust implementation and validation](../RustRaptor/docs/rust-rewrite.md),
|
||||
[modding API](../RustRaptor/docs/modding-api.md) and
|
||||
[optional HD art](../RustRaptor/docs/hd-art.md).
|
||||
The historical C++ reference is described below.
|
||||
|
||||
A faithful, playable replica of **Raptor: Call of the Shadows** (Cygnus
|
||||
Studios, 1994) running the reconstructed original engine on Linux, with the
|
||||
original game data extracted into a modern, editable asset tree.
|
||||
|
||||
## Quick start
|
||||
|
||||
```sh
|
||||
./play.sh --engine legacy
|
||||
```
|
||||
|
||||
Builds the engine on first run, then launches fullscreen. Saves and
|
||||
`SETUP.INI` live in `~/.local/share/Raptor/` — nothing in the repo is
|
||||
written to.
|
||||
|
||||
## Repository layout
|
||||
|
||||
| Path | What it is |
|
||||
|-------------|------------|
|
||||
| `raptor/` | Vendored engine source (GPLv2+), trimmed to a Linux-only build. Patches against upstream are listed in [upstream-patches.md](upstream-patches.md). |
|
||||
| `data/` | The asset tree the engine loads: one `INDEX` catalog plus category directories (`music/`, `sprites/`, ...). Derived from the copyrighted originals — **gitignored, never commit**. See [asset-pipeline.md](asset-pipeline.md). |
|
||||
| `tools/glbextract.py` | Cracks the original `.GLB` archives into `data/`. See [glb-format.md](glb-format.md). |
|
||||
| `docs/` | This documentation. |
|
||||
| `play.sh` | One-command launcher (build if needed, run from `data/`). |
|
||||
| `PLAN.md` | Project status and roadmap. |
|
||||
|
||||
## Documentation index
|
||||
|
||||
- [architecture.md](architecture.md) — how the engine starts up and loads assets
|
||||
- [glb-format.md](glb-format.md) — the original archive format and its cipher
|
||||
- [asset-pipeline.md](asset-pipeline.md) — extraction, categories, the INDEX catalog
|
||||
- [building.md](building.md) — dependencies, building, config and save paths
|
||||
- [upstream-patches.md](upstream-patches.md) — every local diff against the vendored engine
|
||||
|
||||
## Status
|
||||
|
||||
Playable end to end: title/menu, hangar, store, ship computer, all mission
|
||||
phases, original OPL2 soundtrack and Sound Blaster SFX through PipeWire/ALSA.
|
||||
Verified on this machine at 1920x1080.
|
||||
|
||||
## Legal
|
||||
|
||||
- `raptor/` is GPLv2+; this project inherits GPL for everything built on it.
|
||||
- Game data is copyrighted. `data/` is derived from the user's own purchased
|
||||
copy and is never committed.
|
||||
@@ -1,74 +0,0 @@
|
||||
# Engine architecture
|
||||
|
||||
The engine is the reconstructed original Raptor source (nukeykt's port,
|
||||
vendored under `raptor/`), patched for loose-file assets and Linux. All game
|
||||
code is C++11, built with CMake + SDL2 + ALSA.
|
||||
|
||||
## Startup flow
|
||||
|
||||
`main()` in `raptor/src/rap.cpp`:
|
||||
|
||||
1. `RAP_InitLoadSave()` — resolves the user data dir via
|
||||
`SDL_GetPrefPath("", "Raptor")` (`~/.local/share/Raptor/`). Saves,
|
||||
`SETUP.INI` and high scores live there; the repo stays read-only.
|
||||
2. **Data detection** — `GLB_SlotItems(n)` (from `src/glbapi.cpp`) parses
|
||||
`data/INDEX` and counts assets per archive slot:
|
||||
- slot 1 present -> `gameflag[0]` (mission pack 1)
|
||||
- slot 2 present -> `gameflag[1]` (mission pack 2)
|
||||
- slots 3 and 4 present -> `gameflag[2]`, `gameflag[3]`
|
||||
- slot 2 or 3 present -> `reg_flag = 1` (registered version)
|
||||
- slot 0 missing or no packs -> fatal "data files NOT FOUND"
|
||||
3. `GLB_InitSystem(RAP_GetPath(), 6, 0)` — loads the catalog, builds one
|
||||
item-descriptor array per non-empty slot (name + size per item).
|
||||
4. Sound, input, graphics init; palettes/fonts/UI load from the catalog.
|
||||
5. Main loop dispatches between intro, menu, hangar, store, ship computer
|
||||
and mission gameplay.
|
||||
|
||||
## Asset system
|
||||
|
||||
Assets are addressed by **handles**: `(archive slot << 16) | item index`.
|
||||
The constants live in `src/fileids.h` (e.g. `FILE100_PALETTE_DAT = 0x10000`
|
||||
= slot 1, item 0). At runtime:
|
||||
|
||||
- `data/INDEX` maps each handle to a file: `<hex id> <size> <path>`, sorted
|
||||
by id. `GLB_LoadCatalog()` parses it once; `GLB_CatFind()` binary-searches
|
||||
it.
|
||||
- `GLB_GetItem / GLB_LockItem / GLB_CacheItem` allocate and read the file
|
||||
through a VM/cache layer (`src/vmemapi.cpp`) — locked items stay resident,
|
||||
others are discarded under memory pressure.
|
||||
- `GLB_GetItemID(name)` resolves a NUL-terminated item name to a handle by
|
||||
scanning the per-slot descriptor arrays.
|
||||
|
||||
The "GLB" API names are historical — no `.GLB` archive is read at runtime.
|
||||
|
||||
## Archive slots
|
||||
|
||||
| Slot | Items | Content |
|
||||
|------|--------|---------|
|
||||
| 0 | 230 | Text files, help, credits, music (`*_MUS`), SFX (`*_FX`), `GENMIDI_OP2`, `DMXGUS_INI` |
|
||||
| 1 | 1538 | Fonts, palettes, UI pictures, SWD window layouts, animations |
|
||||
| 2 | 1708 | Mission pack 2 assets (tiles, maps, sprites, demos) |
|
||||
| 3 | 1321 | Mission pack 3 assets |
|
||||
| 4 | 1559 | Mission pack 4 assets |
|
||||
| 5 | 92 | Extra data (not referenced by `fileids.h`) |
|
||||
| 6 | 1 | Uninstaller leftover |
|
||||
|
||||
## Subsystems
|
||||
|
||||
- **Graphics** (`gfxapi.cpp`, `i_video.cpp`): original 320x200 VGA-mode
|
||||
renderer, palette-faded, scaled to the desktop by SDL. SWD (`swdapi.cpp`)
|
||||
is the window/widget system driving every menu.
|
||||
- **Music** (`i_oplmusic.cpp`, `opl3.cpp`): the original OPL2 FM soundtrack,
|
||||
emulated sample-accurately; `*_MUS` items are OPL register captures.
|
||||
- **SFX** (`fx.cpp`, `dspapi.cpp`): Sound Blaster digital effects, mixed
|
||||
through ALSA/PipeWire.
|
||||
- **MIDI** (`mpualsa.cpp` etc.): optional external MIDI path using
|
||||
`GENMIDI_OP2`/`DMXGUS_INI`.
|
||||
- **Movies** (`movie.cpp`): intro cinematics described by `*_MDS` scripts.
|
||||
- **Demos** (`demo.cpp`): attract-mode gameplay recordings (`*_REC`).
|
||||
|
||||
## Exit path
|
||||
|
||||
`ShutDown()` (`rap.cpp`) closes the window, frees the asset cache and quits.
|
||||
The upstream DOS-style ENDOOM farewell screen was removed — quitting exits
|
||||
immediately. See [upstream-patches.md](upstream-patches.md).
|
||||
@@ -1,87 +0,0 @@
|
||||
# Asset pipeline
|
||||
|
||||
`data/` is a plain directory tree the engine reads directly. It is derived
|
||||
from the copyrighted original archives, so it is **gitignored** — restore it
|
||||
with the extractor, never commit it.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
data/
|
||||
├── INDEX # catalog: "<hex asset id> <size> <relative path>", sorted by id
|
||||
├── music/ # *_MUS OPL2 register captures
|
||||
├── sound/ # *_FX, *_SFX Sound Blaster digital samples
|
||||
├── sprites/ # *_PIC, *_AGX pictures and animated sprites
|
||||
├── tiles/ # *_BLK, *TILES level tile graphics
|
||||
├── maps/ # *_MAP mission maps
|
||||
├── fonts/ # *_FNT bitmap fonts
|
||||
├── text/ # *_TXT, *_DIZ, *_DIX, *_ERR, *_HELP
|
||||
├── ui/ # *_SWD menu/window layouts
|
||||
├── movies/ # *_MDS cinematic scripts
|
||||
├── demos/ # *_REC attract-mode recordings
|
||||
└── misc/ # palettes, GENMIDI_OP2, DMXGUS_INI, item tables, labels
|
||||
```
|
||||
|
||||
Categories are derived from the GLib item-name suffixes (see `classify()`
|
||||
in `tools/glbextract.py`).
|
||||
|
||||
## INDEX catalog
|
||||
|
||||
One line per asset:
|
||||
|
||||
```
|
||||
<hex id> <size in bytes> <category>/<filename>
|
||||
10005 768 sprites/N0_PIC
|
||||
```
|
||||
|
||||
- `id` is the engine handle: `(archive slot << 16) | item index`, matching
|
||||
the constants in `raptor/src/fileids.h`. Example: `10005` = slot 1,
|
||||
item 5 = `FILE105_N0_PIC`.
|
||||
- Lines are sorted by id; the engine binary-searches them
|
||||
(`GLB_CatFind` in `raptor/src/glbapi.cpp`).
|
||||
- `size` is trusted by the engine; the extractor guarantees it matches the
|
||||
file on disk.
|
||||
|
||||
## Regenerating data/
|
||||
|
||||
From a copy of the original game (the seven `FILE000N.GLB` archives):
|
||||
|
||||
```sh
|
||||
tools/glbextract.py extract -o data FILE0000.GLB ... FILE0006.GLB
|
||||
```
|
||||
|
||||
The extractor decrypts everything, classifies by name suffix, resolves name
|
||||
collisions with numeric suffixes (`unnamed.3` -> `unnamed.3.1` when a second
|
||||
archive contributes the same name to the same category — suffixes are global
|
||||
per category so catalog sizes always match the file on disk), and writes the
|
||||
catalog.
|
||||
|
||||
`tools/glbextract.py list FILE0000.GLB` dumps an archive's descriptor table
|
||||
without writing anything.
|
||||
|
||||
## Editing content
|
||||
|
||||
Because the engine reads loose files through the catalog:
|
||||
|
||||
- **Replace an asset**: overwrite the file, keep the path. If the size
|
||||
changes, update the size in `INDEX` (the engine allocates and reads by
|
||||
that number).
|
||||
- **Add a mission pack**: add files under a category, append catalog lines
|
||||
with unused ids in the desired slot (slots 2-4 are the packs; slot ids are
|
||||
`0x2xxxx`, `0x3xxxx`, `0x4xxxx`). Presence of slots 2/3 flips the engine
|
||||
into registered mode.
|
||||
- **Rename**: never rename an existing catalog file without updating its
|
||||
`INDEX` line — the engine looks assets up by id, but item *names* (used by
|
||||
`GLB_GetItemID`) come from the path's basename.
|
||||
|
||||
## Engine-side contract
|
||||
|
||||
`raptor/src/glbapi.cpp` is the only code that touches `data/`:
|
||||
|
||||
- `GLB_LoadCatalog()` — parses `INDEX` (working directory first, then the
|
||||
directory the binary was launched from).
|
||||
- `GLB_SlotItems(slot)` — counts a slot's assets (used for pack detection).
|
||||
- `GLB_CatFind(id)` — binary search.
|
||||
- `GLB_Load()` — `fopen` + `fread` of the cataloged file.
|
||||
|
||||
Payloads are stored pre-decrypted; the runtime never runs the GLB cipher.
|
||||
@@ -1,80 +0,0 @@
|
||||
# Building and running
|
||||
|
||||
For RustRaptor, use `./play.sh` or `./RustRaptor/run.sh`. Rust 1.88+,
|
||||
Cargo and SDL2 development libraries are required. The launcher builds in release
|
||||
mode and sets the local SDL2 library path. See [Rust build/test commands and
|
||||
separate profile locations](../RustRaptor/docs/rust-rewrite.md). The sections below describe the
|
||||
retained C++ engine; its launcher now rebuilds the target before each launch.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Linux with X11 (developed and verified on Arch, Cinnamon)
|
||||
- GCC or Clang with C++11
|
||||
- CMake >= 3.10
|
||||
- SDL2 development libraries (a user-local build in `~/.local` works —
|
||||
`play.sh` extends `LD_LIBRARY_PATH` accordingly)
|
||||
- ALSA (PipeWire's ALSA bridge is what this machine actually uses)
|
||||
|
||||
## Build and run
|
||||
|
||||
```sh
|
||||
./play.sh --engine legacy
|
||||
```
|
||||
|
||||
`play.sh` checks `data/INDEX`, builds `raptor/build/bin/raptor` on first run
|
||||
(`cmake -DCMAKE_BUILD_TYPE=Release`, then `make -j$(nproc)`), and executes
|
||||
the engine with `data/` as the working directory — the engine resolves the
|
||||
catalog and assets relative to the CWD.
|
||||
|
||||
Manual equivalent:
|
||||
|
||||
```sh
|
||||
cmake -E make_directory raptor/build
|
||||
cmake -S raptor -B raptor/build -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build raptor/build -j$(nproc)
|
||||
cd data && ../raptor/build/bin/raptor
|
||||
```
|
||||
|
||||
Targets:
|
||||
|
||||
- `raptor` — the game
|
||||
- `raptorsetup` — the original text-mode setup tool (audio/input settings)
|
||||
|
||||
## Runtime files
|
||||
|
||||
| File | Location |
|
||||
|------|----------|
|
||||
| `SETUP.INI` | `~/.local/share/Raptor/` |
|
||||
| Save games | `~/.local/share/Raptor/` |
|
||||
| High scores | `~/.local/share/Raptor/` |
|
||||
|
||||
Useful `SETUP.INI` settings (created on first exit): `fullscreen=1` for
|
||||
borderless 1080p, `aspect_ratio_correct`, `txt_fullscreen` (obsolete here —
|
||||
the text-mode exit screen was removed).
|
||||
|
||||
## Command-line options
|
||||
|
||||
The engine accepts the original demo flags:
|
||||
|
||||
```sh
|
||||
raptor REC demo.dmo # record a demo
|
||||
raptor PLAY demo.dmo # play one back
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **"All game data files NOT FOUND"** — `data/INDEX` missing or empty;
|
||||
restore the asset tree (see [asset-pipeline.md](asset-pipeline.md)).
|
||||
- **No audio** — check the ALSA/PipeWire sink; the original mixer expects a
|
||||
working default ALSA device. If a session-manager glitch leaves the wrong
|
||||
card profile active, `pactl set-card-profile` back to the real one.
|
||||
- **Silent early exit** — since the local patch, engine errors print to
|
||||
stderr before exiting; run from a terminal and read the message.
|
||||
|
||||
## Build layout
|
||||
|
||||
- `raptor/CMakeLists.txt` — Linux-only (Windows/Android/OSX support was
|
||||
stripped; see [upstream-patches.md](upstream-patches.md))
|
||||
- `raptor/build/` — gitignored build tree
|
||||
- `raptor/include/textscreen/` — text UI library (used by `raptorsetup`)
|
||||
- `raptor/include/TinySoundFont/` — SF2 MIDI fallback synth
|
||||
@@ -0,0 +1,138 @@
|
||||
# Original campaign validation
|
||||
|
||||
Local checks on 2026-10-10 reached mission completion for all 27 original waves
|
||||
at each of the four difficulties. These 108 runs are assisted inspection fixtures,
|
||||
not ordinary player victories and not per-wave C++ parity proofs. Original game
|
||||
data, reference recordings, profiles and CSV reports remain under ignored local
|
||||
directories.
|
||||
|
||||
The separate, complete `DEMO1G1_REC` comparison matches the rebuilt C++ engine
|
||||
across all 1,889 available simulation steps. Captured fields are the RNG seed,
|
||||
player coordinates/banking/shields/credits, ordered enemy asset/position/health/
|
||||
animation/movement coordinates, and ordered projectile asset/position/hostility.
|
||||
The recording starts in sector 1 wave 3 and ends before the boss. Inventory,
|
||||
internal AI counters, terrain damage, effects, audio samples and presentation are
|
||||
outside this gameplay trace; this does not establish full-game parity.
|
||||
|
||||
## Assisted campaign fixture
|
||||
|
||||
`tools/campaign_check.rs` gives the pilot the original arsenal and five mega
|
||||
bombs, fires continuously, aims horizontally at visible enemies while preferring
|
||||
bosses, cycles specials every 180 simulation steps and presses the bomb control
|
||||
every 480 steps. Before each step it replenishes energy and super-shield values
|
||||
to 100 each. Inputs still use the game input path. Enemy definitions, damage,
|
||||
spawn timing, RNG and mission outcomes are not replaced. A run stops at the
|
||||
reported outcome or its 12,000-step inspection limit.
|
||||
|
||||
The final run visited every spawn record, observed every expected boss instance
|
||||
and finished with no remaining enemies. Its local CSV is
|
||||
`../.reference/campaign-validation-final.csv` (SHA-256
|
||||
`c4e1fc0ca1199efd2a9e72ceb7ebbe3d71325b517cb9829995138ead99486901`).
|
||||
|
||||
| Difficulty | Completed waves | Steps per wave | Enabled spawns | Boss instances seen | Nonpositive last health | Positive last health |
|
||||
| --- | ---: | --- | ---: | ---: | ---: | ---: |
|
||||
| 0 | 27 | 4,601–4,873 | 3,751 | 88 | 53 | 35 |
|
||||
| 1 | 27 | 4,607–4,873 | 3,751 | 88 | 53 | 35 |
|
||||
| 2 | 27 | 4,651–5,079 | 6,259 | 88 | 37 | 51 |
|
||||
| 3 | 27 | 4,650–5,082 | 8,668 | 88 | 36 | 52 |
|
||||
|
||||
The 108 runs cover 513,003 simulation steps, 22,429 enabled spawns and 352 boss
|
||||
appearances. Boss retirement counts classify the health observed immediately
|
||||
before removal: 179 nonpositive and 173 positive. Positive health commonly
|
||||
indicates a boss leaving the screen; damage and removal within the same step
|
||||
can also affect this classification. These counts are not an exact death-event
|
||||
audit. The harness deliberately allows the original wave completion condition
|
||||
after all spawned ships have died or left, rather than requiring every boss to
|
||||
be killed.
|
||||
|
||||
Additional opt-in tests exercised all 15 original player weapon definitions with
|
||||
live mission targets and observed emitted projectiles. This verifies availability
|
||||
and basic firing paths, not exact weapon effects. Progression checks exercise
|
||||
training's four-wave route, each normal 27-wave route, difficulty promotion,
|
||||
campaign resets and the training reward. They call the progression API directly
|
||||
and bypass combat; end cinematics and menu transitions need separate checks.
|
||||
|
||||
Run the four original-data integration tests from the `RustRaptor/` directory:
|
||||
|
||||
```sh
|
||||
RAPTOR_CAMPAIGN_REPORT=../.reference/campaign-validation-final.csv \
|
||||
cargo test --release --no-default-features --test campaigns -- --ignored --nocapture
|
||||
```
|
||||
|
||||
Set `RAPTOR_TEST_DATA` if the original catalog is elsewhere. Set
|
||||
`RAPTOR_REFERENCE_DEMO1` to use a different full C++ reference trace. Without
|
||||
`--ignored`, these tests do not require or distribute original assets.
|
||||
|
||||
The reporting helper is also a standalone Rust CLI. Build the library, supply
|
||||
its resulting `libraptor_replica-*.rlib` path to `rustc --extern`, and retain the
|
||||
dependency directory:
|
||||
|
||||
```sh
|
||||
cargo build --release --no-default-features --lib
|
||||
rustc --edition=2024 tools/campaign_check.rs \
|
||||
--extern raptor_replica=target/release/deps/libraptor_replica-HASH.rlib \
|
||||
--extern anyhow=target/release/deps/libanyhow-HASH.rlib \
|
||||
-L dependency=target/release/deps -o ../.reference/campaign-check
|
||||
../.reference/campaign-check ../data
|
||||
```
|
||||
|
||||
Its optional second argument selects one difficulty, for example
|
||||
`../.reference/campaign-check ../data 3`.
|
||||
|
||||
## Reproduce the complete demo comparison
|
||||
|
||||
Rebuild the retained C++ source before capturing a reference. The local debug
|
||||
build used `../.reference/cpp-rng-build`; use the available CMake executable if it
|
||||
is outside `PATH`. Prepare an isolated legacy profile with `raptorsetup`, or
|
||||
copy an existing `SETUP.INI` into `../.reference/full-demo-profile/Raptor/` before
|
||||
running the following commands. The original executable requires `data/` as its
|
||||
working directory. A complete reference takes about 81 seconds at 70/3 Hz.
|
||||
|
||||
```sh
|
||||
cmake --build ../.reference/cpp-rng-build --parallel 4
|
||||
(
|
||||
cd ../data
|
||||
XDG_DATA_HOME="$PWD/../.reference/full-demo-profile" \
|
||||
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
|
||||
RAPTOR_REFERENCE_TRACE="$PWD/../.reference/cpp-demo1-full.jsonl" \
|
||||
RAPTOR_REFERENCE_TICKS=1889 \
|
||||
../.reference/cpp-rng-build/bin/raptor PLAY demos/DEMO1G1_REC
|
||||
)
|
||||
cargo run --no-default-features -- \
|
||||
--replay DEMO1G1_REC --ticks 1889 --trace ../.reference/rust-demo1-full.jsonl
|
||||
python3 tools/compare_replays.py \
|
||||
../.reference/cpp-demo1-full.jsonl ../.reference/rust-demo1-full.jsonl
|
||||
```
|
||||
|
||||
The comparator reports `Identical gameplay snapshots across 1889 simulation
|
||||
steps.` and rejects unequal lengths, repeated tick numbers or the first differing
|
||||
field. The local C++ trace SHA-256 is
|
||||
`6f0378dbe4d819b44de86dc22822d2e70cb65546e6aed89dd45a44a8eb3d02fe`.
|
||||
|
||||
This comparison found and repaired beam damage before player-follow artwork,
|
||||
raw recorded positions beyond ordinary input bounds, low-shield equipment loss
|
||||
and automatic selection cycling, and the last authored kamikaze leg's transition
|
||||
into the player chase. Portable synthetic regressions in `tests/gameplay.rs`
|
||||
cover those rules without original pixels or recordings.
|
||||
|
||||
## Remaining gameplay validation
|
||||
|
||||
The catalog inspection found no wave completion blocker under the assisted
|
||||
fixture. Source inspection also found and repaired three original weapon rules,
|
||||
with four additional portable regressions:
|
||||
|
||||
- Energy Grab damages only airborne ships by one point, drains the original
|
||||
resistance counter and disables susceptible enemies' firing. Immunity and
|
||||
continued suppression after animation updates have separate coverage.
|
||||
- Ground Bomb damages terrain before checking a ground enemy and applies the
|
||||
original left/up/right neighbor effects, including the preceding-row pass.
|
||||
One test verifies simultaneous terrain and ground-enemy damage.
|
||||
- Mega Bomb applies its original 50 enemy damage, clears hostile shots and
|
||||
subtracts 20 health only from visible destructible terrain. A test covers
|
||||
distant tiles, inert terrain, destroyed tiles and the final visible tile.
|
||||
|
||||
A longer record set, C++ traces through bosses and departure/death sequences,
|
||||
ordinary pilot playthroughs, all weapon effects, shield recharging and complete
|
||||
save/menu/cinematic routes are still needed for full acceptance. The complete
|
||||
demo match and assisted campaign coverage should be retained as regression
|
||||
evidence while those gaps are resolved.
|
||||
@@ -0,0 +1,116 @@
|
||||
# Original first mission: ordinary-input victory
|
||||
|
||||
The opt-in `tests/first_mission.rs` check completes `base:sector_1_wave_1`
|
||||
through its original boss with a fresh pilot on difficulties 1 and 2. It uses
|
||||
the locally installed original catalog and the production `World::step` path.
|
||||
Both runs start with the default forward guns, shield 100, no super shield,
|
||||
no special weapon, and zero credits. No extra equipment is required.
|
||||
|
||||
Run it with:
|
||||
|
||||
```sh
|
||||
RAPTOR_TEST_DATA=/absolute/path/to/data cargo test --release --no-default-features --test first_mission -- --ignored --nocapture
|
||||
```
|
||||
|
||||
Without `RAPTOR_TEST_DATA`, it reads this checkout's `../data/` directory. The
|
||||
test is ignored in the default suite because original game data is not shipped
|
||||
in Git. It writes local results to `../.reference/`, which is also ignored.
|
||||
|
||||
## Recorded results
|
||||
|
||||
Local validation on 2026-10-10 produced:
|
||||
|
||||
| Difficulty | Boss spawned | First visible alive | Boss killed / reward paid | Complete | Shield remaining | Final credits | Confirmed kills | Positive-health retirements |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| 1 | 4,441 | 4,487 | 4,583 | 4,644 | 80 | 42,775 | 118 | 12 |
|
||||
| 2 | 4,441 | 4,487 | 4,703 | 4,764 | 65 | 59,025 | 161 | 37 |
|
||||
|
||||
Step counts use the original 70/3 Hz simulation. Both runs visit all 299
|
||||
cataloged spawn entries; the original difficulty flags decide which entries
|
||||
actually spawn. The boss is `base:enemy_0_28`, with an original reward of 3,000
|
||||
credits. Its last observed health before removal was 0 on difficulty 1 and
|
||||
−3 on difficulty 2. The test asserts that its death pays the reward, that every
|
||||
eligible boss enters the playfield alive and is killed, and that the surviving
|
||||
player reaches `Outcome::Complete`.
|
||||
|
||||
The healthy departure cue plays at timer 40 (steps 4,603 and 4,723), pauses
|
||||
ordinary controls, and starts the forced flight offscreen. At timer 24
|
||||
(steps 4,619 and 4,739), the surviving ship remains drawable. It ends the
|
||||
departure at logical y = −160 before completing. The test checks these
|
||||
milestones and records every countdown step; it does not substitute a direct
|
||||
victory command or terminate immediately after the boss kill.
|
||||
|
||||
A confirmed kill requires an entity to be observed with health at or below zero
|
||||
before removal. An entity removed after a positive-health observation is kept
|
||||
separately and never counted as a confirmed kill. That count is conservative
|
||||
around a possible same-step collision death; it is not an all-enemies kill
|
||||
claim. Mission completion alone cannot satisfy the boss-kill assertion.
|
||||
|
||||
The local catalog's `INDEX` SHA-256 was
|
||||
`4ce78e5e638b6603114dd233ceaa41f386c0963ba4aa02ecf88ce0215b096681`.
|
||||
|
||||
## Controller and assistance limits
|
||||
|
||||
The controller supplies only normal keyboard direction values (`dx`, `dy`
|
||||
each −1, 0 or 1) and a held fire button. Production acceleration, speed limits,
|
||||
banking, shot cooldowns, collisions, incoming damage, rewards and RNG all run
|
||||
normally. The actual playthrough never uses absolute positioning, scripted
|
||||
commands, bombs, shield refills, inventory grants or direct gameplay-state
|
||||
edits after `World::new`.
|
||||
|
||||
For target selection, it leads visible enemies using their current observed
|
||||
motion and gives the boss priority. To choose movement, it evaluates nine
|
||||
direction pairs in disposable cloned worlds over 12 normal simulation steps:
|
||||
three steps holding that direction and nine steps releasing movement. It
|
||||
penalizes predicted damage, then balances aiming, lower-screen positioning and
|
||||
distance from hostile shots. Cloned predictions are discarded; they cannot
|
||||
change the live world's state or RNG.
|
||||
|
||||
This is an automated viability proof with full simulation-state knowledge and
|
||||
precise timing. It demonstrates that the implemented first mission is winnable
|
||||
with ordinary movement and starter equipment. It does not measure human
|
||||
difficulty, input-device usability, graphics quality, or audio accuracy.
|
||||
|
||||
## Replay artifacts and reproducibility
|
||||
|
||||
For each difficulty, the test generates:
|
||||
|
||||
- `../.reference/first-mission-fresh-difficulty-N.dem`: the original 12-byte demo
|
||||
record layout, with a sector/wave header, button bytes, observed player
|
||||
position and bank. It includes the complete boss encounter and ends at the
|
||||
flyby cue, before stats-stage forced departure movement: 4,603 and 4,723
|
||||
frames respectively. The first record is the first completed simulation step.
|
||||
- `...-controls.json`: the actual keyboard direction and fire inputs per step.
|
||||
- `...-report.json`: starting pilot, boss observations and deaths, rewards,
|
||||
conservative retirement classification, shield changes and periodic states.
|
||||
- `....jsonl`: every production reference snapshot, including tick, shared RNG,
|
||||
player state, ordered enemies and ordered projectiles.
|
||||
|
||||
The test independently decodes and replays both recordings from an identical
|
||||
fresh pilot. The full keyboard log must reproduce every reference snapshot
|
||||
through `Outcome::Complete`: all 4,644 and 4,764 steps. Legacy demo playback
|
||||
uses recorded absolute positions and banking, and must reproduce every
|
||||
snapshot through the defeated boss and flyby cue: 4,603 and 4,723 steps.
|
||||
The winning controller run itself uses keyboard movement.
|
||||
|
||||
The retained C++ engine records demos before its stats-stage automatic
|
||||
departure movement and skips that movement during playback. End-of-step
|
||||
coordinates therefore stop being equivalent after the flyby cue. A full
|
||||
end-of-step demo showed its first divergence at step 4,605 in player projectiles
|
||||
and RNG. Keeping the legacy-format recording to the verified prefix preserves
|
||||
that record-phase distinction; the full keyboard replay verifies departure.
|
||||
|
||||
The recorded demo SHA-256 values are:
|
||||
|
||||
| Difficulty | SHA-256 |
|
||||
| --- | --- |
|
||||
| 1 | `c5385b30c600f91dab9c0f5009a24204f114626b7f655d18687799e834da34ef` |
|
||||
| 2 | `8d245a537812535ebf0e8e66594a73afef6ff01a14581b80332b095d971cda63` |
|
||||
|
||||
These boss-encounter recordings exceed the retained C++ engine's 2,800-frame
|
||||
static demo buffer. A checked reference-input loader with a larger buffer is
|
||||
needed before loading them there. It must also preserve the fresh pilot and
|
||||
difficulty, rather than substituting `DEMO_MakePlayer`'s showcase loadout.
|
||||
The new recordings have **not** yet been compared against a C++ first-mission
|
||||
boss victory. The existing 1,889-step `DEMO1G1_REC` parity test covers
|
||||
a different original demo and remains a separate acceptance check.
|
||||
@@ -1,74 +0,0 @@
|
||||
# GLB archive format
|
||||
|
||||
The original game ships its data as `FILE0000.GLB` ... `FILE0006.GLB`,
|
||||
produced by the "GLib" library bundled with the commercial source. The
|
||||
format below was reverse-engineered from `raptor/src/glbapi.cpp` (upstream
|
||||
loader) and verified against all seven shipped archives (6449 items total).
|
||||
|
||||
## Container
|
||||
|
||||
A GLB file is a flat sequence of 28-byte records:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint32_t opt; // 0 = plain payload, 1 = encrypted payload
|
||||
uint32_t offset; // payload offset in the file
|
||||
uint32_t filesize; // payload size in bytes
|
||||
char name[16]; // NUL-padded item name, e.g. "GENMIDI_OP2"
|
||||
} KEYFILE; // little-endian fields
|
||||
```
|
||||
|
||||
- **Record 0 is the header.** Its `offset` field holds the *item count*;
|
||||
its other fields are unused.
|
||||
- Records `1 .. count` are the item descriptor table, starting at byte 28.
|
||||
- Item payloads live at each descriptor's `offset`, `filesize` bytes long.
|
||||
- Names are unique per archive only in practice; duplicates (e.g. dozens of
|
||||
`CHASE_AGX`, thousands of empty `unnamed` labels) are common.
|
||||
- Descriptor entries with `filesize == 0` are labels/placeholders.
|
||||
|
||||
## Cipher
|
||||
|
||||
Every record *and* every payload flagged `opt == 1` is scrambled with a
|
||||
rolling additive cipher keyed on the ASCII string `32768GLB`, seeded at 25
|
||||
(`SEED` in `glbapi.h`):
|
||||
|
||||
```
|
||||
kidx = 25 % len(key) # 25 % 8 = 1
|
||||
prev = key[kidx]
|
||||
|
||||
# decrypt (GLB_DeCrypt):
|
||||
for each byte b:
|
||||
out = (b - key[kidx] - prev) mod 256
|
||||
prev = b # chain uses the CIPHERTEXT byte
|
||||
kidx = (kidx + 1) mod len(key)
|
||||
|
||||
# encrypt (GLB_EnCrypt) is the inverse:
|
||||
out = (b + key[kidx] + prev) mod 256
|
||||
prev = out # chain uses the PLAINTEXT byte
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- The cipher state resets **per record** and **per payload**.
|
||||
- The original C uses signed `char`; the stored byte equals the
|
||||
mathematical result mod 256 either way, so unsigned arithmetic is
|
||||
byte-exact.
|
||||
- Payload encryption is independent of descriptor encryption: an archive
|
||||
typically mixes plain and encrypted items.
|
||||
|
||||
## Verification
|
||||
|
||||
`tools/glbextract.py` implements this format. Correctness was checked
|
||||
against known payloads:
|
||||
|
||||
- `GENMIDI_OP2` starts with the DMX magic `#OPL_II#` (11908 bytes)
|
||||
- `PALETTE_DAT` is exactly 768 bytes (256 x RGB)
|
||||
- `DMXGUS_INI` decodes to readable INI text
|
||||
- item counts per archive match the id ranges in `raptor/src/fileids.h`
|
||||
(e.g. slot 0: 230 items, last id `0x0e5`)
|
||||
- every payload lies within its archive's bounds (extractor warns otherwise)
|
||||
|
||||
## See also
|
||||
|
||||
- [asset-pipeline.md](asset-pipeline.md) — how archives become `data/`
|
||||
- `tools/glbextract.py` — reference implementation (Python, stdlib only)
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
# Optional HD artwork
|
||||
|
||||
The Rust Options screen includes **HD art**. It starts disabled. Enable it to use reviewed artwork from `../data/hd/hd.json`; each missing or rejected frame keeps the selected original / 2x / 3x / 4x crisp treatment. The scale setting still determines the indexed rendering resolution, and the HD toggle can be changed while paused. HD art is presentation only: simulation, collision sizes, banking positions and save content IDs stay unchanged.
|
||||
|
||||
The local base-game pack currently supplies **215 reviewed replacement frames**. Complete additions cover all twenty-one light, dark and firing-flash player poses, thirteen vehicle families, four industrial bosses, seven ground-defense targets, a bonus crate and complete five- and fifteen-frame opening bunkers, nineteen equipment/pickup families, seven complete projectile families, the twelve-frame energy ring, nine blue-spark phases, four death-ray hit phases, fourteen lightning phases, ten platoon fragment phases, sixteen air-blast phases, four pickup glow masks, nine pilot portraits, both original title logos and sixteen canonical terrain tiles. The complete catalog backlog and generation status are listed in [hd-backlog.md](hd-backlog.md).
|
||||
|
||||
| Reviewed artwork | Original asset IDs | Supplied / complete frames |
|
||||
| --- | --- | ---: |
|
||||
| Light player banking | `0x1011a`–`0x10120` | 7 / 7 |
|
||||
| Dark player banking | `0x20045`–`0x2004b` | 7 / 7 |
|
||||
| Firing-flash player banking | `0x2004c`–`0x20052` | 7 / 7 |
|
||||
| First-mission fighter `SHIP01G1_PIC` | `0x102f8` | 1 / 1 |
|
||||
| First-mission fighter `SHIP27G1_PIC` | `0x10330` | 1 / 1 |
|
||||
| Fighters `SHIP02G1_PIC`, `SHIP14G1_PIC` | `0x102f9`, `0x1030a` | 2 / 2 |
|
||||
| `SHIP03`, `SHIP06`, `SHIP13`, `SHIP24`, `SHIP26`, `SHIP28` craft | `0x102fa`, `0x102fd`, `0x10309`, `0x10327`, `0x1032f`, `0x10331` | 6 / 6 |
|
||||
| `SHIP07` two-state craft | `0x102fe`–`0x102ff` | 2 / 2 |
|
||||
| `SHIP08` moving tread phases | `0x10300`–`0x10303` | 4 / 4 |
|
||||
| `SHIP09` hovercraft wake phases | `0x10304`–`0x10305` | 2 / 2 |
|
||||
| First-mission industrial boss `SHIP10G1_PIC` | `0x10306` | 1 / 1 |
|
||||
| Industrial bosses `SHIP12G1_PIC`, `SHIP17G1_PIC` | `0x10308`, `0x1030f` | 2 / 2 |
|
||||
| Industrial boss `SHIP11G1_PIC` | `0x10307` | 1 / 1 |
|
||||
| Ground-defense target `TARGT1G1_PIC` | `0x10138` | 1 / 1 |
|
||||
| Ground-defense targets `TARGT2G1_PIC`, `TARGT3G1_PIC` | `0x10139`, `0x1013a` | 2 / 2 |
|
||||
| Ground-defense target `TARGT5G1_PIC` | `0x1013c` | 1 / 1 |
|
||||
| Opening ground-defense bunker `TARGT6G1_PIC` | `0x1013d`–`0x10141` | 5 / 5 |
|
||||
| Opening ground-defense bunker `TARGT7G1_PIC` | `0x10142`–`0x10150` | 15 / 15 |
|
||||
| Ground targets `TARGT4G1_PIC`, `TARGT9G1_PIC` | `0x1013b`, `0x10160` | 2 / 2 |
|
||||
| Ground target `TARG10G1_PIC` | `0x10161` | 1 / 1 |
|
||||
| Bonus crate `BONUS1G1_PIC` | `0x10151` | 1 / 1 |
|
||||
| Forward Guns equipment | `0x102b0` | 1 / 1 |
|
||||
| Plasma Guns equipment | `0x102b1`–`0x102b2` | 2 / 2 |
|
||||
| Dumb Missile equipment | `0x102b5` | 1 / 1 |
|
||||
| Air Missile equipment | `0x102bf` | 1 / 1 |
|
||||
| Micro Missile equipment | `0x102b3`–`0x102b4` | 2 / 2 |
|
||||
| MiniGun equipment rotation | `0x102b6`–`0x102b9` | 4 / 4 |
|
||||
| Turret equipment rotation | `0x102ba`–`0x102bd` | 4 / 4 |
|
||||
| Ground Missile equipment | `0x102c0` | 1 / 1 |
|
||||
| Energy Grab equipment phases | `0x102c1`–`0x102c4` | 4 / 4 |
|
||||
| Pulse Cannon equipment phases | `0x102c6`–`0x102c7` | 2 / 2 |
|
||||
| Missile Pods equipment | `0x102be` | 1 / 1 |
|
||||
| Mega Bomb equipment | `0x102c5` | 1 / 1 |
|
||||
| Super Shield pickup | `0x102d0` | 1 / 1 |
|
||||
| Detector and additional shop/pickup images | `0x102d5`–`0x102da` | 6 / 6 |
|
||||
| Plasma projectile | `0x101cf`–`0x101d0` | 2 / 2 |
|
||||
| Enemy cannon pulse | `0x101d6`–`0x101d7` | 2 / 2 |
|
||||
| Dumb Missile projectile | `0x101ba`–`0x101bc` | 3 / 3 |
|
||||
| Missile Pods / Air Missile 2 projectile | `0x101bd`–`0x101be` | 2 / 2 |
|
||||
| Ground Missile 2 projectile | `0x101bf`–`0x101c0` | 2 / 2 |
|
||||
| Forward Guns / MiniGun firing patterns | `0x101c3`–`0x101c6` | 4 / 4 |
|
||||
| Mine pulse | `0x101d1`–`0x101d2` | 2 / 2 |
|
||||
| Energy ring | `0x101ae`–`0x101b9` | 12 / 12 |
|
||||
| Blue sparks | `0x1019c`–`0x101a4` | 9 / 9 |
|
||||
| Death-ray hits | `0x101f1`–`0x101f4` | 4 / 4 |
|
||||
| Lightning growth/retraction | `0x1018e`–`0x1019b` | 14 / 14 |
|
||||
| Platoon fragments | `0x10213`–`0x1021c` | 10 / 10 |
|
||||
| Air blast | `0x102a0`–`0x102af` | 16 / 16 |
|
||||
| Pickup glow light masks | `0x10225`–`0x10228` | 4 / 4 |
|
||||
| Pilot portraits, small and retained large variants | `0x10111`–`0x10119` | 9 / 9 |
|
||||
| Original English and Chinese title logos | `0x10013`, `0x10014` | 2 / 2 |
|
||||
| Canonical terrain set 1, first shard | `0x10360`–`0x1036f` | 16 / 16 |
|
||||
|
||||
Every registered frame passed the exact original source hash, logical canvas, aspect, visible alpha and path checks. Complete contact sheets were inspected at source size and the 4× presentation size; per-frame geometry metrics and review evidence remain with each family. Their logical collision sizes are unchanged. Small contours and interpreted metal detail differ. Missing artwork keeps the crisp original fallback. HD remains an opt-in preview while the remaining catalog and game screenshots are reviewed.
|
||||
|
||||
An actual Rust `HdAssets::sprite` pass also checked all registered frames at scales 1–4 with the game palette: every output has the expected dimensions and visible pixels, and the pack reports no diagnostics. Complete 4× source/HD sheets from that runtime path were reviewed for readable projectiles, equipment alignment, banking progression and energy-ring timing stages. These sprite checks complement live gameplay captures and do not establish full-game visual completion.
|
||||
|
||||
The complete light banking sequence uses matching silver/gunmetal materials, dark red accents and the seven distinct original roll poses. Its safe square crop rectangles remove transparent canvas margin without excluding any pixel with alpha at least 32. Independent Rust `image::Lanczos3` checks use the runtime's two resampling stages, first to the resident 128×128 canvas and then to the original 32×32 logical size. All seven frames have source silhouette overlap of 87.7–92.3%, bounds within one logical pixel and centroid displacement below 0.74 logical pixels. Full-sequence visual review also checked the nose, canopies, wing ends and engine exits; numeric overlap alone does not approve a frame.
|
||||
|
||||
The first four equipment families' visible material masks overlap their originals by approximately 93–100%, and both Plasma icon frames share the same aligned alpha footprint. Their full alpha-mask overlap is lower, approximately 54–78%, because nearly black original opaque outlines become transparent around the detailed replacement. This difference is recorded separately from the visible metal silhouette. The ten newly added equipment/pickup frames have full logical-mask overlap of approximately 87.5–99.3%. Ground-defense originals and their replacements are fully opaque, which the source-aware loader accepts.
|
||||
|
||||
Small-scale differences remain visible. At 1×, each Plasma projectile loses one tail row: the original 8/6-pixel heights become 7/5 pixels, while 4× retains the original logical extents. The first energy effect's fine cardinal rays also contract under binary alpha at small scales; its 1× bounds become `(7,5)..(34,34)` against original `(3,2)..(38,37)`. At 4× the rays remain visible but end approximately 1–2 logical pixels inside the source extremes. Later rings use smoother, thinner contours. These differences affect presentation and are follow-up candidates for per-asset fallback; source collision dimensions remain unchanged.
|
||||
|
||||
Generated drafts are not active coverage. Earlier player atlas attempts and shortened extreme-bank variants were rejected. The complete light family replaces the initial straight-frame proof; the dark family uses coherent lighting edits of those reviewed poses and the original dark palette. Its exact runtime logical-mask overlap is 87.3–91.4%, with centroid displacement below 0.68 pixels, bounds within one pixel and no clipped meaningful alpha. The newly added Ground Missile, Turret, Pulse Cannon and Energy Grab families preserve their source phase differences. Late Energy Grab interprets two single-pixel peripheral ring holes as filled detail; the main transparent loop remains open. Other equipment families remain uncovered. A 3×3 shoreline prototype preserves the broad coastline and demonstrates the terrain renderer, but remains unregistered: repeated canonical tiles, neighbors and destroyed states need individual artwork and seam review. Original AGX cinematics retain their original artwork.
|
||||
|
||||
The firing family preserves all seven original poses and their localized amber reflections. Its distinct straight-frame contour points were checked explicitly: `(19,8)` remains opaque, `(11,12)` and `(11,13)` remain transparent, and `(11,14)` stays opaque. The MiniGun's four rotation phases and the seven newly registered missile phases passed complete sequence inspection; each missile retains its original exhaust length and fin layout. Native scene captures remain a separate reachability check. The additional `SHIP11G1_PIC` boss interprets its dark perimeter: logical alpha overlap is 82.9%, with 373 removed original occupied pixels (252 very dark) and 23 added pixels. Its towers, side machinery, center weapon and lower pods remain readable at every scale, but its alpha contour is not identical to the source.
|
||||
|
||||
The additional fifteen-frame bunker preserves its door apertures, gun and blink phases. Craft tread and hover-wake animations keep their distinct source poses and negative spaces. `SHIP13G1_PIC` has brighter detailed copper panels (mean occupied RGB about `64/47/42` becomes `82/58/52`); its original copper identity and asymmetric silhouette remain readable. `TARG10G1_PIC` uses a reviewed subdued soil correction; the superseded orange-speckled variants remain held.
|
||||
|
||||
The two additional ground targets use subdued brown soil corrections; their machinery and original directions remain intact. The tiny gun sprites keep all four original gray/white patterns, and the mine keeps its pink then dark burgundy center with gold corners. These families have complete gallery review; it does not establish a placed gun or mine scene occurrence.
|
||||
|
||||
The first sixteen canonical terrain replacements passed independent review of every source/logical/4× frame, all 48 supplied mission contexts and all 755 declared cardinal neighbor pairs: 170 HD/HD and 585 HD/original. Actual native scene checks exercised all 32 cached scroll phases and content-hash/load-order invalidation. None of these sixteen IDs has a declared destroyed partner; the separate native destruction-cache proof therefore adds no HD debris coverage. Mixed detail contrast remains until neighboring tiles are remastered. The next terrain family has a corrected treeless `0x20111` and passed independent source and complete neighborhood review. It and the first thirty-two industrial terrain candidates remain held for recurring grain lines at some composed tile boundaries; they add no active coverage yet.
|
||||
|
||||
Four small pilot portraits (`0x10116`–`0x10119`) passed actual SDL main-menu SD/HD captures. Their faces, equipment and slanted ID-card backgrounds stay recognizable, and every surrounding UI pixel was identical. The five large portraits and both title logos have complete gallery review but no verified active UI route. The Chinese logo legitimately uses 478 original fire-class pixels; its HD output preserves that class and adds no water or blink labels.
|
||||
|
||||
The newly reviewed effect families retain their complete phase ordering. Lightning branches are thinner and slightly cooler blue; the late AirBlast rim is thinner; PickupGlow uses a rounded interpreted contour rather than identical alpha. Actual `Canvas::shade` checks over original terrain confirm that pickup-mask holes preserve the background and mask RGB never paints foreground pixels. Other effects remain held where generation filled transparent holes, removed final fragments or made subdued smoke/embers too bright. A successfully decoded draft does not establish artistic approval.
|
||||
|
||||
Original pixels and original-derived base-game artwork remain outside Git under `../data/hd/`. The separate authored mod packages include their generated SD/HD PNGs, sounds and provenance in Git. The new authored menu backdrop has its own packaged rendering path and is not counted as an original catalog sprite replacement.
|
||||
|
||||
The sources were decoded with `tools/hd_art_source.py`, inspected, and passed to the **built-in image_gen tool** using the imagegen skill. Sources, raw generations, prompts, contact sheets, frame metadata and review notes remain in local family folders (`enemies/`, `bosses/`, `effects/`, `equipment/`, `player-light/`, `player-dark/`, and `terrain/`). The superseded initial straight-player proof and its exact generation prompt remain saved locally at:
|
||||
|
||||
- `../data/hd/light-player-straight-hd-v1.png`
|
||||
- `../data/hd/prompts/light-player-straight-v1.txt`
|
||||
- `../data/hd/provenance.json`
|
||||
- `../data/hd/source/light-player-straight.png`
|
||||
|
||||
That proof PNG has real alpha transparency, is 1254×1254, and was copied unchanged from the tool's output. Its logical silhouette overlap was 92.1%, but it is no longer mapped into the active pack because the complete family now has consistent materials across all seven poses. An earlier extreme-bank attempt remains at `../data/hd/light-player-bank-0-hd-v1.png` with `prompts/light-player-bank-0-v1.txt`; it is rejected because its footprint overlap is only 62.2% and its engine ends are shortened. Reviewed production frames and their crop metadata are in `../data/hd/player-light/`. The generated PNG bytes remain unchanged.
|
||||
|
||||
## Pack format
|
||||
|
||||
The following is metadata only; artwork belongs in a separately distributed local pack. This example shows one entry from the complete seven-frame light family. Decimal IDs are shown alongside their hexadecimal meaning.
|
||||
|
||||
```json
|
||||
{
|
||||
"format_version": 1,
|
||||
"name": "Source-aligned HD artwork",
|
||||
"description": "Optional artwork; original fallback for uncovered frames",
|
||||
"sequences": [
|
||||
{
|
||||
"name": "Light player banking",
|
||||
"base_asset": 65818,
|
||||
"frame_count": 7,
|
||||
"frames": [
|
||||
{
|
||||
"frame": 0,
|
||||
"file": "player-light/frame-0-hd-v1.png",
|
||||
"logical_width": 32,
|
||||
"logical_height": 32,
|
||||
"origin": [0, 0],
|
||||
"crop": [30, 10, 1220, 1220],
|
||||
"source_sha256": "SHA-256 of the exact original 0x1011a catalog payload"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`base_asset` is `0x1011a`; the supplied frame targets `base_asset + frame`, so frames never depend on filenames or glob ordering. `frame_count` declares the whole original animation sequence and `frames` explicitly declares replacement coverage. An optional `crop: [x,y,width,height]` selects a rectangle from a PNG atlas or aligns a generated canvas by removing only transparent margin. The current pack supplies all seven light-player entries.
|
||||
|
||||
Version 1 requires exact original logical canvas dimensions and the original top-left anchor `[0,0]`. Canvas alignment is retained even when transparent margins surround the visible image. The PNG or crop must have the same aspect ratio as the original canvas and at least twice its logical resolution. The source hash prevents artwork silently matching a changed original asset. Original sprites with transparent negative space require transparent HD artwork; fully opaque originals may retain fully opaque replacements. Duplicate IDs, invalid sequence metadata, bad paths, crop bounds and unreasonable image allocations are rejected. Paths must remain inside the pack directory, including after symlink resolution. Open **D: details** from the main menu to inspect rejected frames and coverage.
|
||||
|
||||
The original picture header's two fields before width/height are `opt1` and `opt2`; they are not drawing origins. The audit records these as `header_options`. Replacement `origin: [0,0]` refers to the original canvas anchor and does not reinterpret those header options as sprite offsets.
|
||||
|
||||
The streaming runtime source-checks the complete manifest and validates each original generator-sized PNG/crop when used. It stores no more than the original logical canvas at 4× resolution, the highest supported presentation scale. Raw generated PNGs and provenance remain intact on disk. Three 32 MiB LRU caches hold decoded atlases, derived RGBA frames and palette-bound indexed sprites; eviction never removes a registered frame. A separate derived disk cache is capped at 512 MiB. The full catalog is no longer subject to a 128 MiB artwork admission limit. See [hd-streaming.md](hd-streaming.md) for validation ceilings, cache identities and corruption handling.
|
||||
|
||||
Original source locations also constrain palette classes: ordinary art uses indices 0–239, water stays in 240–244, fire in 245–249, and original blink/flash labels 250–255 remain exact. New HD coverage over an originally transparent location stays ordinary. Compact source-label metadata has a separate 32 MiB ceiling and is omitted for wholly ordinary sources. This prevents static metal or equipment highlights from acquiring fire cycles or blinking merely because a generated RGB value matches an animated palette entry. The accepted frames passed all-scale checks of these reserved-index counts. Static cinematic cards use a separate palette mode with all 256 colors; source references bind Apogee, Cygnus and Taiwan cards to their authentic palettes. These cards still await authored HD artwork and are not counted as remasters.
|
||||
|
||||
HD frames are resampled directly to the selected presentation canvas and quantized to the game palette. They bypass ScaleNx filtering, preserve transparent pixels and use the normal layer order. Indexed output allows palette effects to apply consistently; subtle RGB shades and alpha edges are reduced by the original palette and binary sprite transparency. The validated [terrain renderer](hd-terrain.md) overlays canonical, full-opaque HD tiles on the joined original layer. It shares HD edges and corners where source material classes agree, transitions HD/original boundaries over two logical pixels, and preserves water/fire/flash palette classes. Intact and destroyed IDs are independent targets. The renderer adds no remaster coverage by itself; each terrain family needs the same contextual review before registration.
|
||||
|
||||
The metadata-only [completion ledger](hd-completion.json) tracks every one of the 5,753 original picture IDs, including unused enemies, damaged variants, HUD graphics and unnamed terrain. It separates missing, in-progress, candidate, reviewed, registered and render-reachable stages. `tools/hd_job_queue.py` creates 674 disjoint picture jobs, plus 13 classic cinematic and four bitmap-font verification jobs. Original AGX artwork and sharp native glyphs have explicit preservation policies; they are never reported as AI remasters. The ignored `data/hd/jobs/queues/` files assign continuous enemy, boss, equipment/UI, effect/projectile and terrain work with exact source hashes, complete animation families and neighbor/state references. Exact-payload aliases may share reviewed artwork while retaining each ID's source binding.
|
||||
|
||||
Workers can validate an isolated proposal with `python3 tools/hd_candidate_review.py --candidate ../data/hd/jobs/JOB_ID/candidate.json`. The helper hard-links raw artwork into a temporary pack, links against the newest built Rust game library, and exports all scales 1–4 plus source/logical/4× contact sheets. It records raw artwork hashes and the library identity, warns when asset-loader source is newer than the build, and pages large reviews in groups of sixteen frames to avoid an unbounded giant image. Every page listed in `runtime-contact-sheets.json` requires inspection. It never edits the active pack or generated pixels. A successful gallery still needs coordinator visual approval and separate scene/context evidence.
|
||||
|
||||
After explicit artistic and independent approval, the coordinator can run `python3 tools/hd_register_reviewed.py --job JOB_ID --dry-run`, then omit `--dry-run` to register the reviewed family. The command checks every original target, complete sequences, the exact approved frame bindings, all four runtime scales, the reviewed library and unchanged PNG hashes. Held artwork, stale evidence and duplicate registered IDs are rejected. It writes the complete manifest atomically and preserves provenance; registration does not claim a native scene occurrence or finish the catalog backlog.
|
||||
|
||||
Generation workers share a four-submission gate through `tools/hd_generation_gate.py`. They prepare references and save prompts before acquiring. One orchestration acquires, marks the request started, awaits built-in imagegen and reports its actual outcome immediately before returning to the model or doing local review. Started and legacy-unknown requests require explicit completion; only unstarted reservations expire. Status shows phases, ages and admissions, and supports owner recovery without cancelling tools. Connection failures retain exact sanitized text and UTC timestamps, impose a 60–600-second cooldown, and permit at most three connection failures per job in a thirty-minute retry batch. See the [mandatory workflow and wrapper](hd-generation-workflow.md). Logs and gate state remain ignored.
|
||||
|
||||
## Source and sequence review
|
||||
|
||||
Use the existing catalog decoder to prepare a transparent reference without redrawing it:
|
||||
|
||||
```sh
|
||||
python3 tools/hd_art_source.py --asset 1011d \
|
||||
--output ../data/hd/source/light-player-straight.png --preview-scale 16
|
||||
```
|
||||
|
||||
Inspect every source frame before generating its replacement. Request the same orthographic camera, silhouette, canvas placement, orientation, material colors and lighting for every frame. Preserve the generated alpha. Do not substitute rotations of one frame for the original bank poses: compare the complete sequence at its logical size and at the intended display resolution. Verify that the nose, engine exits, wing extremes and animation pivots remain aligned and that no clipping or background halos appear.
|
||||
|
||||
Keep image-generation prompts and provenance alongside the artwork. Never overwrite original catalog assets. The loader deliberately accepts partial coverage so a reviewed sprite can be previewed before the complete pack is ready, while preserving per-frame fallback.
|
||||
@@ -0,0 +1,149 @@
|
||||
# Original artwork HD backlog
|
||||
|
||||
The checked catalog contains **5,753 standalone picture frames**. The active local HD pack supplies **215**; **5,538** have no registered HD replacement. These counts include unnamed graphics outside `sprites/`. Scale2x/3x/4x filtering is crisp scaling, not a newly authored HD replacement. Authored mod SD/HD art is separate from this original-catalog audit.
|
||||
|
||||
| Category | Picture frames | Registered HD | Missing HD | Declared sequences / terrain sets |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| Player banking | 21 | 21 | 0 | 3 |
|
||||
| Bosses | 249 | 4 | 245 | 79 |
|
||||
| Enemies and ground objects | 419 | 46 | 373 | 183 |
|
||||
| Player and enemy projectiles | 51 | 17 | 34 | 18 |
|
||||
| Explosions and effects | 281 | 69 | 212 | 25 |
|
||||
| Equipment and pickups | 47 | 31 | 16 | 23 |
|
||||
| Terrain | 4,535 | 16 | 4,519 | 4 |
|
||||
| Menus, portraits, HUD and other pictures | 150 | 11 | 139 | mixed static / animated |
|
||||
|
||||
## Registered pack and generation jobs
|
||||
|
||||
Only registered frames passing canvas, source-hash, PNG and alpha checks count as registered HD. This is loader coverage, not proof that every original animation event is reachable in the current renderer. A generated image or successful still-image review does not complete an animated family. The optional pack remains disabled by default.
|
||||
|
||||
| Registered sequence | Asset base | Valid / complete frames |
|
||||
| --- | --- | ---: |
|
||||
| Light player banking | `0x1011a` | 7 / 7 |
|
||||
| First mission industrial boss | `0x10306` | 1 / 1 |
|
||||
| Plasma Guns projectile HD | `0x101cf` | 2 / 2 |
|
||||
| Enemy cannon projectile HD | `0x101d6` | 2 / 2 |
|
||||
| SHIP01G1 faithful HD remaster | `0x102f8` | 1 / 1 |
|
||||
| Energy effect HD | `0x101ae` | 12 / 12 |
|
||||
| SHIP27G1 faithful HD remaster | `0x10330` | 1 / 1 |
|
||||
| Forward Guns equipment | `0x102b0` | 1 / 1 |
|
||||
| Plasma Guns equipment | `0x102b1` | 2 / 2 |
|
||||
| Dumb Missile equipment | `0x102b5` | 1 / 1 |
|
||||
| Air Missile equipment | `0x102bf` | 1 / 1 |
|
||||
| TARGT1G1 faithful HD remaster | `0x10138` | 1 / 1 |
|
||||
| TARGT6G1_PIC | `0x1013d` | 5 / 5 |
|
||||
| Dark player banking | `0x20045` | 7 / 7 |
|
||||
| TARGT2G1_PIC | `0x10139` | 1 / 1 |
|
||||
| TARGT3G1_PIC | `0x1013a` | 1 / 1 |
|
||||
| Terrain 10360 | `0x10360` | 1 / 1 |
|
||||
| Terrain 10361 | `0x10361` | 1 / 1 |
|
||||
| Terrain 10362 | `0x10362` | 1 / 1 |
|
||||
| Terrain 10363 | `0x10363` | 1 / 1 |
|
||||
| Terrain 10364 | `0x10364` | 1 / 1 |
|
||||
| Terrain 10365 | `0x10365` | 1 / 1 |
|
||||
| Terrain 10366 | `0x10366` | 1 / 1 |
|
||||
| Terrain 10367 | `0x10367` | 1 / 1 |
|
||||
| Terrain 10368 | `0x10368` | 1 / 1 |
|
||||
| Terrain 10369 | `0x10369` | 1 / 1 |
|
||||
| Terrain 1036a | `0x1036a` | 1 / 1 |
|
||||
| Terrain 1036b | `0x1036b` | 1 / 1 |
|
||||
| Terrain 1036c | `0x1036c` | 1 / 1 |
|
||||
| Terrain 1036d | `0x1036d` | 1 / 1 |
|
||||
| Terrain 1036e | `0x1036e` | 1 / 1 |
|
||||
| Terrain 1036f | `0x1036f` | 1 / 1 |
|
||||
| Super Shield pickup | `0x102d0` | 1 / 1 |
|
||||
| Micro Missile | `0x102b3` | 2 / 2 |
|
||||
| Missile Pods | `0x102be` | 1 / 1 |
|
||||
| Mega Bomb | `0x102c5` | 1 / 1 |
|
||||
| Detect / Itembuy1 | `0x102d5` | 1 / 1 |
|
||||
| Itembuy2 | `0x102d6` | 1 / 1 |
|
||||
| Itembuy3 | `0x102d7` | 1 / 1 |
|
||||
| Itembuy4 | `0x102d8` | 1 / 1 |
|
||||
| Itembuy5 | `0x102d9` | 1 / 1 |
|
||||
| Bomb | `0x102da` | 1 / 1 |
|
||||
| SHIP14G1_PIC | `0x1030a` | 1 / 1 |
|
||||
| SHIP02G1_PIC | `0x102f9` | 1 / 1 |
|
||||
| SHIP12G1_PIC | `0x10308` | 1 / 1 |
|
||||
| SHIP17G1_PIC | `0x1030f` | 1 / 1 |
|
||||
| TARGT5G1_PIC | `0x1013c` | 1 / 1 |
|
||||
| Mini Gun | `0x102b6` | 4 / 4 |
|
||||
| Player firing-flash banking | `0x2004c` | 7 / 7 |
|
||||
| Dumb Missile | `0x101ba` | 3 / 3 |
|
||||
| Missile Pods / Air Missile | `0x101bd` | 2 / 2 |
|
||||
| Grd Missile | `0x101bf` | 2 / 2 |
|
||||
| SHIP11G1_PIC source-faithful HD candidate | `0x10307` | 1 / 1 |
|
||||
| Blue sparks v1 | `0x1019c` | 9 / 9 |
|
||||
| Death ray hit companion v1 | `0x101f1` | 4 / 4 |
|
||||
| Original Raptor pilot portraits and ID pictures | `0x10111` | 9 / 9 |
|
||||
| Original English Raptor title | `0x10013` | 1 / 1 |
|
||||
| Original Chinese Raptor title | `0x10014` | 1 / 1 |
|
||||
| Platoon effect v1 | `0x10213` | 10 / 10 |
|
||||
| Air blast v1 | `0x102a0` | 16 / 16 |
|
||||
| Pickup light-mask glow v1 | `0x10225` | 4 / 4 |
|
||||
| Lightning and energy grab v1 | `0x1018e` | 14 / 14 |
|
||||
| Grd Missile | `0x102c0` | 1 / 1 |
|
||||
| Turret | `0x102ba` | 4 / 4 |
|
||||
| Pulse Cannon | `0x102c6` | 2 / 2 |
|
||||
| Energy Grab | `0x102c1` | 4 / 4 |
|
||||
| TARGT7G1_PIC | `0x10142` | 15 / 15 |
|
||||
| BONUS1G1_PIC | `0x10151` | 1 / 1 |
|
||||
| SHIP08G1_PIC | `0x10300` | 4 / 4 |
|
||||
| SHIP09G1_PIC | `0x10304` | 2 / 2 |
|
||||
| SHIP03G1_PIC | `0x102fa` | 1 / 1 |
|
||||
| SHIP06G1_PIC | `0x102fd` | 1 / 1 |
|
||||
| SHIP07G1_PIC | `0x102fe` | 2 / 2 |
|
||||
| SHIP24G1_PIC | `0x10327` | 1 / 1 |
|
||||
| SHIP28G1_PIC | `0x10331` | 1 / 1 |
|
||||
| TARG10G1_PIC | `0x10161` | 1 / 1 |
|
||||
| SHIP26G1_PIC | `0x1032f` | 1 / 1 |
|
||||
| SHIP13G1_PIC | `0x10309` | 1 / 1 |
|
||||
| TARGT4G1_PIC | `0x1013b` | 1 / 1 |
|
||||
| TARGT9G1_PIC | `0x10160` | 1 / 1 |
|
||||
| Forward Guns / Mini Gun | `0x101c3` | 4 / 4 |
|
||||
| Enemy beam v1 | `0x101d1` | 2 / 2 |
|
||||
|
||||
| Generation job | Status |
|
||||
| --- | --- |
|
||||
| Light player banking, 7 poses | Complete 7-pose family registered after source and full-sequence review and exact Rust resident 4× → logical QA; earlier failed drafts stay unregistered. |
|
||||
| Dark player banking, 7 poses | Complete coherent seven-frame family registered after actual Rust source/logical/indexed4× review. Earlier independent drafts remain rejected. |
|
||||
| Firing-flash player, 7 poses | Awaiting a complete coherent family; the straight firing-flash pose has a distinct source alpha mask and needs separate review. |
|
||||
| First mission enemies and boss | Boss, two common fighters and opaque ground-defense target complete and registered. |
|
||||
| Projectiles and effects | Plasma 2, enemy cannon 2 and energy ring 12 frames complete and registered; other originals retain crisp fallback. |
|
||||
| Equipment and pickups | Forward Guns 1, Plasma 2, Dumb Missile 1 and Air Missile 1 frames complete and registered; other originals retain crisp fallback. |
|
||||
| Terrain shoreline | Contextual 3×3 prototype remains unregistered. Canonical terrain renderer and ten portable checks pass; all reusable tile IDs, neighbors and intact/destroyed states still require generated artwork review. |
|
||||
| Menu background | Separate authored background job and menu renderer integration; not part of original sprite HD manifest. |
|
||||
| Original cinematic artwork | Original AGX sequences retained; no HD cinematic replacement registered. |
|
||||
| First canonical terrain shard,16 IDs | Registered after all48 actualcontexts,755cardinalpairs and32native scrollphases; independent review passes. No IDhas a destroyedpartner; this adds no HD debris coverage. |
|
||||
|
||||
The catalog also contains **303 AGX cinematic delta frames in 13 sequences** and **4 bitmap fonts**. These have explicit native-art preservation policies: keep the classic cinematics and sharp original glyphs, validate complete playback/alignment, and never count them as generated HD replacements. The AGX frames are separate from standalone pictures.
|
||||
|
||||
## Sequence priorities
|
||||
|
||||
1. Keep all light/dark-player banking poses and firing-flash poses coherent as complete families. Preserve the exact firing-flash source mask, which differs from the light/dark mask for the straight pose.
|
||||
2. Ground defenses, airborne enemies and complete boss animation sequences; share palettes and the original logical canvases, rather than treating each enemy frame as an unrelated illustration.
|
||||
3. Original projectiles and pickup/equipment animations. Tiny projectiles need careful readability checks; per-asset nearest fallback remains temporary until a clean remaster passes review.
|
||||
4. Explosions, flame/spark sequences and other effects. Check full animation timing, transparency and palette cycling, not only a still image.
|
||||
5. All four terrain sets, including unnamed catalog tiles. Generate with neighboring context and verify joined seams before changing runtime coverage.
|
||||
6. Every original menu background, portrait, HUD and retained unused picture. Gameplay UI text and button labels must remain readable and match their actions. Verify the preserved native cinematic/font paths separately.
|
||||
|
||||
## Reproduce the audit
|
||||
|
||||
```sh
|
||||
python3 tools/hd_art_audit.py --data ../data \
|
||||
--json ../data/hd/coverage-audit.json --markdown docs/hd-backlog.md
|
||||
python3 tools/hd_job_queue.py --data ../data
|
||||
```
|
||||
|
||||
The JSON gives every picture's asset ID, path, logical dimensions, original header options, source hash and HD status, plus each declared animation/set. The [completion ledger](hd-completion.json) records every required picture and native-art verification target, including complete families, ownership and candidate/review/registration stages. Both contain metadata only. Original and original-derived artwork remains outside Git.
|
||||
|
||||
Folder totals: `sprites/`: 985, `tiles/`: 232, `misc/`: 4,536. These folders are not semantic categories; terrain and other pictures also live in `misc/`.
|
||||
|
||||
## Secondary effect coverage
|
||||
|
||||
Native animation registrations, pickup masks and beam impact/power companion arrays contribute 281 distinct effect frames. The 25 declared ranges include one five-frame descending-smoke subset of the nine-frame smoke family, leaving 24 complete families. This includes muzzle flashes/casings, laser impacts, smoke trails, lightning, sparks and the super-shield glow as well as explosions. Pickup glow uses indexed light-table mask semantics and needs a different HD treatment from an ordinary color sprite. These assets are not UI artwork merely because they are absent from the content registries. Renderer event reachability needs its own check before claiming complete gameplay effect coverage.
|
||||
|
||||
## Runtime reachability and duplicate artwork
|
||||
|
||||
The terrain backlog contains **4,524 distinct payload hashes** and **4,524 distinct decoded images** across 4,535 catalog IDs. Duplicate IDs can share the same reviewed treatment; they still require explicit source-bound metadata. The canonical HD terrain renderer is validated: intact/destroyed source-bound tiles, shared HD edge/corner treatment and palette-class preservation are implemented. This adds no artwork coverage; each generated tile still requires its own contextual seam review.
|
||||
|
||||
The Python inventory matches the Rust decoder's complete picture ID and logical-dimension inventory. The shipped **27 missions** reference **512 of 528 enemy definitions**, which reduce to **260 of 262 distinct art sequences**, and **4,217 terrain frames**, including destroyed variants. Enemy names may alias the same artwork; the category table counts each asset ID once and the sequence count deduplicates identical base/frame ranges.
|
||||
+201387
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,82 @@
|
||||
# Complete-original HD review
|
||||
|
||||
Independent review started **2026-10-10**. This is an ongoing acceptance record, separate from the generation jobs and registration coordinator. Full completion requires all **5,753 standalone source picture IDs** to have reviewed, registered remasters and their retained rendering paths to work. Candidate images, queue entries, original fallback and a successful PNG decode do not establish completion.
|
||||
|
||||
## Inventory checked independently
|
||||
|
||||
A strict picture decoder read every one of the 6,449 original catalog entries and checked the recorded byte lengths. Its 5,753 picture IDs, source SHA-256 hashes and logical canvases match the maintained completion ledger and the previous independent Rust pixel audit. **4,768 pictures are outside `sprites/`.** Every picture has `remaster_required: true`; no unused enemy, directional, terrain or miscellaneous picture has been excluded.
|
||||
|
||||
The disjoint queues contain 659 jobs and 5,714 source targets in this snapshot. Their union with the 46 registered pictures covers all 5,753 IDs exactly; seven registered dark-player pictures remain in their frozen queue. Every job target's source hash and canvas match the independent inventory. The original unused WORM direction and DSHP picture families remain explicitly required.
|
||||
|
||||
All 4,535 terrain pictures remain required, including the 734 destroyed-only IDs absent from intact-map screenshots. Original missions reference 3,483 intact and 736 destroyed IDs, a union of 4,217. The complete target inventory includes the terrain pictures not referenced by those missions.
|
||||
|
||||
The **303 AGX delta frames** and **four bitmap fonts** have explicit preservation policies: classic original cinematics and sharply scaled native glyphs. These exceptions are separate from picture coverage and are never counted as HD replacements.
|
||||
|
||||
Snapshot ledger SHA-256: `3b024a1a1dd342afabfb0f3829f1933769869f6a8798059826e796b3f7e34fb4`. The ignored evidence is `.reference/hd-full-review/inventory.json`; later ledger updates need a fresh snapshot rather than treating this dated count as current.
|
||||
|
||||
## Registered pack reviewed
|
||||
|
||||
Manifest SHA-256 `aa2b823acce157f0b49ee2f44bad133cefeca0304905038f004b59b4b6c22835` supplies **46 frames in 16 complete families**. The actual Rust loader decoded each at all four presentation scales, producing 184 correctly sized indexed sprites with no diagnostics. Every source hash, logical canvas and zero origin passed. All seven source/1×/4× contact pages and six separate native-resolution 2×/3× pages were inspected, including every bank pose and every animation frame.
|
||||
|
||||
| Reviewed family | Original IDs | Frames | Assessment |
|
||||
| --- | --- | ---: | --- |
|
||||
| Light player banking | `1011a–10120` | 7 | Bank ordering, materials, canopy, wing tips and engines remain readable. |
|
||||
| Dark player banking | `20045–2004b` | 7 | Distinct dark lighting preserves the approved bank geometry and canvas. |
|
||||
| Ground targets TARGT1/2/3 | `10138–1013a` | 3 | Source orientation and centered mechanism preserved; full opaque canvases intentional. |
|
||||
| Ground target TARGT6 | `1013d–10141` | 5 | Complete opening/firing sequence aligned on the same square structure. |
|
||||
| Fighters SHIP01G1 / SHIP27G1 | `102f8`, `10330` | 2 | Source directions, canopy and gunmetal/blue or neutral material cues retained. |
|
||||
| Industrial boss | `10306` | 1 | Full source footprint and left/right machinery retained. |
|
||||
| Plasma and enemy-cannon projectiles | `101cf–101d0`, `101d6–101d7` | 4 | Readable indexed projectiles; Plasma's known 1× tail shortening remains documented. |
|
||||
| Energy flash/rings | `101ae–101b9` | 12 | Complete expansion sequence aligned; known narrower first flash and thin rings remain documented. |
|
||||
| Forward, Plasma, Dumb Missile, Air Missile equipment | `102b0–102b2`, `102b5`, `102bf` | 5 | Logical inventory canvases retained; loss of original dark opaque outlines remains documented rather than called identical transparency. |
|
||||
|
||||
This pack is **partial: 5,707 required pictures still have no registered remaster in this snapshot**. Static fighter approval does not cover other old enemies, bosses, directional variants or terrain. A generated menu background does not count as remastering the original portraits, HUD and retained menu pictures.
|
||||
|
||||
Evidence: `.reference/hd-full-review/snapshot-aa2b823a/` contains the exact manifest snapshot, fresh actual Rust 1×–4× indexed outputs, `runtime-qa.json` and every contact-sheet page. The loader library is current for asset decoding and HD loading. Desktop rendering changed after that library was built, so this gallery does not establish those newer runtime routes.
|
||||
|
||||
A subsequent registered snapshot, **63 frames**, has manifest SHA-256 `33ec0a782a852ffef36880432d3e8d472f18a39e9a31d545fe47eb0256bb0dfd`. Fresh actual Rust validation passes all 252 scale outputs without diagnostics. Every prior 46 frame's indexed output is byte-identical to the previous reviewed snapshot; the 16 canonical terrain frames and Super Shield pickup match their independently reviewed isolated outputs. The newly added terrain's native 2× and 3× outputs were also inspected. The picture inventory still has **5,690 unregistered targets** at this snapshot. Evidence: `.reference/hd-full-review/snapshot-33ec0a78/review-comparison.json`.
|
||||
|
||||
**Palette animation defect found and corrected:** static equipment highlights can select native cycling/fire/flash indices 240–255 despite their original source having none. The PNG source and shape can be valid while the runtime asset flashes unintentionally. The coordinator reported this in Micro Missile/Missile Pods/Mega Bomb candidates and requested an engine correction. Independent pre-fix review found 24 of 63 registered pictures affected across 86 scale outputs. Source-bound palette classes now constrain ordinary art to indices 0–239, water/fire to their own cycling group, and flash masks to exact original labels. Fresh independent post-fix galleries pass **all 63 registered pictures and ten additional equipment frames at scales 1–4 (292 outputs)** with no newly introduced dynamic index. All 24 changed registered frames and all ten equipment frames were visually re-inspected without a shape or readability regression. Evidence: `.reference/hd-full-review/snapshot-63-palette-audit-fixed/` and `equipment-palette-fixed/`.
|
||||
|
||||
## Isolated candidate reviews
|
||||
|
||||
| Candidate | Candidate JSON SHA-256 | Actual Rust outputs | Status |
|
||||
| --- | --- | ---: | --- |
|
||||
| Terrain `10360–1036f` | `ad6062f325e730bdb71d22190ff1589f5d86f6c69c62ed94e8caacfdd9ce955c` | 16 × four scales | All source/1×/4× rows inspected; 48 contexts, 755 cardinal pairs and four native scene captures independently pass. Approved for partial-pack registration. |
|
||||
| Terrain `2010d–2011c` | `74d3a5e8cc32569c849c8baeee3a47d7c265e238c5d00b5f84a98a52198e8527` | 16 × four scales | Held: `20111` invents a tree canopy absent from source. |
|
||||
| TARGT5 v2 `1013c` | `e7af1db9fb30f995eaf2c2c360f86c952401bac874087b4f4c462487a9e9ca8a` | One × four scales | Structure preserved and prior orange speckles removed; darker soil needs neighbor review. |
|
||||
| TARG10 v1 `10161` | `44a2720e6b6164cb96888a024b5fc068c4abe6e331cff54e3389c721890c4eb4` | One × four scales | Cannon pose preserved; orange border speckles and soil color need correction or contextual approval. |
|
||||
| Super Shield pickup `102d0` | `adf4b4823aee9bade62b755b170bc0393b73d84c02653e51827109016b3d97ea` | One × four scales | Independently approved; golden S glyph, pointed silhouette, transparency and source canvas preserved. |
|
||||
|
||||
All five isolated packs passed source hash, canvas, origin, PNG/crop and nonempty indexed validation without diagnostics. That result does not approve their artistry. TARGT5's outer three logical pixels change from source mean RGB 124/76/63 to indexed 1× 74/32/28. TARG10 changes from 104/65/55 to 4× 92/40/30 and introduces bright orange border pixels (1.63% of that band; source zero). These measurements explain the requested ground-neighbor check; they are not universal numeric art gates.
|
||||
|
||||
Evidence is under `.reference/hd-full-review/terrain-10360-1036f/`, `terrain-2010d-2011c/`, `held-targt5/`, `held-targ10/` and `equipment-102d0/`. Only source inspection and preview extraction were performed; no candidate pixels or global registration were changed.
|
||||
|
||||
## First terrain family's contextual acceptance
|
||||
|
||||
Candidate `10360–1036f` passed independent inspection of all **48 actual 3×3 contexts** and all **755 cardinal pairs** across 24 review pages: 170 HD/HD joins and 585 HD/original joins. Trees, dirt/grass boundaries, adjoining native coastlines, roads, structures and existing destroyed neighbors keep their source arrangement. No tile hole, crossing-material leakage or low-resolution interior edge grid was found. Texture contrast at the temporary HD/original boundary remains visible, so this approval is for the current partial pack and does not establish finished full-map consistency.
|
||||
|
||||
Four actual native scene captures show phase 0, phase 31, intact terrain and settled destruction. The engineer's runtime assertions compare cached and fresh composition at every one of 32 scroll phases, preserve simulation/pilot/script/RNG state across draw calls, and invalidate the prepared layer after mod hash or load-order changes. The original debug-profile isolated warmed scene averaged 2.97 ms with a 2.76 MiB retained strip and 2 MiB transition lookup. These scene results are distinct from the PNG gallery check.
|
||||
|
||||
This family has **no declared destroyed-state partners**. Its destruction capture exercises another native tile's intact/settled cache transition; it does not approve generated HD debris. Those 734 destroyed-only targets remain required in their own jobs.
|
||||
|
||||
Runtime proof SHA-256: `c00e11d26e2a49a6b0f2e156581a7a7b1fc217f922a34e6b70ed7dbd5b95a0de`. Independent evidence is `.reference/hd-full-review/terrain-10360-1036f/context-review.json`, its 24 cardinal pages and three 48-context contact parts. Original native captures are under `.reference/terrain-hd/terrain-10360-1036f/`.
|
||||
|
||||
## Runtime routes and remaining gates
|
||||
|
||||
The former original beam/effect routing gaps have dedicated native composition paths. Beam shafts, muzzle/power/impact companions and secondary smoke/sparks/glows preserve original frame IDs and original anchors. Original shadows deliberately retain the original logical masks and collision dimensions.
|
||||
|
||||
Code review of the newly extended HUD, timed currency pickup and light-mask routes confirms they request HD frames at the presentation scale while retaining source logical placement. Light masks call indexed `Canvas::shade` instead of painting RGBA colors. Generic HUD assets keep nearest-neighbor source fallback; bitmap font text remains sharply scaled. Focused runtime fixtures now pass at **1× and 4×**: generic HUD and equipped-weapon icons use supplied HD pixels at the original logical anchor; a sparse red RGBA shield replacement uses its own alpha holes and transforms background index **37→44** through the light table rather than painting its red foreground. World/pilot state remains unchanged. The broader 13 original-rendering checks also pass. A dedicated full `Host::scene` currency fixture now passes at 1×/4× and interpolation α=0/0.5: synthetic native `N$_PIC` stays green in SD while supplied red/blue HD halves appear at the interpolated source anchor, with every pickup pixel and its surrounding black border checked. At 4× the half-step anchor is `(246,410)`. World digest, RNG, pickup countdown/previous-art-position/glow-frame, pilot and clock state remain unchanged; no HD diagnostic appears. This proves route behavior separately from artistic acceptance; no currency remaster is approved by that synthetic test.
|
||||
|
||||
Canonical terrain candidate review and actual terrain composition checks are ongoing. Fresh isolated Rust review decoded all 32 tiles in the first two groups at scales 1–4 without diagnostics; all eight paged source/1×/4× contact sheets were inspected. The first group (`10360–1036f`) keeps the broad tree/grass/dirt placements. The second group is held for the concrete source drift below. Every candidate tile requires independent source geometry review plus neighboring placements, mixed HD/original joins, destroyed states and scroll phases. Opaque alpha-mask overlap alone is trivial and is not an artistic quality gate. Rendering test success adds no artwork coverage.
|
||||
|
||||
## Open findings
|
||||
|
||||
- Full art generation and registration are unfinished; preserve the per-ID checklist through every cohort.
|
||||
- Generic HD palette quantization is corrected and independently passes all current supplied frames at scales 1–4. Preserve this source-class gate for every future family.
|
||||
- Firing-player frames require separate source-contour review, especially straight frame `2004f`; the dark/light banking review does not approve that sequence.
|
||||
- Generic HUD, lighting-mask and timed-currency routes now pass supplied-art runtime fixtures at 1×/4×, preserving indexed shading and original/interpolated anchors. Review their actual generated artwork separately when candidates arrive.
|
||||
- **Hold terrain `2010d–2011c`: picture `20111` invents a tree canopy.** Its original palette contains olive grass and brown dirt only; the remaster adds a bright green canopy at the upper-left covering about 15% of logical pixels. The source hash and opaque alpha-mask overlap pass, demonstrating why those checks do not establish source geometry. Regenerate that picture with the original grass/dirt arrangement before approving the family. Coordinator, artist and terrain engineer were notified. Proof: `.reference/hd-full-review/terrain-2010d-2011c/runtime-contact-sheet-002.png` and `20111-original-enlarged.png`.
|
||||
- First canonical terrain group `10360–1036f` is independently approved; complete the corrected second group contextual review before registration.
|
||||
|
||||
This record will be extended as new complete families arrive. Original-derived artwork and review renders remain in ignored `data/` and `.reference/`; authored mod artwork and sounds remain Git-tracked under `RustRaptor/mods/`.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Shared HD generation workflow
|
||||
|
||||
Prepare and inspect every source reference, check the palette binding, and save the complete prompt before acquiring a slot. Acquire only when ready to submit. A slot must never cover local preparation, a model turn, artwork copying or visual review.
|
||||
|
||||
Use **one `functions.exec` orchestration** for acquire → start → built-in imagegen → outcome report. Submit only when acquisition succeeds and `start.changed` is `true`. Run the report immediately after the awaited tool returns or throws, before returning the result to the model. Then copy the saved raw PNG unchanged and perform artistic/runtime review. Successful generation does not mean accepted artwork.
|
||||
|
||||
Observed built-in successes have `image_url` and `output_hint` keys. `image_url` contains a PNG data URL; never print or serialize it to a log. `output_hint` identifies the saved raw PNG. Observed connection failures throw `image generation failed: connection failed: error sending request`. Also check returned `isError`, textual error fields and unexpected result shapes; a nonthrowing failure is still a failure.
|
||||
|
||||
The following skeleton runs inside `functions.exec`. `preparedArguments`, `owner`, `job`, `gatePath`, `projectRoot` and `errorFile` must already be known. Shell arguments are quoted individually; the error file is written without evaluating its contents.
|
||||
|
||||
```javascript
|
||||
const quote = s => "'" + String(s).replaceAll("'", "'\"'\"'") + "'";
|
||||
async function gate(...args) {
|
||||
const r = await tools.exec_command({
|
||||
cmd: ["python3", gatePath, ...args].map(quote).join(" "),
|
||||
workdir: projectRoot, max_output_tokens: 800
|
||||
});
|
||||
if (r.exit_code !== 0) throw new Error("Generation gate command failed");
|
||||
return JSON.parse(r.output);
|
||||
}
|
||||
const lease = await gate("acquire", "--owner", owner, "--job", job);
|
||||
if (!lease.acquired) { text(lease); exit(); }
|
||||
const started = await gate("start", "--owner", owner, "--lease", lease.lease_id);
|
||||
if (!started.changed) {
|
||||
if (["admissions_paused", "active_request_accounting_over_capacity"].includes(started.reason)) {
|
||||
// This newly acquired reservation has not submitted a request.
|
||||
text(await gate("release", "--owner", owner, "--lease", lease.lease_id));
|
||||
}
|
||||
text(started); exit();
|
||||
}
|
||||
let result, outcome = "failure", errorText = "";
|
||||
try {
|
||||
result = await tools.image_gen__imagegen(preparedArguments);
|
||||
const returnedError = result?.error?.message || result?.error || result?.message;
|
||||
if (result?.isError || returnedError) {
|
||||
errorText = typeof returnedError === "string" ? returnedError :
|
||||
result?.content?.filter(x => x.type === "text").map(x => x.text).join("\n") ||
|
||||
"Built-in imagegen returned an error";
|
||||
} else if (typeof result?.image_url === "string" &&
|
||||
result.image_url.startsWith("data:image/") &&
|
||||
typeof result?.output_hint === "string") {
|
||||
outcome = "success";
|
||||
} else {
|
||||
errorText = "Unexpected built-in imagegen result shape";
|
||||
}
|
||||
} catch (error) {
|
||||
errorText = String(error?.message || error);
|
||||
} finally {
|
||||
if (/connection failed|error sending request/i.test(errorText)) outcome = "connection_failure";
|
||||
const reportArgs = ["report", "--owner", owner, "--lease", lease.lease_id,
|
||||
"--outcome", outcome];
|
||||
if (errorText) {
|
||||
const code = "from pathlib import Path; import sys; Path(sys.argv[1]).write_text(sys.argv[2])";
|
||||
const written = await tools.exec_command({
|
||||
cmd: ["python3", "-c", code, errorFile, errorText].map(quote).join(" "),
|
||||
workdir: projectRoot, max_output_tokens: 200
|
||||
});
|
||||
if (written.exit_code !== 0) throw new Error("Could not save imagegen error; retain lease for recovery");
|
||||
reportArgs.push("--error-file", errorFile);
|
||||
}
|
||||
text(await gate(...reportArgs));
|
||||
}
|
||||
if (outcome === "success") generatedImage(result);
|
||||
```
|
||||
|
||||
The four-slot gate retains started requests and old requests with unknown status until explicit completion or recovery. Only unstarted reservations expire after thirty minutes plus a two-minute grace period. `start` refuses duplicate starts, unknown legacy requests, paused admissions and over-capacity accounting. A restored unknown request is immediately flagged for owner attention. No gate command starts, stops or terminates an imagegen tool.
|
||||
|
||||
After interruption, recover the original yielded cell or saved result before any new submission. Report a recovered completion, or release a reservation only after confirming no request was submitted/running. Do not infer completion from age. `status` reads lease phases, ages, admission state and recent outcomes without changing reservations. Root can pause new admissions while reconciling unknown calls; existing calls may still finish and report. Eligibility currently uses the first available slot with one request per owner, without a FIFO queue.
|
||||
|
||||
Exact sanitized service errors and UTC report times stay in ignored `data/hd/jobs/.imagegen-gate/events.jsonl`. Connection failures impose a 60–600-second cooldown and at most three failures per job in a thirty-minute retry batch. During denial or cooldown, prepare or review another job. Do not replace a failed remaster with scaled original art.
|
||||
@@ -0,0 +1,81 @@
|
||||
# Independent review of original HD artwork
|
||||
|
||||
Reviewed on 2026-10-10 against the original loose catalog, native artwork references, a fresh Rust decoder/HD-loader probe, and the registered pack. This review was performed separately from the artwork generation and registration jobs.
|
||||
|
||||
The original catalog inventory is complete. **The remaster is still partial:** the reviewed snapshot has 32 registered replacements out of 5,753 standalone pictures. All 32 load successfully and produce visible, correctly sized indexed sprites at presentation scales 1–4. No blocking clipping, background, source-hash or banking-order defect was found in those registered frames. This does not establish full-game visual parity or approve the unregistered drafts.
|
||||
|
||||
## Independent evidence
|
||||
|
||||
Every one of the 6,449 catalog entries passed its recorded byte-size check. A separate strict Python decoder, which does not import the existing artwork audit or source decoder, produced exactly the same picture IDs, dimensions and indexed pixels as a fresh Rust `Sprite::decode` pass. This verified all 5,753 standalone pictures, including **4,768 outside `sprites/`**: 232 in `tiles/` and 4,536 in `misc/`.
|
||||
|
||||
The fresh Rust probe also decoded and applied all **303 AGX delta frames across 13 streams**, checked the active HD pack with no diagnostics, and requested each registered frame at all four scales. Four bitmap fonts were inventoried separately. Their glyphs should retain sharp indexed scaling; cinematic delta streams require a dedicated treatment rather than ordinary sprite registration.
|
||||
|
||||
Original source SHA-256 hashes, logical dimensions and replacement anchors match every registered frame. All active player crop rectangles exclude **zero pixels with alpha at least 32**. The complete runtime sheets were inspected for light-player banking, the boss, two fighters and ground target, equipment, projectiles, and every energy-ring frame.
|
||||
|
||||
Local evidence is saved under `.reference/hd-independent-review/`: `runtime_probe.rs`, `runtime.json`, `audit.py` and `report.json`. The report records per-frame hashes, silhouettes, visible bounds and scale-specific results. It is ignored alongside the original assets. The reviewed manifest SHA-256 is `b75330698a7baac975988cc206f040e1addbe1bb873a210e032a343c78a9e582`; later registered families require another review.
|
||||
|
||||
## Coverage and omissions
|
||||
|
||||
The category correction below includes gameplay artwork that the earlier generation backlog had incorrectly grouped under UI/misc. The overall picture inventory was already complete; these were omissions from the dedicated animation job list.
|
||||
|
||||
| Original category | Picture frames | Registered HD in this snapshot | Remaining |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Player banking and firing flash | 21 | 7 | 14 |
|
||||
| Bosses | 249 | 1 | 248 |
|
||||
| Enemies and ground objects referenced by sprite libraries | 419 | 3 | 416 |
|
||||
| Main player/enemy projectile sprites | 51 | 4 | 47 |
|
||||
| Explosions, smoke, sparks, glows and beam companions | 281 | 12 | 269 |
|
||||
| Equipment and pickups | 47 | 5 | 42 |
|
||||
| Terrain | 4,535 | 0 | 4,535 |
|
||||
| Menus, portraits, HUD and other retained pictures | 150 | 0 | 150 |
|
||||
| **Total standalone pictures** | **5,753** | **32** | **5,721** |
|
||||
|
||||
Native animation registration and direct rendering references revealed **77 gameplay effect frames** missing from the explicit effect sequence backlog:
|
||||
|
||||
| Family | Original asset range / count | Original reference |
|
||||
| --- | --- | --- |
|
||||
| Laser impact | `0x10229`, 4 | `anims.cpp` |
|
||||
| Small smoke | `0x1020a`, 9 | `anims.cpp`; descending smoke reuses the last 5 |
|
||||
| Smoke trail | `0x1022d`, 4 | `anims.cpp` |
|
||||
| Lightning / energy grab | `0x1018e`, 14 | `anims.cpp`; two animation handles share these frames |
|
||||
| Blue sparks | `0x1019c`, 9 | `anims.cpp` |
|
||||
| Orange sparks | `0x101a5`, 9 | `anims.cpp` |
|
||||
| Gun/casing streaks | `0x101c7`, 4 | `anims.cpp`; muzzle and ejected-casing handles share them |
|
||||
| Super-shield glow | `0x10249`, 4 | `anims.cpp` |
|
||||
| Death Ray power | `0x10239`, 4 | `shots.cpp` |
|
||||
| Laser power | `0x1023d`, 4 | `shots.cpp` |
|
||||
| Beam impact | `0x101f1`, 4 | `shots.cpp` / `eshot.cpp` |
|
||||
| Enemy laser power | `0x10245`, 4 | `eshot.cpp` |
|
||||
| Pickup light mask | `0x10225`, 4 | `bonus.cpp` |
|
||||
|
||||
These ranges are counted once despite shared handles and overlapping smoke subsets. The coordinator has incorporated them into the maintained backlog. Pickup glow, descending smoke and super-shield masks use palette-table shading in the original renderer; a colored RGBA replacement alone does not reproduce their operation. The lightning family uses ordinary indexed sprite drawing.
|
||||
|
||||
The four native sprite libraries contain 528 enemy records sharing 262 art sequences and 668 picture frames. The 27 original mission layouts reference 512 records and 260 sequences. Static fighters do not represent the remaining animated bosses, ground targets or directional variants. An additional 33 enemy-shaped pictures remain in the retained miscellaneous inventory: the three unused eight-frame `WORMDN`, `WORMRITE` and `WORMLEFT` families, and nine `DSHP` pictures. No references to those pictures were found in the native sprite libraries or rendering code; retain their catalog entries and identify them explicitly before any cleanup.
|
||||
|
||||
The original `SPACE_AGX` stream contains 22 of the 303 cinematic frames and has no playback reference in the current native source. It remains cataloged and decodable. The classic intro, landing, death and campaign-ending streams remain original artwork; the new authored menu background is a separate packaged asset, not HD coverage for the original logo/menu/portrait pictures.
|
||||
|
||||
## Registered artwork quality
|
||||
|
||||
All seven light-player bank poses remain distinct, progress in the original order and keep coherent silver/gunmetal materials and red markings. The independent logical masks match the recorded 87.7–92.3% silhouette overlap. The nose, canopy, wing ends and engine exits remain visible; maximum logical bound difference is one pixel. The original mask still determines collision dimensions and shadow geometry. The dark-sector `DPLAYER` and firing-flash `FPLAYER` seven-frame families are not registered in this snapshot. The straight firing-flash frame has a different source mask and must receive separate review, even when other poses can reuse lighting edits.
|
||||
|
||||
The first industrial boss, two fighters and opaque ground target preserve their source orientation and canvas. The boss's interpreted metal machinery and both fighters' finer contours differ from the source. Neither their static-frame approval nor their shared palette establishes completion of other enemy/boss families.
|
||||
|
||||
Equipment retains readable visible metal geometry and the two Plasma icon frames remain aligned. The remaster removes nearly black opaque source outlines around the equipment. Consequently full alpha-mask overlap is only about 54–78%, while the separately measured visible-material masks have approximately 93–100% overlap. Both measurements should remain visible in provenance; the material result must not be reported as identical source transparency.
|
||||
|
||||
Small-scale resampling has two documented differences. At 1×, the Plasma projectile loses one tail row (original heights 8/6 visible pixels become 7/5); 4× retains its original logical extent. The first energy flash becomes narrower at 1×, with source bounds `[3,2,38,37]` becoming `[7,5,34,34]`; at 4× its bounds are approximately `[4.75,3.25,36.5,35.75]` in logical units. The later energy rings are smoother and thinner, so silhouette overlap alone is not a useful acceptance gate. These are presentation differences rather than discarded PNG crop pixels. Check the 1× option in motion and retain a crisp fallback if the shorter flash or projectile reduces readability.
|
||||
|
||||
## Remaining rendering and integration checks
|
||||
|
||||
The review found routing gaps that are independent of generation quality. The original equipped-weapon HUD path uses the raw catalog sprite, so registered base equipment HD is bypassed there even though the shop and pickups can use it. Native HUD digits, bomb/shield icons and warning labels also use an intentionally crisp path. New HD portraits already have an appropriate optional route, but no portrait replacements are registered.
|
||||
|
||||
The native beam renderer repeats the main laser sprite along its full line and draws power/impact companion frames. The reviewed Rust projectile path draws the main original projectile sprite without that native composite. Similarly, the current `EffectKind` models the eleven explosion/effect families, but the additional smoke, sparks, muzzle/casing, lightning and glow events found above have no corresponding original-art effect path in that snapshot. Registering their HD PNGs would not by itself make those native visual events appear. Restore and screenshot the original composition/event paths before claiming complete visual fidelity, then review their HD replacements against that output.
|
||||
|
||||
Terrain has four sets of 672, 1,437, 1,082 and 1,344 pictures. They contain 4,524 unique original payloads and decoded indexed images. The original missions require **3,483 intact frame IDs and 736 destroyed frame IDs**, a union of 4,217; **734 are destroyed-only** and therefore absent from an intact-map screenshot. There are 889 destructible intact frames. Those destruction states belong in the remaster jobs and seam tests.
|
||||
|
||||
The reviewed 3×3 shoreline draft is continuous internally and preserves the broad coast, but nine placements contain only six distinct original tile IDs. The generated copies of a repeated tile are not guaranteed identical. It cannot safely be split into global per-ID replacements without a reusable edge contract or context-aware terrain chunks. External neighbors, alternate placements, destroyed states, scrolling offsets and palette cycling remain unverified, and native terrain currently lacks a catalog-HD composition route. The terrain draft remains correctly unregistered.
|
||||
|
||||
Before wider opt-in release, prioritize complete dark/firing player families, the original effect/beam routing gaps, representative remaining animated enemy and boss sequences, and context-aware terrain including destroyed variants. Follow that with remaining equipment/projectiles, original portraits/HUD/logo assets where retained, and a separately approved cinematic treatment. Keep the complete ID inventory as the completion checklist; filenames, a single mission screenshot and the number of generated PNGs are insufficient coverage measures.
|
||||
|
||||
## Follow-up after this snapshot
|
||||
|
||||
The engine now restores native beam composition and secondary visual events, routes optional HD artwork through HUD pictures and indexed light masks, and composes canonical source-bound HD terrain with neighboring context and a bounded scrolling strip. The complete dark-player family has also passed review. These corrections do not complete the remaster: all 5,753 picture IDs remain required, including unused original pictures and destroyed terrain. The ongoing [complete-original review](hd-full-review.md) records subsequent exact manifest hashes, complete-family approvals and held corrections; the [completion ledger](hd-completion.json) is the current per-ID checklist. Classic original cinematics and sharp bitmap fonts keep their explicit preservation policies.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Opening-credit HD rendering
|
||||
|
||||
The existing HD setting can display supplied replacements for the standalone
|
||||
opening-credit pictures. Each keeps its original 320×200 canvas and top-left
|
||||
origin, with original-picture fallback when a replacement is absent or rejected.
|
||||
|
||||
| Picture | Original ID | Unfaded source palette |
|
||||
| --- | --- | --- |
|
||||
| `APOGEE_PIC` | `1002b` | `POGPAL_DAT` |
|
||||
| `CYGNUS_PIC` | `1002d` | `CYGPAL_DAT` |
|
||||
|
||||
`Cinematic::still_picture` exposes the current card and its unfaded palette.
|
||||
`HdAssets::sprite_static_palette` quantizes supplied artwork using all 256 colors
|
||||
of that palette. Gameplay water/fire/flash index classes do not apply to these
|
||||
static palettes. The indexed cache distinguishes this policy from ordinary
|
||||
gameplay sprites while sharing source validation, RGBA residency and derived
|
||||
disk assets. Existing cinematic presentation applies the fade afterward, so
|
||||
black or partially faded colors never become quantization inputs.
|
||||
|
||||
Opening timing, music and PCM patch generation are unchanged. AGX delta films
|
||||
and bitmap fonts retain their classic artwork and playback. This rendering
|
||||
support adds no artwork coverage; actual opening-card remasters still require
|
||||
source-palette, composition and artistic review before registration.
|
||||
|
||||
Synthetic tests cover both cards at 1× and 4×, colors above index 239, complete
|
||||
fade phases, missing/rejected replacement fallback, separate cache policies,
|
||||
and unchanged world/pilot/RNG plus cinematic audio/music event streams. The
|
||||
complete synthetic opening remains 569 ticks. Local original-data checks also
|
||||
pass: the opening retains 966 ticks and the AGX intro retains 925 ticks, with
|
||||
their original cue times and palettes. The focused run passed 28 portable checks
|
||||
and both original-data cinematic checks on 2026-10-10.
|
||||
|
||||
Run the desktop route fixtures from `RustRaptor/` with
|
||||
`cargo test --locked --lib opening_card`. The cinematic and palette/cache suites
|
||||
are `cargo test --locked --lib cinematics::tests::` and
|
||||
`cargo test --locked --lib hd::tests::`. Original-data checks require
|
||||
`RAPTOR_TEST_DATA=../data cargo test --locked --lib cinematics::tests:: -- --ignored`.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Full-catalog HD loading and caches
|
||||
|
||||
The runtime can register the complete original picture catalog without retaining every decoded replacement in RAM. Registration accepts up to 8 MiB of JSON metadata, 8,192 sequences and 16,384 distinct asset IDs. Each sequence still has at most 512 declared frames. These are validation ceilings, not coverage claims.
|
||||
|
||||
`HdAssets::load_lazy(root, catalog, cache_dir)` checks all sequence IDs before committing the manifest. Duplicate IDs, arithmetic overflow, unsupported formats and invalid sequence lengths reject the pack atomically. Each supplied frame must match the checked original payload SHA-256, logical canvas and `[0,0]` anchor. Its PNG path must resolve inside the pack, including symlinks. Missing files and individual source mismatches produce diagnostics and native-art fallback.
|
||||
|
||||
Registration does not imply a PNG has been decoded. `registered_count()` reports source-checked candidates that have not been rejected; `validated_count()` and the compatibility alias `coverage()` report frames whose artwork has passed validation. First use checks PNG format, crop bounds, matching aspect ratio, at least 2× logical resolution, visible pixels and source-aware transparency. Transparent originals require transparent negative space; opaque originals accept opaque art. A failed frame returns `Ok(None)` from `sprite`, records one diagnostic and keeps native fallback until the pack is reloaded.
|
||||
|
||||
`HdAssets::load` performs full eager validation using the same bounded caches. `validate_all()` can also validate a lazily registered pack; its report distinguishes registered, validated and rejected frames. Full validation can therefore be used in a CLI or audit without raising runtime memory limits. It validates supplied artwork and does not infer coverage for absent originals.
|
||||
|
||||
## Memory bounds
|
||||
|
||||
Each PNG is limited to 32 MiB on disk, 4,096 pixels per dimension, 4,194,304 total pixels and a 64 MiB decoder allocation limit. Reads are bounded even if a file grows after metadata inspection. A loaded frame retains at most its logical canvas at 4× resolution. Larger generated originals remain unchanged on disk.
|
||||
|
||||
Three least-recently-used caches each have a 32 MiB ceiling:
|
||||
|
||||
- Decoded atlas images, keyed by the exact source PNG SHA-256.
|
||||
- Cropped, resampled RGBA frames, keyed by PNG identity, original payload SHA-256, crop, canvas, anchor, alpha requirement and kernel version.
|
||||
- Indexed sprites, keyed by the derived frame identity, presentation scale, complete palette identity and palette policy.
|
||||
|
||||
Resident cached pixels total at most 96 MiB, irrespective of catalog coverage. Decoder input, resampling work buffers and returned owned sprites are additional bounded working allocations; callers should retain only currently needed sprites. Eviction removes cached pixels, not frame registrations or validated coverage. Adjacent crops reuse the decoded atlas; an atlas is not cloned in full merely to extract a small crop. Repeated draws copy only the final indexed canvas. Palette changes cannot reuse stale indexed colors.
|
||||
|
||||
Source palette labels have a separate hard 32 MiB metadata ceiling. Frames using only ordinary colors need no label map. Frames containing animated labels retain one byte per original logical pixel; transparent and ordinary source locations use the ordinary class. Quantization restricts ordinary HD pixels to indices 0–239, water to 240–244 and fire to 245–249. Original glow/flash labels 250–255 remain exact. Newly visible HD pixels outside the original silhouette use ordinary colors. This prevents a bright static highlight from acquiring fire, water or blinking behavior. Cache color keys include the source class; equal RGB values in different classes cannot share an incorrect palette result.
|
||||
|
||||
Standalone opening cards use `sprite_static_palette` with all 256 entries of their own unfaded palette. Its indexed cache policy is distinct from gameplay source classes; both share the same checked PNG and RGBA caches. Cinematic fades apply during presentation. See [hd-opening-cards.md](hd-opening-cards.md) for the route and fallback checks.
|
||||
|
||||
## Derived disk cache
|
||||
|
||||
The optional cache belongs under the separate Rust profile cache, for example `RaptorReplica/cache/hd-art-v1`. It stores original-independent derived RGBA files; originals and raw generations are never overwritten. Each filename binds the source PNG hash, exact original payload hash, crop, logical dimensions, anchor, alpha policy and resampling kernel. Palette-dependent indexed sprites remain in memory only.
|
||||
|
||||
A derived file has a versioned header, exact frame identity, dimensions and payload SHA-256. Reads recheck dimensions, byte count, alpha policy and checksum. A corrupted or escaping symlink cache entry is discarded and rebuilt from the checked PNG. Artwork continues to work if the cache is unwritable. Files are written to a new temporary file and atomically renamed; interruption may lose a cache entry, which is safe to regenerate.
|
||||
|
||||
The disk cache is capped at 512 MiB. Older generated `.rgba` files are evicted when space is needed; unrelated files and original art are untouched. Delete this cache to reclaim space at any time. A changed PNG, original source payload, crop or kernel produces a different cache identity.
|
||||
|
||||
## Validation
|
||||
|
||||
`raptor-rust --data /absolute/path/to/data --validate-hd` fully validates the supplied pack and exits unsuccessfully if any supplied artwork fails. Its totals describe supplied frames; the full-catalog completion ledger separately records originals that are still missing replacements.
|
||||
|
||||
Portable synthetic tests exercise a 5,753-frame manifest, full validation, adjacent atlas crops, memory eviction, palette changes and source animation classes at every scale, 4× residency, malformed PNGs, original hash mismatches, corrupted caches, disk eviction and escaping artwork/cache symlinks.
|
||||
|
||||
Run the local original-data integration check with:
|
||||
|
||||
```sh
|
||||
RAPTOR_TEST_DATA=/absolute/path/to/data \
|
||||
cargo test --locked --lib hd::tests::original_hd_pack_validates_with_bounded_residency \
|
||||
-- --ignored --nocapture
|
||||
```
|
||||
|
||||
That check validates every supplied frame and renders it through `HdAssets::sprite` at scales 1–4 with the original palette. It checks dimensions, visible output and each cache ceiling. Visual review, animation alignment and terrain seams remain separate acceptance checks.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Original terrain remaster pipeline
|
||||
|
||||
HD terrain uses the original catalog ID of each currently displayed tile. Intact and destroyed pictures have separate IDs and source hashes; an intact replacement is never reused for a destroyed tile. Native maps keep intact art until their original destruction animation settles. JSON missions retain their existing immediate destroyed-state selection. Rendering does not change terrain health, rewards, collision dimensions, scrolling, update order or RNG.
|
||||
|
||||
The renderer first joins the original tiles at their logical dimensions on a row-aligned 320×268 indexed strip. The additional rows and two-pixel filtering gutter supply neighboring context for the selected Scale2x, Scale3x or Scale4x filter throughout all 32 native scroll phases. This original layer is assembled without consulting HD art, even when HD mode is enabled. Missing or rejected replacements therefore retain the complete original terrain treatment.
|
||||
|
||||
Reviewed native HD tiles are fetched lazily from `HdAssets` only for visible placements. Mod terrain can supply `hd_png`; a source PNG already at least twice its logical dimensions can also keep its supplied detail. Replacements are drawn directly at the selected presentation scale. Every placement uses its original logical anchor and the gutter offset. The completed layer is translated once for fractional scrolling before shadows, enemies, projectiles, effects and the ship are drawn. Individual tiles do not round their scroll offsets independently.
|
||||
|
||||
## Canonical tile and edge contract
|
||||
|
||||
Each original terrain ID needs one canonical full opaque 32×32 logical image, including IDs stored outside the named sprite folders. A generation job should include the exact original source, all cardinal neighbors encountered in mission layouts, actual 3×3 context samples and any intact/destroyed partner. Preserve coast crossings, road widths, building footprints, color families and edge geometry. Fine texture may improve; a displaced coastline or invented solid structure needs correction before acceptance.
|
||||
|
||||
A canonical tile may occur beside several different neighbors. The renderer prepares one pristine joined HD layer for all accepted visible tiles before resolving their edges. Where original material classes and source tones agree, adjacent HD tiles share corresponding pristine detail at each mirrored distance from the join. Four-tile corners apply this rule in both axes. This preserves fine grain rather than stretching a single outer-edge sample into repeated ridges. Distinct original coastline, road or material boundaries remain sharp. Each tile reaches its own full detail two logical pixels inward using integer smoothstep weights.
|
||||
|
||||
At an HD/original join, the replacement transitions to the unchanged joined original context instead. This preserves mixed coverage and source fallback. Full HD neighborhoods retain detailed colors across their internal edges, avoiding the grid produced by forcing every tile back to low-resolution source colors. Source geometry remains a generation and review requirement; edge blending does not repair a shifted coastline.
|
||||
|
||||
Opaque replacements must match `logical_width × scale` and `logical_height × scale` exactly. An unexpected transparent pixel or invalid canvas rejects the whole terrain overlay and adds a diagnostic. Fully opaque source pictures are accepted by the source-aware HD loader; artificial transparent holes are unnecessary.
|
||||
|
||||
The previous generated 3×3 shoreline prototype remains unregistered. It contains nine placements but only six unique original IDs, and its fine detail varies between repeated placements. Splitting it into global ID replacements would select arbitrary placement-specific art. Canonical jobs and source-bound review are required before those IDs count as remastered.
|
||||
|
||||
## Palette and memory behavior
|
||||
|
||||
The final terrain canvas remains indexed. Original water labels stay within the 240–244 cycling group; fire stays within 245–249. Original glow, flash and blink masks at 250–255 retain their exact labels. Ordinary terrain stays within 0–239, so quantizing an HD highlight cannot accidentally turn it into a blinking light. Palette cycling, blue flashes, fades, scanlines and subsequent indexed shadow shading therefore continue to apply to the complete scene.
|
||||
|
||||
The compositor retains a lazy 2 MiB palette transition table, invalidated when the base palette changes, and one prepared row-aligned viewport strip. At 4× its indexed image uses at most 2,744,320 bytes, plus a cache key capped at 64 KiB. It retains no individual HD tiles, atlases or mission map images. HD PNG, RGBA and indexed caches use the bounded lazy `HdAssets` provider. Working layers are temporary viewport images, so registering the complete terrain catalog does not require keeping every generated PNG in memory.
|
||||
|
||||
The prepared strip key includes the mission, row origin, selected intact/destroyed art at every visible placement, active mod hashes in priority order, base palette, presentation scale and HD setting. Terrain destruction invalidates it when the selected artwork changes; mod edits or load-order changes cannot reuse a previous content strip. Between those changes, integer scroll phases and interpolation translate the completed indexed strip without reconstructing or re-quantizing tiles. Palette cycling and fades continue at presentation time. Oversized keys or strips are discarded instead of retained.
|
||||
|
||||
## Verification and acceptance
|
||||
|
||||
Run the portable checks with:
|
||||
|
||||
```sh
|
||||
LD_LIBRARY_PATH=/home/quintine/.local/lib cargo test --locked --manifest-path RustRaptor/Cargo.toml --test hd_terrain
|
||||
```
|
||||
|
||||
The thirteen portable tests cover shared edges and four-tile corners at all four scales, fully remastered neighborhoods without a source-pixel grid, fine-grain preservation across join bands, hard material boundaries, mixed HD/original joins, overlay order independence, negative gutters, fractional scroll offsets, whole-tile rejection, palette masks, bounded cache memory, logical dimensions, native destruction settling and source-bound lazy loading of distinct intact/destroyed images. The row-aligned strip matches the previous joined SD viewport pixel-for-pixel at all 32 scroll phases, four scales, five interpolation values and negative row origins. Presentation work between simulation steps is compared against an untouched control for world state, RNG, damage and rewards.
|
||||
|
||||
Two additional original-data checks verified all 27 original missions and 4,217 distinct declared intact/destroyed terrain IDs are opaque 32×32 canvases, and produced a compositor comparison from the unregistered shoreline draft. The comparison is under ignored `.reference/terrain-hd/shoreline-draft-context-proof.png`: joined original Scale4x, the raw placement-specific HD draft, and the actual indexed HD composition. This corrected the visible grid found in an earlier source-color edge transition. A separate ignored release timing test exercises 72 visible HD placements with the 1280×816 gutter canvas. With the distance-aware grain correction, its 50 warm compositions measured 8.97 ms median and 10.46 ms p95; cold composition took 10.79 ms. The earlier edge-sample version measured 5.0/6.6 ms but stretched grain into ridges. These timings cover strip rebuilding only, excluding asset loading, SDL and the rest of the scene; normal cached scrolling reuses the prepared strip.
|
||||
|
||||
Independent review inspected all 66 pages of 1,706 actual neighbor, context and destruction-partner cases, plus seven focused original/pristine/previous/fixed panels. The fix removes repeated industrial and surface-grain ridges without new material, corner or pipe drift. The corpus checked 14,928 overlay applications and 175,134,720 palette-class pixels; a cached native scene averaged 1.63 ms per draw. Evidence is in `.reference/terrain-hd/join-ridge/` and `.reference/hd-full-review/terrain-ridge-final-independent.json`. Artwork acceptance and original-only destroyed-state fallback remain separate from this compositor review.
|
||||
|
||||
The completed strip cache was also measured in the real OpenGL SDL game with HD Stormwall terrain, enemies, projectiles, ship, shadows and HUD. Across 114 measured frames, 1280×800 presentation took 4.08 ms median and 9.86 ms p95 of game work, compared with 15.55/18.64 ms before strip reuse. Fullscreen 1920×1080 with aspect correction took 4.27/9.10 ms; total frame times were 5.86/9.28 ms. A separate cached original-mission `Host` scene averaged 2.97 ms across 60 draws. These local timings use a dummy audio output and describe this scene and machine, not every mission. Both SDL screenshots were inspected for clipping, coastline joins, low-resolution tile grids, foreground order and aspect presentation. Evidence is under `.reference/terrain-hd/stormwall-hd-prepared*.png` and the adjacent timing files.
|
||||
|
||||
Passing renderer tests makes the full catalog renderable; it does not count outstanding generation jobs as accepted artwork. Each supplied tile still needs source/HD review, every encountered neighboring edge and destroyed-state transition checked in real mission layouts, and live captures at the intended display size. Registration, decoded validity, visual acceptance and complete catalog coverage are separate statuses.
|
||||
|
||||
Canonical candidate review tooling uses an isolated manifest and the exact checked `HdAssets` loader, leaving the global manifest and coverage ledger untouched. The first 16-ID group, `terrain-10360-1036f`, produced all 48 source-layout contexts and 755 declared directional neighbor pairs: 170 HD/HD and 585 HD/original. The candidate has no declared destroyed partners. Its actual `Host` scene proof separately tests native destruction settling on a destructible placement, cached-versus-fresh equality at all 32 scroll phases and invalidation on changed mod order or content hash. Captures and JSON records are in `.reference/terrain-hd/terrain-10360-1036f/`. An independent reviewer inspected all context and pair sheets before artistic approval.
|
||||
|
||||
Use these ignored original-data checks for subsequent candidate groups:
|
||||
|
||||
```sh
|
||||
RAPTOR_HD_TERRAIN_JOB=terrain-10360-1036f LD_LIBRARY_PATH=/home/quintine/.local/lib cargo test --locked --release --manifest-path RustRaptor/Cargo.toml --test hd_terrain canonical_terrain_candidate -- --ignored --nocapture
|
||||
RAPTOR_HD_TERRAIN_JOB=terrain-10360-1036f LD_LIBRARY_PATH=/home/quintine/.local/lib cargo test --locked --release --manifest-path RustRaptor/Cargo.toml --lib canonical_terrain_live_scene -- --ignored --nocapture
|
||||
```
|
||||
|
||||
The context/pair check is reusable for any complete canonical terrain candidate. The live scene check selects its first supplied source context and requires that original mission to contain destructible terrain. Successful execution verifies render reachability and consistency; it does not automatically approve generated artwork.
|
||||
@@ -0,0 +1,66 @@
|
||||
# Original opening cinematics
|
||||
|
||||
The normal startup sequence retains the original Apogee and Cygnus logo cards,
|
||||
their individual palettes, the Apogee fanfare and the original city chase,
|
||||
ship, cockpit, firing and explosion animations. **Watch intro** replays that
|
||||
sequence. The new menu background is used after the cinematics; it does not
|
||||
replace any of their artwork.
|
||||
|
||||
The cinematic player uses a separate 70 Hz clock. Original movie rates are
|
||||
converted using the native `GFX_WaitUpdate` rule, integer `70 / frame_rate`
|
||||
ticks per frame. This matters especially for the ship scenes: their native
|
||||
18 fps setting holds each frame for three service ticks. It previously held
|
||||
them for eighteen ticks in the Rust implementation. Frame sound effects now
|
||||
occur after each frame's timed display, as in `MOVIE_Play`.
|
||||
|
||||
| Sequence | Service ticks | Seconds |
|
||||
| --- | ---: | ---: |
|
||||
| Apogee and Cygnus cards, with local original fanfare | 966 | 13.80 |
|
||||
| Complete original introduction | 925 | 13.21 |
|
||||
| Complete startup sequence | 1,891 | 27.01 |
|
||||
| Landing | 288 | 4.11 |
|
||||
| Air and ground death | 1,384 | 19.77 |
|
||||
| Campaign 1 ending, base approach and landing | 1,426 | 20.37 |
|
||||
| Campaign 2 ending, base approach and landing | 1,171 | 16.73 |
|
||||
| Campaign 3 ending, base approach and landing | 1,058 | 15.11 |
|
||||
|
||||
The Apogee hold is derived from the decoded MUS end event, with the original
|
||||
120-tick minimum. The local original score ends at music tick 516, requiring
|
||||
517 music service ticks after it starts. Cygnus plays its two boss sound cues
|
||||
at hold ticks 3 and 120, and starts the looping introduction music at tick 135.
|
||||
The logo fades are 64 ticks in and 63 ticks out. The final landing record is
|
||||
decoded and checked, but its delta is not displayed: that native record fades
|
||||
the preceding frame rather than drawing another frame.
|
||||
|
||||
Jet background patches resolve through the original sound bank's digital
|
||||
variants. The zero-byte catalog FX markers are not decoded as audio. Their
|
||||
repeat intervals use the same quantized pitch table and DSP stepping as the
|
||||
real mixer. The duration helper is checked against actual voice expiry across
|
||||
72 combinations of sample rate, pitch and service clock.
|
||||
Clip transitions stop the outgoing background patch before starting the next
|
||||
one, so a long jet sample cannot spill into the cockpit or firing scene.
|
||||
Chronological play/stop events preserve that order even when an update crosses
|
||||
several scene boundaries. Tagged mixer stops remove the patch's held output
|
||||
immediately while preserving other sounds and their playback positions.
|
||||
|
||||
Run the portable decoder, timing and cue checks from the repository root:
|
||||
|
||||
```sh
|
||||
LD_LIBRARY_PATH="$HOME/.local/lib" cargo test --locked \
|
||||
--manifest-path RustRaptor/Cargo.toml --lib cinematics::tests
|
||||
```
|
||||
|
||||
Run the original catalog integration checks locally:
|
||||
|
||||
```sh
|
||||
LD_LIBRARY_PATH="$HOME/.local/lib" RAPTOR_TEST_DATA="$PWD/data" \
|
||||
cargo test --locked --manifest-path RustRaptor/Cargo.toml \
|
||||
--lib cinematics::tests -- --ignored
|
||||
```
|
||||
|
||||
The checks cover both card palettes, complete logo and introduction durations,
|
||||
music and sound cue times, all original cinematic delta decoding, all sequence
|
||||
rates and rendering-rate independence. Original assets and screenshot captures
|
||||
remain outside version control. The retained native source supplies the frame
|
||||
rates and cue placement; these checks are not a frame-by-frame native video or
|
||||
whole-sequence PCM comparison.
|
||||
@@ -0,0 +1,249 @@
|
||||
# Iron Coast content packages
|
||||
|
||||
Iron Coast contains six independently toggleable mission mods, five shared
|
||||
weapon and enemy mods, a shared soundtrack, six stage boss mods, and an optional
|
||||
campaign assembly mod. Enable **Iron
|
||||
Coast — Complete Campaign** (`iron_coast_campaign`) to select the full campaign
|
||||
and its eighteen dependencies automatically. Each individual mission can also be
|
||||
enabled separately; it selects the five shared content mods, soundtrack and its
|
||||
own boss. Disabling a package also deselects unused dependencies. When this
|
||||
affects other packages, a confirmation lists all affected mods; a single-mod
|
||||
toggle with no dependency changes applies immediately. Dependencies still
|
||||
needed by another enabled mod remain selected. The Mods manager validates versions
|
||||
and dependency order and applies the change at the main menu. The new weapons can also be used in the original campaigns;
|
||||
the enemy packages can be referenced from other missions.
|
||||
|
||||
| Package | Content |
|
||||
| --- | --- |
|
||||
| `iron_coast_campaign` | Links all six missions into one campaign without replacing mission scripts or artwork |
|
||||
| `iron_coast_mission_1` | Breakwater mission, campaign, supply depots, field repairs, terrain, briefing art and mission audio |
|
||||
| `iron_coast_mission_2` | Tidal Relay mission and its local communications targets, terrain, briefing art and audio |
|
||||
| `iron_coast_mission_3` | Saltworks Convoy mission and its local cargo transports, terrain, briefing art and audio |
|
||||
| `iron_coast_mission_4` | Stormwall mission, cliff batteries, terrain, briefing art and audio |
|
||||
| `iron_coast_mission_5` | Cinder Docks mission and its armored foundries, coolant repairs, terrain, briefing art and audio |
|
||||
| `iron_coast_mission_6` | Iron Crown finale, fortified batteries, dreadnoughts, terrain, briefing art and audio |
|
||||
| `iron_coast_scout` | Airborne scout, weaving AI, projectiles, art and audio |
|
||||
| `iron_coast_turret` | Ground turret, aimed firing, projectiles, art and audio |
|
||||
| `iron_coast_boss` | Harbor command ship, persistent patrol and fan volleys, art and audio |
|
||||
| `iron_coast_boss_2` | Relay Warden, authored movement and firing pattern, SD/HD art and audio |
|
||||
| `iron_coast_boss_3` | Saltbreaker VTOL, authored movement and firing pattern, SD/HD art and audio |
|
||||
| `iron_coast_boss_4` | Stormwall Arsenal, authored movement and firing pattern, SD/HD art and audio |
|
||||
| `iron_coast_boss_5` | Cinder Hammer, authored movement and firing pattern, SD/HD art and audio |
|
||||
| `iron_coast_boss_6` | Iron Crown Dreadnought, authored movement and firing pattern, SD/HD art and audio |
|
||||
| `iron_coast_music` | Coastal Steel, original 96-second MUS loop played through the game's Rust FM synthesizer |
|
||||
| `pulse_cannon` | Primary paired pulses and scripted burst rounds |
|
||||
| `seeker_swarm` | Selectable homing missile volleys |
|
||||
| `ion_lance` | Selectable penetrating beam |
|
||||
|
||||
All packages are version 1.0.0 and use mod API 1. Every mission requires
|
||||
compatible 1.x versions of its five shared content mods, soundtrack and stage
|
||||
boss. The campaign assembly requires all six mission packages; shared dependencies are selected transitively.
|
||||
Save compatibility records each enabled package's exact version and content
|
||||
hash. A changed or missing mod prompts before loading. Unavailable saved
|
||||
equipment and progression are retained, and the first subsequent write backs
|
||||
up the original pilot.
|
||||
|
||||
The assembly explicitly overrides `iron_coast_mission_1:campaign`, changing its
|
||||
mission list from Breakwater alone to missions 1–6. The Mods manager reports
|
||||
this winning override. Campaign order supplies the next mission when a mission
|
||||
has no explicit `next` ID, so each mission keeps its own asset root and saved
|
||||
script identity. Finishing the sixth mission marks it completed and leaves it
|
||||
available for replay. Disabling the assembly leaves each mission independently
|
||||
playable. Disabling it while another enabled package needs one of its dependencies
|
||||
retains that dependency; the confirmation lists the actual affected packages.
|
||||
|
||||
## Mission 1: Breakwater
|
||||
|
||||
The player raids a coastal industrial harbor. Ten scouts cover the approach,
|
||||
four ground turrets protect the docks, two destructible supply depots release
|
||||
repair kits, and the command ship arrives at step 720. Destroy at least two
|
||||
turrets and the command ship to finish. Surviving turrets stop at y=72 and the boss stays in the playfield until
|
||||
killed; waiting out the mission duration cannot produce victory. Missing an
|
||||
early battery does not leave an unreachable required target.
|
||||
|
||||
When inventory space allows, the mission grants a trial Pulse Cannon, which
|
||||
fires alongside forward guns, and three field repairs. The cannon is a mission-only loan and is returned before a successful flight
|
||||
is saved; a cannon already owned by the pilot remains untouched. A full inventory can still start; new grants are
|
||||
skipped while existing equipment is preserved. Cycle equipment to **Iron Coast Field Repair** and
|
||||
press the special button to restore 25 shield per repair. Depots drop extra
|
||||
repairs. Victory pays 5,000 credits plus ordinary enemy rewards. The central
|
||||
turrets and boss patrol overlap the starter-gun firing corridor; the mission
|
||||
is intended to remain playable without the trial weapon.
|
||||
|
||||
Script objectives are namespaced and tracked for the current flight. Script
|
||||
state and completed mission IDs are saved with the pilot:
|
||||
|
||||
| Objective | Meaning |
|
||||
| --- | --- |
|
||||
| `iron_coast_mission_1:phase` | 1 approach, 2 docks, 3 command ship, 4 complete |
|
||||
| `iron_coast_mission_1:turrets_required` | Required count of two |
|
||||
| `iron_coast_turret:defeated` | Turrets destroyed |
|
||||
| `iron_coast_boss:defeated` | Command ships destroyed |
|
||||
| `iron_coast_mission_1:depots_opened` | Supply depots destroyed |
|
||||
| `iron_coast_mission_1:complete` | All required targets destroyed |
|
||||
| `iron_coast_mission_1:trial_issued` | A loan or an already owned cannon is available |
|
||||
|
||||
## Six-mission route
|
||||
|
||||
The linked route progresses from the outer harbor to the fleet headquarters.
|
||||
Every mission requires explicit objectives; reaching its duration never awards
|
||||
victory. Every mission holds essential structures in the firing area so
|
||||
missed targets cannot make the rest of a flight impossible. Bosses patrol until
|
||||
destroyed. Optional scouts and additional enemies pay their normal rewards.
|
||||
|
||||
| Mission | Required targets | Completion bonus | Support |
|
||||
| --- | --- | ---: | --- |
|
||||
| 1 — Breakwater | Two turrets and one command cruiser | 5,000 | Pulse Cannon loan and three 25-shield field repairs when inventory fits; depot repairs |
|
||||
| 2 — Tidal Relay | Three radar relays and Relay Warden | 7,500 | Pulse Cannon loan when inventory fits; each destroyed relay restores 12 shield |
|
||||
| 3 — Saltworks Convoy | Three cargo transports, two escort turrets and Saltbreaker VTOL | 7,500 | Each intercepted transport restores 15 shield; no equipment grant |
|
||||
| 4 — Stormwall | Four cliff batteries and Stormwall Arsenal | 12,000 | Pulse Cannon loan when inventory fits |
|
||||
| 5 — Cinder Docks | Two armored foundries and Cinder Hammer | 15,000 | Three selectable 30-shield coolant injectors when inventory fits; outer-lane slag volleys have audible warnings |
|
||||
| 6 — Iron Crown | Six citadel batteries and two Iron Crown Dreadnoughts, including the final arrival | 30,000 | Ion Lance loan when inventory fits; 25 shield on entry and 20 at each dreadnought phase |
|
||||
|
||||
Loaned weapons are returned at mission end; owned weapons remain equipped.
|
||||
Inventory grants check the twenty-slot limit, including reserve super-shield
|
||||
modules, and never remove existing equipment to make room. Mission repair
|
||||
items use normal equipment selection and the **Special** control. Keep **Ctrl**
|
||||
held to fire, use **Alt** or **Shift** to cycle, **Z** for Special, and **Space**
|
||||
for a carried bomb with the default bindings.
|
||||
|
||||
Missions 2–6 reference original `base:enemy_*` definitions directly. Those foes
|
||||
retain the original sprite library's flight paths, firing types, animation and
|
||||
rewards; no mod override substitutes generic downward movement.
|
||||
|
||||
Progression records each completed mission and carries shield charge, credits,
|
||||
equipment and script state into the hangar. The player can buy an original
|
||||
Energy refill for 10,000 credits to recover 25 shield between sorties. Selecting
|
||||
a mission directly remains available for replay and independent level mods.
|
||||
|
||||
## Art and audio
|
||||
|
||||
Original terrain asset `0x10360` and military sprite `0x102f6` were decoded and
|
||||
inspected before generating the new art with the built-in imagegen skill.
|
||||
Mission, enemy and weapon packages include generated masters, packaged
|
||||
SD/HD art, WAV files and prompt/provenance metadata in Git. The assembly owns
|
||||
its SD/HD campaign portrait; the soundtrack package owns its MUS composition. Original style-reference images and original
|
||||
game data remain outside Git. Mission sources are a generated shoreline texture,
|
||||
harbor briefing backdrop and transparent supply depot. The source images
|
||||
remain unchanged; the build step normalizes logical footprints and derives
|
||||
palette SD versions and HD counterparts.
|
||||
|
||||
Mission tiles have 32×32 logical canvases, 32×32 palette SD images and 128×128
|
||||
HD images. The 288×384 logical shoreline map is converted globally before
|
||||
cropping into 108 tiles. A reflected copy produces 216 definitions; twelve
|
||||
forward rows followed by twelve reflected rows give exact matching pixels
|
||||
at each vertical repetition boundary. This keeps the map continuous but
|
||||
repeats and reflects port structures. The 72-row mission map uses three such
|
||||
periods. This is a deliberate first-mission content choice, not unique terrain
|
||||
for the whole original game.
|
||||
|
||||
Briefing/menu art uses a 320×200 logical canvas, with 320×200 SD and 1280×800
|
||||
HD files. Supply depots preserve a 32×32 collision canvas and transparent
|
||||
sprite margins; repair icons use 16×16 logical canvases. The asset report
|
||||
records hashes, palette membership, footprints and repeat-boundary checks.
|
||||
HD remains constrained by the renderer's indexed palette, so generated color
|
||||
detail is reduced to the selected palette during play.
|
||||
|
||||
Mission audio is original procedural synthesis: approach signal, dock alarm,
|
||||
boss warning, victory arpeggio, depot impact and repair cue. These are bounded
|
||||
mono PCM16 WAVs at 22,050 Hz, 0.35–1.2 seconds, made from deterministic
|
||||
oscillators and seeded noise. No original sound recording, SoundFont or online
|
||||
audio service is used. The generators and output hashes are tracked. The soundtrack is an original
|
||||
MUS score played through the same FM synthesizer as the original game music;
|
||||
its source composition, generated score and provenance are included.
|
||||
|
||||
The generated mod assets are included in Git, so a fresh checkout includes
|
||||
the complete content packages. The original base-game asset catalog is still
|
||||
required locally to play the game and remains excluded from version control.
|
||||
To rebuild the mission assets from their included generated masters, run:
|
||||
|
||||
```sh
|
||||
python3 mods/iron_coast_mission_1/build_assets.py --palette ../data/misc/PALETTE_DAT
|
||||
cargo run --no-default-features -- --mods mods --validate-mods iron_coast_scout,iron_coast_turret,iron_coast_boss,iron_coast_music,pulse_cannon,seeker_swarm,ion_lance,iron_coast_mission_1
|
||||
```
|
||||
|
||||
From `RustRaptor/`, validating the full assembly uses all dependencies explicitly
|
||||
at the CLI (the in-game Mods manager selects them automatically):
|
||||
|
||||
```sh
|
||||
cargo run --no-default-features -- --mods mods --validate-mods iron_coast_scout,iron_coast_turret,iron_coast_boss,iron_coast_boss_2,iron_coast_boss_3,iron_coast_boss_4,iron_coast_boss_5,iron_coast_boss_6,iron_coast_music,pulse_cannon,seeker_swarm,ion_lance,iron_coast_mission_1,iron_coast_mission_2,iron_coast_mission_3,iron_coast_mission_4,iron_coast_mission_5,iron_coast_mission_6,iron_coast_campaign
|
||||
```
|
||||
|
||||
## Acceptance checks
|
||||
|
||||
`tests/iron_coast.rs` loads the standalone Breakwater manifests, definitions
|
||||
and scripts with synthetic PNG/WAV fixtures and the mod-authored MUS score. It checks dependency activation, artwork
|
||||
footprints, explicit boss/turret completion gating, one-time victory rewards,
|
||||
and actual headless runs through the scout waves and boss using starter guns
|
||||
and the trial Pulse Cannon. These tests use no original images and replace the
|
||||
generated assets with synthetic fixtures. Local package validation additionally checks
|
||||
the actual generated SD/HD images and WAV files.
|
||||
|
||||
The standalone suite also checks mission-only loan cleanup, preservation of
|
||||
owned equipment, full-inventory starts, and required turrets remaining reachable
|
||||
after their scheduled entrance. The full campaign run below exercises the
|
||||
current Mission 1 package together with the later missions and shared soundtrack.
|
||||
|
||||
Release measurements exclude compilation.
|
||||
|
||||
`tests/iron_coast_campaign.rs` adds six portable structural checks and one
|
||||
original-data integration check for the complete route. The portable tests
|
||||
load the real nineteen manifests, content definitions and Rhai scripts with
|
||||
synthetic PNG/WAV resources. Referenced native IDs receive labeled registry
|
||||
placeholders only for these structural checks; their movement is tested using
|
||||
the original catalog in the separate integration run. Generated artwork and
|
||||
sound files are checked separately by local package validation. The six portable checks passed in **0.54 seconds** in release mode, excluding
|
||||
compilation. The suite checks automatic dependency selection, shared dependency retention, independent
|
||||
mission activation, one explicit campaign override, unchanged mission owners,
|
||||
full twenty-slot inventory starts, explicit-next priority, deterministic
|
||||
campaign ordering, terminal replay behavior and versioned pilot save/load.
|
||||
|
||||
The original-data headless campaign starts with the registered pilot's 75
|
||||
shield and 10,000 credits at difficulty 2. It holds fire, moves at most four horizontal
|
||||
pixels per step toward mission structures, turrets, cruisers and scouts, and
|
||||
uses actual consumable callbacks when shield is at most 70. It carries damage,
|
||||
equipment, credits and script state between all six flights and round-trips an
|
||||
atomic save after each victory. It then replays the finale with the recovered
|
||||
mission and boss state, verifies both boss deaths and keeps exactly six
|
||||
completed mission records after another save/load. The harness uses no direct shield refills, enemy-damage commands, invulnerability or
|
||||
objective mutations. Any Energy refills use the actual shop purchase and are
|
||||
recorded in the report.
|
||||
|
||||
The final nineteen-package release run passed in **19.44 seconds**, excluding
|
||||
compilation, after every package was frozen and ignored bytecode was removed.
|
||||
It needed no Energy refills or purchased weapons. All original enemy types
|
||||
authored into missions 2–6 appeared and moved through their native paths.
|
||||
|
||||
| Mission | Completion step | Starting shield | Final shield | Total credits | Native types seen moving |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| 1 — Breakwater | 785 | 75 | 92 | 18,480 | 0 |
|
||||
| 2 — Tidal Relay | 926 | 92 | 86 | 30,685 | 3 |
|
||||
| 3 — Saltworks Convoy | 1,290 | 86 | 82 | 43,425 | 4 |
|
||||
| 4 — Stormwall | 1,217 | 82 | 91 | 61,280 | 6 |
|
||||
| 5 — Cinder Docks | 1,818 | 91 | 89 | 84,510 | 6 |
|
||||
| 6 — Iron Crown | 2,428 | 89 | 100 | 122,705 | 6 |
|
||||
|
||||
The recovered finale replay also completed in **2,428 steps** with **100 shield**,
|
||||
bringing credits to **160,900**. Both dreadnought deaths were required again,
|
||||
and the completed mission list stayed at six entries. Pilot saves and loads
|
||||
preserved the exact active package versions, hashes, inventory, script state
|
||||
and script RNG after every flight and the replay. The local JSON report records
|
||||
these package pins, objectives, native movement metadata and state digests.
|
||||
|
||||
The machine-readable report can be captured locally with:
|
||||
|
||||
```sh
|
||||
RAPTOR_TEST_DATA=../data RAPTOR_IRON_COAST_REPORT=../.reference/iron-coast-six-level.json cargo test --locked --release --no-default-features --test iron_coast_campaign -- --ignored --nocapture
|
||||
```
|
||||
|
||||
A passing original-data run establishes a viable continuous route using
|
||||
ordinary controls and campaign-provided support. It does not establish balance
|
||||
for every loadout, difficulty or normal-player route. Each individual mission also includes its
|
||||
own acceptance runner and generated asset provenance.
|
||||
|
||||
Visual approval remains provisional: sprite and terrain previews were
|
||||
inspected for readability, canvas alignment and transparency, with numeric
|
||||
palette and repeat-seam checks. Wider player testing should assess difficulty,
|
||||
audio balance and HD details during normal play before treating the new art
|
||||
as final. These packages extend the Rust engine; they do not establish full
|
||||
parity across the original campaigns.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Default machine gun and ground collisions
|
||||
|
||||
The original Forward Guns remain an air-and-ground weapon: two banked muzzle
|
||||
positions, one damage per round, a two-step firing interval and the original
|
||||
8-to-16 projectile acceleration. They do not require another selected weapon.
|
||||
|
||||
The confirmed compatibility defect was in JSON mission terrain. Original guns
|
||||
used the native eight-row collision window even when a mod map was displayed
|
||||
at a different position. Their terrain damage also bypassed destruction: a
|
||||
two-health test tile reached minus 52 health without exploding or paying its
|
||||
reward. JSON terrain now uses its displayed row coordinates and completes
|
||||
destruction immediately, just as mod projectile terrain damage does. This
|
||||
includes short maps, continued scrolling and optional replacement artwork.
|
||||
Destroyed tiles cannot absorb more shots or award their reward again. Their
|
||||
destruction emits the original positional ground-explosion sound.
|
||||
|
||||
Original mission terrain keeps its delayed explosion, neighbor damage and
|
||||
replacement-art timing. Seven portable regressions in
|
||||
`tests/machine_gun_ground.rs` cover ground enemy damage/rewards, one-time terrain
|
||||
destruction and sound, native delayed destruction, short maps, continued
|
||||
scrolling, offscreen rejection and optional replacement artwork. The 15
|
||||
existing gameplay checks, nine enemy package checks and nine Breakwater mission
|
||||
checks also pass. After the correction, the complete 1,889-step native DEMO1
|
||||
trace still matches every recorded enemy, projectile, player and RNG snapshot.
|
||||
|
||||
The local original-data test isolates actual enemy definitions, movement,
|
||||
artwork and callbacks in a quiet test mission. It uses a fresh pilot, only the
|
||||
default guns and ordinary mouse-position/fire input; it never injects
|
||||
projectiles, changes health or grants equipment. It checks classic non-boss
|
||||
ground units from the original first mission and the Iron Coast turret, depot,
|
||||
relay, convoy and foundry, including their kill rewards.
|
||||
|
||||
That local combat check passed for all ten definitions (five classic and five
|
||||
Iron Coast), each starting with 75 shield and 10,000 credits. The 90-health
|
||||
foundry was destroyed in 99 steps with 71 shield remaining. Relay and convoy
|
||||
shield recovery came from their real destruction callbacks. The optional
|
||||
`RAPTOR_GROUND_REPORT` output records each unit's health, kill timing, shield and
|
||||
reward; this run wrote `.reference/machine-gun-ground.json` outside Git.
|
||||
Package PNG/WAV decoding remains covered by the separate mod validator; this
|
||||
collision test loads the actual definitions, logical canvases and Rhai sources.
|
||||
|
||||
Run the portable checks with:
|
||||
|
||||
```sh
|
||||
cargo test --locked --test machine_gun_ground --test gameplay
|
||||
```
|
||||
|
||||
After campaign package files are stable, run the local original-data check with:
|
||||
|
||||
```sh
|
||||
RAPTOR_TEST_DATA=../data cargo test --locked --test machine_gun_ground \
|
||||
real_forward_guns_damage_classic_and_iron_coast_ground_units -- --ignored
|
||||
```
|
||||
|
||||
Commands above run from `RustRaptor/`. Set the usual local SDL library path
|
||||
when needed. The native reference comparison is the ignored
|
||||
`complete_demo_1_matches_every_reference_gameplay_snapshot` test in
|
||||
`tests/campaigns.rs` and reads the untracked original catalog/reference trace.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Menu and classic-intro screenshot review
|
||||
|
||||
The full local-original-data audit captures 31 menu states and 11 frames of the original logos/intro. All 42 captures were inspected, including main menu, hangar, registration, shop purchase/sale details, equipment, missions, options, pilot slots, empty legacy archive, mod dependencies and disable confirmation, mismatched-save loading, pause and results.
|
||||
|
||||
The authored coastal-hangar background is packaged at `art/menu-background-v1.png`. Menus use a 400×225 logical canvas at 16:9. Main-menu navigation occupies the open right side, with a compact pilot card above the visible ship; off-white labels and amber focus match the hangar lighting. Secondary screens retain their detailed classic layout centered within the wider background. Bitmap labels remain sharp and long text stays within panels. The mission preview is outside the actionable list hitboxes. Shop cards display artwork, prices, resale value, quantity and automatic/selectable/passive behavior.
|
||||
|
||||
Gameplay uses the original 320×200 simulation canvas, presented at 4:3 with aspect correction enabled. At 1080p its viewport is 1440×1080, centered between subdued hangar sidebars. Original cinematics always use 4:3 with black borders, including when gameplay aspect correction is disabled. New profiles default to 1280×720; existing saved window sizes remain respected.
|
||||
|
||||
The widescreen mouse check covers all eight main actions at 1280×720, 1280×800 and 1920×1080, secondary-screen offsets and clicks outside the viewport. The full 42-frame menu/intro audit was rerun after the layout change. A relocated packaged SDL executable also rendered menu and gameplay captures at 1280×800 and 1920×1080, with nonzero stereo audio captures. Those screenshots are under ignored `.reference/standalone-package/`.
|
||||
|
||||
Installed mod rows stay in stable ID order through activation, dependency
|
||||
selection, disabling and priority changes. Requested load priority remains
|
||||
separate and is visible in diagnostics; arrow-key changes report the new
|
||||
priority without moving the cursor. Returning from details or a disable
|
||||
confirmation restores the selected mod and its list position. Opening details
|
||||
again while already in diagnostics preserves its parent, so Escape always
|
||||
returns one level. Portable regressions exercise these navigation sequences.
|
||||
|
||||
The audit also verifies the complete startup reaches its return screen at exactly 1,891 70 Hz service ticks and restores menu music. Cancelling a mod-mismatch prompt preserves the original save; accepting it displays unavailable equipment without silently discarding it.
|
||||
|
||||
Run the local audit from `RustRaptor/`:
|
||||
|
||||
```sh
|
||||
RAPTOR_TEST_DATA="$PWD/../data" LD_LIBRARY_PATH="$HOME/.local/lib" \
|
||||
cargo test --locked --lib every_menu_screen_and_original_intro_visual_audit -- --ignored
|
||||
```
|
||||
|
||||
Captures and the action-row manifest stay ignored in `../.reference/ui-all/`. The background was generated with the imagegen skill's built-in image generation tool; the exact prompt and provenance are tracked in `art/menu-background-v1.prompt.txt` and `art/provenance.json`. Original cinematic pixels remain outside Git.
|
||||
@@ -0,0 +1,238 @@
|
||||
# Rust modding API, version 1
|
||||
|
||||
This is an experimental API for the Rust engine. It provides local content packages, deterministic Rhai callbacks, checked commands, an in-game manager and save compatibility checks. The rewrite is still undergoing gameplay parity work; this document describes the implemented API, not a claim that the full remaster has passed its acceptance checks. API compatibility is currently exact: `api_version = 1`.
|
||||
|
||||
## Try the example
|
||||
|
||||
`mods/aurora/` adds two projectiles, a pulse cannon, a repair cell, two enemies, a mission and a campaign. The weapon adds angled shots in Rhai; enemy movement, item rewards and mission objectives also use scripts. Its artwork references asset IDs in the locally extracted original catalog. The example contains no original or generated game artwork.
|
||||
|
||||
Validate the package against your original game data:
|
||||
|
||||
```sh
|
||||
cargo run --no-default-features -- --data ../data --mods mods --validate-mods aurora
|
||||
```
|
||||
|
||||
Run its mission without a window:
|
||||
|
||||
```sh
|
||||
cargo run --no-default-features -- --data ../data --mods mods \
|
||||
--enabled aurora --headless aurora:field_trials --ticks 700
|
||||
```
|
||||
|
||||
The manager discovers the bundled `mods/` folder by default when no user mods folder exists. You can also copy `mods/aurora` into the `mods/` directory under `$XDG_DATA_HOME/RaptorReplica` (default `~/.local/share/RaptorReplica/mods/`), or start the Rust engine with `--mods mods`. Open **Mods**, toggle Aurora, then select **Apply selections**. Select its mission from the mission menu. Existing pilots can add mods while preserving required versions, hashes and relative load order; the next save records the expanded active set. Disabling, changing or reordering required mods shows compatibility details and asks whether to load anyway. Missing equipment and progression remain saved until their packages are restored.
|
||||
|
||||
In the Mods screen, Enter toggles a package or activates the selected action. Enabling a package immediately checks versions, API compatibility, cycles and conflicts, then selects its installed dependencies recursively. A failed check leaves the selection unchanged. Disabling includes dependent mods and dependencies that become unused, preserving dependencies required by other remaining packages. A single-package disable toggles immediately; changes affecting multiple packages show an **Are you sure?** popup listing every affected mod, with Cancel selected initially. Cancel or Back preserves the pending selection. Rescan after editing a package on disk. Dependencies load before dependents; independent packages retain your chosen priority. Later explicit overrides win. Changes made through the pause menu remain pending until the main menu. Candidate registries and scripts are validated before the active set changes.
|
||||
|
||||
## Package layout and manifest
|
||||
|
||||
```text
|
||||
mods/
|
||||
aurora/
|
||||
mod.toml
|
||||
content.json
|
||||
scripts/
|
||||
weapon.rhai
|
||||
art/
|
||||
cannon.png
|
||||
```
|
||||
|
||||
```toml
|
||||
id = "aurora"
|
||||
name = "Aurora Field Trials"
|
||||
version = "1.0.0"
|
||||
api_version = 1
|
||||
content = "content.json"
|
||||
scripts = ["scripts/weapon.rhai"]
|
||||
description = "A new weapon and mission."
|
||||
conflicts = ["alternate_aurora"]
|
||||
|
||||
[dependencies]
|
||||
common_tools = "^1.2"
|
||||
```
|
||||
|
||||
IDs use lowercase ASCII letters, digits, `_` and `-`, with a maximum of 64 bytes. `base` is reserved. Package versions and dependency requirements use semantic version syntax. The in-game manager selects compatible installed dependencies when enabling a package. It never downloads or installs missing packages. The CLI still requires a complete explicit `--enabled` set. Cycles, missing packages, unsupported API versions, incompatible dependency versions and declared conflicts reject activation. Two installed folders claiming the same ID make that ID unusable; rename or remove one folder, then rescan.
|
||||
|
||||
All package paths are relative. Absolute paths, parent traversal, symlinks and special files are rejected. Packages are limited to 10,000 files, 16 directory levels, 64 MiB per file and 256 MiB in total. Manifests are limited to 64 KiB; display names to 128 bytes; descriptions to 4 KiB; script declarations to 64, with a 1 MiB limit per source file. Every file, including artwork and documentation, contributes to the package SHA-256 hash. A package changed after discovery requires rescanning before it can be applied.
|
||||
|
||||
## Content definitions and overrides
|
||||
|
||||
`content.json` has optional `weapons`, `items`, `enemies`, `missions`, `campaigns`, `sounds`, `music` and `overrides` arrays. Unknown fields are rejected. IDs have the form `namespace:name`, for example `aurora:pulse_cannon`. IDs are stable save references and unique across content types. Keep item and projectile IDs distinct: the item `aurora:pulse_cannon` refers to the weapon/projectile `aurora:pulse_projectile` through its `weapon` property.
|
||||
|
||||
Items may include an optional `description` of up to 4096 UTF-8 bytes for shop information. The shop displays the description, artwork, effective target types, firing mode, price, ownership and inventory limits.
|
||||
|
||||
The item price buys `initial_count` units, up to its capacity. Selling an
|
||||
ordinary item removes one unit and returns half its unit purchase price,
|
||||
rounded down. Original energy refills and super-shield modules retain their
|
||||
half-price sale per refill/module. This prevents multi-unit mod packs from
|
||||
creating credits through repeated buying and selling.
|
||||
|
||||
New definitions must belong to the package namespace. An existing definition may be replaced only when its ID appears in `overrides` and the replacement retains the same content type. Overrides are complete replacements, not field merges. An override targeting a missing ID, a replacement without explicit permission, an override without its replacement, or duplicate IDs within a pack rejects the whole pack. The manager exposes the override history and its winning owner.
|
||||
|
||||
```json
|
||||
{
|
||||
"overrides": ["base:projectile_forward_guns"],
|
||||
"weapons": [{
|
||||
"id": "base:projectile_forward_guns",
|
||||
"name": "Modified Forward Guns",
|
||||
"damage": 2,
|
||||
"speed": 8,
|
||||
"cooldown": 2,
|
||||
"behavior": "straight",
|
||||
"target": "air",
|
||||
"offsets": [[-8, 0], [8, 0]]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
`legacy` fields exist to map original numeric definitions into the base registries. New mods should omit them: they select legacy engine behavior and are not a general scripting extension mechanism.
|
||||
|
||||
| Definition | Properties and behavior |
|
||||
| --- | --- |
|
||||
| Weapon/projectile | Damage, integer speed, cooldown in simulation ticks, targeting (`air`, `ground`, `all`), behavior (`straight`, `homing`, `beam`, `spread`, `bomb`, `energy_grab`), firing offsets, optional animation and script. Scripts can emit additional projectiles with explicit velocity. |
|
||||
| Item | Price, maximum and initial count, optional weapon, shield/credit effects, consumable/special flags, artwork and script. `initial_count` controls how many units a purchase or pickup adds. |
|
||||
| Enemy | Health, speed, rewards, air/ground and boss flags, firing cooldown, path points and repeat flag, weapon, dropped item, animation and script. Scripts can query entities and change enemy movement. |
|
||||
| Mission | Duration, `auto_complete` (default true), optional briefing artwork, ordered tick-based spawns, terrain tile definitions and row-major layout, column count, next mission, optional legacy music asset or namespaced `music_track`, and script. Scripts can manage objectives, spawn waves and decide success/failure. |
|
||||
| Campaign | Name, optional menu artwork and ordered mission IDs. Missions and campaigns appear through the same registries as original content. |
|
||||
| Music | Namespaced ID, display name, package-relative MUS file and `looping` (default true). Mission `music_track` references this ID; playback uses original GENMIDI FM and the Music volume control. |
|
||||
|
||||
For non-legacy missions, an explicit `next` takes priority. Otherwise progression
|
||||
uses the following entry in the first containing campaign, ordered by stable
|
||||
campaign ID. The last stage stays selected for replay, and completing it again
|
||||
does not duplicate its completion record. This lets an optional assembly mod
|
||||
join independently owned missions without overriding their scripts or artwork.
|
||||
|
||||
Simulation uses integer coordinates in the original 320×200 logical canvas and runs at 70/3 Hz: one tick is 3/70 seconds. Artwork detail does not enlarge collision boxes. Script movement and projectile speed are bounded to 128 logical pixels per tick; spawn and movement coordinates must stay within ±32,768.
|
||||
|
||||
JSON Schemas are checked into `schemas/`: `mod-v1.json`, `content-v1.json`, `hook-v1.json`, `weapon-projectile-v1.json`, `item-v1.json`, `enemy-v1.json`, `mission-v1.json`, `campaign-v1.json`, `sound-v1.json` and `music-v1.json`. Schemas describe serialization shape; the validation CLI also checks references, numeric limits, package paths, dependencies, artwork and script compilation. Generate them from the Rust types with:
|
||||
|
||||
```sh
|
||||
cargo run --no-default-features -- --schemas schemas
|
||||
```
|
||||
|
||||
## Artwork
|
||||
|
||||
An `art` object requires exactly one of `asset` (an original numeric catalog ID) or `png` (a package-relative PNG path), plus `logical_width` and `logical_height`. `frames` defaults to 1. `nearest = true` opts that artwork out of Scale2x/3x/4x filtering.
|
||||
|
||||
```json
|
||||
{
|
||||
"png": "art/cannon.png",
|
||||
"logical_width": 16,
|
||||
"logical_height": 16,
|
||||
"frames": 4
|
||||
}
|
||||
```
|
||||
|
||||
PNG animations are horizontal strips of equally sized frames. For example, four 64×64 frames use a 256×64 PNG and may declare logical dimensions of 16×16. The strip width must divide evenly by `frames`; supply consistent canvases and alignment across frames. Alpha below 128 is transparent. Opaque colors are mapped to ordinary entries 0–239 of the selected game palette. Plain PNGs carry no palette-animation labels, so their highlights cannot acquire reserved water, fire or blinking indices 240–255. Artwork already detailed enough for the selected rendering scale bypasses automatic sprite enlargement. Original asset animations use consecutive catalog IDs and retain their native palette labels. Terrain artwork and destroyed terrain replacements follow the same format.
|
||||
|
||||
Artwork dimensions and decoding allocation are bounded. PNGs may be up to 4096 pixels in either direction, with at most 4,194,304 pixels and a 64 MiB decoding allocation limit. Logical dimensions are separately validated and stay independent of physical PNG resolution. The extracted original catalog and local base-game remaster proofs stay outside version control; authored mod artwork and sounds are included in Git.
|
||||
|
||||
## Rhai callbacks
|
||||
|
||||
Every callback takes `(event, state, random)` and returns a map containing `commands` and `state`. All scripts referenced by definitions must appear in `manifest.scripts`. A missing optional callback does nothing; a declared callback with the wrong parameter count is rejected during compilation.
|
||||
|
||||
```javascript
|
||||
fn on_fire(event, state, random) {
|
||||
let shots = if state.contains("shots") { state.shots + 1 } else { 1 };
|
||||
#{
|
||||
commands: [#{
|
||||
op: "projectile",
|
||||
weapon: "aurora:pulse_projectile",
|
||||
x: event.x,
|
||||
y: event.y,
|
||||
vx: (random[0] % 3) - 1,
|
||||
vy: -8
|
||||
}],
|
||||
state: #{ shots: shots }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Callback | Definition and event fields |
|
||||
| --- | --- |
|
||||
| `on_spawn` | Enemy: `entity`, `x`, `y`. |
|
||||
| `on_tick` | Enemy: `entity`, `x`, `y`, `health`, `tick`, `player_x`, `player_y`. Mission: `tick`, `player_x`, `player_y`. |
|
||||
| `on_fire` | Weapon or firing enemy: `entity`, `x`, `y`, `hostile`, `tick`; enemy callbacks also include `weapon`. Player entity is zero. The normal shot has already been emitted. Enemy callbacks run for each emitted gun shot. |
|
||||
| `on_hit` | Weapon/projectile and damaged enemy: `entity`, `projectile`, `damage`, `x`, `y`; enemy events also include `weapon` and remaining `health`. Scripted enemy damage uses `scripted = true` and omits projectile-specific fields. |
|
||||
| `on_death` | Enemy: `entity`, `x`, `y`. The entity is retired; use queries to act on surviving entities. |
|
||||
| `on_pickup` | Item: pickup `entity`. The item and its standard effects have already been awarded. |
|
||||
| `on_use` | Selected special item without a weapon: player `entity = 0`, `x`, `y`, `tick`. Triggered on the rising edge of special input; standard effects and consumption happen first. |
|
||||
| `on_mission_start` | Mission: `tick = 0`. Runs when the first simulation step dispatches events. |
|
||||
| `on_mission_end` | Mission: `success`, `tick`. |
|
||||
|
||||
Every dispatched event also includes `tick`, `player`, `entities`, `projectiles`, `pickups` and `objectives`. `player` is a snapshot including position, velocity, shields, credits, inventory and selected equipment. `entities` is the current enemy array; each entry includes `entity`, `definition`, position, health, animation and movement state. Projectiles and pickups likewise carry stable entity IDs. These are read-only snapshots: return commands to change the simulation. Later callbacks in a tick can observe commands committed by earlier callbacks. Events raised by commands, such as a spawned enemy, are dispatched on the following tick.
|
||||
|
||||
`state` is JSON-serializable and shared by all callbacks in the same script file. It starts as an empty map, is keyed by `mod_id/script_path`, and is saved with the pilot. Avoid values dependent on wall time, filesystem access or rendering rate. `random` contains 32 deterministic nonnegative integers in the range 0–32,767. Each successful callback advances that script's saved RNG by exactly 32 values, whether or not the script reads them. Missing callbacks do not advance state or RNG.
|
||||
|
||||
Commands apply only after a callback returns and passes validation. If a callback or any command fails, the complete simulation tick restores its previous world, script state and script RNG. Runtime errors identify the mod, source file, callback and Rhai source location when available. In the desktop engine a script failure returns to the pause screen with the error; fix the package, rescan and reload at the main menu.
|
||||
|
||||
## Commands
|
||||
|
||||
The 20-slot inventory limit counts physical super-shield modules separately.
|
||||
`player.super_shield_reserves` lists the stored charge of reserve modules;
|
||||
`player.inventory["base:super_shield"]` is the equipped module's charge.
|
||||
For the built-in `base:energy` and `base:super_shield`, `give_item` and
|
||||
`consume_item` change charge units, not the number of modules. Zero-charge
|
||||
shield entries remain valid. Other items are removed when their quantity
|
||||
reaches zero, releasing their slot and selecting another available special
|
||||
item when needed.
|
||||
|
||||
Use the `op` field shown below. All fields listed are required unless stated otherwise. Entity zero identifies the player for damage; movement commands currently address enemies.
|
||||
|
||||
| `op` | Fields | Effect |
|
||||
| --- | --- | --- |
|
||||
| `spawn` | `enemy`, `x`, `y` | Spawn a registered enemy. |
|
||||
| `projectile` | `weapon`, `x`, `y`, `vx`, `vy`, optional `hostile` (false) | Spawn a projectile using a registered weapon. |
|
||||
| `damage` | `entity`, `amount` | Deal 0–100,000 damage to the player or an existing enemy. |
|
||||
| `despawn` | `entity` | Remove an existing enemy or projectile. |
|
||||
| `move` | `entity`, `x`, `y` | Move an enemy and rebase its movement path. |
|
||||
| `velocity` | `entity`, `vx`, `vy` | Set enemy velocity. |
|
||||
| `give_item` | `item`, `count` | Add registered inventory, clamped to item capacity; adding a new entry beyond the 20-item limit fails. |
|
||||
| `consume_item` | `item`, `count` | Remove owned inventory; insufficient quantity fails. |
|
||||
| `credits` | signed `amount` | Adjust credits with saturation at valid limits. |
|
||||
| `shield` | signed `amount` | Adjust ordinary shield, clamped to 0–100. |
|
||||
| `effect` | `x`, `y` | Add an explosion effect. |
|
||||
| `sound` | numeric `asset` | Queue an original digital sound asset for playback. |
|
||||
| `play_sound` | namespaced `sound` ID | Queue a validated package-local sound. |
|
||||
| `objective` | `key`, integer `value` | Set an objective counter. Use a namespaced key. |
|
||||
| `complete` | none | Complete the mission. |
|
||||
| `fail` | none | Fail the mission. |
|
||||
|
||||
Rhai uses an explicit package allowlist for arithmetic, strings, collections, iteration and function helpers. The default language/time packages are excluded, so wall-clock timestamps and native `sleep` cannot be reached, including through function pointers. There are no exposed filesystem, network or OS interfaces. Dynamic `eval` and imports are disabled. Limits are 100,000 operations per callback and a shared 500,000-operation budget per simulation tick; 32 call levels; expression depths of 64/32; arrays of 4,096 entries; maps of 1,024 entries; strings of 16 KiB; 256 commands per callback; and 64 KiB of serialized state per script. See the [Rhai execution-limit API](https://docs.rs/rhai/latest/rhai/struct.Engine.html) for the underlying engine controls. Scripts cannot increase these limits.
|
||||
|
||||
## Saves and compatibility
|
||||
|
||||
Rust pilots use separate, versioned JSON files under `$XDG_DATA_HOME/RaptorReplica`, and settings use TOML under `$XDG_CONFIG_HOME/RaptorReplica`. Atomic writes replace completed saves after file and directory synchronization. Legacy saves are read and imported; their original bytes are not modified.
|
||||
|
||||
Each pilot records the ordered mod IDs, package versions, full content hashes, script states and script RNG positions. The load screen reports missing packages, changed versions or hashes, changed required load order, and unavailable equipment or missions. Changing a package without changing its version still changes its hash and therefore requires confirmation. Cancel leaves the current pilot and original save untouched; choosing to load accepts the differences for that pilot session.
|
||||
|
||||
Unavailable equipment stays in the pilot's inventory with its saved quantity and occupies its original inventory slots, but cannot be fired, selected or traded until its definition is available. Unavailable completed missions remain recorded. If the saved current mission is missing, choose an available mission in the hangar before flying. Runtime saves retain inactive package pins, script state and script RNG alongside the active packages, so restoring a disabled package can recover its data. Current active versions and hashes are recorded after an accepted load is successfully saved; the original save is backed up before its first replacement. Structural corruption, unsupported save versions and invalid IDs still cannot be loaded. This API does not yet provide scripted mod save migrations, so a changed script must handle its earlier serializable state.
|
||||
|
||||
For Rust tooling, `Pilot::inspect_load` returns the unmodified pilot and a structured `SaveCompatibility` report. `Pilot::load` remains strict for callers that require a fully matching environment. Only consume `PilotLoad::confirm_load` after explicit acceptance. Use `validate_tolerant` and `complete_mission_tolerant` for that accepted session, and `merge_runtime_snapshot` to retain saved state and pins belonging to inactive packages.
|
||||
|
||||
## Validation tests
|
||||
|
||||
`cargo test --no-default-features --test modding` uses synthetic definitions and temporary packages. It checks dependency ordering, version/API failures, conflicts, cycles, duplicate namespaces, changed hashes, traversal/symlinks, file/depth limits, explicit override priority, registry rollback, script budgets, command failure rollback, RNG resumption, incompatible saves and example callbacks. These tests do not require the original game assets. The validation CLI above provides local integration coverage against the separately installed asset catalog.
|
||||
|
||||
## SD/HD artwork and local sound effects
|
||||
|
||||
An artwork definition may add `hd_png`, a separate package-relative PNG with the same logical canvas, frame count and aspect ratio as the SD `png` or original `asset`. HD inputs require at least twice the logical resolution. The Options HD toggle selects it; missing variants retain SD artwork. Detailed mod terrain is drawn on the higher-resolution canvas over the joined SD fallback. Authored mod art/audio is included in Git; original catalog references remain excluded.
|
||||
|
||||
`sounds` definitions have `id`, `name`, `file` (relative path) and optional `volume` (0–127, default127), `pan` (0–255, default127), `pitch` (0–255, default128) and `priority` (0–100000, default1). Lower numeric priority wins, matching the original DSP. Play one with `#{op:"play_sound",sound:"your_mod:shot"}`. WAV inputs must be uncompressed PCM8/16, mono/stereo, 8–48kHz, at most30seconds/8MiB. Stereo is mixed to mono and converted to the original unsigned8-bit digital format; channel/pan/pitch/volume use the original mixer. Original DMX patches are also accepted. Invalid/missing audio rejects activation.
|
||||
|
||||
For objective-driven missions set `auto_complete=false` and explicitly return `complete` or `fail`. Scripted endings dispatch `on_mission_end` in the same transaction. Commands can create further hook events, bounded by the shared execution budget and an8192-event tick limit.
|
||||
|
||||
|
||||
## Package mission music
|
||||
|
||||
Register a track in the optional `music` array. A music definition contains `id`, `name`, a package-relative `.mus` `file` and optional `looping` (default `true`). A mission selects it with `music_track`; setting both `music_track` and legacy numeric `music` rejects activation. Declare a dependency on a separate music package so enabling the mission also selects its installed soundtrack. Music IDs use the same namespace, duplicate and explicit-override rules as other content. Winning overrides resolve their score from the winning package.
|
||||
|
||||
```json
|
||||
{
|
||||
"music": [{"id":"your_mod:theme", "name":"Mission theme", "file":"music/theme.mus", "looping":true}],
|
||||
"missions": [{"id":"your_mod:mission", "name":"New mission", "duration":2000, "music_track":"your_mod:theme"}]
|
||||
}
|
||||
```
|
||||
|
||||
Only checked MUS scores are accepted, played through the original GENMIDI FM synthesizer and the **Music volume** control. Digital effects keep their separate volume. External MIDI and SoundFont playback remain deferred. Scores are limited to 128 KiB, 16,384 events, 256 events per service tick and 30 minutes of score time. The MUS header, event types, controller values, primary-channel count and end marker are checked by the existing decoder. Zero-duration scores, missing files, escaping paths, invalid scores and unsupported formats reject activation with the package/file identified. Score bytes participate in the normal mod hash and pilot compatibility checks. A bounded score cache is cleared when mods are applied.
|
||||
|
||||
The selected track starts with the mission, honors its loop flag, and can be scheduled before the SDL device is ready. Selecting the same active track preserves playback position; ordinary menus and classic cinematics select their original tracks. See [music validation](music-validation.md) for the registry, file, volume, loop and device-attachment checks.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Mod mission music
|
||||
|
||||
Package music uses namespaced `MusicDef` entries and a mission's optional
|
||||
`music_track`. It plays through the existing Rust MUS/GENMIDI FM synthesizer,
|
||||
with the ordinary Music volume and fade controls. Sound effects keep their
|
||||
separate digital bus. Original numeric mission tracks, menus and classic
|
||||
cinematics keep their existing music paths.
|
||||
|
||||
Tracks use package-relative `.mus` files. The registry checks references,
|
||||
duplicate IDs, namespaces and explicit overrides. Activation decodes the
|
||||
winning owner's file and rejects missing, escaping, malformed or oversized
|
||||
scores. The package's existing SHA-256 hash includes the score file, so pilot
|
||||
mod compatibility covers music changes. Applying mods clears the bounded
|
||||
decoded-score cache and returns an active mod track to main-menu music.
|
||||
|
||||
Limits apply to mod music without changing the legacy decoder: 128 KiB per
|
||||
file, 16,384 events, at most 256 events at any service tick, positive duration,
|
||||
and at most 126,000 score ticks (30 minutes at 70 Hz). Header ranges, supported
|
||||
event types, primary channel count, velocities, controllers and the final end
|
||||
event are checked by `MusScore::decode`. Cache allocation is bounded separately
|
||||
from digital effects. Only MUS is supported; external MIDI and SoundFonts
|
||||
remain deferred.
|
||||
|
||||
The host retains a namespaced desired-track ID and its loop flag. A mission
|
||||
preview can select its music before SDL audio is initialized; attaching the
|
||||
device applies that pending score. Re-selecting the same active ID/loop
|
||||
preserves playback position. Selecting original menu or cinematic music clears
|
||||
the mod identity. A failed score load leaves the pilot and menu state intact.
|
||||
|
||||
Synthetic checks cover registration and missing references, conflicting
|
||||
legacy/custom choices, explicit overrides and owner resolution, hash changes,
|
||||
cache invalidation, path and symlink containment, unsupported formats, file
|
||||
size, event density, duration and channel limits. FM output checks exercise the
|
||||
definition's actual loop flag and show that Music volume mutes the mod score
|
||||
while digital effects remain audible. A one-play score's ending tail has less
|
||||
than one twentieth the energy of the looping score in the tested fixture.
|
||||
|
||||
```sh
|
||||
cargo test --locked --no-default-features \
|
||||
--manifest-path RustRaptor/Cargo.toml --test music
|
||||
```
|
||||
|
||||
Portable host checks verify pending identity, original-menu transitions and
|
||||
rollback when music cannot load. The paused SDL test attaches a real device
|
||||
object and compares consecutive FM PCM blocks against uninterrupted reference
|
||||
playback, proving that selecting the same track does not restart it:
|
||||
|
||||
```sh
|
||||
LD_LIBRARY_PATH="$HOME/.local/lib" cargo test --locked \
|
||||
--manifest-path RustRaptor/Cargo.toml --lib mission_mod_music
|
||||
LD_LIBRARY_PATH="$HOME/.local/lib" cargo test --locked \
|
||||
--manifest-path RustRaptor/Cargo.toml --lib failed_mission_music_load
|
||||
LD_LIBRARY_PATH="$HOME/.local/lib" SDL_AUDIODRIVER=dummy cargo test --locked \
|
||||
--manifest-path RustRaptor/Cargo.toml --lib pending_mod_music_resumes -- --ignored
|
||||
```
|
||||
|
||||
These checks validate the engine path using synthetic scores. Composition,
|
||||
listening review and original-data in-game soundtrack captures are separate
|
||||
release checks for each music package.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Desktop display measurements
|
||||
|
||||
Local release measurements on 2026-10-10 used SDL's OpenGL renderer at
|
||||
1920×1080 fullscreen output, aspect correction, Scale4, no scanlines, and all seven Iron Coast
|
||||
packages. They include normal terrain, sprites, indexed shadows, effects and
|
||||
mod scripting. They exclude Cargo compilation.
|
||||
|
||||
| Mode | Startup | Render work median | Render work 95th percentile | Frame median | Frame 95th percentile |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| SD artwork | 2,345.82 ms | 7.40 ms | 8.76 ms | 7.52 ms | 8.88 ms |
|
||||
| HD artwork | 2,375.56 ms | 8.67 ms | 14.66 ms | 8.79 ms | 14.78 ms |
|
||||
|
||||
Each run sampled 174 frames from a 180-frame flight, excluding five warmup
|
||||
frames and the screenshot frame. Work time excludes the presentation call;
|
||||
frame time includes it. Startup covers desktop-host construction and SDL setup;
|
||||
the preceding CLI catalog registration is excluded. The explicit one-millisecond smoke-loop sleep is
|
||||
outside both measurements. These short runs establish local performance for
|
||||
this scene and machine, not worst-case performance across every boss or device.
|
||||
The fixed 70/3 Hz simulation advances independently of display timing.
|
||||
|
||||
From `RustRaptor/`, prepare two isolated profile configurations with
|
||||
`width = 1920`, `height = 1080`, `fullscreen = true`, `scale = 4`, `scanlines = false`, and
|
||||
`hd_mode = false` or `true`. Enable the mission's six dependencies and mission
|
||||
package in `enabled_mods`; see [Iron Coast](iron-coast.md). Run:
|
||||
|
||||
```sh
|
||||
./run.sh --profile-root ../.reference/enemy-sd-profile \
|
||||
--preview-mission iron_coast_mission_1:mission_01 \
|
||||
--smoke-frames 180 --benchmark \
|
||||
--screenshot ../.reference/rustraptor-sd-1080-final.png
|
||||
./run.sh --profile-root ../.reference/enemy-hd-profile \
|
||||
--preview-mission iron_coast_mission_1:mission_01 \
|
||||
--smoke-frames 180 --benchmark \
|
||||
--screenshot ../.reference/rustraptor-hd-1080-final.png
|
||||
```
|
||||
|
||||
Both captures were checked to be exactly 1920×1080. The benchmark reports the
|
||||
actual SDL output size, since a window manager can constrain a requested window.
|
||||
Use a real SDL display. Dummy-video screenshots verify raster composition but
|
||||
cannot measure GPU presentation. Frame-limited runs do not save pilots or
|
||||
settings. Profiles, caches and screenshots remain ignored local files.
|
||||
@@ -0,0 +1,29 @@
|
||||
# Repository and playable bundle
|
||||
|
||||
`RustRaptor/` is the Git repository root. Run Git commands, `./run.sh` and
|
||||
Cargo commands from this folder. The public remote is
|
||||
[Quintine/RustRaptor](https://github.com/Quintine/RustRaptor), on branch
|
||||
`codex/rust-rewrite`.
|
||||
|
||||
The repository includes the Linux executable in `bin/`, its bundled libraries
|
||||
in `lib/`, the complete original game catalog and approved HD snapshot in
|
||||
`data/`, and `runtime-manifest.json`. All authored mod definitions, scripts,
|
||||
SD/HD artwork, sounds and music are included. A fresh checkout can launch the
|
||||
bundled game with `./run.sh` without compiling it.
|
||||
|
||||
Build output in `target/`, local review evidence in `.reference/`, generation
|
||||
source references and duplicate zip exports in `dist/` are ignored. Working
|
||||
HD candidates in the old parent directory remain outside this repository;
|
||||
the assembler copies approved runtime artwork into tracked `data/`.
|
||||
|
||||
Refresh the bundle after reviewed source, mod or HD changes:
|
||||
|
||||
```sh
|
||||
python3 tools/assemble_runtime.py --source-data ../data
|
||||
git add bin lib data runtime-manifest.json
|
||||
```
|
||||
|
||||
The 11 October 2026 repository relocation preserves previous commits and uses
|
||||
a normal forward commit. Older commits use paths such as `RustRaptor/src/`;
|
||||
the current tree uses `src/`. The legacy C++ checkout remains on this drive
|
||||
outside the current Git root and is available in the preserved history.
|
||||
@@ -0,0 +1,76 @@
|
||||
# Restart checkpoint — 11 October 2026
|
||||
|
||||
The shutdown interrupted the final coordinated stop before the unified note
|
||||
was written. Work is preserved on `codex/rust-rewrite`; no game, Cargo, review
|
||||
helper or packaging shell process remained at recovery. The image-generation
|
||||
gate was paused with zero active leases, and has now been resumed. Recover each
|
||||
worker from its existing outputs before submitting another generation request.
|
||||
|
||||
## Engine and package
|
||||
|
||||
- Last committed engine checkpoint: `fdf2ff0`, standalone RustRaptor, widescreen
|
||||
menus and always-4:3 original cinematics, including when gameplay stretching
|
||||
is selected. Fresh 1080p intro capture confirms black 240-pixel side borders.
|
||||
- `RustRaptor/bin/raptor-rust`, `lib/`, `data/` and
|
||||
`dist/RustRaptor-linux-x86_64.zip` exist. The latest snapshot includes all
|
||||
6,449 original assets, 221 approved HD frames and 22 local mods. It was
|
||||
launched from a copied folder containing spaces, from `/tmp`, without Cargo.
|
||||
- Before shutdown, 320 default tests, the complete 42-image menu/intro audit,
|
||||
separate cinematic aspect regression, mod/catalog/HD validation and copied
|
||||
1280×800/1080p SDL/audio smoke checks passed. Later fixes require fresh checks.
|
||||
- Uncommitted lightning weapon/API packages and terrain compositor improvements
|
||||
are preserved. Review and commit them separately; do not discard their work.
|
||||
The executable/package must be refreshed after source or accepted-art changes.
|
||||
|
||||
## Next user fixes
|
||||
|
||||
1. Mod/save diagnostics: parent and selection are now preserved when details
|
||||
are reopened. The Escape regression passes; full screenshot audit is running.
|
||||
2. Installed mod rows now stay in ID order through toggles and priority changes,
|
||||
with dependency and confirmation behavior preserved. Navigation tests pass.
|
||||
3. Default Forward Guns still appear to miss miscellaneous buildings/cars.
|
||||
Expand actual original-content coverage beyond the earlier ten-unit test.
|
||||
|
||||
Root owns menu fixes. `iron_coast_5` temporarily owns the ground-target diagnosis;
|
||||
its completed boss candidates and pending equipment review remain preserved.
|
||||
|
||||
## HD state and owners
|
||||
|
||||
The original picture inventory is 5,753 IDs; 221 are registered and 5,532 remain
|
||||
unregistered. Existing raw candidates are not counted as accepted remasters.
|
||||
All original/generated base pixels and proof captures remain ignored; authored
|
||||
mod artwork and sound belong in Git.
|
||||
|
||||
| Agent | Recover and continue |
|
||||
| --- | --- |
|
||||
| hd_coordinator | Sole manifest/ledger owner; 29 boss and 48 terrain frames await final registration checks; retain Mole9 hold |
|
||||
| hd_full_review | All 66 terrain pages and 1,706 cases now reviewed; exact-hash terrain approval saved |
|
||||
| hd_terrain_render | Frozen compositor SHA b30b4a65…; 29 boss frames approved, Mole9 held; all eight new portrait screenshots approved |
|
||||
| campaign_integration | 34 effect/projectile families, 303 IDs have raw outputs; geometry, brightness and layout corrections continue |
|
||||
| campaign_release_review | Terrain 4: 84 jobs, 1,344 IDs; 64 candidates saved, half await further visual/Host QA |
|
||||
| hd_dark_player | Equipment and HUD digits; reconcile accepted frames, preserve pending Death Ray/Energy review |
|
||||
| hd_enemies_01 | Enemy 1: 139 IDs; source geometry holds, SHIP25 connection failures and APE service refusal preserved |
|
||||
| hd_terrain_set_1 | Terrain 1: 42 jobs, 672 IDs; first 16 registered; 80 other candidates and explicit rejected scenery remain |
|
||||
| hd_terrain_set_2 | Terrain 2: 90 jobs, 1,437 IDs; continue canonical chunks and full neighboring-context review |
|
||||
| iron_coast_2 | Enemy 3: 59 families, 138 raw IDs; recovered 45-family enemy 2 queue continues, old 11 families frozen |
|
||||
| iron_coast_3 | Boss 1: 26 families, 83 IDs; 11 recovered atlas families need binding/QA; previous 15 candidates preserved |
|
||||
| iron_coast_4 | Boss 2: 27 families, 83 IDs; continue corrections, complete reviews and respect service cooldowns |
|
||||
| iron_coast_5 | Boss 3: 25 families, 82 candidates and 328 scale checks; temporarily auditing every destructible classic tile for ground-gun misses |
|
||||
| iron_coast_6 | Terrain 3: 68 jobs, 1,082 IDs with middle/late shards; retain destroyed-state geometry holds |
|
||||
|
||||
## Durable worker evidence
|
||||
|
||||
- `data/hd/jobs/effects-10249-1024c/RESTART.md` and adjacent checkpoint JSON:
|
||||
effect/projectile raw outputs, exact hashes, request outcomes and next tasks.
|
||||
- `.reference/hd-terrain-cross-review/restart-checkpoint.json`:
|
||||
frozen terrain fix, 29 boss approvals, Mole9 hold and current portrait captures.
|
||||
- `.reference/hd-boss3-cross-review/shutdown-checkpoint.json`:
|
||||
completed boss shard, exact equipment8 candidate hashes and unfinished review.
|
||||
- `data/hd/jobs/bosses-03-review-summary.json` and
|
||||
`bosses-03-owner-progress.json`: all boss03 candidates and known caveats.
|
||||
- Current source-bound candidates/provenance under `data/hd/jobs/<job>/`;
|
||||
lease history under `data/hd/jobs/.imagegen-gate/`.
|
||||
|
||||
Do not regenerate already-returned raw images, infer approval from validation,
|
||||
clear source-fidelity holds or reset the shared checkout. Follow the documented
|
||||
single-call gate lifecycle and refresh checkpoints before the next shutdown.
|
||||
@@ -0,0 +1,101 @@
|
||||
# Rust engine and validation
|
||||
|
||||
Rust is the default engine. The Rust binary never calls or embeds the C++ engine. SDL2 provides window/input/controller/audio services. Simulation and the validation CLI build without SDL2. The retained C++ checkout is optional reference material.
|
||||
|
||||
| Module | Responsibility |
|
||||
| --- | --- |
|
||||
| assets | Checked catalog, palettes, raw/span pictures, libraries, maps and demo records. |
|
||||
| content, legacy_tables | Typed registries and generated original numeric mappings. |
|
||||
| simulation | Rational clock, integer motion, shared RNG, collision/weapon/enemy/terrain rules, script transactions. |
|
||||
| render, hd | Indexed canvas, ScaleNx, caches, PNG/palette conversion and optional source-hashed HD art. |
|
||||
| audio, sound | Original MUS/GENMIDI FM and digital DSP behavior; package-local PCM WAV decoding. |
|
||||
| cinematics | Checked AGX delta frames and movie timing. |
|
||||
| persistence | TOML settings, versioned JSON pilots, atomic writes and read-only legacy import. |
|
||||
| modding | Packages, dependency ordering, overrides, bounded Rhai callbacks/state and save pins. |
|
||||
| desktop | SDL2 services, UI, shop/equipment, mod manager, settings and interpolation. |
|
||||
|
||||
## Build and run
|
||||
|
||||
Rust 1.88+, Cargo and SDL2 development libraries are required. A local SDL2 installation in `~/.local/lib` is supported. Commands below run from `RustRaptor/`. The launcher sets the library path and uses a release build:
|
||||
|
||||
```sh
|
||||
./run.sh
|
||||
./run.sh --mods mods
|
||||
```
|
||||
|
||||
For development and tests:
|
||||
|
||||
```sh
|
||||
export LD_LIBRARY_PATH="$HOME/.local/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
cargo test --locked
|
||||
cargo test --locked --no-default-features
|
||||
RAPTOR_TEST_DATA="$PWD/../data" cargo test --locked
|
||||
RAPTOR_TEST_DATA="$PWD/../data" cargo test --locked -- --ignored
|
||||
```
|
||||
|
||||
Portable fixtures use synthetic assets and the bundled authored mod art/audio. Original integration checks are opt-in and local. Native audio comparisons compile a small retained-source harness with g++ and SDL2 headers; pure Rust playback itself uses no C++ runtime.
|
||||
|
||||
## Profiles and settings
|
||||
|
||||
Rust uses `$XDG_DATA_HOME/RaptorReplica/pilot-NNNN.json` and `mods/`, `$XDG_CONFIG_HOME/RaptorReplica/config.toml`, and `$XDG_CACHE_HOME/RaptorReplica/` for derived assets. Normal XDG defaults apply. Pilots record active package versions/hashes and script state/RNG. Missing, changed or reordered requirements show a compatibility prompt with Cancel selected and **Load anyway** available. Confirming keeps unavailable equipment, quantities, progression and inactive script state. The first subsequent write preserves the previous save in a hash-named `before-mod-change` backup. Corrupt files and unsafe structural values remain errors; inspecting or cancelling never modifies the file.
|
||||
|
||||
Legacy `~/.local/share/Raptor/SETUP.INI` and pilot files are imported read-only. All difficulties, waves, portrait and training metadata are retained. Rust writes only its separate versioned files. Explicit CLI import output cannot be the original file.
|
||||
|
||||
Options include scaling, scanlines, aspect correction, fullscreen/borderless, input bindings/controller, volumes and optional HD art. New/load pilot controls show compatibility details for affected save slots. The Mods screen applies validated selections at the main menu; selections made during flight remain pending until returning there.
|
||||
|
||||
The main menu leads to the Hangar, new/load/import pilots, options, mods and
|
||||
extras. The Hangar keeps flying, mission selection, the supply room, inventory,
|
||||
**Save pilot**, pilot ID editing and exit visible together. Saving writes the
|
||||
pilot atomically and shows the callsign and slot. Completed missions and
|
||||
equipment purchases also save automatically. Saves contain hangar/progression
|
||||
state; an in-flight save does not create a mid-mission checkpoint.
|
||||
New pilot registration selects a name, callsign, ID picture and difficulty,
|
||||
and starts with the original 10,000 credits and 75 energy. Imported ID pictures
|
||||
retain their native catalog identity.
|
||||
|
||||
Keyboard defaults are arrows for movement, Ctrl for firing, Space for bombs,
|
||||
Alt or Shift for cycling equipment, and Z for selected consumables. Selecting
|
||||
a weapon makes the fire control fire it alongside the forward guns. Settings
|
||||
that retain the old complete default cycle/bomb/special binding combination
|
||||
migrate on load; custom combinations are preserved.
|
||||
|
||||
## Local asset and platform checks
|
||||
|
||||
```sh
|
||||
cargo run --locked --no-default-features -- --data ../data --verify-assets
|
||||
cargo run --locked --no-default-features -- --data ../data --mods mods --validate-mods aurora
|
||||
cargo run --locked --no-default-features -- --data ../data --mods mods \
|
||||
--enabled aurora --headless aurora:field_trials --ticks 700
|
||||
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy cargo run --locked -- \
|
||||
--data ../data --profile-root ../.reference/smoke --smoke-frames 5 \
|
||||
--screenshot ../.reference/menu.png
|
||||
```
|
||||
|
||||
Use real SDL video for interactive/display-performance checks. Dummy screenshots verify raster placement, not GPU/vsync performance. `--preview-mission ID` starts a mission; `--preview-cinematic credits|intro|landing|death|end1|end2|end3` previews movies. Frame-limited runs do not save pilots/settings; cache stays within the selected profile.
|
||||
|
||||
## Controlled reference replays
|
||||
|
||||
This optional comparison workflow uses the retained C++ source. Rebuild it before recording. Trace output is opt-in during legacy demo playback and records complete updates after palette/stat processing, which also consumes the shared RNG.
|
||||
|
||||
```sh
|
||||
mkdir -p ../.reference
|
||||
cmake --build ../raptor/build --target raptor --parallel 8
|
||||
(cd ../data && env SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
|
||||
XDG_DATA_HOME="$PWD/../.reference/legacy-profile" \
|
||||
RAPTOR_REFERENCE_TRACE="$PWD/../.reference/cpp-demo.jsonl" \
|
||||
RAPTOR_REFERENCE_TICKS=1889 \
|
||||
../raptor/build/bin/raptor PLAY "$PWD/demos/DEMO1G1_REC")
|
||||
cargo run --locked --no-default-features -- --data ../data \
|
||||
--replay DEMO1G1_REC --ticks 1889 --trace ../.reference/rust-demo.jsonl
|
||||
python3 tools/compare_replays.py ../.reference/cpp-demo.jsonl ../.reference/rust-demo.jsonl
|
||||
```
|
||||
|
||||
All 1,889 available steps of DEMO1G1_REC match the captured fields: RNG, player coordinates/bank/shields/credits, ordered enemy position/health/animation/path state and ordered projectile assets/positions. The comparator fails at first divergence or unequal lengths. This is a subset of full state; inventory, effects, objectives and internal AI counters are outside this trace. That demo starts sector 1 wave 3 and ends before its boss. See the separate [first-mission wins](first-mission-validation.md), [108 campaign runs](campaign-validation.md) and [departure/death checks](transition-validation.md). Recordings stay ignored in `../.reference/`.
|
||||
|
||||
## Audio and remaining acceptance
|
||||
|
||||
The Rust FM/DSP implementation has synthetic sample-by-sample native comparisons and a four-second original intro-music comparison at 44.1kHz. The checked sound bank also dispatches original and mod cues through the SDL callback; real-device output checks are documented in [sound validation](sound-validation.md). These establish those cases, not every track/input/output rate. The Rust core retains the original envelope/noise semantics and LGPL source provenance.
|
||||
|
||||
The campaign checks cover all waves and difficulties with assistance, and first-mission tests win without replenishing pilot health or editing enemy state. Wider ordinary playtesting and more complete native state comparisons remain useful. Normal startup includes the original logos, fanfare and intro. Native timing, sound-marker resolution, scene stops and custom logo palettes are checked in [intro validation](intro-validation.md). The refined menu uses an authored hangar backdrop and has a [42-frame screenshot review](menu-validation.md). Base-game HD coverage is partial; bundled mods supply SD/HD art. See [display measurements](performance.md).
|
||||
|
||||
Scale2x/3x follows the [published algorithm](https://www.scale2x.it/algorithm); Scale4x applies Scale2x twice. WAV parsing follows the [RIFF chunk format](https://learn.microsoft.com/en-us/windows/win32/xaudio2/resource-interchange-file-format--riff-). Original catalog/reference assets remain outside version control. Authored mod artwork and sounds are tracked.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Sound playback validation
|
||||
|
||||
The Rust engine plays original digital effects, MUS music through its Rust FM
|
||||
synthesizer, and package-local WAV cues. SDL2 provides the output device.
|
||||
|
||||
Original sound-event IDs identify an empty catalog marker. The actual digital
|
||||
patch is four catalog entries later, matching the original setup routine.
|
||||
The sound bank resolves and checks that patch, preloads the 26 original
|
||||
digital assets, and reports missing or invalid cues. Mod WAVs are decoded and
|
||||
cached before acquiring the audio-device lock. Successful mod activation
|
||||
invalidates that cache.
|
||||
|
||||
Music/effect gains and channel limits are applied before the device starts.
|
||||
Changing effect volume updates playing voices; muting clears held samples.
|
||||
Music volume zero gates FM output while the score and synthesizer continue
|
||||
advancing, so restoring volume resumes the current music position.
|
||||
|
||||
Portable audio and sound-bank tests cover checked decoding, mixing, pitch,
|
||||
priority, channel limits, gains, mute behavior and missing/invalid assets.
|
||||
Optional original-data checks compare FM and digital samples with a small
|
||||
retained-source harness. Another integration check dispatches all 36 original
|
||||
cue variants through the production Rust SDL callback and checks their output.
|
||||
|
||||
Local real-device checks on 2026-10-10 used SDL's PulseAudio backend through
|
||||
PipeWire to the default analog stereo sink. A first-mission run with music
|
||||
muted produced digital-effect output with peak 16,390 and RMS 6,657.51. A
|
||||
concurrent sink-monitor recording correlated with the callback output at
|
||||
0.99929. A seven-package Iron Coast run also produced audible-level output
|
||||
(peak 16,406; RMS 4,909.62). System volume and mute were unchanged. These
|
||||
measurements confirm output routing and signal; they do not assess acoustic
|
||||
balance on every speaker or every cue during ordinary play.
|
||||
|
||||
The final combined build was checked through the same real device after the
|
||||
mute fix: 172,544 captured stereo frames of first-mission effects had peak
|
||||
16,384 and RMS 6,506.91. A separate run with both gains at zero captured
|
||||
54,784 stereo frames with zero nonzero samples, peak zero and RMS zero.
|
||||
|
||||
For a bounded diagnostic capture of the actual production callback, run from
|
||||
`RustRaptor/` with an isolated profile:
|
||||
|
||||
```sh
|
||||
RAPTOR_AUDIO_CAPTURE="$PWD/../.reference/audio.wav" \
|
||||
RAPTOR_AUDIO_CAPTURE_SECONDS=10 \
|
||||
SDL_AUDIODRIVER=pulseaudio ./run.sh \
|
||||
--profile-root ../.reference/audio-profile \
|
||||
--preview-mission base:sector_1_wave_1 --smoke-frames 540
|
||||
```
|
||||
|
||||
The capture is PCM16 stereo and accepts 1–120 seconds. Configure music and
|
||||
effects in that profile before capture. Original assets, captures and reports
|
||||
remain ignored under `../data/` and `../.reference/`. Authored mod WAVs and
|
||||
their synthesis recipes are tracked in Git.
|
||||
@@ -0,0 +1,50 @@
|
||||
# Standalone Linux package
|
||||
|
||||
`RustRaptor/` contains the Rust executable, its original data catalog, approved
|
||||
HD replacements, authored mods, SD/HD mod artwork, sound effects and music.
|
||||
Normal play needs no C++ engine, Cargo, Python or source checkout. Run
|
||||
`./run.sh`; installed pilots and configuration remain in the user's separate
|
||||
`RaptorReplica` profile directories.
|
||||
|
||||
The executable finds its package from `bin/raptor-rust`, including when launched
|
||||
from another working directory. A missing packaged catalog reports an error
|
||||
instead of selecting another checkout's data. `--data` and `--mods` remain
|
||||
available for deliberate overrides.
|
||||
|
||||
From this folder, run:
|
||||
|
||||
```sh
|
||||
python3 tools/assemble_runtime.py --zip dist/RustRaptor-linux-x86_64.zip
|
||||
```
|
||||
|
||||
The assembler builds the desktop release, copies all catalog-listed files and
|
||||
the exact currently approved HD manifest and referenced PNGs, installs the
|
||||
binary and SDL2 shared library if linked, and checks the packaged catalog.
|
||||
`runtime-manifest.json` records file hashes, catalog/HD counts and platform
|
||||
dependencies. Unapproved HD drafts, reference screenshots and the Cargo build
|
||||
cache are excluded from the zip. The exported `RustRaptor` folder can be moved
|
||||
or unpacked anywhere, including paths containing spaces.
|
||||
|
||||
The binary targets Linux x86_64. It still uses the operating system's C runtime
|
||||
and desktop/audio drivers; this package is not a Windows or macOS executable.
|
||||
Source, Cargo.lock, code licenses and mod API documentation are included.
|
||||
The packaged executable, runtime libraries, original catalog and approved HD
|
||||
artwork are tracked in Git, along with the assembler and authored mod images
|
||||
and sounds. Build caches, working art drafts, review evidence and duplicate zip
|
||||
exports are excluded. See [repository layout](repository.md).
|
||||
|
||||
Re-run the assembler after engine changes, new approved HD registrations or
|
||||
mod edits to refresh the package. The runtime snapshot contains approved art
|
||||
only; it does not claim the full original HD catalog is complete.
|
||||
|
||||
The current package was unpacked into a folder containing spaces and launched
|
||||
from `/tmp` with only its bundled SDL2 library on the library search path.
|
||||
Catalog validation checked 6,449 assets, 27 missions, 528 enemies, 16 weapons
|
||||
and 24 items. All 22 bundled mods and all 221 approved HD frames validated.
|
||||
Menu and gameplay screenshots at 1280×800 and 1920×1080, plus stereo audio
|
||||
captures, completed successfully. Native `unzip` preserves the executable
|
||||
permissions stored in the archive.
|
||||
An original-intro capture at 1080p also confirms a 1440×1080 video area and
|
||||
black 240-pixel side borders when gameplay stretching is enabled. Removing
|
||||
the copied catalog produces an error at the copied data path, with no fallback
|
||||
to the development checkout.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Original mission ending and death
|
||||
|
||||
The Rust simulation now follows the retained engine's `RAP_ThinkStats` ending
|
||||
sequence for missions with `legacy_map`. Authored mod missions retain their
|
||||
immediate completion/failure contract. This is a source-based port with portable
|
||||
synthetic regressions; a C++ recording through a complete ending or death is
|
||||
still required before claiming transition parity.
|
||||
|
||||
## Departure
|
||||
|
||||
Removing the final eligible original enemy arms a 60-step countdown before
|
||||
hostile-shot processing, matching `ENEMY_Remove`. Player damage is suppressed
|
||||
while the countdown is active, matching `OBJS_SubEnergy`. At the start of each
|
||||
subsequent step the countdown decrements after input and before world updates.
|
||||
An entry value of zero still executes that final simulation step before the
|
||||
outcome changes.
|
||||
|
||||
At 40, control pauses and `FX_FLYBY` is emitted. Below 40, the ship moves toward
|
||||
the horizontal corridor 152–168 in eight-pixel increments and upward four
|
||||
pixels. The original paused input phase retains the prior forced delta; the
|
||||
statistics phase then applies the new delta. Consequently most departure steps
|
||||
contain both moves. Boundary clamping is suspended during the ending. A healthy
|
||||
ship remains drawable as it leaves the screen; `Outcome::Complete` follows the
|
||||
final simulation pass, rather than appearing while the ship is stationary.
|
||||
|
||||
`World.control_paused`, `departure_delta`, `end_timer` and `draw_player` expose
|
||||
these states to the SDL-independent renderer/input consumer. The synthetic
|
||||
stationary-pilot fixture, starting with an already armed countdown, completes
|
||||
after 61 steps at `(160, -160)`.
|
||||
|
||||
Original demo playback assigns recorded coordinates and banking directly and
|
||||
bypasses both automatic moves, matching `IPT_MovePlayer`'s playback early return.
|
||||
Coordinates captured at the end of a newly authored step do not necessarily
|
||||
represent the original recording phase; use deterministic keyboard-input replay
|
||||
for strict reproduction of an entire newly recorded ending.
|
||||
|
||||
## Death
|
||||
|
||||
Zero energy starts the same countdown instead of immediately failing the
|
||||
mission. Each statistics pass requests a medium-air and small-air explosion
|
||||
with randomized coordinates. Above 24 it also emits a randomly panned air
|
||||
explosion sound; the original randomized sound pitch consumes the shared RNG.
|
||||
|
||||
At 24, `draw_player` becomes false. The sequence requests its centered explosion,
|
||||
three explosion sounds and 512 alternating large-air/air-blast effects. The
|
||||
original animation pool holds at most 100 effects. All 1,024 burst-coordinate
|
||||
RNG calls still occur when that pool fills. The portable fixture verifies the
|
||||
first randomized positions, sound pan choices, the pool limit and exact RNG
|
||||
advance at the burst, including the separate palette-update RNG call. A pilot
|
||||
whose energy is initially zero remains `Playing` through the visible explosion
|
||||
sequence and reaches `Failed` after 62 steps.
|
||||
|
||||
Low-energy equipment loss runs after the death/departure statistics, preserving
|
||||
the existing shared RNG and automatic weapon-selection behavior.
|
||||
|
||||
## Verification
|
||||
|
||||
Run the portable regressions without original assets:
|
||||
|
||||
```sh
|
||||
cargo test --no-default-features --test transitions --test gameplay
|
||||
```
|
||||
|
||||
The transition suite has five tests covering successful departure, direct demo
|
||||
positions, immunity beginning on final-enemy removal, delayed death with the
|
||||
burst RNG/pool cap, and authored mod outcome compatibility. The gameplay suite
|
||||
also retains beam ordering, raw demo bounds, equipment loss, kamikaze movement,
|
||||
Energy Grab, Ground Bomb and Mega Bomb checks.
|
||||
|
||||
After this change, the complete available `DEMO1G1_REC` still matched the rebuilt
|
||||
C++ engine's gameplay snapshots across all 1,889 simulation steps. That recording
|
||||
ends before its boss, so it proves the transition changes did not disturb the
|
||||
captured pre-ending behavior; it does not validate the new transition itself.
|
||||
See [campaign-validation.md](campaign-validation.md) for reference rebuild and
|
||||
comparison commands, captured fields, original-data checks and limitations.
|
||||
|
||||
The relevant retained sources are `../raptor/src/rap.cpp` (`RAP_ThinkStats` and
|
||||
`Do_Game`), `../raptor/src/rap.h` (`END_DURATION`, `END_EXPLODE`, `END_FLYOFF`),
|
||||
`../raptor/src/input.cpp` (`IPT_MovePlayer`, `IPT_FMovePlayer`),
|
||||
`../raptor/src/enemy.cpp` (`ENEMY_Remove`) and `../raptor/src/objects.cpp`
|
||||
(`OBJS_SubEnergy`). God-mode/debug behavior, rumble, warning-light presentation,
|
||||
cinematic sequencing and a native transition reference are outside this scoped
|
||||
verification.
|
||||
@@ -1,190 +0,0 @@
|
||||
# Patches against upstream
|
||||
|
||||
Base: nukeykt's reconstructed Raptor source (skynettx/raptor mirror),
|
||||
vendored in `raptor/` (GPLv2+, see `raptor/LICENSE` and `raptor/VENDOR.txt`).
|
||||
Everything below diverges from upstream on purpose. Keep this list current
|
||||
when touching engine code.
|
||||
|
||||
## Asset system: catalog of loose files (largest change)
|
||||
|
||||
Upstream reads seven encrypted `FILE000N.GLB` archives via
|
||||
`src/glbapi.cpp`. This fork replaces the archive reader with a catalog of
|
||||
loose files:
|
||||
|
||||
- `GLB_LoadCatalog()` parses `data/INDEX` (`<hex handle> <size> <path>`,
|
||||
sorted); `GLB_CatFind()` binary-searches it; `GLB_Load()` reads the file.
|
||||
- `GLB_SlotItems(slot)` (new public API, `src/glbapi.h`) counts a slot's
|
||||
assets; `src/rap.cpp` uses it for mission-pack and registered-version
|
||||
detection instead of probing for `.GLB` filenames.
|
||||
- `GLB_InitSystem` keeps its signature but ignores the archive-name prefix
|
||||
parameter.
|
||||
- Payloads are pre-decrypted at extraction time; the runtime never runs the
|
||||
GLB cipher. `GLB_EnCrypt/GLB_DeCrypt` remain for reference/tooling.
|
||||
- `FILEDESC` shrank to `{item, items}`; the archive file-handle cache
|
||||
(`GLB_FindFile/GLB_OpenFile/GLB_CloseFiles`) was removed.
|
||||
|
||||
Rationale and format docs: [glb-format.md](glb-format.md),
|
||||
[asset-pipeline.md](asset-pipeline.md).
|
||||
|
||||
## Exit: no ENDOOM screen
|
||||
|
||||
Upstream's `ShutDown()` (`src/rap.cpp`) loaded `LASTSCR*_TXT` and opened a
|
||||
second, text-mode "farewell" window (`I_LASTSCR`, `src/i_lastscr.cpp`)
|
||||
that blocked until a keypress. Removed: `ShutDown` now closes the game
|
||||
window and exits directly; `src/i_lastscr.cpp/.h` deleted.
|
||||
|
||||
## Diagnostics: EXIT_Error prints
|
||||
|
||||
Upstream's `EXIT_Error` (`src/common.h`) was a stub that called `exit(0)`
|
||||
without printing — every engine error died silently. It now prints its
|
||||
formatted message to stderr before exiting (still `exit(0)`, matching the
|
||||
original exit code behavior).
|
||||
|
||||
## Platform: Linux-only
|
||||
|
||||
- `CMakeLists.txt` reduced to the Linux/SDL2/ALSA build; the Android
|
||||
(FetchContent SDL), Windows (MSVC/MinGW + `rsrc/` resources) and macOS
|
||||
branches are gone.
|
||||
- Deleted upstream trees: `raptor/android/`, `msvc/`, `gcc/`, `pkg/`,
|
||||
`rsrc/`, `win32build.bat`, `osxbuild.sh`, the bundled Windows SDL2 SDKs
|
||||
under `include/`, and the `SETUP(*).INI` distribution templates.
|
||||
- `src/loadsave.cpp`: removed the `__ANDROID__` block that copied `.GLB`
|
||||
files to external storage (obsolete concept in this fork).
|
||||
|
||||
## Data detection
|
||||
|
||||
`src/rap.cpp`: mission-pack presence is derived from catalog slot counts
|
||||
(`GLB_SlotItems`) rather than `access("FILE000N.GLB")` probes; the fatal
|
||||
"data files NOT FOUND" check now keys off slot 0 / no packs.
|
||||
|
||||
## Rendering: decoupled presentation loop
|
||||
|
||||
The original game advanced simulation and presentation in lockstep at
|
||||
70/3 Hz (~23 fps): `Do_Game()` ran one think+draw pass, then waited three
|
||||
timer ticks. This fork keeps the **simulation** at exactly that rate but
|
||||
presents interpolated frames at display rate:
|
||||
|
||||
- Simulation is paced by a wall-clock accumulator (`RAP_SIM_STEP` in
|
||||
`src/rap.cpp`, units of 1/70000 s = 3 ticks). Input handling, all
|
||||
`*_Think()`, `DEMO_Think()` (one attract-mode record per step), palette
|
||||
fades and the stats state machine run once per step, preserving game
|
||||
speed, demo playback timing and RNG consumption order.
|
||||
- After each step the scene is redrawn and presented with an interpolation
|
||||
fraction until the next step is due (`GFX_SetInterp` /
|
||||
`GFX_InterpCoord`, `src/gfxapi.cpp`). When behind schedule (blocking
|
||||
menus ate wall time) renders are skipped so the simulation catches up,
|
||||
matching the old tick-wait behavior.
|
||||
- Draw positions are lerped between the previous and current simulation
|
||||
states for player ship, tile map scroll, enemies, enemy/player shots,
|
||||
bonuses, anims and shadows. The map scroll interpolates an unwrapped
|
||||
counter (`vscroll` in `TILE_Scroll`) because raw `tileyoff` wraps by
|
||||
32 px while the on-screen tile grid stays positionally continuous;
|
||||
`TILE_Scroll` runs before `TILE_Think` so wrap intervals render
|
||||
seamlessly.
|
||||
- State changes that used to ride inside draw calls were moved to
|
||||
simulation or gated per step: enemy engine-flame toggle (`eframe`,
|
||||
`ENEMY_Think`), beam-shot animation counter (`SHOTS_Think`),
|
||||
ion-scanner sweep (`OBJS_DisplayStats`, gated by `gl_cnt`), S_LINE
|
||||
tracers are purged by `SHOTS_Think` instead of being removed mid-draw.
|
||||
The S_LINE purge keys off a per-shot `age` counter (one simulation step
|
||||
of life, matching upstream's draw-then-remove); the turret's retire
|
||||
path never frees its shot, so the purge must not reuse `doneflag`,
|
||||
which is hit/retire game state — reusing it leaked one `SHOTS` pool
|
||||
slot per turret fire until `SHOTS_Get()` could service no weapon, and
|
||||
hid the beam on hitting ticks.
|
||||
- `RAP_DisplayStats` was split into `RAP_ThinkStats` (state: death
|
||||
sequence, wave-exit control, low-shield warnings) and the drawing half.
|
||||
The shield bars are redrawn unconditionally each rendered frame: the
|
||||
old `g_oldsuper`/`g_oldshield` dirty check can never fire when the
|
||||
latch runs in `RAP_ThinkStats` before the draw, which froze the bars.
|
||||
`g_oldsuper` is gone; `g_oldshield` survives as the sim-side
|
||||
previous-step shield used for damage detection. The muzzle-flash ship
|
||||
pic (`g_flash`) is likewise cleared once per simulation step, not per
|
||||
rendered frame, or it would be invisible at display refresh rates.
|
||||
- The above-player anim layer (`ANIMS_DisplayHigh`) is drawn between the
|
||||
player and enemy shots as upstream does; most air-death explosions
|
||||
register `HIGH_AIR`, so dropping that call made dying ships vanish
|
||||
without exploding.
|
||||
- Vsync is enabled when the display reports a refresh rate;
|
||||
`I_VsyncActive()` lets the game loop fall back to a delay guard when
|
||||
the renderer ignores the flag.
|
||||
- Menus keep their 70 Hz `GFX_DisplayUpdate` pacing; intro/story screens
|
||||
still pace at 4 ticks (~17.5 fps).
|
||||
|
||||
## Video: borderless window mode
|
||||
|
||||
New `Video/borderless` SETUP.INI option (`VIDEO_LoadPrefs`). When set,
|
||||
`SetVideoMode` creates a decoration-free window at the desktop resolution
|
||||
anchored at (0,0) (`SDL_WINDOW_BORDERLESS`). Alt+Enter cycles between the
|
||||
borderless desktop window and a normal decorated window while the mode is
|
||||
active. Exclusive `fullscreen_width/height` modes ignore it; if both
|
||||
`fullscreen=1` and `borderless=1` are set, `fullscreen` wins. The stale
|
||||
commented-out `-borderless` command-line snippet was removed.
|
||||
|
||||
## Video: HD presentation filter (scale2x/scale3x)
|
||||
|
||||
New `[Video]` SETUP.INI keys, read once at boot in `VIDEO_LoadPrefs`
|
||||
(`src/i_video.cpp`): `upscaler` (0 = off, 2/3/4 = scale2x/scale3x/
|
||||
2x scale2x, default 3) and `scanlines` (0/1, default 0). The filter is a
|
||||
presentation-level CPU pass over the final RGBA frame before it enters the
|
||||
streaming texture — no engine logic or asset changes; the 4:3 playfield is
|
||||
kept. Kernels are the AdvMAME scale2x/scale3x rules by Andrea Mazzoleni,
|
||||
reimplemented in-repo (`src/i_upscale.cpp`, GPLv2+-compatible; nothing
|
||||
vendored). Filtered frames render directly from the factor-sized streaming
|
||||
texture and bypass the nearest-integer intermediate (`texture_upscaled`),
|
||||
which only sharpens sub-integer fits of a 1x source. With `upscaler=0` the
|
||||
original four-stage pixel-perfect path is byte-for-byte unchanged. The
|
||||
scanline overlay darkens every third logical row (320x240 space) after the
|
||||
final copy, so it stays geometry-safe under every window mode. Like
|
||||
`Video/borderless`, these are hand-edited INI settings; `raptorsetup` is
|
||||
not extended.
|
||||
|
||||
|
||||
## Effects: machine-gun casings eject into world space
|
||||
|
||||
Upstream's forward-gun muzzle effect (`A_PLAYER_SHOOT`, the GUNSTR
|
||||
diagonal streak art) is a player-relative anim: the streak is glued to
|
||||
the wingtip and flickers in place for its 4-step life, which at the
|
||||
decoupled presentation rate reads as shells vibrating at the gun rather
|
||||
than being ejected. This fork registers the same art once more as
|
||||
`A_PLAYER_EJECTA` (GROUND layer, `src/anims.cpp`) and spawns that at each
|
||||
wingtip's world position (`SHOTS_PlayerShoot` case `S_FORWARD_GUNS`) via
|
||||
`ANIMS_StartGAnim`. The casings therefore stay behind as the plane moves
|
||||
on and drift down with the map scroll (1 px per simulation step, ~23 px/s)
|
||||
for their 172 ms life. Sim-side timing is untouched: spawn cadence
|
||||
(shoot_rate 2), anim rate, RNG consumption and demo playback are
|
||||
identical to upstream; only the anim's reference frame changes.
|
||||
|
||||
## Input: no cursor snapping
|
||||
|
||||
Dialog/menu code no longer warps the mouse cursor onto the default
|
||||
button: `SWD_SetWindowPtr`/`SWD_SetFieldPtr` were deleted along with all
|
||||
callers (`store.cpp`, `windows.cpp`), as were the direct
|
||||
`PTR_SetPos(field-center)` calls when opening ask dialogs, the difficulty
|
||||
select, the hangar keyboard navigation and the ship computer. Keyboard
|
||||
selection still highlights fields without moving the pointer. The
|
||||
mission-start `PTR_SetPos(playerx, playery)` in `Do_Game` is kept — it
|
||||
seeds mouse ship control, not menu focus.
|
||||
|
||||
## UI: in-game options menu with runtime video settings
|
||||
|
||||
The Options dialog now carries the HD-remaster video settings that were
|
||||
previously boot-only INI keys. `ui/OPTS_SWD` was re-authored (same catalog
|
||||
handle `0x1003f`; built by `tools/swdbuild.py` from
|
||||
`tools/opts_swd_spec.py`) from 13 to 23 fields, adding FILTER, SCANLINES,
|
||||
RESOLUTION, FULLSCREEN and ASPECT rows with selection icons beside the
|
||||
existing Music/FX/Detail rows, and new `OPTS_*` ids in `src/winids.h`.
|
||||
`src/i_video.cpp` gained runtime setters (`I_SetVideoFilter/Scanlines/
|
||||
VideoResolution/Fullscreen/AspectCorrect`) that only touch presentation
|
||||
resources — the factor-sized surface/texture pair was extracted from
|
||||
`SetVideoMode` into `RebuildStreamTextures` so the filter can change
|
||||
without re-creating the 8-bit screenbuffer. Changes live-apply from the
|
||||
dialog and persist to `[Video]` SETUP.INI keys (`upscaler`, `scanlines`,
|
||||
`fullscreen`, `aspect_ratio_correct`) on EXIT, and the dialog is also
|
||||
reachable in-mission via the O key (`Do_Game`, bracketed like F1 help).
|
||||
|
||||
## Deliberately unchanged
|
||||
|
||||
- Game logic, mission data formats, audio synthesis — untouched.
|
||||
- `GLB_*` API names and handle layout (`slot << 16 | item`) — kept so
|
||||
`src/fileids.h` and all call sites stay identical to upstream.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Sprite and terrain validation
|
||||
|
||||
`tests/visuals.rs` separates portable synthetic checks from local checks of the original catalog. Generated contact sheets and original pixels stay under ignored `../.reference/visuals/`; they are not shipped with the engine or committed.
|
||||
|
||||
The independent test oracle follows the unoptimized corner rules in the [published Scale2x/Scale3x algorithm specification](https://www.scale2x.it/algorithm), with its clamped border sampling. Scale4x applies the independent Scale2x oracle twice. The renderer uses a shared optimized guard, so the oracle and renderer express the rules differently.
|
||||
|
||||
The portable suite checks all 19,683 neighborhoods over two palette colors plus transparency, additional multi-color patterns and one-pixel borders. It verifies exact output indices, opaque palette indices 0/255, transparent composition, palette changes without changing stored indices, clipping on every viewport edge, indexed shadow masks, sharply scaled bitmap text and neighbor context at terrain boundaries. Its independent-tile fixture deliberately produces different results when tiles are filtered separately, so the seam check cannot pass trivially. Shadow checks cover six-bit RGB clamping, the original Manhattan palette-distance and last-equal tie rules, native ground-mask offsets, signed air projection, collapsed-row suppression, sparse spans, clipping at 1×..4× and interpolated translation without coverage loss.
|
||||
|
||||
Run portable checks with:
|
||||
|
||||
```sh
|
||||
cargo test --test visuals
|
||||
```
|
||||
|
||||
Run the original-data checks and create private contact sheets with:
|
||||
|
||||
```sh
|
||||
cargo test --test visuals original_sequences_preserve_canvases_palette_origins_and_terrain_context -- --ignored
|
||||
cargo test --test visuals original_shadow_tables_and_banking_masks_match_indexed_projection -- --ignored
|
||||
```
|
||||
|
||||
The local checks cover all seven light, dark and firing-flash player banking frames; all 42 `GEXPLO_BLK` ground explosion frames; all 16 `AIRBOOM_PIC` and 13 `EXPLO2_BLK` frames; four micro-missile and gun-ejecta frames; six `SHIP22G1_PIC` boss frames and four `SHIP08G1_PIC` enemy frames. Each original frame is compared against the oracle at 1×, 2×, 3× and 4×, with exact logical canvas dimensions and original palette labels retained. The report records frame IDs, logical canvases, full-canvas center anchors and visible bounds at every scale.
|
||||
|
||||
The original 20-byte GFX header contains type, two option fields, width and height; its option fields are not replacement x/y origins. Span x/y coordinates locate pixels within the unchanged canvas. Player rendering uses that top-left canvas origin. Original centered effects use half the full canvas width/height, as recorded by `ANIMS_Register` in `../raptor/src/anims.cpp`; visible explosion bounds must not become animation anchors. Missing transparent margins or auto-cropping frames would move those anchors.
|
||||
|
||||
A three-by-three patch from the first original mission is composed at native resolution and filtered as one image. The center tile filtered with two original pixels of neighboring context must exactly match the center of the full joined patch at 2×, 3× and 4×. The renderer's two-pixel terrain gutter covers Scale4x's two successive passes. This establishes filter consistency across the tested boundaries; it does not assess whether different source tiles were artistically seamless.
|
||||
|
||||
Each sequence emits `<NAME>-nearest4.png`, `<NAME>-scale4.png` and `<NAME>-runtime4.png`. The runtime sheet applies the engine's per-asset fallback. Read left-to-right, then top-to-bottom, up to seven frames per row. Terrain comparisons are `terrain-joined-2x.png`, `terrain-joined-3x.png`, `terrain-joined-4x.png` and matching `terrain-independent-*` files. `report.json` stores frame order, bounds, opaque pixel counts, fallback flags, selected terrain row and the number of pixels changed by independent tile filtering.
|
||||
|
||||
## Review findings
|
||||
|
||||
On 2026-10-10, all fourteen portable tests and both local catalog checks passed. The sequence check covered all 110 frames at every scale plus the terrain patch. Complete nearest-neighbor and Scale4 contact sheets were inspected for banking, explosions, projectiles, the sample enemy and the opening boss animation. No canvas shift, clipped replacement canvas, lost palette identity or changed simulation dimensions was found. Banking retains the nose and engine registration across all seven poses. Large explosion lobes and expanding rings remain aligned throughout their sequences.
|
||||
|
||||
Two nearest-neighbor fallback groups are implemented in `render::prefers_nearest`, covering 26 original frames:
|
||||
|
||||
- `NMSHOT_BLK` frames 0..3, assets `0x101c3..0x101c6`: each frame has four visible source pixels. At 4×, ScaleNx keeps 44 opaque pixels against nearest-neighbor's 64, reducing visible coverage by 31.25% and changing the small square mark into a round bead. The implemented nearest-neighbor fallback preserves this tiny projectile's brightness and shape.
|
||||
- Sparse late `GEXPLO_BLK` frames 20..41, assets `0x10178..0x1018d`: Scale4 reduces opaque coverage by approximately 10..25%. The implemented nearest-neighbor fallback preserves those residual embers; the large early flame frames retain the smoother filter. The initial 17-pixel flash also loses about 10.3% coverage and remains a candidate for the tiny-effect fallback after playback review.
|
||||
|
||||
The original-data test exercises the actual `SpriteCache` fallback route at all four factors and asserts exact nearest-neighbor pixels and full original opaque coverage for every fallback frame. Other frames must still match ScaleNx. Supplied HD PNGs bypass this original-asset filtering policy and retain their detail.
|
||||
|
||||
The desktop shadow pass follows `shadow.cpp` and `gfxapi.cpp`: ground masks use the -9 palette table at (-3,+4); aircraft and player masks use a separate -6 table at (-10,+20), projected through view (160,100,1000), depth 1280 and distance 200. The renderer retains signed integer division followed by arithmetic shift, inclusive horizontal span endpoints, and first-row coverage when several source rows collapse to one projected row. It shades existing palette indices rather than painting flat translucent black. Ground shadows precede ground enemies; air and player shadows follow ground enemies and precede ground effects and aircraft. Legacy enemy field 11 controls presence, with the original 25-ground/50-air limits. Mod enemies receive the corresponding ground/air shadow from their supplied SD logical canvas. Optional HD artwork retains this logical shadow mask.
|
||||
|
||||
The original shadow code's preliminary bounds check projects its origin twice and calculates height using the projected x coordinate. Rust clips each actual projected span instead, correcting missing valid shadows near the lower edge. This is a presentation culling repair; original span projection and palette mapping are retained. It does not alter collisions or simulation state.
|
||||
|
||||
The local shadow test compares all 256 entries of the original -9, -6 and +9 tables against an independent distance/tie oracle. It verifies all seven player banking masks against an independent native span raster at all four factors and emits `player-shadow-banking-4x.png`, `ground-shadow-4x.png` and `shadow-tables.json` under the private visual directory. The first contact sheet pairs each complete banking pose with its projected shadow on original terrain; the second checks native ground offset and registration.
|
||||
|
||||
Both shadow contacts were inspected after the tests passed. The seven banking shadows retain their changing silhouette and full-canvas registration without transparent rectangles or missing lower-screen spans. The ground defense shadow retains the native (-3,+4) offset. Shadow pixels remain terrain palette colors, so palette changes and indexed composition remain available.
|
||||
|
||||
The inspected light ship frames retain 97.2..98.3% of nearest-neighbor opaque coverage at 4×, dark/flash frames retain at least 97.8%, the large boss retains at least 99.6% and the sample enemy retains 99.9%. These are presentation measurements, not collision masks. Their full original logical canvases remain unchanged. No nearest-neighbor fallback is recommended for those reviewed sequences.
|
||||
|
||||
The original terrain patch exposed 113 differing pixels at 2×, 297 at 3× and 727 at 4× when the tiles were filtered independently. Filtering the joined patch with the tested two-pixel neighbor gutter exactly matched the full-image center at each factor. The paired contact sheets show subtle texture discontinuities along independent tile boundaries; joined filtering retains the diagonal water transition. This review covers the crisp original terrain path; supplied HD terrain needs its own artistic edge review.
|
||||
|
||||
Optional HD replacements receive a separate source, canvas and silhouette review, recorded in `hd-art.md` and `hd-backlog.md`. The runtime selects accepted frames from the HD manifest and retains the original crisp fallback for unregistered or rejected frames. Original ScaleNx checks above do not certify generated HD artwork.
|
||||
|
||||
The frontend previously requested `AIRMED_BLK`, which does not exist in the original catalog. Effects now carry typed identities for the original air/ground explosions, flak, energy, people, flares and sparkles. The renderer selects each sequence's asset, duration, centered canvas origin and ground/air layer. Enemy deaths retain their original multiple bursts and RNG consumption; ground effects follow terrain scroll. This source-based port repairs the missing sequence and generic fallback. Exhaustive visual comparison of every effect remains outside the sampled checks above.
|
||||
|
||||
The new enemy packages include their generated SD/HD artwork and audio in Git. Their original style-reference pixels under `art/source/` remain excluded. Their per-package `art-qa.json` and `provenance.json` provide a separate record of supplied-art canvas alignment and generation prompts.
|
||||
|
||||
## Native effects and player presentation
|
||||
|
||||
The 2026-10-10 source review also compared `ANIMS_Register`, `SHOTS_Display`, `ESHOT_Display`, `BONUS_Display`, `RAP_GetShipPic` and `FLAME_Down` against their Rust render paths. It restored player and enemy laser shafts tiled every three logical pixels, their distinct power animations, native impact caps and three indexed turret-tracer lines. Emissions now include missile smoke, blue/orange hit sparks, energy-grab animations attached to enemies, world-space gun casings, missile-pod muzzle animations, shield lighting and delayed terrain flare/sparkle overlays. Gun casings use the original four-frame streak rather than an explosion at the muzzle. Native explosions and secondary animations jointly obey the original 100-animation allocation limit; visual allocation failure leaves weapon firing and RNG consumption intact.
|
||||
|
||||
Pickup halos, smoke masks and shield glow use the original +9 VGA light lookup to transform the existing background indices. Mask colors are not painted as foreground sprites. Colored companions use the normal optional HD selection and original fallback. The secondary queue and pickup presentation latches are excluded from saved gameplay serialization, advance on simulation steps and never consume the simulation RNG. Eight focused portable tests cover beam direction, cap offsets, tracer colors, lighting-mask holes, casing placement, smoke drift/lifetime, unchanged gameplay state and pool saturation. The complete retained native demo still matches all 1,889 gameplay snapshots, finishing with shield 3 and 60,825 credits.
|
||||
|
||||
Player presentation selects all seven `LPLAYER` poses normally. The two native dark missions, sector 2 wave 8 and sector 3 wave 3, select `DPLAYER` between shots and `FPLAYER` when the existing simulation firing-flash latch is set. Light missions retain `LPLAYER` during the flash. The player and its original shadow mask use the same selected canvas. Accepted HD frames are read by that asset ID, with original fallback for an unregistered dark or flash frame.
|
||||
|
||||
Engine flames are native procedural shading, not missing sprite files. Each simulation tick selects an eight-row or twelve-row phase; the two four-pixel-wide bands follow the original seven bank offsets and draw immediately before the ship. Eight original VGA remap tables add 16, 14, 12, 10, 8, 6, 4 and 2 to six-bit palette components. The ramp preserves the native 16.16 accumulator and recomputes its height after viewport clipping. The ship covers the bands at its rear edge, and both flame passes disappear when the player is hidden. Four portable tests cover all campaign/frame mappings, bank offsets, interpolated placement, exact ramp phases, clipping, overlapping shade passes, composition order and render-only behavior at 1× and 4×.
|
||||
|
||||
Local render captures are under ignored `../.reference/secondary-effects/`. `native-effects-{sd,hd}-{1,2,3}.png` show controlled beam and secondary-animation frames. `native-player-family-{0,1,2}-{sd,hd}.png` show the light, dark and flash families in their original mission scenes. `native-player-banking-flames-{sd,hd}.png` contain all seven banking poses, with rows ordered light/eight-row phase, light/twelve-row phase, dark/eight-row phase, dark/twelve-row phase, flash/eight-row phase and flash/twelve-row phase. Their fixtures assert that drawing does not mutate the world. These controlled captures assess placement and composition; they are not an exhaustive pixel comparison of every effect against C++.
|
||||
|
||||
Both player contact sheets and the light HD and dark firing-flash mission scenes were inspected after capture. Engines remain registered across the seven banks, both phases retain their indexed terrain colors and the ship covers the upper flame row. At capture time, the light family selected its supplied HD artwork; the dark and firing-flash frames retained the original crisp fallback. The earlier secondary-effect captures were also inspected for beam caps, pickup halos and shield composition. Subsequent complete-family approvals are recorded in the maintained HD ledger and independent full review.
|
||||
|
||||
```sh
|
||||
cargo test --lib original_
|
||||
cargo test --test secondary_effects --test transitions
|
||||
cargo test --lib original_secondary_effects_capture_sd_and_hd_without_gameplay_mutation -- --ignored
|
||||
cargo test --lib original_player_flames_and_flash_capture_complete_banking_families_in_sd_and_hd -- --ignored
|
||||
cargo test --test campaigns complete_demo_1_matches_every_reference_gameplay_snapshot -- --ignored
|
||||
```
|
||||
|
||||
The generic `A_SMALL_SMOKE` and `A_ENERGY_GRAB_HIT` handles have no C++ call sites, so no invented emitters were added. The restored render paths do not imply that every original effect has an HD remaster; remaining artwork is tracked in the HD backlog.
|
||||
|
||||
Generic HUD pictures, timed currency pickups and native light masks now consult the same source-bound optional HD pack. HUD and pickup artwork retains its original logical anchor and canvas. Smoke, shield and pickup glow use replacement alpha only to shade existing palette indices through the native light table. They do not paint the replacement's RGB colors onto the scene. Synthetic HUD and sparse HD-mask regressions at 1× and 4× verify the supplied artwork reaches these paths, indexed shading retains holes and centers, and drawing does not change world state. Original collision and projected shadow masks continue to use native geometry.
|
||||
@@ -0,0 +1,107 @@
|
||||
# Shipped weapon prices and balance
|
||||
|
||||
The three independent weapon mods use the original game's credit scale. Their
|
||||
initial example prices of 6,500, 14,000 and 22,000 made reusable upgrades cheaper
|
||||
than much weaker original equipment. The revised prices preserve their firing,
|
||||
damage, scripts, sounds and artwork.
|
||||
|
||||
| Mod weapon | Price | Place in the original progression |
|
||||
| --- | ---: | --- |
|
||||
| Pulse Cannon | 185,000 | Just above the 175,600-credit Micro Missile primary upgrade. It adds similar sustained damage and also adds a spread burst every third volley. |
|
||||
| Seeker Swarm | 285,000 | Above the 250,650-credit Mini Gun. Its three missiles track air and ground enemies and hits fragment into at most two nearby enemies. It remains below the 512,850-credit instant Turret. |
|
||||
| Ion Lance | 425,000 | Below the Turret and the 950,000-credit Death Ray. Its lower damage per target trades against immediate hits and the ability to pierce several enemies in one column. |
|
||||
|
||||
Pulse Cannon is always equipped and fires alongside other primary guns. Seeker
|
||||
Swarm and Ion Lance occupy the selected special slot and fire with Fire or
|
||||
Special while selected. All three are reusable, consume no ammunition and have
|
||||
a maximum inventory count of one. Each equipment definition includes a shop
|
||||
description stating these controls and its target behavior.
|
||||
|
||||
## Controlled damage observations
|
||||
|
||||
The local benchmark loads the original asset catalog and original registered
|
||||
weapons, plus all six shipped weapon/enemy packages. Every weapon runs through
|
||||
the real `World::step` simulation and real scripts at difficulty 2. Each scene
|
||||
holds Fire for 700 steps, exactly 30 seconds at 70/3 Hz, against 32×32 fixtures
|
||||
with 1,000,000 health. The pilot remains at the initial position with no other
|
||||
weapons equipped. Ground fixtures use a test-only movement callback to cancel
|
||||
the normal one-pixel terrain scroll after each collision pass.
|
||||
|
||||
The numbers below include travel delay and misses during that 30-second window.
|
||||
They measure health lost per second, rather than nominal damage divided by
|
||||
cooldown.
|
||||
|
||||
| Weapon | Price | Aligned air damage/s | Aligned ground damage/s | Air target 76 pixels to the right damage/s |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| Original Forward Guns | 12,000 | 23.13 | 23.13 | 0 |
|
||||
| Original Plasma Guns | 78,800 | 4.67 | 0 | 0 |
|
||||
| Original Micro Missile | 175,600 | 23.07 | 23.07 | 0 |
|
||||
| Original Mini Gun | 250,650 | 12.57 | 12.57 | 16.27 |
|
||||
| Original Turret | 512,850 | 19.50 | 0 | 19.50 |
|
||||
| Original Pulse Cannon | 725,000 | 38.50 | 38.50 | 0 |
|
||||
| Original Death Ray | 950,000 | 80.00 | 80.00 | 0 |
|
||||
| Original Forward Laser | 1,750,000 | 266.67 | 266.67 | 0 |
|
||||
| Mod Pulse Cannon | 185,000 | 24.00 | 24.00 | 0 |
|
||||
| Seeker Swarm | 285,000 | 23.40 | 23.40 | 23.40 |
|
||||
| Ion Lance | 425,000 | 26.67 | 26.67 | 0 |
|
||||
|
||||
The original forward laser's native beam damage pass reaches ground enemies
|
||||
despite its historical air target metadata; the observations use its actual
|
||||
behavior. The original turret selects airborne enemies. Original weapons retain
|
||||
their native update and collision paths throughout this comparison.
|
||||
|
||||
Equipping Pulse Cannon with the starter Forward Guns produced 46.33 damage/s
|
||||
against either aligned target, compared with 46.20 for Forward Guns plus Micro
|
||||
Missile. Pulse therefore belongs beside that original primary upgrade, rather
|
||||
than below the 12,000-credit starter guns. Scripted angled shots also reached a
|
||||
second target in the cluster scene.
|
||||
|
||||
Seeker Swarm delivered 23.40 damage/s to an offset airborne or ground target.
|
||||
In a tight three-enemy cluster, it delivered 23.40 to the primary target plus
|
||||
7.80 to each of two neighbors: 39.00 combined. Its fragmentation is bounded;
|
||||
it does not damage every enemy on screen.
|
||||
|
||||
Ion Lance delivered 26.67 damage/s to each of two vertically separated targets
|
||||
in the same column: 53.33 combined. It delivered 80.00 combined across three
|
||||
overlapping column targets. It dealt zero damage to targets outside its launch
|
||||
column. It is a brief aimed beam, rather than a screen-wide attack.
|
||||
|
||||
## Economy and limits
|
||||
|
||||
Controlled first-mission runs starting with 100 energy and zero credits earned
|
||||
42,775 credits at difficulty 1 and 59,025 at difficulty 2 with starter guns.
|
||||
The in-game registration flow starts with 10,000 credits and 75 energy, matching
|
||||
the original hangar setup. Compared with the latter controlled gross
|
||||
reward, these prices represent approximately 3.13, 4.83 and 7.20 such payouts.
|
||||
Repairs and other purchases increase the saving time; later missions have
|
||||
different rewards. This comparison sets a progression tier and does not promise
|
||||
an exact purchase mission.
|
||||
|
||||
Stationary fixtures favor perfect alignment and long-lived targets. They do
|
||||
not establish equal campaign difficulty, measure moving-target accuracy,
|
||||
account for overkill against weak enemies or quantify projectile travel time
|
||||
against fast enemies. The benchmark also isolates weapons; real pilots can
|
||||
combine several primaries with one selected special. These prices are concrete
|
||||
initial balance choices, not an assertion that every loadout is equally strong.
|
||||
|
||||
Iron Coast Mission 1 intentionally supplies a free Pulse Cannon loan if the
|
||||
pilot does not already own one. The mission returns the loan at its end while
|
||||
preserving previously owned equipment. The trial therefore helps this mission
|
||||
without granting a permanent shop upgrade or a repeatable resale reward.
|
||||
|
||||
## Reproduce
|
||||
|
||||
Run the local-only test from the repository root with the original catalog:
|
||||
|
||||
```sh
|
||||
RAPTOR_BALANCE_REPORT="$PWD/.reference/weapon-balance.json" \
|
||||
cargo test --release --manifest-path RustRaptor/Cargo.toml \
|
||||
--no-default-features --test weapon_balance -- --ignored --nocapture
|
||||
```
|
||||
|
||||
`RAPTOR_TEST_DATA` can override the catalog directory. The optional JSON report
|
||||
records all 96 observations: fifteen weapons in six scenes, plus the three
|
||||
primary upgrades combined with Forward Guns against air and ground. Assertions
|
||||
check all-target homing, column piercing, off-axis beam misses, target survival
|
||||
and unchanged pilot shields. The test uses the Rust engine without SDL or C++.
|
||||
Original data and locally written reference reports stay outside Git.
|
||||
Reference in new issue
Block a user