Fix diagnostics back navigation and keep mod selection rows stable
This commit is contained in:
1 parent
ab23255d2c
commit
4b1edefeec
3 files changed
+136
-41
No files matched your search
@@ -8,6 +8,14 @@ Gameplay uses the original 320×200 simulation canvas, presented at 4:3 with asp
|
||||
|
||||
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/`:
|
||||
|
||||
@@ -24,10 +24,10 @@ worker from its existing outputs before submitting another generation request.
|
||||
|
||||
## Next user fixes
|
||||
|
||||
1. Mod/save diagnostics: Escape can get trapped after details are reopened from
|
||||
diagnostics, replacing its parent with itself. Preserve parent and selection.
|
||||
2. Keep installed mod rows stable when enabled/disabled. Load priority must be
|
||||
separate from the displayed order; dependencies and confirmation stay intact.
|
||||
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.
|
||||
|
||||
@@ -43,20 +43,20 @@ mod artwork and sound belong in Git.
|
||||
|
||||
| Agent | Recover and continue |
|
||||
| --- | --- |
|
||||
| hd_coordinator | Sole global manifest/ledger owner; register fresh approved29boss frames, keepMole9hold; audit all recoveredrequests before resuming |
|
||||
| hd_full_review | Terrain corpus49/66pagesreviewed; pages50–66remain;48terrainframeholdstaysuntilfullreview |
|
||||
| hd_terrain_render | Frozen compositor SHA b30b4a65…;29bossframesapproved,Mole9held;8newportraitscreenssaved,7/fullboardinspectionremain |
|
||||
| campaign_integration | 34effect/projectilefamilies303IDsraw; detailed RESTART.md preserved; severalgeometry/brightness/layout corrections and independentreviewsremain |
|
||||
| campaign_release_review | Terrain4queue84jobs1344IDs; recover currentraw/contextcandidates andholds |
|
||||
| hd_dark_player | Equipment/HUDdigits, acceptedandheldfamilies; reconcilecurrentmanifest before newgeneration |
|
||||
| hd_enemies_01 | Enemy01queue139IDs; sourcegeometryholds,SHIP25connectionfailure,APE4service-refusalpending |
|
||||
| hd_terrain_set_1 | Terrain1queue42jobs672IDs; first16registered; recover alreadycompleted103e0output andsubsequentcandidateholds |
|
||||
| hd_terrain_set_2 | Terrain2queue90jobs1437IDs; continue savedcanonicalchunks andall-contextreview |
|
||||
| iron_coast_2 | Frozenenemy03firstdrafts59families138IDs; recovered45familyenemy02queuecontinues, old11familiesfrozen |
|
||||
| iron_coast_3 | Boss01queue26families83IDs; recovered11completedatlasfamiliesneedbindings/QA; previous15candidatespreserved |
|
||||
| iron_coast_4 | Boss02queue27families83IDs; approvedsubset/corrections,connectionfailurecooldowns |
|
||||
| iron_coast_5 | Boss03complete25families82IDs/328scalechecks; equipment8reviewpending; temporarygroundgunbugassignment |
|
||||
| iron_coast_6 | Terrain3queue68jobs1082IDs, withmiddleandlateshards; preserve strictdestroyedgeometryholds |
|
||||
| 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
|
||||
|
||||
|
||||
+110
-23
@@ -189,6 +189,8 @@ struct Host {
|
||||
awaiting_binding: Option<String>,
|
||||
diagnostic_mod: Option<String>,
|
||||
diagnostic_return: Screen,
|
||||
diagnostic_selection: usize,
|
||||
mods_selection: usize,
|
||||
pending_disable: Option<crate::mod_selection::DisablePlan>,
|
||||
world: Option<World>,
|
||||
cinematic: Option<Cinematic>,
|
||||
@@ -333,6 +335,8 @@ impl Host {
|
||||
awaiting_binding: None,
|
||||
diagnostic_mod: None,
|
||||
diagnostic_return: Screen::Main,
|
||||
diagnostic_selection: 0,
|
||||
mods_selection: 0,
|
||||
pending_disable: None,
|
||||
world: None,
|
||||
cinematic: None,
|
||||
@@ -373,8 +377,15 @@ impl Host {
|
||||
})
|
||||
}
|
||||
fn go(&mut self, screen: Screen) {
|
||||
if self.screen == Screen::Mods {
|
||||
self.mods_selection = self.selected;
|
||||
}
|
||||
self.screen = screen;
|
||||
self.selected = 0;
|
||||
self.selected = if screen == Screen::Mods {
|
||||
self.mods_selection.min(self.rows().len().saturating_sub(1))
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if screen == Screen::Shop {
|
||||
self.shop_selling = false;
|
||||
}
|
||||
@@ -905,13 +916,7 @@ impl Host {
|
||||
.collect(),
|
||||
Screen::Mods => {
|
||||
let mut r = vec!["Apply selections".into(), "Rescan installed mods".into()];
|
||||
let mut ids = self.pending.clone();
|
||||
for id in self.discovery.mods.keys() {
|
||||
if !ids.contains(id) {
|
||||
ids.push(id.clone());
|
||||
}
|
||||
}
|
||||
for id in ids {
|
||||
for id in self.mod_ids() {
|
||||
if let Some(m) = self.discovery.mods.get(&id) {
|
||||
r.push(format!(
|
||||
"[{}] {} {}",
|
||||
@@ -966,18 +971,9 @@ impl Host {
|
||||
}
|
||||
}
|
||||
fn mod_ids(&self) -> Vec<String> {
|
||||
let mut ids = self
|
||||
.pending
|
||||
.iter()
|
||||
.filter(|id| self.discovery.mods.contains_key(*id))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
for id in self.discovery.mods.keys() {
|
||||
if !ids.contains(id) {
|
||||
ids.push(id.clone());
|
||||
}
|
||||
}
|
||||
ids
|
||||
// Installed rows stay in ID order. Activation priority is a separate
|
||||
// pending list, so toggles and dependency selection cannot move rows.
|
||||
self.discovery.mods.keys().cloned().collect()
|
||||
}
|
||||
fn require_pilot(&self) -> Result<()> {
|
||||
if let Some(error) = &self.pilot_error {
|
||||
@@ -1310,6 +1306,13 @@ impl Host {
|
||||
manifest.conflicts.join(", ")
|
||||
}
|
||||
));
|
||||
if let Some(priority) = self.pending.iter().position(|id| id == mod_id) {
|
||||
lines.push(format!(
|
||||
"Requested load priority: {} / {} (later overrides win)",
|
||||
priority + 1,
|
||||
self.pending.len()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(e) = &self.pilot_error {
|
||||
@@ -1335,6 +1338,7 @@ impl Host {
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
));
|
||||
lines.push(format!("Pending order: {}", self.pending.join(", ")));
|
||||
lines.extend(self.active.registry.overrides.clone());
|
||||
for m in self.discovery.mods.values() {
|
||||
lines.push(format!(
|
||||
@@ -1358,12 +1362,16 @@ impl Host {
|
||||
.collect()
|
||||
}
|
||||
fn open_diagnostics(&mut self) {
|
||||
if self.screen == Screen::Diagnostics {
|
||||
return;
|
||||
}
|
||||
self.diagnostic_mod = if self.screen == Screen::Mods && self.selected >= 2 {
|
||||
self.mod_ids().get(self.selected - 2).cloned()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.diagnostic_return = self.screen;
|
||||
self.diagnostic_selection = self.selected;
|
||||
self.go(Screen::Diagnostics);
|
||||
self.play_ui_sound("FX_SWEP");
|
||||
}
|
||||
@@ -1727,8 +1735,11 @@ impl Host {
|
||||
};
|
||||
if next != n {
|
||||
self.pending.swap(n, next);
|
||||
// Keep the selected mod under the cursor as the order changes.
|
||||
self.selected = next + 2;
|
||||
self.message = format!(
|
||||
"Load priority {} / {}; later overrides win",
|
||||
next + 1,
|
||||
self.pending.len()
|
||||
);
|
||||
self.play_ui_sound("FX_SWEP");
|
||||
}
|
||||
}
|
||||
@@ -1818,7 +1829,12 @@ impl Host {
|
||||
self.go(self.previous)
|
||||
}
|
||||
Screen::Mods if self.world.is_some() => self.go(Screen::Pause),
|
||||
Screen::Diagnostics => self.go(self.diagnostic_return),
|
||||
Screen::Diagnostics => {
|
||||
self.go(self.diagnostic_return);
|
||||
self.selected = self
|
||||
.diagnostic_selection
|
||||
.min(self.rows().len().saturating_sub(1));
|
||||
}
|
||||
Screen::Bindings => {
|
||||
self.awaiting_binding = None;
|
||||
self.save_config()?;
|
||||
@@ -6767,6 +6783,70 @@ mod tests {
|
||||
assert_eq!(host.slots(), vec![0, 1]);
|
||||
}
|
||||
#[test]
|
||||
fn mod_diagnostics_reopening_preserves_parent_and_selected_row() {
|
||||
let (_dir, mut host) = ui_fixture();
|
||||
install_dependency_fixture(&mut host);
|
||||
host.go(Screen::Mods);
|
||||
host.selected = 3;
|
||||
host.open_diagnostics();
|
||||
assert!(host.screen == Screen::Diagnostics);
|
||||
assert_eq!(host.diagnostic_mod.as_deref(), Some("dependent"));
|
||||
host.move_selection(3);
|
||||
host.open_diagnostics(); // A second D press must not replace its parent.
|
||||
assert!(host.diagnostic_return == Screen::Mods);
|
||||
assert_eq!(host.diagnostic_mod.as_deref(), Some("dependent"));
|
||||
host.back().unwrap();
|
||||
assert!(host.screen == Screen::Mods);
|
||||
assert_eq!(host.selected, 3);
|
||||
host.back().unwrap();
|
||||
assert!(host.screen == Screen::Main);
|
||||
|
||||
host.go(Screen::Options);
|
||||
host.selected = 8;
|
||||
host.open_diagnostics();
|
||||
host.open_diagnostics();
|
||||
host.back().unwrap();
|
||||
assert!(host.screen == Screen::Options);
|
||||
assert_eq!(host.selected, 8);
|
||||
}
|
||||
#[test]
|
||||
fn mod_rows_and_cursor_stay_fixed_through_dependency_enable_disable_and_priority() {
|
||||
let (_dir, mut host) = ui_fixture();
|
||||
install_dependency_fixture(&mut host);
|
||||
host.go(Screen::Mods);
|
||||
let installed = host.mod_ids();
|
||||
let row = installed.iter().position(|id| id == "dependent").unwrap() + 2;
|
||||
host.selected = row;
|
||||
host.activate().unwrap();
|
||||
assert_eq!(host.mod_ids(), installed);
|
||||
assert_eq!(host.selected, row);
|
||||
assert_eq!(host.pending, vec!["dependency", "dependent"]);
|
||||
host.adjust(-1);
|
||||
assert_eq!(host.pending, vec!["dependent", "dependency"]);
|
||||
assert_eq!(host.mod_ids(), installed);
|
||||
assert_eq!(host.selected, row);
|
||||
host.activate().unwrap();
|
||||
assert!(host.screen == Screen::ConfirmDisable);
|
||||
host.back().unwrap();
|
||||
assert!(host.screen == Screen::Mods);
|
||||
assert_eq!(host.selected, row);
|
||||
host.activate().unwrap();
|
||||
host.selected = 0;
|
||||
host.activate().unwrap();
|
||||
assert!(host.screen == Screen::Mods);
|
||||
assert!(host.pending.is_empty());
|
||||
assert_eq!(host.mod_ids(), installed);
|
||||
assert_eq!(host.selected, row);
|
||||
|
||||
host.selected = 2;
|
||||
host.activate().unwrap();
|
||||
host.activate().unwrap(); // Standalone dependency disables immediately.
|
||||
assert!(host.screen == Screen::Mods);
|
||||
assert!(host.pending.is_empty());
|
||||
assert_eq!(host.selected, 2);
|
||||
assert_eq!(host.mod_ids(), installed);
|
||||
}
|
||||
#[test]
|
||||
fn selecting_a_mod_selects_its_installed_dependency_without_applying() {
|
||||
let (_dir, mut host) = ui_fixture();
|
||||
install_dependency_fixture(&mut host);
|
||||
@@ -7289,9 +7369,15 @@ mod tests {
|
||||
.unwrap()
|
||||
+ 2;
|
||||
capture(&mut host, "17-mod-manager-dependencies");
|
||||
let mod_rows = host.mod_ids();
|
||||
let mod_selection = host.selected;
|
||||
host.open_diagnostics();
|
||||
capture(&mut host, "18-mod-details");
|
||||
host.open_diagnostics();
|
||||
host.back().unwrap();
|
||||
assert!(host.screen == Screen::Mods);
|
||||
assert_eq!(host.mod_ids(), mod_rows);
|
||||
assert_eq!(host.selected, mod_selection);
|
||||
host.selected = host
|
||||
.mod_ids()
|
||||
.iter()
|
||||
@@ -7302,6 +7388,7 @@ mod tests {
|
||||
assert!(host.screen == Screen::ConfirmDisable);
|
||||
capture(&mut host, "19-mod-disable-confirmation");
|
||||
host.back().unwrap();
|
||||
assert_eq!(host.selected, mod_selection);
|
||||
host.go(Screen::Missions);
|
||||
host.selected = host
|
||||
.missions()
|
||||
|
||||
Reference in new issue
Block a user