TextIndex in sylpheed-formats::localization already indexes per-phase objective text under S<NN>_P<n>_Objective_<i>, with hints and lose conditions alongside. Querying it settles what the wave work spent many iterations circling, and a disc test now pins it. Stage 02 phase 1 is "Shoot down all invading enemy fighters while watching out for attacks on the ACROPOLIS", losing if the ACROPOLIS is sunk. Phase 2 is "Protect the Admiral's ship the CALIBAN until it has entered the safe zone", losing if the CALIBAN is sunk. Phase 3 is "Destroy the interstellar cruise missiles headed for Schlos Base", matching phase 3's roster of exactly nine ISCMissile and SUBOBJ_013. That explains the phase advances and shows they are not all the same kind. Phase 1 is a kill-all-marked-fighters gate -- fighters, not the turrets every run killed almost exclusively. Phase 2 is positional and ends when the CALIBAN reaches a safe zone, so "what advances a phase" never had a single answer. It also closes a loop to the first session. The hints say red mission markers [OB] indicate your targets, so REMAINING OB -- the HUD counter hunted in the earliest iterations and located at 0xbdb59668 -- is the count of remaining marked objective targets, which is phase-1 progress itself. The old reframing that the counter is not a roster was right, and this names what it is. It is also the correct signal to watch for an advance: not deployed, which only changes once the next phase deploys, but REMAINING OB reaching zero, which is the cause. Method lesson recorded because the reflex will recur: localization.rs has had objectives, hints and lose_conditions for some time, and several iterations were spent reconstructing the same information by hand from SUBObjective strings and the guide script. The reconstruction was not wasted -- it produced the IXUD UTF-16BE and language\ prefix findings independently -- but the corpus should have been searched before the disc was, and grep -rl TextIndex crates/ would have saved the detour. Minor discrepancy noted, not investigated: ixud.rs documents the string pool as UTF-16BE and localization.rs as UTF-16LE.
47 lines
1.7 KiB
Rust
47 lines
1.7 KiB
Rust
//! Per-phase MAIN objectives, straight from the localisation index.
|
|
//!
|
|
//! The mission-phase work went looking for what a phase asks the player to do
|
|
//! and reconstructed it from `SUBObjective_local_string.tbl` — the *bonus*
|
|
//! objectives — plus the guide script. `TextIndex` already indexes the main
|
|
//! ones under `S<NN>_P<n>_Objective_<i>`, so this pins them as a disc test and
|
|
//! makes the per-phase goals a checkable artifact rather than an inference.
|
|
|
|
use sylpheed_formats::{localization::TextIndex, PakArchive};
|
|
|
|
fn pak() -> Option<PakArchive> {
|
|
let disc = std::env::var("SYLPHEED_DISC").ok()?;
|
|
PakArchive::open(format!("{disc}/dat/GP_MAIN_GAME_E.pak")).ok()
|
|
}
|
|
|
|
#[test]
|
|
fn stage02_has_a_main_objective_for_each_phase() {
|
|
let Some(pak) = pak() else {
|
|
eprintln!("SYLPHEED_DISC unset - skipping");
|
|
return;
|
|
};
|
|
let text = TextIndex::build(&pak);
|
|
assert!(text.len() > 0, "text index is empty");
|
|
|
|
for stage in ["S01", "S02"] {
|
|
for phase in 1..=3u32 {
|
|
let objs = text.objectives(stage, phase);
|
|
eprintln!("{stage} phase {phase}: {} objective line(s)", objs.len());
|
|
for o in &objs {
|
|
eprintln!(" {o}");
|
|
}
|
|
for h in text.hints(stage, phase) {
|
|
eprintln!(" hint: {h}");
|
|
}
|
|
for l in text.lose_conditions(stage, phase) {
|
|
eprintln!(" lose: {l}");
|
|
}
|
|
}
|
|
}
|
|
// Phase 1 of Stage 02 is the case the wave work could not observe; if the
|
|
// game states its goal at all, it is here.
|
|
assert!(
|
|
!text.objectives("S02", 1).is_empty(),
|
|
"no main objective text for Stage 02 phase 1"
|
|
);
|
|
}
|