diff --git a/README.md b/README.md index 8bb22b1..1812fed 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,10 @@ invoke the periodic-boundary service automatically on year rollover, so shellles can drive the annual finance stack instead of requiring a separate manual service command. That stepped world-time path now also refreshes the rehosted selected-year gap scalar owner lane instead of leaving `[world+0x4ca2]` as a frozen load-time residue. +The same save-native world restore surface now also carries the grounded locomotive-policy bytes +and cached available-locomotive rating from the fixed world block, so the `All +Steam/Diesel/Electric Locos Avail.` descriptor strip now writes through owner state instead of +living only as ad hoc world flags. Those bankruptcy branches now follow the grounded owner semantics too: they stamp the bankruptcy year and halve live bond principals in place instead of treating bankruptcy as a liquidation path. The same save-native live bond-slot surface now also carries per-slot maturity years all the way diff --git a/crates/rrt-cli/src/main.rs b/crates/rrt-cli/src/main.rs index 8e23845..f52fd1e 100644 --- a/crates/rrt-cli/src/main.rs +++ b/crates/rrt-cli/src/main.rs @@ -4934,6 +4934,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, diff --git a/crates/rrt-fixtures/src/load.rs b/crates/rrt-fixtures/src/load.rs index 3810978..9504c8e 100644 --- a/crates/rrt-fixtures/src/load.rs +++ b/crates/rrt-fixtures/src/load.rs @@ -283,6 +283,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -427,6 +428,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, diff --git a/crates/rrt-runtime/src/import.rs b/crates/rrt-runtime/src/import.rs index f2cdfb1..43c5100 100644 --- a/crates/rrt-runtime/src/import.rs +++ b/crates/rrt-runtime/src/import.rs @@ -836,6 +836,42 @@ fn project_save_slice_components( limited_track_building_amount: None, economic_status_code: None, territory_access_cost: None, + linked_site_removal_follow_on_gate_raw_u8: save_slice + .world_locomotive_policy_state + .as_ref() + .and_then(|state| state.linked_site_removal_follow_on_gate_raw_u8), + linked_site_removal_follow_on_gate_enabled: save_slice + .world_locomotive_policy_state + .as_ref() + .and_then(|state| state.linked_site_removal_follow_on_gate_raw_u8) + .map(|raw| raw != 0), + all_steam_locomotives_available_raw_u8: save_slice + .world_locomotive_policy_state + .as_ref() + .and_then(|state| state.all_steam_locomotives_available_raw_u8), + all_steam_locomotives_available_enabled: save_slice + .world_locomotive_policy_state + .as_ref() + .and_then(|state| state.all_steam_locomotives_available_raw_u8) + .map(|raw| raw != 0), + all_diesel_locomotives_available_raw_u8: save_slice + .world_locomotive_policy_state + .as_ref() + .and_then(|state| state.all_diesel_locomotives_available_raw_u8), + all_diesel_locomotives_available_enabled: save_slice + .world_locomotive_policy_state + .as_ref() + .and_then(|state| state.all_diesel_locomotives_available_raw_u8) + .map(|raw| raw != 0), + all_electric_locomotives_available_raw_u8: save_slice + .world_locomotive_policy_state + .as_ref() + .and_then(|state| state.all_electric_locomotives_available_raw_u8), + all_electric_locomotives_available_enabled: save_slice + .world_locomotive_policy_state + .as_ref() + .and_then(|state| state.all_electric_locomotives_available_raw_u8) + .map(|raw| raw != 0), issue_37_value: save_slice .world_issue_37_state .as_ref() @@ -934,6 +970,18 @@ fn project_save_slice_components( .as_ref() .map(|state| state.lane_value_f32_text.clone()) .unwrap_or_default(), + cached_available_locomotive_rating_raw_u32: save_slice + .world_locomotive_policy_state + .as_ref() + .and_then(|state| state.cached_available_locomotive_rating_raw_u32), + cached_available_locomotive_rating_value_f32_text: save_slice + .world_locomotive_policy_state + .as_ref() + .and_then(|state| { + state + .cached_available_locomotive_rating_value_f32_text + .clone() + }), selected_year_gap_scalar_raw_u32: None, selected_year_gap_scalar_value_f32_text: None, absolute_counter_restore_kind: Some( @@ -5839,6 +5887,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -5885,6 +5934,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -6085,6 +6135,24 @@ mod tests { "0.000000".to_string(), ], }), + world_locomotive_policy_state: Some(crate::smp::SmpLoadedWorldLocomotivePolicyState { + source_kind: "save-fixed-world-block".to_string(), + semantic_family: "world-locomotive-policy".to_string(), + selected_year_gap_scalar_raw_u32: Some(0x3eaaaaab), + selected_year_gap_scalar_raw_hex: Some("0x3eaaaaab".to_string()), + selected_year_gap_scalar_value_f32_text: Some("0.333333".to_string()), + linked_site_removal_follow_on_gate_raw_u8: Some(1), + linked_site_removal_follow_on_gate_raw_hex: Some("0x01".to_string()), + all_steam_locomotives_available_raw_u8: Some(1), + all_steam_locomotives_available_raw_hex: Some("0x01".to_string()), + all_diesel_locomotives_available_raw_u8: Some(0), + all_diesel_locomotives_available_raw_hex: Some("0x00".to_string()), + all_electric_locomotives_available_raw_u8: Some(1), + all_electric_locomotives_available_raw_hex: Some("0x01".to_string()), + cached_available_locomotive_rating_raw_u32: Some(0x41a00000), + cached_available_locomotive_rating_raw_hex: Some("0x41a00000".to_string()), + cached_available_locomotive_rating_value_f32_text: Some("20.000000".to_string()), + }), company_roster: None, chairman_profile_table: None, special_conditions_table: Some(crate::SmpLoadedSpecialConditionsTable { @@ -6401,6 +6469,41 @@ mod tests { .economic_tuning_lane_value_f32_text, vec!["0.750000".to_string(), "0.007000".to_string()] ); + assert_eq!( + import + .state + .world_restore + .linked_site_removal_follow_on_gate_raw_u8, + Some(1) + ); + assert_eq!( + import + .state + .world_restore + .all_steam_locomotives_available_enabled, + Some(true) + ); + assert_eq!( + import + .state + .world_restore + .all_diesel_locomotives_available_enabled, + Some(false) + ); + assert_eq!( + import + .state + .world_restore + .all_electric_locomotives_available_enabled, + Some(true) + ); + assert_eq!( + import + .state + .world_restore + .cached_available_locomotive_rating_raw_u32, + Some(0x41a00000) + ); assert_eq!( import .state @@ -6547,6 +6650,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: Some(save_company_roster()), chairman_profile_table: Some(save_chairman_profile_table()), special_conditions_table: None, @@ -6634,6 +6738,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: Some(save_company_roster()), chairman_profile_table: Some(save_chairman_profile_table()), special_conditions_table: None, @@ -6790,6 +6895,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: Some(crate::SmpLoadedCompanyRoster { source_kind: "save-direct-world-block-company-selection-only".to_string(), semantic_family: "scenario-selected-company-context".to_string(), @@ -6850,6 +6956,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -6975,6 +7082,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -7078,6 +7186,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -7207,6 +7316,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -7297,6 +7407,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -7455,6 +7566,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -7703,6 +7815,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -7786,6 +7899,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -7893,6 +8007,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -7973,6 +8088,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: Some(save_company_roster()), chairman_profile_table: Some(save_chairman_profile_table()), special_conditions_table: None, @@ -8056,6 +8172,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: Some(save_company_roster()), chairman_profile_table: Some(save_chairman_profile_table()), special_conditions_table: None, @@ -8152,6 +8269,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -8264,6 +8382,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -8347,6 +8466,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -8507,6 +8627,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -8612,6 +8733,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -8694,6 +8816,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -8776,6 +8899,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -8932,6 +9056,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -9033,6 +9158,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -9113,6 +9239,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -9203,6 +9330,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -9302,6 +9430,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -9409,6 +9538,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -9505,6 +9635,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -9587,6 +9718,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -9739,6 +9871,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -9901,6 +10034,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -10011,6 +10145,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -10102,6 +10237,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -10220,6 +10356,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -10332,6 +10469,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -10434,6 +10572,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -10532,6 +10671,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -10644,6 +10784,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -10741,6 +10882,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -10820,6 +10962,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -10904,6 +11047,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -10993,6 +11137,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -11082,6 +11227,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -11187,6 +11333,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -11283,6 +11430,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -11382,6 +11530,27 @@ mod tests { .get("world.all_electric_locos_available"), Some(&true) ); + assert_eq!( + import + .state + .world_restore + .all_steam_locomotives_available_raw_u8, + Some(1) + ); + assert_eq!( + import + .state + .world_restore + .all_diesel_locomotives_available_raw_u8, + Some(0) + ); + assert_eq!( + import + .state + .world_restore + .all_electric_locomotives_available_raw_u8, + Some(1) + ); } #[test] @@ -11405,6 +11574,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -11547,6 +11717,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -12080,6 +12251,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -12267,6 +12439,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -12399,6 +12572,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: Some(save_company_roster()), chairman_profile_table: Some(save_chairman_profile_table()), special_conditions_table: None, @@ -12530,6 +12704,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: Some(save_company_roster()), chairman_profile_table: Some(save_chairman_profile_table()), special_conditions_table: None, @@ -12662,6 +12837,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -12778,6 +12954,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -12969,6 +13146,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -13068,6 +13246,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -13169,6 +13348,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -13341,6 +13521,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -13496,6 +13677,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -13592,6 +13774,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -13721,6 +13904,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -13838,6 +14022,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -14024,6 +14209,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, @@ -14225,6 +14411,7 @@ mod tests { world_issue_37_state: None, world_economic_tuning_state: None, world_finance_neighborhood_state: None, + world_locomotive_policy_state: None, company_roster: None, chairman_profile_table: None, special_conditions_table: None, diff --git a/crates/rrt-runtime/src/runtime.rs b/crates/rrt-runtime/src/runtime.rs index 925c5b9..1601a5d 100644 --- a/crates/rrt-runtime/src/runtime.rs +++ b/crates/rrt-runtime/src/runtime.rs @@ -1358,6 +1358,22 @@ pub struct RuntimeWorldRestoreState { #[serde(default)] pub territory_access_cost: Option, #[serde(default)] + pub linked_site_removal_follow_on_gate_raw_u8: Option, + #[serde(default)] + pub linked_site_removal_follow_on_gate_enabled: Option, + #[serde(default)] + pub all_steam_locomotives_available_raw_u8: Option, + #[serde(default)] + pub all_steam_locomotives_available_enabled: Option, + #[serde(default)] + pub all_diesel_locomotives_available_raw_u8: Option, + #[serde(default)] + pub all_diesel_locomotives_available_enabled: Option, + #[serde(default)] + pub all_electric_locomotives_available_raw_u8: Option, + #[serde(default)] + pub all_electric_locomotives_available_enabled: Option, + #[serde(default)] pub issue_37_value: Option, #[serde(default)] pub issue_38_value: Option, @@ -1398,6 +1414,10 @@ pub struct RuntimeWorldRestoreState { #[serde(default)] pub economic_tuning_lane_value_f32_text: Vec, #[serde(default)] + pub cached_available_locomotive_rating_raw_u32: Option, + #[serde(default)] + pub cached_available_locomotive_rating_value_f32_text: Option, + #[serde(default)] pub selected_year_gap_scalar_raw_u32: Option, #[serde(default)] pub selected_year_gap_scalar_value_f32_text: Option, @@ -2358,6 +2378,24 @@ impl RuntimeState { } pub fn refresh_derived_world_state(&mut self) { + self.world_restore + .linked_site_removal_follow_on_gate_enabled = self + .world_restore + .linked_site_removal_follow_on_gate_raw_u8 + .map(|raw| raw != 0); + self.world_restore.all_steam_locomotives_available_enabled = self + .world_restore + .all_steam_locomotives_available_raw_u8 + .map(|raw| raw != 0); + self.world_restore.all_diesel_locomotives_available_enabled = self + .world_restore + .all_diesel_locomotives_available_raw_u8 + .map(|raw| raw != 0); + self.world_restore + .all_electric_locomotives_available_enabled = self + .world_restore + .all_electric_locomotives_available_raw_u8 + .map(|raw| raw != 0); let year_word = self .world_restore .packed_year_word_raw_u16 @@ -2376,6 +2414,27 @@ impl RuntimeState { self.world_restore.selected_year_gap_scalar_value_f32_text = Some(format!("{value:.6}")); } + for (key, value) in [ + ( + "world.all_steam_locos_available", + self.world_restore.all_steam_locomotives_available_enabled, + ), + ( + "world.all_diesel_locos_available", + self.world_restore.all_diesel_locomotives_available_enabled, + ), + ( + "world.all_electric_locos_available", + self.world_restore + .all_electric_locomotives_available_enabled, + ), + ] { + if let Some(enabled) = value { + self.world_flags.insert(key.to_string(), enabled); + } else { + self.world_flags.remove(key); + } + } } } @@ -4981,6 +5040,16 @@ mod tests { limited_track_building_amount: None, economic_status_code: None, territory_access_cost: None, + linked_site_removal_follow_on_gate_raw_u8: None, + linked_site_removal_follow_on_gate_enabled: None, + all_steam_locomotives_available_raw_u8: None, + all_steam_locomotives_available_enabled: None, + all_diesel_locomotives_available_raw_u8: None, + all_diesel_locomotives_available_enabled: None, + all_electric_locomotives_available_raw_u8: None, + all_electric_locomotives_available_enabled: None, + cached_available_locomotive_rating_raw_u32: None, + cached_available_locomotive_rating_value_f32_text: None, issue_37_value: None, issue_38_value: None, issue_39_value: None, diff --git a/crates/rrt-runtime/src/smp.rs b/crates/rrt-runtime/src/smp.rs index e2e6aa5..2ef7a4d 100644 --- a/crates/rrt-runtime/src/smp.rs +++ b/crates/rrt-runtime/src/smp.rs @@ -2289,6 +2289,40 @@ pub struct SmpLoadedWorldFinanceNeighborhoodState { pub value_f32_text: Vec, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SmpLoadedWorldLocomotivePolicyState { + pub source_kind: String, + pub semantic_family: String, + #[serde(default)] + pub selected_year_gap_scalar_raw_u32: Option, + #[serde(default)] + pub selected_year_gap_scalar_raw_hex: Option, + #[serde(default)] + pub selected_year_gap_scalar_value_f32_text: Option, + #[serde(default)] + pub linked_site_removal_follow_on_gate_raw_u8: Option, + #[serde(default)] + pub linked_site_removal_follow_on_gate_raw_hex: Option, + #[serde(default)] + pub all_steam_locomotives_available_raw_u8: Option, + #[serde(default)] + pub all_steam_locomotives_available_raw_hex: Option, + #[serde(default)] + pub all_diesel_locomotives_available_raw_u8: Option, + #[serde(default)] + pub all_diesel_locomotives_available_raw_hex: Option, + #[serde(default)] + pub all_electric_locomotives_available_raw_u8: Option, + #[serde(default)] + pub all_electric_locomotives_available_raw_hex: Option, + #[serde(default)] + pub cached_available_locomotive_rating_raw_u32: Option, + #[serde(default)] + pub cached_available_locomotive_rating_raw_hex: Option, + #[serde(default)] + pub cached_available_locomotive_rating_value_f32_text: Option, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SmpLoadedCompanyRosterEntry { pub company_id: u32, @@ -2699,6 +2733,8 @@ pub struct SmpLoadedSaveSlice { #[serde(default)] pub world_finance_neighborhood_state: Option, #[serde(default)] + pub world_locomotive_policy_state: Option, + #[serde(default)] pub company_roster: Option, #[serde(default)] pub chairman_profile_table: Option, @@ -2892,6 +2928,10 @@ pub fn load_save_slice_from_report( .save_world_finance_neighborhood_probe .as_ref() .map(derive_loaded_world_finance_neighborhood_state_from_probe); + let world_locomotive_policy_state = report + .locomotive_policy_neighborhood_probe + .as_ref() + .map(derive_loaded_world_locomotive_policy_state_from_probe); let company_roster = report.save_company_roster_probe.clone().or_else(|| { report .save_world_selection_context_probe @@ -3028,6 +3068,7 @@ pub fn load_save_slice_from_report( world_issue_37_state, world_economic_tuning_state, world_finance_neighborhood_state, + world_locomotive_policy_state, company_roster, chairman_profile_table, special_conditions_table, @@ -3585,6 +3626,52 @@ fn derive_loaded_world_finance_neighborhood_state_from_probe( } } +fn derive_loaded_world_locomotive_policy_state_from_probe( + probe: &SmpLocomotivePolicyNeighborhoodProbe, +) -> SmpLoadedWorldLocomotivePolicyState { + let field_by_name = |name: &str| { + probe + .grounded_field_observations + .iter() + .find(|field| field.field_name == name) + }; + let selected_year_gap_scalar = field_by_name("selected-year bucket companion scalar"); + let linked_site_gate = field_by_name("linked-site removal follow-on gate"); + let all_steam = field_by_name("All Steam Locos Avail."); + let all_diesel = field_by_name("All Diesel Locos Avail."); + let all_electric = field_by_name("All Electric Locos Avail."); + let cached_available_rating = field_by_name("cached available-locomotive rating"); + SmpLoadedWorldLocomotivePolicyState { + source_kind: probe.source_kind.clone(), + semantic_family: "world-locomotive-policy".to_string(), + selected_year_gap_scalar_raw_u32: selected_year_gap_scalar + .and_then(|field| field.value_u32), + selected_year_gap_scalar_raw_hex: selected_year_gap_scalar + .and_then(|field| field.value_u32_hex.clone()), + selected_year_gap_scalar_value_f32_text: selected_year_gap_scalar + .and_then(|field| field.probable_f32_le.clone()), + linked_site_removal_follow_on_gate_raw_u8: linked_site_gate + .and_then(|field| field.value_u8), + linked_site_removal_follow_on_gate_raw_hex: linked_site_gate + .and_then(|field| field.value_u8_hex.clone()), + all_steam_locomotives_available_raw_u8: all_steam.and_then(|field| field.value_u8), + all_steam_locomotives_available_raw_hex: all_steam + .and_then(|field| field.value_u8_hex.clone()), + all_diesel_locomotives_available_raw_u8: all_diesel.and_then(|field| field.value_u8), + all_diesel_locomotives_available_raw_hex: all_diesel + .and_then(|field| field.value_u8_hex.clone()), + all_electric_locomotives_available_raw_u8: all_electric.and_then(|field| field.value_u8), + all_electric_locomotives_available_raw_hex: all_electric + .and_then(|field| field.value_u8_hex.clone()), + cached_available_locomotive_rating_raw_u32: cached_available_rating + .and_then(|field| field.value_u32), + cached_available_locomotive_rating_raw_hex: cached_available_rating + .and_then(|field| field.value_u32_hex.clone()), + cached_available_locomotive_rating_value_f32_text: cached_available_rating + .and_then(|field| field.probable_f32_le.clone()), + } +} + fn derive_selection_only_company_roster_from_save_world_probe( probe: &SmpSaveWorldSelectionContextProbe, header_probe: Option<&SmpSaveTaggedCollectionHeaderProbe>, diff --git a/crates/rrt-runtime/src/step.rs b/crates/rrt-runtime/src/step.rs index c4a4bd1..c5ca5ac 100644 --- a/crates/rrt-runtime/src/step.rs +++ b/crates/rrt-runtime/src/step.rs @@ -1389,6 +1389,26 @@ fn apply_runtime_effects( match effect { RuntimeEffect::SetWorldFlag { key, value } => { state.world_flags.insert(key.clone(), *value); + let raw = u8::from(*value); + match key.as_str() { + "world.all_steam_locos_available" => { + state.world_restore.all_steam_locomotives_available_raw_u8 = Some(raw); + state.world_restore.all_steam_locomotives_available_enabled = Some(*value); + } + "world.all_diesel_locos_available" => { + state.world_restore.all_diesel_locomotives_available_raw_u8 = Some(raw); + state.world_restore.all_diesel_locomotives_available_enabled = Some(*value); + } + "world.all_electric_locos_available" => { + state + .world_restore + .all_electric_locomotives_available_raw_u8 = Some(raw); + state + .world_restore + .all_electric_locomotives_available_enabled = Some(*value); + } + _ => {} + } } RuntimeEffect::SetWorldScalarOverride { key, value } => { state.world_scalar_overrides.insert(key.clone(), *value); @@ -5341,6 +5361,63 @@ mod tests { assert_eq!(result.service_events[0].applied_effect_count, 7); } + #[test] + fn applies_locomotive_policy_world_flags_through_owner_state() { + let mut state = RuntimeState { + event_runtime_records: vec![RuntimeEventRecord { + record_id: 213, + trigger_kind: 7, + active: true, + service_count: 0, + marks_collection_dirty: false, + one_shot: false, + has_fired: false, + conditions: Vec::new(), + effects: vec![ + RuntimeEffect::SetWorldFlag { + key: "world.all_steam_locos_available".to_string(), + value: true, + }, + RuntimeEffect::SetWorldFlag { + key: "world.all_diesel_locos_available".to_string(), + value: false, + }, + RuntimeEffect::SetWorldFlag { + key: "world.all_electric_locos_available".to_string(), + value: true, + }, + ], + }], + ..state() + }; + + let result = execute_step_command( + &mut state, + &StepCommand::ServiceTriggerKind { trigger_kind: 7 }, + ) + .expect("locomotive policy world flags should succeed"); + + assert_eq!( + state.world_flags.get("world.all_steam_locos_available"), + Some(&true) + ); + assert_eq!( + state.world_restore.all_steam_locomotives_available_raw_u8, + Some(1) + ); + assert_eq!( + state.world_restore.all_diesel_locomotives_available_raw_u8, + Some(0) + ); + assert_eq!( + state + .world_restore + .all_electric_locomotives_available_raw_u8, + Some(1) + ); + assert_eq!(result.service_events[0].applied_effect_count, 3); + } + #[test] fn applies_runtime_variable_effects() { let mut state = RuntimeState { diff --git a/crates/rrt-runtime/src/summary.rs b/crates/rrt-runtime/src/summary.rs index bab58f2..8ae996e 100644 --- a/crates/rrt-runtime/src/summary.rs +++ b/crates/rrt-runtime/src/summary.rs @@ -49,6 +49,14 @@ pub struct RuntimeSummary { pub world_restore_limited_track_building_amount: Option, pub world_restore_economic_status_code: Option, pub world_restore_territory_access_cost: Option, + pub world_restore_linked_site_removal_follow_on_gate_raw_u8: Option, + pub world_restore_linked_site_removal_follow_on_gate_enabled: Option, + pub world_restore_all_steam_locomotives_available_raw_u8: Option, + pub world_restore_all_steam_locomotives_available_enabled: Option, + pub world_restore_all_diesel_locomotives_available_raw_u8: Option, + pub world_restore_all_diesel_locomotives_available_enabled: Option, + pub world_restore_all_electric_locomotives_available_raw_u8: Option, + pub world_restore_all_electric_locomotives_available_enabled: Option, pub world_restore_issue_37_value: Option, pub world_restore_issue_38_value: Option, pub world_restore_issue_39_value: Option, @@ -70,6 +78,8 @@ pub struct RuntimeSummary { pub world_restore_economic_tuning_mirror_value_f32_text: Option, pub world_restore_economic_tuning_lane_count: usize, pub world_restore_economic_tuning_lane_value_f32_text: Vec, + pub world_restore_cached_available_locomotive_rating_raw_u32: Option, + pub world_restore_cached_available_locomotive_rating_value_f32_text: Option, pub world_restore_selected_year_gap_scalar_raw_u32: Option, pub world_restore_selected_year_gap_scalar_value_f32_text: Option, pub world_restore_absolute_counter_restore_kind: Option, @@ -430,6 +440,37 @@ impl RuntimeSummary { .world_restore .economic_tuning_lane_value_f32_text .clone(), + world_restore_linked_site_removal_follow_on_gate_raw_u8: state + .world_restore + .linked_site_removal_follow_on_gate_raw_u8, + world_restore_linked_site_removal_follow_on_gate_enabled: state + .world_restore + .linked_site_removal_follow_on_gate_enabled, + world_restore_all_steam_locomotives_available_raw_u8: state + .world_restore + .all_steam_locomotives_available_raw_u8, + world_restore_all_steam_locomotives_available_enabled: state + .world_restore + .all_steam_locomotives_available_enabled, + world_restore_all_diesel_locomotives_available_raw_u8: state + .world_restore + .all_diesel_locomotives_available_raw_u8, + world_restore_all_diesel_locomotives_available_enabled: state + .world_restore + .all_diesel_locomotives_available_enabled, + world_restore_all_electric_locomotives_available_raw_u8: state + .world_restore + .all_electric_locomotives_available_raw_u8, + world_restore_all_electric_locomotives_available_enabled: state + .world_restore + .all_electric_locomotives_available_enabled, + world_restore_cached_available_locomotive_rating_raw_u32: state + .world_restore + .cached_available_locomotive_rating_raw_u32, + world_restore_cached_available_locomotive_rating_value_f32_text: state + .world_restore + .cached_available_locomotive_rating_value_f32_text + .clone(), world_restore_selected_year_gap_scalar_raw_u32: state .world_restore .selected_year_gap_scalar_raw_u32, @@ -1756,6 +1797,16 @@ mod tests { "0.01".to_string(), "0.01".to_string(), ], + linked_site_removal_follow_on_gate_raw_u8: Some(1), + linked_site_removal_follow_on_gate_enabled: Some(true), + all_steam_locomotives_available_raw_u8: Some(1), + all_steam_locomotives_available_enabled: Some(true), + all_diesel_locomotives_available_raw_u8: Some(0), + all_diesel_locomotives_available_enabled: Some(false), + all_electric_locomotives_available_raw_u8: Some(1), + all_electric_locomotives_available_enabled: Some(true), + cached_available_locomotive_rating_raw_u32: Some(0x41a00000), + cached_available_locomotive_rating_value_f32_text: Some("20.000000".to_string()), selected_year_gap_scalar_raw_u32: Some(0x3eaaaaab), selected_year_gap_scalar_value_f32_text: Some("0.333333".to_string()), ..RuntimeWorldRestoreState::default() @@ -1868,6 +1919,48 @@ mod tests { .as_deref(), Some("0.7766201") ); + assert_eq!( + summary.world_restore_linked_site_removal_follow_on_gate_raw_u8, + Some(1) + ); + assert_eq!( + summary.world_restore_linked_site_removal_follow_on_gate_enabled, + Some(true) + ); + assert_eq!( + summary.world_restore_all_steam_locomotives_available_raw_u8, + Some(1) + ); + assert_eq!( + summary.world_restore_all_steam_locomotives_available_enabled, + Some(true) + ); + assert_eq!( + summary.world_restore_all_diesel_locomotives_available_raw_u8, + Some(0) + ); + assert_eq!( + summary.world_restore_all_diesel_locomotives_available_enabled, + Some(false) + ); + assert_eq!( + summary.world_restore_all_electric_locomotives_available_raw_u8, + Some(1) + ); + assert_eq!( + summary.world_restore_all_electric_locomotives_available_enabled, + Some(true) + ); + assert_eq!( + summary.world_restore_cached_available_locomotive_rating_raw_u32, + Some(0x41a00000) + ); + assert_eq!( + summary + .world_restore_cached_available_locomotive_rating_value_f32_text + .as_deref(), + Some("20.000000") + ); assert_eq!(summary.world_restore_economic_tuning_lane_count, 6); assert_eq!( summary.world_restore_economic_tuning_lane_value_f32_text, diff --git a/docs/README.md b/docs/README.md index ef3d10a..ad2361f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -169,6 +169,10 @@ The highest-value next passes are now: - that stepped world-time path now also refreshes the derived selected-year gap scalar owner lane `[world+0x4ca2]`, so later selected-year and periodic-boundary world work can build on runtime state instead of a frozen load-time scalar +- that same save-native world restore surface now also carries the grounded locomotive-policy bytes + and cached available-locomotive rating from the fixed world block, so the `All + Steam/Diesel/Electric Locos Avail.` descriptor strip now writes through owner state instead of + living only as mirrored world flags - the project rule on the remaining closure work is now explicit too: when one runtime-facing field is still ambiguous, prefer rehosting the owning source state or real reader/setter family first instead of guessing another derived leaf field from neighboring raw offsets diff --git a/docs/rehost-queue.md b/docs/rehost-queue.md index 5d52043..5021300 100644 --- a/docs/rehost-queue.md +++ b/docs/rehost-queue.md @@ -11,11 +11,11 @@ Working rule: - Rehost the next selected-year periodic-boundary world seam under `simulation_service_periodic_boundary_work`, starting with the save-world economic tuning mirror - `[world+0x0bde]` and any directly adjacent selected-year companion bands that can be refreshed - from stepped world time without shell ownership. -- Expand the selected-year world-owner surface beyond the new stepped calendar and gap-scalar - lanes when the owning reader/rebuild family is grounded strongly enough to avoid one-off leaf - guesses. + `[world+0x0bde]` and the directly adjacent selected-year bucket ladder rooted in the grounded + `0x00433bd0` reader family instead of another isolated scalar guess. +- Expand the selected-year world-owner surface beyond the stepped calendar, gap scalar, and + locomotive-policy lanes when the owning reader/rebuild family is grounded strongly enough to + avoid one-off leaf guesses. ## In Progress @@ -44,6 +44,9 @@ Working rule: - Stepped calendar progression now also refreshes save-world owner time fields, including packed year, packed tuple words, absolute counter, and the derived selected-year gap scalar. - Automatic year-rollover calendar stepping now invokes periodic-boundary service. +- Save-native world locomotive policy owner state now flows through runtime restore state, + summaries, and keyed world-flag execution for the grounded `All Steam/Diesel/Electric Locos + Avail.` descriptor strip plus the cached available-locomotive rating. - Company cash, confiscation, and major governance effects now write through owner state instead of drifting from market/cache readers. - Company credit rating, prime rate, book value per share, investor confidence, and management diff --git a/docs/runtime-rehost-plan.md b/docs/runtime-rehost-plan.md index 5e1830d..e4bcfb7 100644 --- a/docs/runtime-rehost-plan.md +++ b/docs/runtime-rehost-plan.md @@ -217,7 +217,10 @@ the saved world absolute counter as first-class runtime restore state instead of metadata, plus the packed year word and partial-year progress lane that feed the annual-finance recent-history weighting path. Stepped world time now also refreshes the derived selected-year gap scalar owner lane `[world+0x4ca2]`, so later selected-year periodic-boundary world work can build -on runtime state instead of a frozen load-time scalar. The same owned company annual-finance state +on runtime state instead of a frozen load-time scalar. That same save-native world restore surface +now also carries the grounded locomotive-policy bytes and cached available-locomotive rating from +the fixed world block, so the `All Steam/Diesel/Electric Locos Avail.` descriptor strip now writes +through owner state instead of living only as mirrored world flags. The same owned company annual-finance state now also drives a shared company market reader seam for stock-capital, salary, bonus, and the full two-word current/prior issue-calendar tuples, which is a better base for shellless finance simulation than summary-only helpers. That same owned annual-finance state now also derives elapsed