Rehost company periodic side-latch state

This commit is contained in:
Jan Petykiewicz 2026-04-18 07:23:07 -07:00
commit b1c6dad723
5 changed files with 238 additions and 16 deletions

View file

@ -101,6 +101,7 @@ struct SaveSliceProjection {
has_company_selection_override: bool,
selected_company_id: Option<u32>,
company_market_state: BTreeMap<u32, RuntimeCompanyMarketState>,
company_periodic_side_latch_state: BTreeMap<u32, crate::RuntimeCompanyPeriodicSideLatchState>,
has_company_market_projection: bool,
world_issue_opinion_base_terms_raw_i32: Vec<i32>,
chairman_profiles: Vec<RuntimeChairmanProfile>,
@ -318,6 +319,7 @@ pub fn project_save_slice_to_runtime_state_import(
world_issue_opinion_base_terms_raw_i32: projection
.world_issue_opinion_base_terms_raw_i32,
company_market_state: projection.company_market_state,
company_periodic_side_latch_state: projection.company_periodic_side_latch_state,
chairman_issue_opinion_terms_raw_i32: projection.chairman_issue_opinion_terms_raw_i32,
chairman_personality_raw_u8: projection.chairman_personality_raw_u8,
..RuntimeServiceState::default()
@ -443,6 +445,14 @@ pub fn project_save_slice_overlay_to_runtime_state_import(
} else {
base_state.service_state.company_market_state.clone()
},
company_periodic_side_latch_state: if projection.has_company_market_projection {
projection.company_periodic_side_latch_state
} else {
base_state
.service_state
.company_periodic_side_latch_state
.clone()
},
chairman_issue_opinion_terms_raw_i32: if projection.has_chairman_projection {
projection.chairman_issue_opinion_terms_raw_i32
} else {
@ -1192,6 +1202,7 @@ fn project_save_slice_components(
has_company_selection_override,
selected_company_id,
company_market_state,
company_periodic_side_latch_state,
world_issue_opinion_base_terms_raw_i32,
has_company_market_projection,
) = if let Some(roster) = &save_slice.company_roster {
@ -1217,6 +1228,29 @@ fn project_save_slice_components(
.map(|state| (entry.company_id, state.clone()))
})
.collect::<BTreeMap<_, _>>();
let periodic_side_latch_state = roster
.entries
.iter()
.map(|entry| {
(
entry.company_id,
crate::RuntimeCompanyPeriodicSideLatchState {
preferred_locomotive_engine_type_raw_u8: entry
.preferred_locomotive_engine_type_raw_u8,
city_connection_latch: entry
.market_state
.as_ref()
.map(|state| state.city_connection_latch)
.unwrap_or(false),
linked_transit_latch: entry
.market_state
.as_ref()
.map(|state| state.linked_transit_latch)
.unwrap_or(false),
},
)
})
.collect::<BTreeMap<_, _>>();
metadata.insert(
"save_slice.company_market_state_owner_count".to_string(),
market_state.len().to_string(),
@ -1234,6 +1268,7 @@ fn project_save_slice_components(
roster.selected_company_id.is_some(),
roster.selected_company_id,
BTreeMap::new(),
periodic_side_latch_state,
save_slice
.world_issue_37_state
.as_ref()
@ -1268,6 +1303,7 @@ fn project_save_slice_components(
roster.selected_company_id.is_some(),
roster.selected_company_id,
market_state,
periodic_side_latch_state,
save_slice
.world_issue_37_state
.as_ref()
@ -1283,6 +1319,7 @@ fn project_save_slice_components(
false,
None,
BTreeMap::new(),
BTreeMap::new(),
save_slice
.world_issue_37_state
.as_ref()
@ -1456,6 +1493,7 @@ fn project_save_slice_components(
has_company_selection_override,
selected_company_id,
company_market_state,
company_periodic_side_latch_state,
has_company_market_projection,
world_issue_opinion_base_terms_raw_i32,
chairman_profiles,
@ -14191,6 +14229,7 @@ mod tests {
dirty_rerun_count: 2,
world_issue_opinion_base_terms_raw_i32: Vec::new(),
company_market_state: BTreeMap::new(),
company_periodic_side_latch_state: BTreeMap::new(),
annual_finance_last_actions: BTreeMap::new(),
annual_finance_action_counts: BTreeMap::new(),
annual_dividend_adjustment_commit_count: 0,

View file

@ -58,18 +58,18 @@ pub use runtime::{
RuntimeCompanyAnnualFinanceState, RuntimeCompanyAnnualStockIssueState,
RuntimeCompanyAnnualStockRepurchaseState, RuntimeCompanyBondSlot,
RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind, RuntimeCompanyMarketMetric,
RuntimeCompanyMarketState, RuntimeCompanyMetric, 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, 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,

View file

@ -178,10 +178,22 @@ pub struct RuntimeCompanyAnnualFinanceState {
pub prior_issue_calendar_word: u32,
#[serde(default)]
pub prior_issue_calendar_word_2: u32,
#[serde(default)]
pub preferred_locomotive_engine_type_raw_u8: Option<u8>,
pub city_connection_latch: bool,
pub linked_transit_latch: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct RuntimeCompanyPeriodicSideLatchState {
#[serde(default)]
pub preferred_locomotive_engine_type_raw_u8: Option<u8>,
#[serde(default)]
pub city_connection_latch: bool,
#[serde(default)]
pub linked_transit_latch: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RuntimeCompanyAnnualCreditorPressureState {
pub company_id: u32,
@ -1243,6 +1255,8 @@ pub struct RuntimeServiceState {
#[serde(default)]
pub company_market_state: BTreeMap<u32, RuntimeCompanyMarketState>,
#[serde(default)]
pub company_periodic_side_latch_state: BTreeMap<u32, RuntimeCompanyPeriodicSideLatchState>,
#[serde(default)]
pub annual_finance_last_actions: BTreeMap<u32, RuntimeCompanyAnnualFinancePolicyAction>,
#[serde(default)]
pub annual_finance_action_counts: BTreeMap<RuntimeCompanyAnnualFinancePolicyAction, u64>,
@ -2203,6 +2217,14 @@ impl RuntimeState {
));
}
}
for company_id in self.service_state.company_periodic_side_latch_state.keys() {
if !seen_company_ids.contains(company_id) {
return Err(format!(
"service_state.company_periodic_side_latch_state references unknown company_id {}",
company_id
));
}
}
for company_id in self.service_state.annual_finance_last_actions.keys() {
if !seen_company_ids.contains(company_id) {
return Err(format!(
@ -2373,6 +2395,24 @@ impl RuntimeState {
}
}
self.service_state
.company_periodic_side_latch_state
.retain(|company_id, _| {
self.service_state
.company_market_state
.contains_key(company_id)
});
for (company_id, market_state) in &self.service_state.company_market_state {
self.service_state
.company_periodic_side_latch_state
.entry(*company_id)
.or_insert_with(|| RuntimeCompanyPeriodicSideLatchState {
preferred_locomotive_engine_type_raw_u8: None,
city_connection_latch: market_state.city_connection_latch,
linked_transit_latch: market_state.linked_transit_latch,
});
}
for profile in &mut self.chairman_profiles {
let preserved_threshold_adjusted_holdings_component = profile
.purchasing_power_total
@ -4482,6 +4522,10 @@ 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 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 =
@ -4556,8 +4600,14 @@ 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,
city_connection_latch: market_state.city_connection_latch,
linked_transit_latch: market_state.linked_transit_latch,
preferred_locomotive_engine_type_raw_u8: periodic_side_latch_state
.and_then(|latch_state| latch_state.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),
})
}
@ -6681,6 +6731,93 @@ mod tests {
assert_eq!(state.companies[0].management_attitude, 58);
}
#[test]
fn seeds_company_periodic_side_latch_state_from_company_market_state() {
let mut state = RuntimeState {
calendar: CalendarPoint {
year: 1830,
month_slot: 0,
phase_slot: 0,
tick_slot: 0,
},
world_flags: BTreeMap::new(),
save_profile: RuntimeSaveProfileState::default(),
world_restore: RuntimeWorldRestoreState::default(),
metadata: BTreeMap::new(),
companies: vec![RuntimeCompany {
company_id: 1,
current_cash: 0,
debt: 0,
credit_rating_score: None,
prime_rate: None,
track_piece_counts: RuntimeTrackPieceCounts::default(),
active: true,
available_track_laying_capacity: None,
linked_chairman_profile_id: None,
book_value_per_share: 0,
investor_confidence: 0,
management_attitude: 0,
takeover_cooldown_year: None,
merger_cooldown_year: None,
controller_kind: RuntimeCompanyControllerKind::Unknown,
}],
selected_company_id: Some(1),
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([(
1,
RuntimeCompanyMarketState {
city_connection_latch: true,
linked_transit_latch: false,
..RuntimeCompanyMarketState::default()
},
)]),
..RuntimeServiceState::default()
},
};
state.refresh_derived_market_state();
assert_eq!(
state
.service_state
.company_periodic_side_latch_state
.get(&1),
Some(&RuntimeCompanyPeriodicSideLatchState {
preferred_locomotive_engine_type_raw_u8: None,
city_connection_latch: true,
linked_transit_latch: false,
})
);
}
#[test]
fn reads_grounded_company_stat_family_slots_from_runtime_state() {
let mut year_stat_family_qword_bits = vec![
@ -8631,6 +8768,7 @@ mod tests {
current_issue_calendar_word_2: 6,
prior_issue_calendar_word: 4,
prior_issue_calendar_word_2: 5,
preferred_locomotive_engine_type_raw_u8: None,
city_connection_latch: true,
linked_transit_latch: false,
})

View file

@ -119,6 +119,9 @@ pub struct RuntimeSummary {
pub selected_company_current_issue_absolute_counter: Option<u32>,
pub selected_company_prior_issue_absolute_counter: Option<u32>,
pub selected_company_current_issue_age_absolute_counter_delta: Option<i64>,
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_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>,
@ -297,6 +300,13 @@ impl RuntimeSummary {
let selected_company_market_state = state
.selected_company_id
.and_then(|company_id| state.service_state.company_market_state.get(&company_id));
let selected_company_periodic_side_latch_state =
state.selected_company_id.and_then(|company_id| {
state
.service_state
.company_periodic_side_latch_state
.get(&company_id)
});
let selected_company_annual_finance_state = state
.selected_company_id
.and_then(|company_id| runtime_company_annual_finance_state(state, company_id));
@ -603,6 +613,15 @@ impl RuntimeSummary {
.and_then(|finance_state| {
finance_state.current_issue_age_absolute_counter_delta
}),
selected_company_periodic_side_latch_preferred_locomotive_engine_type_raw_u8:
selected_company_periodic_side_latch_state
.and_then(|latch_state| latch_state.preferred_locomotive_engine_type_raw_u8),
selected_company_periodic_side_latch_city_connection_latch:
selected_company_periodic_side_latch_state
.map(|latch_state| latch_state.city_connection_latch),
selected_company_periodic_side_latch_linked_transit_latch:
selected_company_periodic_side_latch_state
.map(|latch_state| latch_state.linked_transit_latch),
selected_company_chairman_bonus_year: selected_company_market_state
.map(|market_state| market_state.chairman_bonus_year)
.filter(|year| *year != 0),
@ -2887,6 +2906,14 @@ mod tests {
direct_control_transfer_int_fields_raw_u32: BTreeMap::new(),
},
)]),
company_periodic_side_latch_state: BTreeMap::from([(
1,
crate::RuntimeCompanyPeriodicSideLatchState {
preferred_locomotive_engine_type_raw_u8: Some(2),
city_connection_latch: true,
linked_transit_latch: false,
},
)]),
..RuntimeServiceState::default()
},
};
@ -2933,6 +2960,18 @@ mod tests {
assert_eq!(summary.selected_company_years_since_founding, None);
assert_eq!(summary.selected_company_years_since_last_bankruptcy, None);
assert_eq!(summary.selected_company_years_since_last_dividend, None);
assert_eq!(
summary.selected_company_periodic_side_latch_preferred_locomotive_engine_type_raw_u8,
Some(2)
);
assert_eq!(
summary.selected_company_periodic_side_latch_city_connection_latch,
Some(true)
);
assert_eq!(
summary.selected_company_periodic_side_latch_linked_transit_latch,
Some(false)
);
assert_eq!(summary.selected_company_chairman_bonus_year, Some(1842));
assert_eq!(summary.selected_company_chairman_bonus_amount, Some(750));
}

View file

@ -11,8 +11,11 @@ Working rule:
- 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 state instead of leaving that pass
split across annual-finance readers and atlas notes.
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.
- Move the periodic-boundary owner from passive imported side-latch state toward same-cycle
service-owned refresh or reset behavior, so earlier periodic branches can eventually set those
lanes before annual finance consumes them.
- 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.
@ -57,6 +60,9 @@ Working rule:
- The 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 side-latch trio now also has a runtime-owned service-state map and summary surface,
so later periodic company-service work can stop reading those lanes directly from imported
market/cache residue.
- 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