Match full reference demo and validate original missions, transitions and sprite rendering
This commit is contained in:
1 parent
967ba69ee8
commit
0844f6dffa
21 files changed
+3984
-219
No files matched your search
@@ -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.csv` (SHA-256
|
||||
`8702d190a039239de154e09ebbb5972f6d7ffe07475d7d47b199b53efb3a811a`).
|
||||
|
||||
| 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 | 36 | 52 |
|
||||
| 3 | 27 | 4,650–5,082 | 8,668 | 88 | 37 | 51 |
|
||||
|
||||
The 108 runs cover 513,002 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 project directory:
|
||||
|
||||
```sh
|
||||
RAPTOR_CAMPAIGN_REPORT=.reference/campaign-validation.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.
|
||||
+31
-2
@@ -28,8 +28,10 @@ repair kits, and the command ship arrives at step 720. Destroy at least two
|
||||
turrets and the command ship to finish. The boss stays in the playfield until
|
||||
killed; waiting out the mission duration cannot produce victory.
|
||||
|
||||
The mission grants a trial Pulse Cannon, which fires alongside forward guns,
|
||||
and three field repairs. Cycle equipment to **Iron Coast Field Repair** and
|
||||
When inventory space allows, the mission grants a trial Pulse Cannon, which
|
||||
fires alongside forward guns, and three field repairs. Granted equipment
|
||||
remains with the pilot. 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
|
||||
@@ -45,6 +47,7 @@ Script objectives are namespaced and saved with the pilot:
|
||||
| `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` | Trial weapon was granted or replenished |
|
||||
|
||||
## Art and audio
|
||||
|
||||
@@ -99,6 +102,32 @@ 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.
|
||||
|
||||
Release acceptance passes all six tests in **1.43 seconds**. Synthetic
|
||||
seven-package activation takes 0.179–0.290 seconds including fixture creation
|
||||
and PNG/WAV validation. Actual seven-package validation, including the generated
|
||||
SD/HD artwork and WAV files, passes in **2.234 seconds** with zero overrides.
|
||||
The two playable runs complete as follows:
|
||||
|
||||
| Loadout | Completion step | Final shield | Credits |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Starter forward guns | 882 | 94 | 7,715 |
|
||||
| Trial Pulse Cannon plus forward guns | 786 | 92 | 7,715 |
|
||||
|
||||
Both start at difficulty 2 with shield 100. The test holds fire, fixes the
|
||||
player at (144,160), selects field repairs on step 2 and presses the special
|
||||
button every 90 steps starting at step 61. The starter run removes the granted
|
||||
trial cannon after initial setup. Granted/dropped repairs heal through normal
|
||||
item callbacks; these runs use no direct shield refills, invulnerability,
|
||||
enemy damage commands or victory-objective mutations. They establish that
|
||||
the actual waves and boss can be completed under this controlled input, not
|
||||
that every normal-player route or loadout has been balanced.
|
||||
|
||||
The default debug suite took 34.15 seconds for the earlier five-test version;
|
||||
the release result shows that its slowdown comes mainly from unoptimized
|
||||
simulation, serialization and scripting. PNG color memoization reduced the
|
||||
previous multi-minute asset validation overhead. Release measurements above
|
||||
exclude compilation.
|
||||
|
||||
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,
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ cargo run --no-default-features -- --data data --mods examples/mods \
|
||||
--enabled aurora --headless aurora:field_trials --ticks 700
|
||||
```
|
||||
|
||||
For the in-game manager, copy `examples/mods/aurora` into the `mods/` directory under `$XDG_DATA_HOME/RaptorReplica` (default `~/.local/share/RaptorReplica/mods/`), or start the Rust engine with `--mods examples/mods`. Open **Mods**, toggle Aurora, then select **Apply selections**. Select its mission from the mission menu. Mods alter the active content set; existing pilots retain the exact mod versions and hashes required by their saves. Use a separate new pilot when trying a different set of mods.
|
||||
For the in-game manager, copy `examples/mods/aurora` into the `mods/` directory under `$XDG_DATA_HOME/RaptorReplica` (default `~/.local/share/RaptorReplica/mods/`), or start the Rust engine with `--mods examples/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 blocks loading with details. Use another pilot for an incompatible set.
|
||||
|
||||
In the Mods screen, Enter toggles a package or activates the selected action. Rescan after editing a package on disk. Load dependencies 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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,57 @@
|
||||
# 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.
|
||||
|
||||
The optional HD player proof remains a separate review: it replaces only straight flight, so banking still changes visual style. The generated extreme-bank attempt remains excluded because it shortened the engines and failed the footprint check; crisp original fallback is retained for that frame.
|
||||
|
||||
The frontend previously requested `AIRMED_BLK` for effects, but that name does not exist in the original catalog. Actual original sequences include `AIRBOOM_PIC`, `GEXPLO_BLK`, `EXPLO2_BLK`, flak and sparks. This finding was reported to the engine owner. A generic explosion sequence repairs the visible fallback, but full effect identity, duration, layer, origin and update parity require separate comparison with the C++ animation system.
|
||||
|
||||
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.
|
||||
Reference in new issue
Block a user