Rehost periodic route preference service seam

This commit is contained in:
Jan Petykiewicz 2026-04-18 07:42:59 -07:00
commit f7fde709f7
9 changed files with 578 additions and 49 deletions

View file

@ -141,6 +141,9 @@ shellless repayment and bond-burden simulation instead of another round of raw-s
The same save-native company direct-record seam now also carries the full outer periodic-company
side-latch trio rooted at `0x0d17/0x0d18/0x0d56`, including the preferred-locomotive engine-type
chooser byte that sits beside the city-connection and linked-transit finance gates.
That same seam now also resolves the base world route-preference byte at `[world+0x4c74]`, the
effective electric-only override fed by `0x0d17`, and the matching `1.4x` versus `1.8x`
route-quality multiplier as a normal runtime reader instead of leaving that bridge in atlas notes.
That same seam now also derives the current live coupon burden directly from owned bond slots, so
later finance service work can consume a runtime reader instead of recomputing from scattered raw
fields.

View file

@ -855,6 +855,34 @@ fn project_save_slice_components(
.as_ref()
.and_then(|state| state.linked_site_removal_follow_on_gate_raw_u8)
.map(|raw| raw != 0),
auto_show_grade_during_track_lay_raw_u8: save_slice
.world_locomotive_policy_state
.as_ref()
.and_then(|state| state.auto_show_grade_during_track_lay_raw_u8),
starting_building_density_level_raw_u8: save_slice
.world_locomotive_policy_state
.as_ref()
.and_then(|state| state.starting_building_density_level_raw_u8),
post_text_building_density_growth_raw_u8: save_slice
.world_locomotive_policy_state
.as_ref()
.and_then(|state| state.building_density_growth_raw_u8),
leftover_simulation_time_accumulator_raw_u32: save_slice
.world_locomotive_policy_state
.as_ref()
.and_then(|state| state.leftover_simulation_time_accumulator_raw_u32),
leftover_simulation_time_accumulator_value_f32_text: save_slice
.world_locomotive_policy_state
.as_ref()
.and_then(|state| {
state
.leftover_simulation_time_accumulator_value_f32_text
.clone()
}),
selected_year_lane_snapshot_raw_u8: save_slice
.world_locomotive_policy_state
.as_ref()
.and_then(|state| state.selected_year_lane_snapshot_raw_u8),
all_steam_locomotives_available_raw_u8: save_slice
.world_locomotive_policy_state
.as_ref()
@ -6191,6 +6219,17 @@ mod tests {
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()),
auto_show_grade_during_track_lay_raw_u8: Some(2),
auto_show_grade_during_track_lay_raw_hex: Some("0x02".to_string()),
starting_building_density_level_raw_u8: Some(3),
starting_building_density_level_raw_hex: Some("0x03".to_string()),
building_density_growth_raw_u8: Some(1),
building_density_growth_raw_hex: Some("0x01".to_string()),
leftover_simulation_time_accumulator_raw_u32: Some(0x3f000000),
leftover_simulation_time_accumulator_raw_hex: Some("0x3f000000".to_string()),
leftover_simulation_time_accumulator_value_f32_text: Some("0.500000".to_string()),
selected_year_lane_snapshot_raw_u8: Some(7),
selected_year_lane_snapshot_raw_hex: Some("0x07".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),
@ -6368,6 +6407,49 @@ mod tests {
.absolute_counter_reconstructible_from_save,
Some(true)
);
assert_eq!(
import
.state
.world_restore
.auto_show_grade_during_track_lay_raw_u8,
Some(2)
);
assert_eq!(
import
.state
.world_restore
.starting_building_density_level_raw_u8,
Some(3)
);
assert_eq!(
import
.state
.world_restore
.post_text_building_density_growth_raw_u8,
Some(1)
);
assert_eq!(
import
.state
.world_restore
.leftover_simulation_time_accumulator_raw_u32,
Some(0x3f000000)
);
assert_eq!(
import
.state
.world_restore
.leftover_simulation_time_accumulator_value_f32_text
.as_deref(),
Some("0.500000")
);
assert_eq!(
import
.state
.world_restore
.selected_year_lane_snapshot_raw_u8,
Some(7)
);
assert_eq!(
import.state.world_restore.packed_year_word_raw_u16,
Some(0x0201)

View file

@ -58,18 +58,19 @@ pub use runtime::{
RuntimeCompanyAnnualFinanceState, RuntimeCompanyAnnualStockIssueState,
RuntimeCompanyAnnualStockRepurchaseState, RuntimeCompanyBondSlot,
RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind, RuntimeCompanyMarketMetric,
RuntimeCompanyMarketState, RuntimeCompanyMetric, RuntimeCompanyPeriodicSideLatchState,
RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector, RuntimeCompanyTarget,
RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition,
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary,
RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary,
RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer,
RuntimePlayerConditionTestScope, RuntimePlayerTarget, RuntimeSaveProfileState,
RuntimeServiceState, RuntimeState, RuntimeTerritory, RuntimeTerritoryMetric,
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain,
RuntimeWorldFinanceNeighborhoodCandidate, RuntimeWorldIssueState, RuntimeWorldRestoreState,
RuntimeCompanyMarketState, RuntimeCompanyMetric, RuntimeCompanyPeriodicServiceState,
RuntimeCompanyPeriodicSideLatchState, RuntimeCompanyStatBandCandidate,
RuntimeCompanyStatSelector, RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess,
RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition, RuntimeConditionComparator,
RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry,
RuntimePackedEventCollectionSummary, RuntimePackedEventCompactControlSummary,
RuntimePackedEventConditionRowSummary, RuntimePackedEventGroupedEffectRowSummary,
RuntimePackedEventNegativeSentinelScopeSummary, RuntimePackedEventRecordSummary,
RuntimePackedEventTextBandSummary, RuntimePlayer, RuntimePlayerConditionTestScope,
RuntimePlayerTarget, RuntimeSaveProfileState, RuntimeServiceState, RuntimeState,
RuntimeTerritory, RuntimeTerritoryMetric, RuntimeTerritoryTarget, RuntimeTrackMetric,
RuntimeTrackPieceCounts, RuntimeTrain, RuntimeWorldFinanceNeighborhoodCandidate,
RuntimeWorldIssueState, RuntimeWorldRestoreState,
runtime_annual_bond_principal_flow_relation_label,
runtime_annual_finance_news_family_candidate_label, runtime_company_annual_bond_policy_state,
runtime_company_annual_creditor_pressure_state, runtime_company_annual_deep_distress_state,
@ -80,14 +81,15 @@ pub use runtime::{
runtime_company_assigned_share_pool, runtime_company_average_live_bond_coupon,
runtime_company_book_value_per_share, runtime_company_credit_rating,
runtime_company_investor_confidence, runtime_company_management_attitude,
runtime_company_market_value, runtime_company_prime_rate,
runtime_company_recent_per_share_subscore, runtime_company_stat_value,
runtime_company_stat_value_f64, runtime_company_unassigned_share_pool,
runtime_world_annual_finance_mode_active, runtime_world_bankruptcy_allowed,
runtime_world_bond_issue_and_repayment_allowed, runtime_world_building_density_growth_setting,
runtime_world_dividend_adjustment_allowed, runtime_world_issue_opinion_multiplier,
runtime_world_issue_opinion_term_sum_raw, runtime_world_issue_state,
runtime_world_prime_rate_baseline, runtime_world_stock_issue_and_buyback_allowed,
runtime_company_market_value, runtime_company_periodic_service_state,
runtime_company_prime_rate, runtime_company_recent_per_share_subscore,
runtime_company_stat_value, runtime_company_stat_value_f64,
runtime_company_unassigned_share_pool, runtime_world_annual_finance_mode_active,
runtime_world_bankruptcy_allowed, runtime_world_bond_issue_and_repayment_allowed,
runtime_world_building_density_growth_setting, runtime_world_dividend_adjustment_allowed,
runtime_world_issue_opinion_multiplier, runtime_world_issue_opinion_term_sum_raw,
runtime_world_issue_state, runtime_world_prime_rate_baseline,
runtime_world_stock_issue_and_buyback_allowed,
};
pub use smp::{
SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION, SmpAlignedRuntimeRuleBandLane,

View file

@ -194,6 +194,21 @@ pub struct RuntimeCompanyPeriodicSideLatchState {
pub linked_transit_latch: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RuntimeCompanyPeriodicServiceState {
pub company_id: u32,
#[serde(default)]
pub preferred_locomotive_engine_type_raw_u8: Option<u8>,
pub city_connection_latch: bool,
pub linked_transit_latch: bool,
#[serde(default)]
pub base_route_preference_raw_u8: Option<u8>,
#[serde(default)]
pub effective_route_preference_raw_u8: Option<u8>,
pub electric_route_preference_override_active: bool,
pub effective_route_quality_multiplier_basis_points: i64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RuntimeCompanyAnnualCreditorPressureState {
pub company_id: u32,
@ -1377,6 +1392,18 @@ pub struct RuntimeWorldRestoreState {
#[serde(default)]
pub linked_site_removal_follow_on_gate_enabled: Option<bool>,
#[serde(default)]
pub auto_show_grade_during_track_lay_raw_u8: Option<u8>,
#[serde(default)]
pub starting_building_density_level_raw_u8: Option<u8>,
#[serde(default)]
pub post_text_building_density_growth_raw_u8: Option<u8>,
#[serde(default)]
pub leftover_simulation_time_accumulator_raw_u32: Option<u32>,
#[serde(default)]
pub leftover_simulation_time_accumulator_value_f32_text: Option<String>,
#[serde(default)]
pub selected_year_lane_snapshot_raw_u8: Option<u8>,
#[serde(default)]
pub all_steam_locomotives_available_raw_u8: Option<u8>,
#[serde(default)]
pub all_steam_locomotives_available_enabled: Option<bool>,
@ -4523,10 +4550,7 @@ pub fn runtime_company_annual_finance_state(
company_id: u32,
) -> Option<RuntimeCompanyAnnualFinanceState> {
let market_state = state.service_state.company_market_state.get(&company_id)?;
let periodic_side_latch_state = state
.service_state
.company_periodic_side_latch_state
.get(&company_id);
let periodic_service_state = runtime_company_periodic_service_state(state, company_id);
let assigned_share_pool = runtime_company_assigned_share_pool(state, company_id)?;
let unassigned_share_pool = runtime_company_unassigned_share_pool(state, company_id)?;
let years_since_founding =
@ -4601,14 +4625,61 @@ pub fn runtime_company_annual_finance_state(
current_issue_calendar_word_2: market_state.current_issue_calendar_word_2,
prior_issue_calendar_word: market_state.prior_issue_calendar_word,
prior_issue_calendar_word_2: market_state.prior_issue_calendar_word_2,
preferred_locomotive_engine_type_raw_u8: periodic_side_latch_state
.and_then(|latch_state| latch_state.preferred_locomotive_engine_type_raw_u8),
preferred_locomotive_engine_type_raw_u8: periodic_service_state
.as_ref()
.and_then(|service_state| service_state.preferred_locomotive_engine_type_raw_u8),
city_connection_latch: periodic_service_state
.as_ref()
.map(|service_state| service_state.city_connection_latch)
.unwrap_or(market_state.city_connection_latch),
linked_transit_latch: periodic_service_state
.as_ref()
.map(|service_state| service_state.linked_transit_latch)
.unwrap_or(market_state.linked_transit_latch),
})
}
pub fn runtime_company_periodic_service_state(
state: &RuntimeState,
company_id: u32,
) -> Option<RuntimeCompanyPeriodicServiceState> {
const DEFAULT_ROUTE_QUALITY_MULTIPLIER_BASIS_POINTS: i64 = 140;
const ELECTRIC_ROUTE_QUALITY_MULTIPLIER_BASIS_POINTS: i64 = 180;
const ELECTRIC_ENGINE_TYPE_RAW_U8: u8 = 2;
let market_state = state.service_state.company_market_state.get(&company_id)?;
let periodic_side_latch_state = state
.service_state
.company_periodic_side_latch_state
.get(&company_id);
let preferred_locomotive_engine_type_raw_u8 = periodic_side_latch_state
.and_then(|latch_state| latch_state.preferred_locomotive_engine_type_raw_u8);
let base_route_preference_raw_u8 = state.world_restore.auto_show_grade_during_track_lay_raw_u8;
let electric_route_preference_override_active =
preferred_locomotive_engine_type_raw_u8 == Some(ELECTRIC_ENGINE_TYPE_RAW_U8);
let effective_route_preference_raw_u8 = if electric_route_preference_override_active {
Some(ELECTRIC_ENGINE_TYPE_RAW_U8)
} else {
base_route_preference_raw_u8
};
Some(RuntimeCompanyPeriodicServiceState {
company_id,
preferred_locomotive_engine_type_raw_u8,
city_connection_latch: periodic_side_latch_state
.map(|latch_state| latch_state.city_connection_latch)
.unwrap_or(market_state.city_connection_latch),
linked_transit_latch: periodic_side_latch_state
.map(|latch_state| latch_state.linked_transit_latch)
.unwrap_or(market_state.linked_transit_latch),
base_route_preference_raw_u8,
effective_route_preference_raw_u8,
electric_route_preference_override_active,
effective_route_quality_multiplier_basis_points:
if electric_route_preference_override_active {
ELECTRIC_ROUTE_QUALITY_MULTIPLIER_BASIS_POINTS
} else {
DEFAULT_ROUTE_QUALITY_MULTIPLIER_BASIS_POINTS
},
})
}
@ -5259,6 +5330,12 @@ mod tests {
territory_access_cost: None,
linked_site_removal_follow_on_gate_raw_u8: None,
linked_site_removal_follow_on_gate_enabled: None,
auto_show_grade_during_track_lay_raw_u8: None,
starting_building_density_level_raw_u8: None,
post_text_building_density_growth_raw_u8: None,
leftover_simulation_time_accumulator_raw_u32: None,
leftover_simulation_time_accumulator_value_f32_text: None,
selected_year_lane_snapshot_raw_u8: None,
all_steam_locomotives_available_raw_u8: None,
all_steam_locomotives_available_enabled: None,
all_diesel_locomotives_available_raw_u8: None,
@ -8865,6 +8942,195 @@ mod tests {
assert_eq!(runtime_company_annual_finance_state(&state, 99), None);
}
#[test]
fn derives_company_periodic_service_state_from_side_latches_and_world_route_preference() {
let state = RuntimeState {
calendar: CalendarPoint {
year: 1845,
month_slot: 0,
phase_slot: 0,
tick_slot: 0,
},
world_flags: BTreeMap::new(),
save_profile: RuntimeSaveProfileState::default(),
world_restore: RuntimeWorldRestoreState {
auto_show_grade_during_track_lay_raw_u8: Some(1),
..RuntimeWorldRestoreState::default()
},
metadata: BTreeMap::new(),
companies: vec![RuntimeCompany {
company_id: 4,
current_cash: 0,
debt: 0,
credit_rating_score: None,
prime_rate: None,
active: true,
available_track_laying_capacity: None,
controller_kind: RuntimeCompanyControllerKind::Unknown,
linked_chairman_profile_id: None,
book_value_per_share: 0,
investor_confidence: 0,
management_attitude: 0,
takeover_cooldown_year: None,
merger_cooldown_year: None,
track_piece_counts: RuntimeTrackPieceCounts::default(),
}],
selected_company_id: Some(4),
players: Vec::new(),
selected_player_id: None,
chairman_profiles: Vec::new(),
selected_chairman_profile_id: None,
trains: Vec::new(),
locomotive_catalog: Vec::new(),
cargo_catalog: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
company_territory_access: Vec::new(),
packed_event_collection: None,
event_runtime_records: Vec::new(),
candidate_availability: BTreeMap::new(),
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
all_cargo_price_override: None,
named_cargo_price_overrides: BTreeMap::new(),
all_cargo_production_override: None,
factory_cargo_production_override: None,
farm_mine_cargo_production_override: None,
named_cargo_production_overrides: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_runtime_variables: BTreeMap::new(),
company_runtime_variables: BTreeMap::new(),
player_runtime_variables: BTreeMap::new(),
territory_runtime_variables: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState {
company_market_state: BTreeMap::from([(
4,
RuntimeCompanyMarketState {
city_connection_latch: true,
linked_transit_latch: false,
..RuntimeCompanyMarketState::default()
},
)]),
company_periodic_side_latch_state: BTreeMap::from([(
4,
RuntimeCompanyPeriodicSideLatchState {
preferred_locomotive_engine_type_raw_u8: Some(2),
city_connection_latch: false,
linked_transit_latch: true,
},
)]),
..RuntimeServiceState::default()
},
};
assert_eq!(
runtime_company_periodic_service_state(&state, 4),
Some(RuntimeCompanyPeriodicServiceState {
company_id: 4,
preferred_locomotive_engine_type_raw_u8: Some(2),
city_connection_latch: false,
linked_transit_latch: true,
base_route_preference_raw_u8: Some(1),
effective_route_preference_raw_u8: Some(2),
electric_route_preference_override_active: true,
effective_route_quality_multiplier_basis_points: 180,
})
);
assert_eq!(runtime_company_periodic_service_state(&state, 99), None);
}
#[test]
fn periodic_service_state_falls_back_to_market_latches_and_world_base_route_preference() {
let state = RuntimeState {
calendar: CalendarPoint {
year: 1845,
month_slot: 0,
phase_slot: 0,
tick_slot: 0,
},
world_flags: BTreeMap::new(),
save_profile: RuntimeSaveProfileState::default(),
world_restore: RuntimeWorldRestoreState {
auto_show_grade_during_track_lay_raw_u8: Some(3),
..RuntimeWorldRestoreState::default()
},
metadata: BTreeMap::new(),
companies: vec![RuntimeCompany {
company_id: 8,
current_cash: 0,
debt: 0,
credit_rating_score: None,
prime_rate: None,
active: true,
available_track_laying_capacity: None,
controller_kind: RuntimeCompanyControllerKind::Unknown,
linked_chairman_profile_id: None,
book_value_per_share: 0,
investor_confidence: 0,
management_attitude: 0,
takeover_cooldown_year: None,
merger_cooldown_year: None,
track_piece_counts: RuntimeTrackPieceCounts::default(),
}],
selected_company_id: Some(8),
players: Vec::new(),
selected_player_id: None,
chairman_profiles: Vec::new(),
selected_chairman_profile_id: None,
trains: Vec::new(),
locomotive_catalog: Vec::new(),
cargo_catalog: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
company_territory_access: Vec::new(),
packed_event_collection: None,
event_runtime_records: Vec::new(),
candidate_availability: BTreeMap::new(),
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
all_cargo_price_override: None,
named_cargo_price_overrides: BTreeMap::new(),
all_cargo_production_override: None,
factory_cargo_production_override: None,
farm_mine_cargo_production_override: None,
named_cargo_production_overrides: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_runtime_variables: BTreeMap::new(),
company_runtime_variables: BTreeMap::new(),
player_runtime_variables: BTreeMap::new(),
territory_runtime_variables: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState {
company_market_state: BTreeMap::from([(
8,
RuntimeCompanyMarketState {
city_connection_latch: true,
linked_transit_latch: false,
..RuntimeCompanyMarketState::default()
},
)]),
..RuntimeServiceState::default()
},
};
assert_eq!(
runtime_company_periodic_service_state(&state, 8),
Some(RuntimeCompanyPeriodicServiceState {
company_id: 8,
preferred_locomotive_engine_type_raw_u8: None,
city_connection_latch: true,
linked_transit_latch: false,
base_route_preference_raw_u8: Some(3),
effective_route_preference_raw_u8: Some(3),
electric_route_preference_override_active: false,
effective_route_quality_multiplier_basis_points: 140,
})
);
}
#[test]
fn derives_annual_creditor_pressure_from_rehosted_finance_owner_state() {
let mut year_stat_family_qword_bits = vec![

View file

@ -2304,6 +2304,28 @@ pub struct SmpLoadedWorldLocomotivePolicyState {
#[serde(default)]
pub linked_site_removal_follow_on_gate_raw_hex: Option<String>,
#[serde(default)]
pub auto_show_grade_during_track_lay_raw_u8: Option<u8>,
#[serde(default)]
pub auto_show_grade_during_track_lay_raw_hex: Option<String>,
#[serde(default)]
pub starting_building_density_level_raw_u8: Option<u8>,
#[serde(default)]
pub starting_building_density_level_raw_hex: Option<String>,
#[serde(default)]
pub building_density_growth_raw_u8: Option<u8>,
#[serde(default)]
pub building_density_growth_raw_hex: Option<String>,
#[serde(default)]
pub leftover_simulation_time_accumulator_raw_u32: Option<u32>,
#[serde(default)]
pub leftover_simulation_time_accumulator_raw_hex: Option<String>,
#[serde(default)]
pub leftover_simulation_time_accumulator_value_f32_text: Option<String>,
#[serde(default)]
pub selected_year_lane_snapshot_raw_u8: Option<u8>,
#[serde(default)]
pub selected_year_lane_snapshot_raw_hex: Option<String>,
#[serde(default)]
pub all_steam_locomotives_available_raw_u8: Option<u8>,
#[serde(default)]
pub all_steam_locomotives_available_raw_hex: Option<String>,
@ -2932,10 +2954,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 world_locomotive_policy_state = derive_loaded_world_locomotive_policy_state_from_probes(
report.post_text_field_neighborhood_probe.as_ref(),
report.locomotive_policy_neighborhood_probe.as_ref(),
);
let company_roster = report.save_company_roster_probe.clone().or_else(|| {
report
.save_world_selection_context_probe
@ -3638,23 +3660,37 @@ fn derive_loaded_world_finance_neighborhood_state_from_probe(
}
}
fn derive_loaded_world_locomotive_policy_state_from_probe(
probe: &SmpLocomotivePolicyNeighborhoodProbe,
) -> SmpLoadedWorldLocomotivePolicyState {
fn derive_loaded_world_locomotive_policy_state_from_probes(
post_text_probe: Option<&SmpPostTextFieldNeighborhoodProbe>,
locomotive_policy_probe: Option<&SmpLocomotivePolicyNeighborhoodProbe>,
) -> Option<SmpLoadedWorldLocomotivePolicyState> {
let field_by_name = |name: &str| {
probe
locomotive_policy_probe?
.grounded_field_observations
.iter()
.find(|field| field.field_name == name)
};
let post_text_field_by_name = |name: &str| {
post_text_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 auto_show_grade = post_text_field_by_name("Auto-Show Grade During Track Lay");
let starting_building_density = post_text_field_by_name("Starting Building Density Level");
let building_density_growth = post_text_field_by_name("Building Density Growth");
let leftover_simulation_time = post_text_field_by_name("leftover simulation time accumulator");
let selected_year_snapshot = post_text_field_by_name("selected-year lane snapshot");
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(),
Some(SmpLoadedWorldLocomotivePolicyState {
source_kind: locomotive_policy_probe
.map(|probe| probe.source_kind.clone())
.or_else(|| post_text_probe.map(|probe| 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),
@ -3666,6 +3702,25 @@ fn derive_loaded_world_locomotive_policy_state_from_probe(
.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()),
auto_show_grade_during_track_lay_raw_u8: auto_show_grade.and_then(|field| field.value_u8),
auto_show_grade_during_track_lay_raw_hex: auto_show_grade
.and_then(|field| field.value_u8_hex.clone()),
starting_building_density_level_raw_u8: starting_building_density
.and_then(|field| field.value_u8),
starting_building_density_level_raw_hex: starting_building_density
.and_then(|field| field.value_u8_hex.clone()),
building_density_growth_raw_u8: building_density_growth.and_then(|field| field.value_u8),
building_density_growth_raw_hex: building_density_growth
.and_then(|field| field.value_u8_hex.clone()),
leftover_simulation_time_accumulator_raw_u32: leftover_simulation_time
.and_then(|field| field.value_u32),
leftover_simulation_time_accumulator_raw_hex: leftover_simulation_time
.and_then(|field| field.value_u32_hex.clone()),
leftover_simulation_time_accumulator_value_f32_text: leftover_simulation_time
.and_then(|field| field.probable_f32_le.clone()),
selected_year_lane_snapshot_raw_u8: selected_year_snapshot.and_then(|field| field.value_u8),
selected_year_lane_snapshot_raw_hex: selected_year_snapshot
.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()),
@ -3681,7 +3736,7 @@ fn derive_loaded_world_locomotive_policy_state_from_probe(
.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(

View file

@ -7,7 +7,7 @@ use crate::{
runtime_company_annual_finance_policy_action_label,
runtime_company_annual_finance_policy_state, runtime_company_annual_finance_state,
runtime_company_annual_stock_issue_state, runtime_company_annual_stock_repurchase_state,
runtime_company_unassigned_share_pool,
runtime_company_periodic_service_state, runtime_company_unassigned_share_pool,
};
fn raw_u32_to_f32_text(raw: u32) -> String {
@ -51,6 +51,12 @@ pub struct RuntimeSummary {
pub world_restore_territory_access_cost: Option<u32>,
pub world_restore_linked_site_removal_follow_on_gate_raw_u8: Option<u8>,
pub world_restore_linked_site_removal_follow_on_gate_enabled: Option<bool>,
pub world_restore_auto_show_grade_during_track_lay_raw_u8: Option<u8>,
pub world_restore_starting_building_density_level_raw_u8: Option<u8>,
pub world_restore_post_text_building_density_growth_raw_u8: Option<u8>,
pub world_restore_leftover_simulation_time_accumulator_raw_u32: Option<u32>,
pub world_restore_leftover_simulation_time_accumulator_value_f32_text: Option<String>,
pub world_restore_selected_year_lane_snapshot_raw_u8: Option<u8>,
pub world_restore_all_steam_locomotives_available_raw_u8: Option<u8>,
pub world_restore_all_steam_locomotives_available_enabled: Option<bool>,
pub world_restore_all_diesel_locomotives_available_raw_u8: Option<u8>,
@ -122,6 +128,10 @@ pub struct RuntimeSummary {
pub selected_company_periodic_side_latch_preferred_locomotive_engine_type_raw_u8: Option<u8>,
pub selected_company_periodic_side_latch_city_connection_latch: Option<bool>,
pub selected_company_periodic_side_latch_linked_transit_latch: Option<bool>,
pub selected_company_periodic_service_base_route_preference_raw_u8: Option<u8>,
pub selected_company_periodic_service_effective_route_preference_raw_u8: Option<u8>,
pub selected_company_periodic_service_electric_route_preference_override_active: Option<bool>,
pub selected_company_periodic_service_route_quality_multiplier_basis_points: Option<i64>,
pub selected_company_chairman_bonus_year: Option<u32>,
pub selected_company_chairman_bonus_amount: Option<i32>,
pub selected_company_creditor_pressure_recent_bad_net_profit_year_count: Option<u32>,
@ -307,6 +317,9 @@ impl RuntimeSummary {
.company_periodic_side_latch_state
.get(&company_id)
});
let selected_company_periodic_service_state = state
.selected_company_id
.and_then(|company_id| runtime_company_periodic_service_state(state, company_id));
let selected_company_annual_finance_state = state
.selected_company_id
.and_then(|company_id| runtime_company_annual_finance_state(state, company_id));
@ -464,6 +477,25 @@ impl RuntimeSummary {
world_restore_linked_site_removal_follow_on_gate_enabled: state
.world_restore
.linked_site_removal_follow_on_gate_enabled,
world_restore_auto_show_grade_during_track_lay_raw_u8: state
.world_restore
.auto_show_grade_during_track_lay_raw_u8,
world_restore_starting_building_density_level_raw_u8: state
.world_restore
.starting_building_density_level_raw_u8,
world_restore_post_text_building_density_growth_raw_u8: state
.world_restore
.post_text_building_density_growth_raw_u8,
world_restore_leftover_simulation_time_accumulator_raw_u32: state
.world_restore
.leftover_simulation_time_accumulator_raw_u32,
world_restore_leftover_simulation_time_accumulator_value_f32_text: state
.world_restore
.leftover_simulation_time_accumulator_value_f32_text
.clone(),
world_restore_selected_year_lane_snapshot_raw_u8: state
.world_restore
.selected_year_lane_snapshot_raw_u8,
world_restore_all_steam_locomotives_available_raw_u8: state
.world_restore
.all_steam_locomotives_available_raw_u8,
@ -622,6 +654,24 @@ impl RuntimeSummary {
selected_company_periodic_side_latch_linked_transit_latch:
selected_company_periodic_side_latch_state
.map(|latch_state| latch_state.linked_transit_latch),
selected_company_periodic_service_base_route_preference_raw_u8:
selected_company_periodic_service_state
.as_ref()
.and_then(|service_state| service_state.base_route_preference_raw_u8),
selected_company_periodic_service_effective_route_preference_raw_u8:
selected_company_periodic_service_state
.as_ref()
.and_then(|service_state| service_state.effective_route_preference_raw_u8),
selected_company_periodic_service_electric_route_preference_override_active:
selected_company_periodic_service_state
.as_ref()
.map(|service_state| service_state.electric_route_preference_override_active),
selected_company_periodic_service_route_quality_multiplier_basis_points:
selected_company_periodic_service_state
.as_ref()
.map(|service_state| {
service_state.effective_route_quality_multiplier_basis_points
}),
selected_company_chairman_bonus_year: selected_company_market_state
.map(|market_state| market_state.chairman_bonus_year)
.filter(|year| *year != 0),
@ -1578,7 +1628,10 @@ mod tests {
},
world_flags: BTreeMap::new(),
save_profile: RuntimeSaveProfileState::default(),
world_restore: RuntimeWorldRestoreState::default(),
world_restore: RuntimeWorldRestoreState {
auto_show_grade_during_track_lay_raw_u8: Some(2),
..RuntimeWorldRestoreState::default()
},
metadata: BTreeMap::new(),
companies: Vec::new(),
selected_company_id: None,
@ -1857,6 +1910,12 @@ mod tests {
],
linked_site_removal_follow_on_gate_raw_u8: Some(1),
linked_site_removal_follow_on_gate_enabled: Some(true),
auto_show_grade_during_track_lay_raw_u8: Some(2),
starting_building_density_level_raw_u8: Some(3),
post_text_building_density_growth_raw_u8: Some(1),
leftover_simulation_time_accumulator_raw_u32: Some(0x3f000000),
leftover_simulation_time_accumulator_value_f32_text: Some("0.500000".to_string()),
selected_year_lane_snapshot_raw_u8: Some(7),
all_steam_locomotives_available_raw_u8: Some(1),
all_steam_locomotives_available_enabled: Some(true),
all_diesel_locomotives_available_raw_u8: Some(0),
@ -2017,6 +2076,32 @@ mod tests {
summary.world_restore_linked_site_removal_follow_on_gate_enabled,
Some(true)
);
assert_eq!(
summary.world_restore_auto_show_grade_during_track_lay_raw_u8,
Some(2)
);
assert_eq!(
summary.world_restore_starting_building_density_level_raw_u8,
Some(3)
);
assert_eq!(
summary.world_restore_post_text_building_density_growth_raw_u8,
Some(1)
);
assert_eq!(
summary.world_restore_leftover_simulation_time_accumulator_raw_u32,
Some(0x3f000000)
);
assert_eq!(
summary
.world_restore_leftover_simulation_time_accumulator_value_f32_text
.as_deref(),
Some("0.500000")
);
assert_eq!(
summary.world_restore_selected_year_lane_snapshot_raw_u8,
Some(7)
);
assert_eq!(
summary.world_restore_all_steam_locomotives_available_raw_u8,
Some(1)
@ -2113,7 +2198,10 @@ mod tests {
},
world_flags: BTreeMap::new(),
save_profile: RuntimeSaveProfileState::default(),
world_restore: RuntimeWorldRestoreState::default(),
world_restore: RuntimeWorldRestoreState {
auto_show_grade_during_track_lay_raw_u8: Some(2),
..RuntimeWorldRestoreState::default()
},
metadata: BTreeMap::new(),
companies: vec![
RuntimeCompany {
@ -2218,7 +2306,10 @@ mod tests {
},
world_flags: BTreeMap::new(),
save_profile: RuntimeSaveProfileState::default(),
world_restore: RuntimeWorldRestoreState::default(),
world_restore: RuntimeWorldRestoreState {
auto_show_grade_during_track_lay_raw_u8: Some(2),
..RuntimeWorldRestoreState::default()
},
metadata: BTreeMap::new(),
companies: Vec::new(),
selected_company_id: None,
@ -2770,7 +2861,10 @@ mod tests {
},
world_flags: BTreeMap::new(),
save_profile: RuntimeSaveProfileState::default(),
world_restore: RuntimeWorldRestoreState::default(),
world_restore: RuntimeWorldRestoreState {
auto_show_grade_during_track_lay_raw_u8: Some(2),
..RuntimeWorldRestoreState::default()
},
metadata: BTreeMap::new(),
companies: vec![RuntimeCompany {
company_id: 1,
@ -2972,6 +3066,22 @@ mod tests {
summary.selected_company_periodic_side_latch_linked_transit_latch,
Some(false)
);
assert_eq!(
summary.selected_company_periodic_service_base_route_preference_raw_u8,
Some(2)
);
assert_eq!(
summary.selected_company_periodic_service_effective_route_preference_raw_u8,
Some(2)
);
assert_eq!(
summary.selected_company_periodic_service_electric_route_preference_override_active,
Some(true)
);
assert_eq!(
summary.selected_company_periodic_service_route_quality_multiplier_basis_points,
Some(180)
);
assert_eq!(summary.selected_company_chairman_bonus_year, Some(1842));
assert_eq!(summary.selected_company_chairman_bonus_amount, Some(750));
}

View file

@ -184,6 +184,9 @@ The highest-value next passes are now:
- the same save-native company direct-record seam now also carries the full outer periodic-company
side-latch trio rooted at `0x0d17/0x0d18/0x0d56`, including the preferred-locomotive
engine-type chooser byte beside the city-connection and linked-transit finance gates
- that same seam now also resolves the base world route-preference byte at `[world+0x4c74]`, the
effective electric-only override fed by `0x0d17`, and the matching `1.4x` versus `1.8x`
route-quality multiplier as a normal runtime reader instead of a pure atlas note
- 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

View file

@ -9,13 +9,13 @@ Working rule:
## Next
- Rehost the outer periodic company-service seam around
`company_service_periodic_city_connection_finance_and_linked_transit_lanes`, using the now
save-native side-latch trio `0x0d17/0x0d18/0x0d56` as owned runtime state instead of leaving
that pass split across annual-finance readers and atlas notes.
- Rehost the world-side `[world+0x4c74]` post-text owner lane so the temporary route-preference
override fed by company-side `0x0d17` can eventually move through a real runtime seam instead
of staying atlas-only.
- Rehost the next live branch inside
`company_service_periodic_city_connection_finance_and_linked_transit_lanes`, especially the
city-connection announcement / linked-transit roster-maintenance side that now sits on top of
the owned periodic-service seam instead of loose atlas notes.
- Keep widening the temporary world route-preference owner around `[world+0x4c74]` from a pure
reader seam into a real service mutation seam when the linked-transit route search / balancing
branch is grounded strongly enough.
- Keep widening selected-year world-owner state only when a full owning reader/rebuild family is
grounded strongly enough to avoid one-off leaf guesses.
@ -66,6 +66,11 @@ Working rule:
- The periodic-boundary owner now also clears the transient preferred-locomotive side latch every
cycle and reseeds the finance latches from market state where present, while preserving
side-latch-only company context when no market projection exists.
- The outer periodic-company seam now also has a first-class runtime reader:
selected-company summaries and finance readers can resolve the base `[world+0x4c74]`
route-preference byte, the effective electric-only override fed by `0x0d17`, and the matching
`1.4x` versus `1.8x` route-quality multiplier through owned periodic-service state instead of
leaving that bridge in atlas notes.
- 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

View file

@ -261,6 +261,9 @@ policy instead of treating dividend changes as shell-dialog-only logic.
The same save-native company direct-record seam now also carries the full outer periodic-company
side-latch trio rooted at `0x0d17/0x0d18/0x0d56`, including the preferred-locomotive
engine-type chooser byte beside the city-connection and linked-transit finance gates.
That same seam now also resolves the base world route-preference byte at `[world+0x4c74]`, the
effective electric-only override fed by `0x0d17`, and the matching `1.4x` versus `1.8x`
route-quality multiplier as a first-class runtime reader rather than a loose atlas-only bridge.
That same seam now also carries the fixed-world building-density growth setting plus the linked
chairman personality byte, which is enough to rehost the annual stock-repurchase gate on owned
save/runtime state instead of another threshold-only note. The stock-capital issue branch now