Rehost annual stock repurchase policy branch
This commit is contained in:
parent
5e19e8985a
commit
0658626a57
8 changed files with 592 additions and 17 deletions
|
|
@ -95,6 +95,9 @@ headlessly as a pure runtime reader over owned annual-finance state, support-adj
|
|||
and current world finance policy rather than as a notes-only atlas fragment. The later deep-
|
||||
distress bankruptcy fallback is now rehosted on that same owner surface too, using the save-native
|
||||
cash reader seam plus the first three trailing net-profit years instead of another ad hoc probe.
|
||||
The same seam now also carries the fixed-world building-density growth setting plus the linked
|
||||
chairman personality byte, which is enough to run the annual stock-repurchase gate as another
|
||||
pure reader over owned save-native state instead of a guessed finance-side approximation.
|
||||
The working rule on the remaining frontier is explicit now too: when a lane is still ambiguous, we
|
||||
should prefer rehosting the owning source state or the real reader/setter family rather than
|
||||
guessing one more derived leaf field from nearby offsets. A checked-in
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ struct SaveSliceProjection {
|
|||
has_chairman_selection_override: bool,
|
||||
selected_chairman_profile_id: Option<u32>,
|
||||
chairman_issue_opinion_terms_raw_i32: BTreeMap<u32, Vec<i32>>,
|
||||
chairman_personality_raw_u8: BTreeMap<u32, u8>,
|
||||
candidate_availability: BTreeMap<String, u32>,
|
||||
named_locomotive_availability: BTreeMap<String, u32>,
|
||||
locomotive_catalog: Option<Vec<RuntimeLocomotiveCatalogEntry>>,
|
||||
|
|
@ -318,6 +319,7 @@ pub fn project_save_slice_to_runtime_state_import(
|
|||
.world_issue_opinion_base_terms_raw_i32,
|
||||
company_market_state: projection.company_market_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()
|
||||
},
|
||||
};
|
||||
|
|
@ -448,6 +450,11 @@ pub fn project_save_slice_overlay_to_runtime_state_import(
|
|||
.chairman_issue_opinion_terms_raw_i32
|
||||
.clone()
|
||||
},
|
||||
chairman_personality_raw_u8: if projection.has_chairman_projection {
|
||||
projection.chairman_personality_raw_u8
|
||||
} else {
|
||||
base_state.service_state.chairman_personality_raw_u8.clone()
|
||||
},
|
||||
..base_state.service_state.clone()
|
||||
},
|
||||
};
|
||||
|
|
@ -867,6 +874,10 @@ fn project_save_slice_components(
|
|||
.world_finance_neighborhood_state
|
||||
.as_ref()
|
||||
.map(|state| state.dividend_policy_raw_u8),
|
||||
building_density_growth_setting_raw_u32: save_slice
|
||||
.world_finance_neighborhood_state
|
||||
.as_ref()
|
||||
.map(|state| state.building_density_growth_setting_raw_u32),
|
||||
stock_issue_and_buyback_allowed: save_slice
|
||||
.world_finance_neighborhood_state
|
||||
.as_ref()
|
||||
|
|
@ -1227,6 +1238,7 @@ fn project_save_slice_components(
|
|||
has_chairman_selection_override,
|
||||
selected_chairman_profile_id,
|
||||
chairman_issue_opinion_terms_raw_i32,
|
||||
chairman_personality_raw_u8,
|
||||
) = if let Some(table) = &save_slice.chairman_profile_table {
|
||||
metadata.insert(
|
||||
"save_slice.chairman_profile_table_source_kind".to_string(),
|
||||
|
|
@ -1253,6 +1265,7 @@ fn project_save_slice_components(
|
|||
table.selected_chairman_profile_id.is_some(),
|
||||
table.selected_chairman_profile_id,
|
||||
BTreeMap::new(),
|
||||
BTreeMap::new(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
|
|
@ -1279,10 +1292,26 @@ fn project_save_slice_components(
|
|||
.iter()
|
||||
.map(|entry| (entry.profile_id, entry.issue_opinion_terms_raw_i32.clone()))
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
table
|
||||
.entries
|
||||
.iter()
|
||||
.filter_map(|entry| {
|
||||
entry
|
||||
.personality_byte_0x291
|
||||
.map(|value| (entry.profile_id, value))
|
||||
})
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(Vec::new(), false, false, None, BTreeMap::new())
|
||||
(
|
||||
Vec::new(),
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
BTreeMap::new(),
|
||||
BTreeMap::new(),
|
||||
)
|
||||
};
|
||||
|
||||
let named_locomotive_cost = BTreeMap::new();
|
||||
|
|
@ -1374,6 +1403,7 @@ fn project_save_slice_components(
|
|||
has_chairman_selection_override,
|
||||
selected_chairman_profile_id,
|
||||
chairman_issue_opinion_terms_raw_i32,
|
||||
chairman_personality_raw_u8,
|
||||
candidate_availability,
|
||||
named_locomotive_availability,
|
||||
locomotive_catalog,
|
||||
|
|
@ -5326,6 +5356,7 @@ mod tests {
|
|||
holdings_value_total: 700,
|
||||
net_worth_total: 1200,
|
||||
purchasing_power_total: 1500,
|
||||
personality_byte_0x291: Some(12),
|
||||
issue_opinion_terms_raw_i32: Vec::new(),
|
||||
},
|
||||
crate::SmpLoadedChairmanProfileEntry {
|
||||
|
|
@ -5338,6 +5369,7 @@ mod tests {
|
|||
holdings_value_total: 600,
|
||||
net_worth_total: 900,
|
||||
purchasing_power_total: 1100,
|
||||
personality_byte_0x291: Some(20),
|
||||
issue_opinion_terms_raw_i32: Vec::new(),
|
||||
},
|
||||
],
|
||||
|
|
@ -6019,6 +6051,8 @@ mod tests {
|
|||
bankruptcy_policy_raw_hex: "0x00".to_string(),
|
||||
dividend_policy_raw_u8: 1,
|
||||
dividend_policy_raw_hex: "0x01".to_string(),
|
||||
building_density_growth_setting_raw_u32: 1,
|
||||
building_density_growth_setting_raw_hex: "0x00000001".to_string(),
|
||||
labels: vec![
|
||||
"current_calendar_tuple_word".to_string(),
|
||||
"current_calendar_tuple_word_2".to_string(),
|
||||
|
|
@ -13956,6 +13990,7 @@ mod tests {
|
|||
world_issue_opinion_base_terms_raw_i32: Vec::new(),
|
||||
company_market_state: BTreeMap::new(),
|
||||
chairman_issue_opinion_terms_raw_i32: BTreeMap::new(),
|
||||
chairman_personality_raw_u8: BTreeMap::new(),
|
||||
},
|
||||
};
|
||||
let save_slice = SmpLoadedSaveSlice {
|
||||
|
|
|
|||
|
|
@ -53,12 +53,13 @@ pub use runtime::{
|
|||
RuntimeCargoPriceTarget, RuntimeCargoProductionTarget, RuntimeChairmanMetric,
|
||||
RuntimeChairmanProfile, RuntimeChairmanTarget, RuntimeCompany,
|
||||
RuntimeCompanyAnnualCreditorPressureState, RuntimeCompanyAnnualDeepDistressState,
|
||||
RuntimeCompanyAnnualFinanceState, RuntimeCompanyBondSlot, RuntimeCompanyConditionTestScope,
|
||||
RuntimeCompanyControllerKind, RuntimeCompanyMarketMetric, RuntimeCompanyMarketState,
|
||||
RuntimeCompanyMetric, RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector,
|
||||
RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount,
|
||||
RuntimeCondition, RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord,
|
||||
RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
|
||||
RuntimeCompanyAnnualFinanceState, 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,
|
||||
|
|
@ -67,17 +68,18 @@ pub use runtime::{
|
|||
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain,
|
||||
RuntimeWorldFinanceNeighborhoodCandidate, RuntimeWorldIssueState, RuntimeWorldRestoreState,
|
||||
runtime_company_annual_creditor_pressure_state, runtime_company_annual_deep_distress_state,
|
||||
runtime_company_annual_finance_state, 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_annual_finance_state, runtime_company_annual_stock_repurchase_state,
|
||||
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_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_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,
|
||||
|
|
|
|||
|
|
@ -227,6 +227,37 @@ pub struct RuntimeCompanyAnnualDeepDistressState {
|
|||
pub eligible_for_bankruptcy_fallback: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RuntimeCompanyAnnualStockRepurchaseState {
|
||||
pub company_id: u32,
|
||||
#[serde(default)]
|
||||
pub annual_mode_active: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub stock_issue_and_buyback_allowed: Option<bool>,
|
||||
pub city_connection_latch: bool,
|
||||
#[serde(default)]
|
||||
pub building_density_growth_setting: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub linked_chairman_profile_id: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub linked_chairman_personality_raw_u8: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub repurchase_batch_size: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub repurchase_factor_basis_points: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub current_cash: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub stock_value_gate_cash_floor: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub support_adjusted_share_price_scalar: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub affordability_cash_floor: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub unassigned_share_pool: Option<u32>,
|
||||
pub eligible_for_single_batch_repurchase: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
pub struct RuntimeTrackPieceCounts {
|
||||
#[serde(default)]
|
||||
|
|
@ -1044,6 +1075,8 @@ pub struct RuntimeServiceState {
|
|||
pub company_market_state: BTreeMap<u32, RuntimeCompanyMarketState>,
|
||||
#[serde(default)]
|
||||
pub chairman_issue_opinion_terms_raw_i32: BTreeMap<u32, Vec<i32>>,
|
||||
#[serde(default)]
|
||||
pub chairman_personality_raw_u8: BTreeMap<u32, u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
|
|
@ -1159,6 +1192,8 @@ pub struct RuntimeWorldRestoreState {
|
|||
#[serde(default)]
|
||||
pub dividend_policy_raw_u8: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub building_density_growth_setting_raw_u32: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub stock_issue_and_buyback_allowed: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub bond_issue_and_repayment_allowed: Option<bool>,
|
||||
|
|
@ -1953,6 +1988,14 @@ impl RuntimeState {
|
|||
));
|
||||
}
|
||||
}
|
||||
for chairman_profile_id in self.service_state.chairman_personality_raw_u8.keys() {
|
||||
if !seen_chairman_profile_ids.contains(chairman_profile_id) {
|
||||
return Err(format!(
|
||||
"service_state.chairman_personality_raw_u8 references unknown chairman_profile_id {}",
|
||||
chairman_profile_id
|
||||
));
|
||||
}
|
||||
}
|
||||
for (player_id, vars) in &self.player_runtime_variables {
|
||||
if !seen_player_ids.contains(player_id) {
|
||||
return Err(format!(
|
||||
|
|
@ -2804,6 +2847,95 @@ pub fn runtime_world_dividend_adjustment_allowed(state: &RuntimeState) -> Option
|
|||
Some(state.world_restore.dividend_policy_raw_u8? == 0)
|
||||
}
|
||||
|
||||
pub fn runtime_world_building_density_growth_setting(state: &RuntimeState) -> Option<u32> {
|
||||
state.world_restore.building_density_growth_setting_raw_u32
|
||||
}
|
||||
|
||||
fn runtime_chairman_stock_repurchase_factor_f64(
|
||||
state: &RuntimeState,
|
||||
chairman_profile_id: Option<u32>,
|
||||
) -> Option<f64> {
|
||||
let personality_byte = chairman_profile_id
|
||||
.and_then(|profile_id| {
|
||||
state
|
||||
.service_state
|
||||
.chairman_personality_raw_u8
|
||||
.get(&profile_id)
|
||||
})
|
||||
.copied();
|
||||
let mut factor = personality_byte
|
||||
.map(|byte| (f64::from(byte) * 39.0 + 300.0) / 400.0)
|
||||
.unwrap_or(1.0);
|
||||
if runtime_world_building_density_growth_setting(state) == Some(1) {
|
||||
factor *= 1.6;
|
||||
}
|
||||
Some(factor)
|
||||
}
|
||||
|
||||
pub fn runtime_company_annual_stock_repurchase_state(
|
||||
state: &RuntimeState,
|
||||
company_id: u32,
|
||||
) -> Option<RuntimeCompanyAnnualStockRepurchaseState> {
|
||||
let annual_finance_state = runtime_company_annual_finance_state(state, company_id)?;
|
||||
let company = state
|
||||
.companies
|
||||
.iter()
|
||||
.find(|company| company.company_id == company_id)?;
|
||||
let current_cash = runtime_company_control_transfer_stat_value_f64(
|
||||
state,
|
||||
company_id,
|
||||
RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
|
||||
)
|
||||
.and_then(runtime_round_f64_to_i64);
|
||||
let support_adjusted_share_price_scalar =
|
||||
runtime_company_support_adjusted_share_price_scalar_f64(state, company_id);
|
||||
let repurchase_factor =
|
||||
runtime_chairman_stock_repurchase_factor_f64(state, company.linked_chairman_profile_id)?;
|
||||
let repurchase_factor_basis_points = runtime_round_f64_to_i64(repurchase_factor * 100.0);
|
||||
let stock_value_gate_cash_floor = runtime_round_f64_to_i64(repurchase_factor * 800_000.0);
|
||||
let affordability_cash_floor = support_adjusted_share_price_scalar
|
||||
.and_then(|value| runtime_round_f64_to_i64(value * repurchase_factor * 1_000.0 * 1.2));
|
||||
let support_adjusted_share_price_scalar =
|
||||
support_adjusted_share_price_scalar.and_then(runtime_round_f64_to_i64);
|
||||
let unassigned_share_pool = runtime_company_unassigned_share_pool(state, company_id);
|
||||
let eligible_for_single_batch_repurchase = runtime_world_annual_finance_mode_active(state)
|
||||
== Some(true)
|
||||
&& runtime_world_stock_issue_and_buyback_allowed(state) == Some(true)
|
||||
&& annual_finance_state.city_connection_latch
|
||||
&& current_cash
|
||||
.zip(stock_value_gate_cash_floor)
|
||||
.is_some_and(|(value, floor)| value >= floor)
|
||||
&& current_cash
|
||||
.zip(affordability_cash_floor)
|
||||
.is_some_and(|(value, floor)| value >= floor)
|
||||
&& unassigned_share_pool.is_some_and(|value| value >= 1_000);
|
||||
Some(RuntimeCompanyAnnualStockRepurchaseState {
|
||||
company_id,
|
||||
annual_mode_active: runtime_world_annual_finance_mode_active(state),
|
||||
stock_issue_and_buyback_allowed: runtime_world_stock_issue_and_buyback_allowed(state),
|
||||
city_connection_latch: annual_finance_state.city_connection_latch,
|
||||
building_density_growth_setting: runtime_world_building_density_growth_setting(state),
|
||||
linked_chairman_profile_id: company.linked_chairman_profile_id,
|
||||
linked_chairman_personality_raw_u8: company
|
||||
.linked_chairman_profile_id
|
||||
.and_then(|profile_id| {
|
||||
state
|
||||
.service_state
|
||||
.chairman_personality_raw_u8
|
||||
.get(&profile_id)
|
||||
})
|
||||
.copied(),
|
||||
repurchase_batch_size: Some(1_000),
|
||||
repurchase_factor_basis_points,
|
||||
current_cash,
|
||||
stock_value_gate_cash_floor,
|
||||
support_adjusted_share_price_scalar,
|
||||
affordability_cash_floor,
|
||||
unassigned_share_pool,
|
||||
eligible_for_single_batch_repurchase,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn runtime_company_annual_creditor_pressure_state(
|
||||
state: &RuntimeState,
|
||||
company_id: u32,
|
||||
|
|
@ -3767,6 +3899,7 @@ mod tests {
|
|||
bond_issue_and_repayment_policy_raw_u8: None,
|
||||
bankruptcy_policy_raw_u8: None,
|
||||
dividend_policy_raw_u8: None,
|
||||
building_density_growth_setting_raw_u32: None,
|
||||
stock_issue_and_buyback_allowed: None,
|
||||
bond_issue_and_repayment_allowed: None,
|
||||
bankruptcy_allowed: None,
|
||||
|
|
@ -7113,6 +7246,132 @@ mod tests {
|
|||
assert!(pressure_state.eligible_for_bankruptcy_fallback);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derives_annual_stock_repurchase_state_from_rehosted_owner_state() {
|
||||
let mut year_stat_family_qword_bits = vec![
|
||||
0u64;
|
||||
((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||
as usize
|
||||
];
|
||||
let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
|
||||
let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
|
||||
bits[index] = value.to_bits();
|
||||
};
|
||||
write_current_value(&mut year_stat_family_qword_bits, 0x0d, 1_600_000.0);
|
||||
|
||||
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 {
|
||||
partial_year_progress_raw_u8: Some(0x0c),
|
||||
stock_issue_and_buyback_policy_raw_u8: Some(0),
|
||||
stock_issue_and_buyback_allowed: Some(true),
|
||||
building_density_growth_setting_raw_u32: Some(1),
|
||||
..RuntimeWorldRestoreState::default()
|
||||
},
|
||||
metadata: BTreeMap::new(),
|
||||
companies: vec![RuntimeCompany {
|
||||
company_id: 12,
|
||||
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: Some(3),
|
||||
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: None,
|
||||
players: Vec::new(),
|
||||
selected_player_id: None,
|
||||
chairman_profiles: vec![RuntimeChairmanProfile {
|
||||
profile_id: 3,
|
||||
name: "Jay".to_string(),
|
||||
active: true,
|
||||
current_cash: 200,
|
||||
linked_company_id: Some(12),
|
||||
company_holdings: BTreeMap::from([(12, 14_500)]),
|
||||
holdings_value_total: 0,
|
||||
net_worth_total: 0,
|
||||
purchasing_power_total: 0,
|
||||
}],
|
||||
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([(
|
||||
12,
|
||||
RuntimeCompanyMarketState {
|
||||
outstanding_shares: 20_000,
|
||||
cached_share_price_raw_u32: 20.0f32.to_bits(),
|
||||
founding_year: 1835,
|
||||
city_connection_latch: true,
|
||||
year_stat_family_qword_bits,
|
||||
..RuntimeCompanyMarketState::default()
|
||||
},
|
||||
)]),
|
||||
chairman_personality_raw_u8: BTreeMap::from([(3, 20)]),
|
||||
..RuntimeServiceState::default()
|
||||
},
|
||||
};
|
||||
|
||||
let repurchase_state = runtime_company_annual_stock_repurchase_state(&state, 12)
|
||||
.expect("stock repurchase state");
|
||||
assert_eq!(repurchase_state.building_density_growth_setting, Some(1));
|
||||
assert_eq!(
|
||||
repurchase_state.linked_chairman_personality_raw_u8,
|
||||
Some(20)
|
||||
);
|
||||
assert_eq!(repurchase_state.repurchase_batch_size, Some(1_000));
|
||||
assert_eq!(repurchase_state.repurchase_factor_basis_points, Some(432));
|
||||
assert_eq!(repurchase_state.current_cash, Some(1_600_000));
|
||||
assert_eq!(
|
||||
repurchase_state.stock_value_gate_cash_floor,
|
||||
Some(3_456_000)
|
||||
);
|
||||
assert_eq!(
|
||||
repurchase_state.support_adjusted_share_price_scalar,
|
||||
Some(20)
|
||||
);
|
||||
assert_eq!(repurchase_state.affordability_cash_floor, Some(103_680));
|
||||
assert_eq!(repurchase_state.unassigned_share_pool, Some(5_500));
|
||||
assert!(!repurchase_state.eligible_for_single_batch_repurchase);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reads_company_market_metrics_from_annual_finance_reader() {
|
||||
let current_issue_calendar_word = 0x0101_0726;
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ const RT3_SAVE_WORLD_BLOCK_STOCK_POLICY_RELATIVE_OFFSET: usize = 0x4a83;
|
|||
const RT3_SAVE_WORLD_BLOCK_BOND_POLICY_RELATIVE_OFFSET: usize = 0x4a87;
|
||||
const RT3_SAVE_WORLD_BLOCK_BANKRUPTCY_POLICY_RELATIVE_OFFSET: usize = 0x4a8b;
|
||||
const RT3_SAVE_WORLD_BLOCK_DIVIDEND_POLICY_RELATIVE_OFFSET: usize = 0x4a8f;
|
||||
const RT3_SAVE_WORLD_BLOCK_BUILDING_DENSITY_GROWTH_RELATIVE_OFFSET: usize = 0x4c78;
|
||||
const RT3_SAVE_WORLD_BLOCK_ECONOMIC_TUNING_MIRROR_RELATIVE_OFFSET: usize = 0x0bda;
|
||||
const RT3_SAVE_WORLD_BLOCK_ECONOMIC_TUNING_PRIMARY_RELATIVE_OFFSETS: [usize; 6] =
|
||||
[0x0bde, 0x0be2, 0x0be6, 0x0bea, 0x0bee, 0x0bf2];
|
||||
|
|
@ -1584,6 +1585,7 @@ pub struct SmpSaveWorldFinanceNeighborhoodProbe {
|
|||
pub bankruptcy_policy_raw_hex: String,
|
||||
pub dividend_policy_raw_u8: u8,
|
||||
pub dividend_policy_raw_hex: String,
|
||||
pub building_density_growth_setting_lane: SmpSaveDwordCandidate,
|
||||
pub dword_candidates: Vec<SmpSaveDwordCandidate>,
|
||||
pub evidence: Vec<String>,
|
||||
}
|
||||
|
|
@ -2276,6 +2278,8 @@ pub struct SmpLoadedWorldFinanceNeighborhoodState {
|
|||
pub bankruptcy_policy_raw_hex: String,
|
||||
pub dividend_policy_raw_u8: u8,
|
||||
pub dividend_policy_raw_hex: String,
|
||||
pub building_density_growth_setting_raw_u32: u32,
|
||||
pub building_density_growth_setting_raw_hex: String,
|
||||
pub labels: Vec<String>,
|
||||
pub relative_offsets: Vec<usize>,
|
||||
pub relative_offset_hex: Vec<String>,
|
||||
|
|
@ -2345,6 +2349,8 @@ pub struct SmpLoadedChairmanProfileEntry {
|
|||
#[serde(default)]
|
||||
pub purchasing_power_total: i64,
|
||||
#[serde(default)]
|
||||
pub personality_byte_0x291: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub issue_opinion_terms_raw_i32: Vec<i32>,
|
||||
}
|
||||
|
||||
|
|
@ -2457,6 +2463,8 @@ pub struct SmpSaveChairmanRecordAnalysisEntry {
|
|||
pub derived_net_worth_share_price_total: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub derived_cached_purchasing_power_total: Option<i64>,
|
||||
pub personality_byte_0x291: u8,
|
||||
pub personality_byte_0x291_hex: String,
|
||||
#[serde(default)]
|
||||
pub cached_scalar_candidates: Vec<SmpSaveScalarCandidate>,
|
||||
}
|
||||
|
|
@ -3255,6 +3263,10 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
|
|||
&bytes,
|
||||
record_offset + SAVE_CHAIRMAN_RECORD_LINKED_COMPANY_OFFSET,
|
||||
)?;
|
||||
let personality_byte_0x291 = read_u8_at(
|
||||
&bytes,
|
||||
record_offset + SAVE_CHAIRMAN_RECORD_PERSONALITY_BYTE_0X291_OFFSET,
|
||||
)?;
|
||||
let mut holdings_by_company = BTreeMap::new();
|
||||
for company_id in 1..=company_id_bound {
|
||||
let slot_offset = record_offset
|
||||
|
|
@ -3293,6 +3305,8 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
|
|||
derived_holdings_share_price_total,
|
||||
derived_net_worth_share_price_total,
|
||||
derived_cached_purchasing_power_total,
|
||||
personality_byte_0x291,
|
||||
personality_byte_0x291_hex: format!("0x{personality_byte_0x291:02x}"),
|
||||
cached_scalar_candidates,
|
||||
});
|
||||
}
|
||||
|
|
@ -3528,6 +3542,11 @@ fn derive_loaded_world_finance_neighborhood_state_from_probe(
|
|||
bankruptcy_policy_raw_hex: probe.bankruptcy_policy_raw_hex.clone(),
|
||||
dividend_policy_raw_u8: probe.dividend_policy_raw_u8,
|
||||
dividend_policy_raw_hex: probe.dividend_policy_raw_hex.clone(),
|
||||
building_density_growth_setting_raw_u32: probe.building_density_growth_setting_lane.raw_u32,
|
||||
building_density_growth_setting_raw_hex: probe
|
||||
.building_density_growth_setting_lane
|
||||
.raw_u32_hex
|
||||
.clone(),
|
||||
labels: probe
|
||||
.dword_candidates
|
||||
.iter()
|
||||
|
|
@ -3688,6 +3707,7 @@ const SAVE_CHAIRMAN_RECORD_HOLDINGS_BASE_OFFSET: usize = 0x15d;
|
|||
const SAVE_CHAIRMAN_RECORD_LINKED_COMPANY_OFFSET: usize = 0x1dd;
|
||||
const SAVE_CHAIRMAN_RECORD_CACHE_0_OFFSET: usize = 0x1e9;
|
||||
const SAVE_CHAIRMAN_RECORD_CACHE_1_OFFSET: usize = 0x1f1;
|
||||
const SAVE_CHAIRMAN_RECORD_PERSONALITY_BYTE_0X291_OFFSET: usize = 0x291;
|
||||
const SAVE_CHAIRMAN_RECORD_ISSUE_OPINION_TERMS_OFFSET: usize = 0x35b;
|
||||
const SAVE_CHAIRMAN_RECORD_ISSUE_OPINION_TERM_COUNT: usize =
|
||||
RT3_SAVE_WORLD_BLOCK_ISSUE_OPINION_TERM_COUNT;
|
||||
|
|
@ -4364,6 +4384,10 @@ fn parse_save_chairman_profile_table_probe(
|
|||
bytes,
|
||||
record_offset + SAVE_CHAIRMAN_RECORD_LINKED_COMPANY_OFFSET,
|
||||
)?;
|
||||
let personality_byte_0x291 = read_u8_at(
|
||||
bytes,
|
||||
record_offset + SAVE_CHAIRMAN_RECORD_PERSONALITY_BYTE_0X291_OFFSET,
|
||||
)?;
|
||||
let cache_0 = round_f64_to_i64(read_f64_at(
|
||||
bytes,
|
||||
record_offset + SAVE_CHAIRMAN_RECORD_CACHE_0_OFFSET,
|
||||
|
|
@ -4409,6 +4433,7 @@ fn parse_save_chairman_profile_table_probe(
|
|||
holdings_value_total,
|
||||
net_worth_total,
|
||||
purchasing_power_total,
|
||||
personality_byte_0x291: Some(personality_byte_0x291),
|
||||
issue_opinion_terms_raw_i32,
|
||||
});
|
||||
}
|
||||
|
|
@ -9144,6 +9169,12 @@ fn parse_save_world_finance_neighborhood_probe(
|
|||
bytes,
|
||||
payload_offset + RT3_SAVE_WORLD_BLOCK_DIVIDEND_POLICY_RELATIVE_OFFSET,
|
||||
)?;
|
||||
let building_density_growth_setting_lane = build_save_dword_candidate(
|
||||
bytes,
|
||||
payload_offset,
|
||||
"building_density_growth_setting",
|
||||
RT3_SAVE_WORLD_BLOCK_BUILDING_DENSITY_GROWTH_RELATIVE_OFFSET,
|
||||
)?;
|
||||
let dword_candidates =
|
||||
build_save_world_finance_neighborhood_candidates(bytes, payload_offset)?;
|
||||
|
||||
|
|
@ -9171,6 +9202,7 @@ fn parse_save_world_finance_neighborhood_probe(
|
|||
bankruptcy_policy_raw_hex: format!("0x{bankruptcy_policy_raw_u8:02x}"),
|
||||
dividend_policy_raw_u8,
|
||||
dividend_policy_raw_hex: format!("0x{dividend_policy_raw_u8:02x}"),
|
||||
building_density_growth_setting_lane,
|
||||
dword_candidates,
|
||||
evidence: vec![
|
||||
format!(
|
||||
|
|
@ -9192,6 +9224,10 @@ fn parse_save_world_finance_neighborhood_probe(
|
|||
RT3_SAVE_WORLD_BLOCK_BANKRUPTCY_POLICY_RELATIVE_OFFSET,
|
||||
RT3_SAVE_WORLD_BLOCK_DIVIDEND_POLICY_RELATIVE_OFFSET
|
||||
),
|
||||
format!(
|
||||
"payload +0x{:x} carries the fixed-world building-density growth setting mirrored from `[world+0x4c7c]`, which the annual repurchase and dividend policy helpers both read directly",
|
||||
RT3_SAVE_WORLD_BLOCK_BUILDING_DENSITY_GROWTH_RELATIVE_OFFSET
|
||||
),
|
||||
"finance-neighborhood candidates cover the fixed dword strip around the grounded world calendar tuple, absolute-counter, selection-context, and issue-0x37 lanes so broader finance reader closure can build on one rehosted owner surface.".to_string(),
|
||||
],
|
||||
});
|
||||
|
|
@ -15931,6 +15967,9 @@ mod tests {
|
|||
bytes[payload_offset + RT3_SAVE_WORLD_BLOCK_BOND_POLICY_RELATIVE_OFFSET] = 2;
|
||||
bytes[payload_offset + RT3_SAVE_WORLD_BLOCK_BANKRUPTCY_POLICY_RELATIVE_OFFSET] = 3;
|
||||
bytes[payload_offset + RT3_SAVE_WORLD_BLOCK_DIVIDEND_POLICY_RELATIVE_OFFSET] = 4;
|
||||
bytes[payload_offset + RT3_SAVE_WORLD_BLOCK_BUILDING_DENSITY_GROWTH_RELATIVE_OFFSET
|
||||
..payload_offset + RT3_SAVE_WORLD_BLOCK_BUILDING_DENSITY_GROWTH_RELATIVE_OFFSET + 4]
|
||||
.copy_from_slice(&2u32.to_le_bytes());
|
||||
let next_chunk_offset = payload_offset + RT3_SAVE_WORLD_BLOCK_LEN;
|
||||
bytes[next_chunk_offset..next_chunk_offset + 4]
|
||||
.copy_from_slice(&RT3_SAVE_WORLD_BLOCK_NEXT_CHUNK_TAG.to_le_bytes());
|
||||
|
|
@ -15968,6 +16007,13 @@ mod tests {
|
|||
assert_eq!(probe.bankruptcy_policy_raw_hex, "0x03");
|
||||
assert_eq!(probe.dividend_policy_raw_u8, 4);
|
||||
assert_eq!(probe.dividend_policy_raw_hex, "0x04");
|
||||
assert_eq!(probe.building_density_growth_setting_lane.raw_u32, 2);
|
||||
assert_eq!(
|
||||
probe
|
||||
.building_density_growth_setting_lane
|
||||
.relative_offset_hex,
|
||||
"0x4c78"
|
||||
);
|
||||
assert_eq!(probe.current_calendar_tuple_word_lane.value_i32, 1);
|
||||
assert_eq!(
|
||||
probe.current_calendar_tuple_word_2_lane.relative_offset_hex,
|
||||
|
|
@ -16773,6 +16819,8 @@ mod tests {
|
|||
bytes[record_offset + SAVE_CHAIRMAN_RECORD_LINKED_COMPANY_OFFSET
|
||||
..record_offset + SAVE_CHAIRMAN_RECORD_LINKED_COMPANY_OFFSET + 4]
|
||||
.copy_from_slice(&linked.to_le_bytes());
|
||||
bytes[record_offset + SAVE_CHAIRMAN_RECORD_PERSONALITY_BYTE_0X291_OFFSET] =
|
||||
(index as u8) + 10;
|
||||
bytes[record_offset + SAVE_CHAIRMAN_RECORD_CACHE_0_OFFSET
|
||||
..record_offset + SAVE_CHAIRMAN_RECORD_CACHE_0_OFFSET + 8]
|
||||
.copy_from_slice(&cache0.to_le_bytes());
|
||||
|
|
@ -16857,10 +16905,12 @@ mod tests {
|
|||
assert_eq!(table.entries[0].current_cash, -107644);
|
||||
assert_eq!(table.entries[0].holdings_value_total, 252508);
|
||||
assert_eq!(table.entries[0].purchasing_power_total, 144864);
|
||||
assert_eq!(table.entries[0].personality_byte_0x291, Some(10));
|
||||
assert_eq!(table.entries[1].profile_id, 2);
|
||||
assert_eq!(table.entries[1].company_holdings.get(&2), Some(&9000));
|
||||
assert_eq!(table.entries[1].holdings_value_total, 822000);
|
||||
assert_eq!(table.entries[1].purchasing_power_total, 1_009_282);
|
||||
assert_eq!(table.entries[1].personality_byte_0x291, Some(11));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::{
|
||||
CalendarPoint, RuntimeState, runtime_company_annual_creditor_pressure_state,
|
||||
runtime_company_annual_deep_distress_state, runtime_company_annual_finance_state,
|
||||
runtime_company_unassigned_share_pool,
|
||||
runtime_company_annual_stock_repurchase_state, runtime_company_unassigned_share_pool,
|
||||
};
|
||||
|
||||
fn raw_u32_to_f32_text(raw: u32) -> String {
|
||||
|
|
@ -55,6 +55,7 @@ pub struct RuntimeSummary {
|
|||
pub world_restore_bond_issue_and_repayment_policy_raw_u8: Option<u8>,
|
||||
pub world_restore_bankruptcy_policy_raw_u8: Option<u8>,
|
||||
pub world_restore_dividend_policy_raw_u8: Option<u8>,
|
||||
pub world_restore_building_density_growth_setting_raw_u32: Option<u32>,
|
||||
pub world_restore_stock_issue_and_buyback_allowed: Option<bool>,
|
||||
pub world_restore_bond_issue_and_repayment_allowed: Option<bool>,
|
||||
pub world_restore_bankruptcy_allowed: Option<bool>,
|
||||
|
|
@ -111,6 +112,18 @@ pub struct RuntimeSummary {
|
|||
pub selected_company_deep_distress_cash_floor: Option<i64>,
|
||||
pub selected_company_deep_distress_net_profit_floor: Option<i64>,
|
||||
pub selected_company_deep_distress_eligible_for_bankruptcy_fallback: Option<bool>,
|
||||
pub selected_company_stock_repurchase_city_connection_latch: Option<bool>,
|
||||
pub selected_company_stock_repurchase_building_density_growth_setting: Option<u32>,
|
||||
pub selected_company_stock_repurchase_linked_chairman_profile_id: Option<u32>,
|
||||
pub selected_company_stock_repurchase_linked_chairman_personality_raw_u8: Option<u8>,
|
||||
pub selected_company_stock_repurchase_batch_size: Option<u32>,
|
||||
pub selected_company_stock_repurchase_factor_basis_points: Option<i64>,
|
||||
pub selected_company_stock_repurchase_current_cash: Option<i64>,
|
||||
pub selected_company_stock_repurchase_stock_value_gate_cash_floor: Option<i64>,
|
||||
pub selected_company_stock_repurchase_support_adjusted_share_price_scalar: Option<i64>,
|
||||
pub selected_company_stock_repurchase_affordability_cash_floor: Option<i64>,
|
||||
pub selected_company_stock_repurchase_unassigned_share_pool: Option<u32>,
|
||||
pub selected_company_stock_repurchase_eligible_for_single_batch: Option<bool>,
|
||||
pub player_count: usize,
|
||||
pub chairman_profile_count: usize,
|
||||
pub active_chairman_profile_count: usize,
|
||||
|
|
@ -207,6 +220,10 @@ impl RuntimeSummary {
|
|||
let selected_company_deep_distress_state = state
|
||||
.selected_company_id
|
||||
.and_then(|company_id| runtime_company_annual_deep_distress_state(state, company_id));
|
||||
let selected_company_stock_repurchase_state =
|
||||
state.selected_company_id.and_then(|company_id| {
|
||||
runtime_company_annual_stock_repurchase_state(state, company_id)
|
||||
});
|
||||
Self {
|
||||
calendar: state.calendar,
|
||||
calendar_projection_source: state.metadata.get("save_slice.calendar_source").cloned(),
|
||||
|
|
@ -294,6 +311,9 @@ impl RuntimeSummary {
|
|||
.bond_issue_and_repayment_policy_raw_u8,
|
||||
world_restore_bankruptcy_policy_raw_u8: state.world_restore.bankruptcy_policy_raw_u8,
|
||||
world_restore_dividend_policy_raw_u8: state.world_restore.dividend_policy_raw_u8,
|
||||
world_restore_building_density_growth_setting_raw_u32: state
|
||||
.world_restore
|
||||
.building_density_growth_setting_raw_u32,
|
||||
world_restore_stock_issue_and_buyback_allowed: state
|
||||
.world_restore
|
||||
.stock_issue_and_buyback_allowed,
|
||||
|
|
@ -480,6 +500,56 @@ impl RuntimeSummary {
|
|||
selected_company_deep_distress_state
|
||||
.as_ref()
|
||||
.map(|pressure_state| pressure_state.eligible_for_bankruptcy_fallback),
|
||||
selected_company_stock_repurchase_city_connection_latch:
|
||||
selected_company_stock_repurchase_state
|
||||
.as_ref()
|
||||
.map(|repurchase_state| repurchase_state.city_connection_latch),
|
||||
selected_company_stock_repurchase_building_density_growth_setting:
|
||||
selected_company_stock_repurchase_state
|
||||
.as_ref()
|
||||
.and_then(|repurchase_state| repurchase_state.building_density_growth_setting),
|
||||
selected_company_stock_repurchase_linked_chairman_profile_id:
|
||||
selected_company_stock_repurchase_state
|
||||
.as_ref()
|
||||
.and_then(|repurchase_state| repurchase_state.linked_chairman_profile_id),
|
||||
selected_company_stock_repurchase_linked_chairman_personality_raw_u8:
|
||||
selected_company_stock_repurchase_state
|
||||
.as_ref()
|
||||
.and_then(|repurchase_state| {
|
||||
repurchase_state.linked_chairman_personality_raw_u8
|
||||
}),
|
||||
selected_company_stock_repurchase_batch_size: selected_company_stock_repurchase_state
|
||||
.as_ref()
|
||||
.and_then(|repurchase_state| repurchase_state.repurchase_batch_size),
|
||||
selected_company_stock_repurchase_factor_basis_points:
|
||||
selected_company_stock_repurchase_state
|
||||
.as_ref()
|
||||
.and_then(|repurchase_state| repurchase_state.repurchase_factor_basis_points),
|
||||
selected_company_stock_repurchase_current_cash: selected_company_stock_repurchase_state
|
||||
.as_ref()
|
||||
.and_then(|repurchase_state| repurchase_state.current_cash),
|
||||
selected_company_stock_repurchase_stock_value_gate_cash_floor:
|
||||
selected_company_stock_repurchase_state
|
||||
.as_ref()
|
||||
.and_then(|repurchase_state| repurchase_state.stock_value_gate_cash_floor),
|
||||
selected_company_stock_repurchase_support_adjusted_share_price_scalar:
|
||||
selected_company_stock_repurchase_state
|
||||
.as_ref()
|
||||
.and_then(|repurchase_state| {
|
||||
repurchase_state.support_adjusted_share_price_scalar
|
||||
}),
|
||||
selected_company_stock_repurchase_affordability_cash_floor:
|
||||
selected_company_stock_repurchase_state
|
||||
.as_ref()
|
||||
.and_then(|repurchase_state| repurchase_state.affordability_cash_floor),
|
||||
selected_company_stock_repurchase_unassigned_share_pool:
|
||||
selected_company_stock_repurchase_state
|
||||
.as_ref()
|
||||
.and_then(|repurchase_state| repurchase_state.unassigned_share_pool),
|
||||
selected_company_stock_repurchase_eligible_for_single_batch:
|
||||
selected_company_stock_repurchase_state
|
||||
.as_ref()
|
||||
.map(|repurchase_state| repurchase_state.eligible_for_single_batch_repurchase),
|
||||
player_count: state.players.len(),
|
||||
chairman_profile_count: state.chairman_profiles.len(),
|
||||
active_chairman_profile_count: state
|
||||
|
|
@ -2590,4 +2660,155 @@ mod tests {
|
|||
Some(true)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn summarizes_selected_company_stock_repurchase_state() {
|
||||
let mut year_stat_family_qword_bits = vec![
|
||||
0u64;
|
||||
((crate::RUNTIME_COMPANY_STAT_SLOT_COUNT + 2)
|
||||
* crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||
as usize
|
||||
];
|
||||
let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
|
||||
let index = (slot_id * crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
|
||||
bits[index] = value.to_bits();
|
||||
};
|
||||
write_current_value(&mut year_stat_family_qword_bits, 0x0d, 1_600_000.0);
|
||||
|
||||
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 {
|
||||
partial_year_progress_raw_u8: Some(0x0c),
|
||||
stock_issue_and_buyback_policy_raw_u8: Some(0),
|
||||
stock_issue_and_buyback_allowed: Some(true),
|
||||
building_density_growth_setting_raw_u32: Some(1),
|
||||
..RuntimeWorldRestoreState::default()
|
||||
},
|
||||
metadata: BTreeMap::new(),
|
||||
companies: vec![RuntimeCompany {
|
||||
company_id: 12,
|
||||
current_cash: 0,
|
||||
debt: 0,
|
||||
credit_rating_score: None,
|
||||
prime_rate: None,
|
||||
active: true,
|
||||
available_track_laying_capacity: None,
|
||||
controller_kind: crate::RuntimeCompanyControllerKind::Unknown,
|
||||
linked_chairman_profile_id: Some(3),
|
||||
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(12),
|
||||
players: Vec::new(),
|
||||
selected_player_id: None,
|
||||
chairman_profiles: vec![crate::RuntimeChairmanProfile {
|
||||
profile_id: 3,
|
||||
name: "Jay".to_string(),
|
||||
active: true,
|
||||
current_cash: 200,
|
||||
linked_company_id: Some(12),
|
||||
company_holdings: BTreeMap::from([(12, 14_500)]),
|
||||
holdings_value_total: 0,
|
||||
net_worth_total: 0,
|
||||
purchasing_power_total: 0,
|
||||
}],
|
||||
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: Vec::new().into_iter().collect(),
|
||||
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([(
|
||||
12,
|
||||
crate::RuntimeCompanyMarketState {
|
||||
outstanding_shares: 20_000,
|
||||
cached_share_price_raw_u32: 20.0f32.to_bits(),
|
||||
founding_year: 1835,
|
||||
city_connection_latch: true,
|
||||
year_stat_family_qword_bits,
|
||||
..crate::RuntimeCompanyMarketState::default()
|
||||
},
|
||||
)]),
|
||||
chairman_personality_raw_u8: BTreeMap::from([(3, 20)]),
|
||||
..RuntimeServiceState::default()
|
||||
},
|
||||
};
|
||||
|
||||
let summary = RuntimeSummary::from_state(&state);
|
||||
assert_eq!(
|
||||
summary.world_restore_building_density_growth_setting_raw_u32,
|
||||
Some(1)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_stock_repurchase_building_density_growth_setting,
|
||||
Some(1)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_stock_repurchase_linked_chairman_personality_raw_u8,
|
||||
Some(20)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_stock_repurchase_batch_size,
|
||||
Some(1_000)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_stock_repurchase_factor_basis_points,
|
||||
Some(432)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_stock_repurchase_current_cash,
|
||||
Some(1_600_000)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_stock_repurchase_stock_value_gate_cash_floor,
|
||||
Some(3_456_000)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_stock_repurchase_support_adjusted_share_price_scalar,
|
||||
Some(20)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_stock_repurchase_affordability_cash_floor,
|
||||
Some(103_680)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_stock_repurchase_unassigned_share_pool,
|
||||
Some(5_500)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_stock_repurchase_eligible_for_single_batch,
|
||||
Some(false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,9 @@ The highest-value next passes are now:
|
|||
bankruptcy, and dividend finance-policy bytes, and the first annual creditor-pressure branch now
|
||||
executes as a pure runtime reader over that owner state instead of remaining atlas-only; the
|
||||
later deep-distress bankruptcy fallback now runs on that same save-native cash and trailing-
|
||||
net-profit surface too
|
||||
net-profit surface too; the same owner seam now also carries the fixed-world building-density
|
||||
growth setting plus the linked chairman personality byte, which is enough to run the annual
|
||||
stock-repurchase gate headlessly as another pure reader
|
||||
- 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
|
||||
|
|
|
|||
|
|
@ -231,6 +231,9 @@ bankruptcy branch now runs as a pure runtime reader over owned annual-finance st
|
|||
adjusted share price, and those policy bytes rather than staying in atlas prose only. The later
|
||||
deep-distress bankruptcy fallback now rides the same owner-state seam too, using the save-native
|
||||
cash reader plus the first three trailing net-profit years instead of a parallel raw-offset guess.
|
||||
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.
|
||||
|
||||
## Why This Boundary
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue