Rehost annual creditor pressure policy branch
This commit is contained in:
parent
e553275b2e
commit
64fc4a8488
8 changed files with 739 additions and 17 deletions
|
|
@ -89,7 +89,10 @@ bond table now also contributes both the largest live bond principal and the cho
|
|||
highest-coupon live bond principal into owned company market and annual-finance state, so the
|
||||
stock-capital approval ladder can extend one rehosted owner-state surface instead of hunting
|
||||
another isolated finance leaf. A checked-in
|
||||
The working rule on the remaining frontier is explicit now too: when a lane is still ambiguous, we
|
||||
fixed-world finance-policy seam now also carries the raw stock, bond, bankruptcy, and dividend
|
||||
policy bytes from the `0x32c8` save block, and the first annual creditor-pressure branch now runs
|
||||
headlessly as a pure runtime reader over owned annual-finance state, support-adjusted share price,
|
||||
and current world finance policy rather than as a notes-only atlas fragment. 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
|
||||
`EventEffects` export now exists too in
|
||||
|
|
|
|||
|
|
@ -851,6 +851,38 @@ fn project_save_slice_components(
|
|||
.world_issue_37_state
|
||||
.as_ref()
|
||||
.map(|state| state.multiplier_value_f32_text.clone()),
|
||||
stock_issue_and_buyback_policy_raw_u8: save_slice
|
||||
.world_finance_neighborhood_state
|
||||
.as_ref()
|
||||
.map(|state| state.stock_policy_raw_u8),
|
||||
bond_issue_and_repayment_policy_raw_u8: save_slice
|
||||
.world_finance_neighborhood_state
|
||||
.as_ref()
|
||||
.map(|state| state.bond_policy_raw_u8),
|
||||
bankruptcy_policy_raw_u8: save_slice
|
||||
.world_finance_neighborhood_state
|
||||
.as_ref()
|
||||
.map(|state| state.bankruptcy_policy_raw_u8),
|
||||
dividend_policy_raw_u8: save_slice
|
||||
.world_finance_neighborhood_state
|
||||
.as_ref()
|
||||
.map(|state| state.dividend_policy_raw_u8),
|
||||
stock_issue_and_buyback_allowed: save_slice
|
||||
.world_finance_neighborhood_state
|
||||
.as_ref()
|
||||
.map(|state| state.stock_policy_raw_u8 == 0),
|
||||
bond_issue_and_repayment_allowed: save_slice
|
||||
.world_finance_neighborhood_state
|
||||
.as_ref()
|
||||
.map(|state| state.bond_policy_raw_u8 == 0),
|
||||
bankruptcy_allowed: save_slice
|
||||
.world_finance_neighborhood_state
|
||||
.as_ref()
|
||||
.map(|state| state.bankruptcy_policy_raw_u8 == 0),
|
||||
dividend_adjustment_allowed: save_slice
|
||||
.world_finance_neighborhood_state
|
||||
.as_ref()
|
||||
.map(|state| state.dividend_policy_raw_u8 == 0),
|
||||
finance_neighborhood_candidates: save_slice
|
||||
.world_finance_neighborhood_state
|
||||
.as_ref()
|
||||
|
|
@ -5979,6 +6011,14 @@ mod tests {
|
|||
absolute_counter_raw_hex: "0x00000003".to_string(),
|
||||
absolute_counter_mirror_raw_u32: 4,
|
||||
absolute_counter_mirror_raw_hex: "0x00000004".to_string(),
|
||||
stock_policy_raw_u8: 0,
|
||||
stock_policy_raw_hex: "0x00".to_string(),
|
||||
bond_policy_raw_u8: 1,
|
||||
bond_policy_raw_hex: "0x01".to_string(),
|
||||
bankruptcy_policy_raw_u8: 0,
|
||||
bankruptcy_policy_raw_hex: "0x00".to_string(),
|
||||
dividend_policy_raw_u8: 1,
|
||||
dividend_policy_raw_hex: "0x01".to_string(),
|
||||
labels: vec![
|
||||
"current_calendar_tuple_word".to_string(),
|
||||
"current_calendar_tuple_word_2".to_string(),
|
||||
|
|
@ -6263,6 +6303,35 @@ mod tests {
|
|||
import.state.world_restore.issue_37_multiplier_raw_u32,
|
||||
Some(0x3d75c28f)
|
||||
);
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.world_restore
|
||||
.stock_issue_and_buyback_policy_raw_u8,
|
||||
Some(0)
|
||||
);
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.world_restore
|
||||
.bond_issue_and_repayment_policy_raw_u8,
|
||||
Some(1)
|
||||
);
|
||||
assert_eq!(import.state.world_restore.bankruptcy_policy_raw_u8, Some(0));
|
||||
assert_eq!(import.state.world_restore.dividend_policy_raw_u8, Some(1));
|
||||
assert_eq!(
|
||||
import.state.world_restore.stock_issue_and_buyback_allowed,
|
||||
Some(true)
|
||||
);
|
||||
assert_eq!(
|
||||
import.state.world_restore.bond_issue_and_repayment_allowed,
|
||||
Some(false)
|
||||
);
|
||||
assert_eq!(import.state.world_restore.bankruptcy_allowed, Some(true));
|
||||
assert_eq!(
|
||||
import.state.world_restore.dividend_adjustment_allowed,
|
||||
Some(false)
|
||||
);
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
|
|
|
|||
|
|
@ -52,12 +52,13 @@ pub use runtime::{
|
|||
RUNTIME_WORLD_ISSUE_PRIME_RATE, RuntimeCargoCatalogEntry, RuntimeCargoClass,
|
||||
RuntimeCargoPriceTarget, RuntimeCargoProductionTarget, RuntimeChairmanMetric,
|
||||
RuntimeChairmanProfile, RuntimeChairmanTarget, RuntimeCompany,
|
||||
RuntimeCompanyAnnualFinanceState, RuntimeCompanyBondSlot, RuntimeCompanyConditionTestScope,
|
||||
RuntimeCompanyControllerKind, RuntimeCompanyMarketMetric, RuntimeCompanyMarketState,
|
||||
RuntimeCompanyMetric, RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector,
|
||||
RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount,
|
||||
RuntimeCondition, RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord,
|
||||
RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
|
||||
RuntimeCompanyAnnualCreditorPressureState, RuntimeCompanyAnnualFinanceState,
|
||||
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,
|
||||
|
|
@ -65,14 +66,18 @@ pub use runtime::{
|
|||
RuntimeServiceState, RuntimeState, RuntimeTerritory, RuntimeTerritoryMetric,
|
||||
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain,
|
||||
RuntimeWorldFinanceNeighborhoodCandidate, RuntimeWorldIssueState, RuntimeWorldRestoreState,
|
||||
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_creditor_pressure_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_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,
|
||||
};
|
||||
pub use smp::{
|
||||
SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION, SmpAlignedRuntimeRuleBandLane,
|
||||
|
|
|
|||
|
|
@ -177,6 +177,37 @@ pub struct RuntimeCompanyAnnualFinanceState {
|
|||
pub linked_transit_latch: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RuntimeCompanyAnnualCreditorPressureState {
|
||||
pub company_id: u32,
|
||||
#[serde(default)]
|
||||
pub annual_mode_active: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub bankruptcy_allowed: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub years_since_last_bankruptcy: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub years_since_founding: Option<u32>,
|
||||
pub recent_bad_net_profit_year_count: u32,
|
||||
#[serde(default)]
|
||||
pub recent_peak_revenue: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub recent_three_year_net_profit_total: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub pressure_ladder_cash_floor: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub current_cash_plus_slot_12_total: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub support_adjusted_share_price_floor: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub support_adjusted_share_price_scalar: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub current_fuel_cost: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub current_fuel_cost_floor: Option<i64>,
|
||||
pub eligible_for_bankruptcy_branch: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
pub struct RuntimeTrackPieceCounts {
|
||||
#[serde(default)]
|
||||
|
|
@ -1101,6 +1132,22 @@ pub struct RuntimeWorldRestoreState {
|
|||
#[serde(default)]
|
||||
pub issue_37_multiplier_value_f32_text: Option<String>,
|
||||
#[serde(default)]
|
||||
pub stock_issue_and_buyback_policy_raw_u8: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub bond_issue_and_repayment_policy_raw_u8: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub bankruptcy_policy_raw_u8: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub dividend_policy_raw_u8: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub stock_issue_and_buyback_allowed: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub bond_issue_and_repayment_allowed: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub bankruptcy_allowed: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub dividend_adjustment_allowed: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub finance_neighborhood_candidates: Vec<RuntimeWorldFinanceNeighborhoodCandidate>,
|
||||
#[serde(default)]
|
||||
pub economic_tuning_mirror_raw_u32: Option<u32>,
|
||||
|
|
@ -2718,6 +2765,135 @@ pub fn runtime_company_average_live_bond_coupon(
|
|||
Some(weighted_coupon_sum / total_principal as f64)
|
||||
}
|
||||
|
||||
pub fn runtime_world_annual_finance_mode_active(state: &RuntimeState) -> Option<bool> {
|
||||
Some(state.world_restore.partial_year_progress_raw_u8? == 0x0c)
|
||||
}
|
||||
|
||||
pub fn runtime_world_bankruptcy_allowed(state: &RuntimeState) -> Option<bool> {
|
||||
Some(state.world_restore.bankruptcy_policy_raw_u8? == 0)
|
||||
}
|
||||
|
||||
pub fn runtime_world_bond_issue_and_repayment_allowed(state: &RuntimeState) -> Option<bool> {
|
||||
Some(state.world_restore.bond_issue_and_repayment_policy_raw_u8? == 0)
|
||||
}
|
||||
|
||||
pub fn runtime_world_stock_issue_and_buyback_allowed(state: &RuntimeState) -> Option<bool> {
|
||||
Some(state.world_restore.stock_issue_and_buyback_policy_raw_u8? == 0)
|
||||
}
|
||||
|
||||
pub fn runtime_world_dividend_adjustment_allowed(state: &RuntimeState) -> Option<bool> {
|
||||
Some(state.world_restore.dividend_policy_raw_u8? == 0)
|
||||
}
|
||||
|
||||
pub fn runtime_company_annual_creditor_pressure_state(
|
||||
state: &RuntimeState,
|
||||
company_id: u32,
|
||||
) -> Option<RuntimeCompanyAnnualCreditorPressureState> {
|
||||
let annual_finance_state = runtime_company_annual_finance_state(state, company_id)?;
|
||||
let current_cash_plus_slot_12_total =
|
||||
runtime_company_control_transfer_stat_value_f64(state, company_id, 0x12)
|
||||
.and_then(runtime_round_f64_to_i64)
|
||||
.and_then(|slot_12| {
|
||||
runtime_company_control_transfer_stat_value_f64(
|
||||
state,
|
||||
company_id,
|
||||
RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
|
||||
)
|
||||
.and_then(runtime_round_f64_to_i64)
|
||||
.map(|current_cash| current_cash + slot_12)
|
||||
});
|
||||
let support_adjusted_share_price_scalar =
|
||||
runtime_company_support_adjusted_share_price_scalar_f64(state, company_id)
|
||||
.and_then(runtime_round_f64_to_i64);
|
||||
let current_fuel_cost = runtime_company_stat_value_f64(
|
||||
state,
|
||||
company_id,
|
||||
RuntimeCompanyStatSelector {
|
||||
family_id: RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
|
||||
slot_id: 0x09,
|
||||
},
|
||||
)
|
||||
.and_then(runtime_round_f64_to_i64);
|
||||
let recent_bad_net_profit_year_count = annual_finance_state
|
||||
.trailing_full_year_net_profits
|
||||
.iter()
|
||||
.take(3)
|
||||
.filter(|value| **value < -10_000)
|
||||
.count() as u32;
|
||||
let recent_peak_revenue = annual_finance_state
|
||||
.trailing_full_year_revenues
|
||||
.iter()
|
||||
.take(3)
|
||||
.copied()
|
||||
.max();
|
||||
let recent_three_year_net_profit_total =
|
||||
if annual_finance_state.trailing_full_year_net_profits.len() >= 3 {
|
||||
Some(
|
||||
annual_finance_state
|
||||
.trailing_full_year_net_profits
|
||||
.iter()
|
||||
.take(3)
|
||||
.sum::<i64>(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let pressure_ladder_cash_floor = recent_peak_revenue.map(|revenue| {
|
||||
if revenue < 120_000 {
|
||||
-600_000
|
||||
} else if revenue < 230_000 {
|
||||
-1_100_000
|
||||
} else if revenue < 340_000 {
|
||||
-1_600_000
|
||||
} else {
|
||||
-2_000_000
|
||||
}
|
||||
});
|
||||
let support_adjusted_share_price_floor = Some(if recent_bad_net_profit_year_count == 3 {
|
||||
20
|
||||
} else {
|
||||
15
|
||||
});
|
||||
let current_fuel_cost_floor = pressure_ladder_cash_floor.map(|floor| floor * 8 / 100);
|
||||
let eligible_for_bankruptcy_branch = runtime_world_annual_finance_mode_active(state)
|
||||
== Some(true)
|
||||
&& runtime_world_bankruptcy_allowed(state) == Some(true)
|
||||
&& annual_finance_state
|
||||
.years_since_last_bankruptcy
|
||||
.is_some_and(|years| years >= 13)
|
||||
&& annual_finance_state
|
||||
.years_since_founding
|
||||
.is_some_and(|years| years >= 4)
|
||||
&& recent_bad_net_profit_year_count >= 2
|
||||
&& current_cash_plus_slot_12_total
|
||||
.zip(pressure_ladder_cash_floor)
|
||||
.is_some_and(|(value, floor)| value <= floor)
|
||||
&& support_adjusted_share_price_scalar
|
||||
.zip(support_adjusted_share_price_floor)
|
||||
.is_some_and(|(value, floor)| value >= floor)
|
||||
&& current_fuel_cost
|
||||
.zip(current_fuel_cost_floor)
|
||||
.is_some_and(|(value, floor)| value <= floor)
|
||||
&& recent_three_year_net_profit_total.is_some_and(|value| value <= -60_000);
|
||||
Some(RuntimeCompanyAnnualCreditorPressureState {
|
||||
company_id,
|
||||
annual_mode_active: runtime_world_annual_finance_mode_active(state),
|
||||
bankruptcy_allowed: runtime_world_bankruptcy_allowed(state),
|
||||
years_since_last_bankruptcy: annual_finance_state.years_since_last_bankruptcy,
|
||||
years_since_founding: annual_finance_state.years_since_founding,
|
||||
recent_bad_net_profit_year_count,
|
||||
recent_peak_revenue,
|
||||
recent_three_year_net_profit_total,
|
||||
pressure_ladder_cash_floor,
|
||||
current_cash_plus_slot_12_total,
|
||||
support_adjusted_share_price_floor,
|
||||
support_adjusted_share_price_scalar,
|
||||
current_fuel_cost,
|
||||
current_fuel_cost_floor,
|
||||
eligible_for_bankruptcy_branch,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn runtime_world_absolute_counter(state: &RuntimeState) -> Option<u32> {
|
||||
state.world_restore.absolute_counter_raw_u32
|
||||
}
|
||||
|
|
@ -3522,6 +3698,14 @@ mod tests {
|
|||
issue_3a_value: None,
|
||||
issue_37_multiplier_raw_u32: None,
|
||||
issue_37_multiplier_value_f32_text: None,
|
||||
stock_issue_and_buyback_policy_raw_u8: None,
|
||||
bond_issue_and_repayment_policy_raw_u8: None,
|
||||
bankruptcy_policy_raw_u8: None,
|
||||
dividend_policy_raw_u8: None,
|
||||
stock_issue_and_buyback_allowed: None,
|
||||
bond_issue_and_repayment_allowed: None,
|
||||
bankruptcy_allowed: None,
|
||||
dividend_adjustment_allowed: None,
|
||||
finance_neighborhood_candidates: Vec::new(),
|
||||
economic_tuning_mirror_raw_u32: None,
|
||||
economic_tuning_mirror_value_f32_text: None,
|
||||
|
|
@ -4859,7 +5043,7 @@ mod tests {
|
|||
fn reads_grounded_company_stat_family_slots_from_runtime_state() {
|
||||
let mut year_stat_family_qword_bits = vec![
|
||||
0u64;
|
||||
(RUNTIME_COMPANY_STAT_SLOT_COUNT * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||
((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||
as usize
|
||||
];
|
||||
year_stat_family_qword_bits[(0x12 * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize] =
|
||||
|
|
@ -5271,7 +5455,7 @@ mod tests {
|
|||
fn reads_year_relative_company_stat_family_from_saved_market_matrix() {
|
||||
let mut year_stat_family_qword_bits = vec![
|
||||
0u64;
|
||||
(RUNTIME_COMPANY_STAT_SLOT_COUNT * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||
((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||
as usize
|
||||
];
|
||||
let write_year_value = |bits: &mut Vec<u64>, slot_id: u32, year_delta: u32, value: f64| {
|
||||
|
|
@ -6626,6 +6810,132 @@ mod tests {
|
|||
assert_eq!(runtime_company_annual_finance_state(&state, 99), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derives_annual_creditor_pressure_from_rehosted_finance_owner_state() {
|
||||
let mut year_stat_family_qword_bits = vec![
|
||||
0u64;
|
||||
(RUNTIME_COMPANY_STAT_SLOT_COUNT * 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();
|
||||
};
|
||||
let write_prior_year_value =
|
||||
|bits: &mut Vec<u64>, slot_id: u32, year_delta: u32, value: f64| {
|
||||
let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN + year_delta) as usize;
|
||||
bits[index] = value.to_bits();
|
||||
};
|
||||
write_current_value(&mut year_stat_family_qword_bits, 0x09, -50_000.0);
|
||||
write_current_value(&mut year_stat_family_qword_bits, 0x0d, -700_000.0);
|
||||
write_current_value(&mut year_stat_family_qword_bits, 0x12, 0.0);
|
||||
write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 1, 100_000.0);
|
||||
write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 2, 90_000.0);
|
||||
write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 3, 80_000.0);
|
||||
write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 1, -115_000.0);
|
||||
write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 2, -110_000.0);
|
||||
write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 3, -110_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 {
|
||||
packed_year_word_raw_u16: Some(1845),
|
||||
partial_year_progress_raw_u8: Some(0x0c),
|
||||
bankruptcy_policy_raw_u8: Some(0),
|
||||
bankruptcy_allowed: Some(true),
|
||||
..RuntimeWorldRestoreState::default()
|
||||
},
|
||||
metadata: BTreeMap::new(),
|
||||
companies: vec![RuntimeCompany {
|
||||
company_id: 7,
|
||||
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: None,
|
||||
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([(
|
||||
7,
|
||||
RuntimeCompanyMarketState {
|
||||
founding_year: 1841,
|
||||
last_bankruptcy_year: 1832,
|
||||
cached_share_price_raw_u32: 25.0f32.to_bits(),
|
||||
year_stat_family_qword_bits,
|
||||
..RuntimeCompanyMarketState::default()
|
||||
},
|
||||
)]),
|
||||
..RuntimeServiceState::default()
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(runtime_world_annual_finance_mode_active(&state), Some(true));
|
||||
assert_eq!(runtime_world_bankruptcy_allowed(&state), Some(true));
|
||||
let pressure_state = runtime_company_annual_creditor_pressure_state(&state, 7)
|
||||
.expect("creditor pressure state");
|
||||
assert_eq!(pressure_state.recent_bad_net_profit_year_count, 3);
|
||||
assert_eq!(pressure_state.recent_peak_revenue, Some(100_000));
|
||||
assert_eq!(
|
||||
pressure_state.recent_three_year_net_profit_total,
|
||||
Some(-65_000)
|
||||
);
|
||||
assert_eq!(pressure_state.pressure_ladder_cash_floor, Some(-600_000));
|
||||
assert_eq!(
|
||||
pressure_state.current_cash_plus_slot_12_total,
|
||||
Some(-700_000)
|
||||
);
|
||||
assert_eq!(pressure_state.support_adjusted_share_price_floor, Some(20));
|
||||
assert_eq!(pressure_state.support_adjusted_share_price_scalar, Some(25));
|
||||
assert_eq!(pressure_state.current_fuel_cost, Some(-50_000));
|
||||
assert_eq!(pressure_state.current_fuel_cost_floor, Some(-48_000));
|
||||
assert!(pressure_state.eligible_for_bankruptcy_branch);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reads_company_market_metrics_from_annual_finance_reader() {
|
||||
let current_issue_calendar_word = 0x0101_0726;
|
||||
|
|
|
|||
|
|
@ -133,6 +133,10 @@ const RT3_SAVE_WORLD_BLOCK_ISSUE_OPINION_TERM_COUNT: usize = 0x3b;
|
|||
const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_SELECTOR_RELATIVE_OFFSET: usize = 0x83;
|
||||
const RT3_SAVE_WORLD_BLOCK_CAMPAIGN_OVERRIDE_FLAG_RELATIVE_OFFSET: usize = 0xc1;
|
||||
const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_RELATIVE_OFFSET: usize = 0x0bbf;
|
||||
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_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];
|
||||
|
|
@ -1572,6 +1576,14 @@ pub struct SmpSaveWorldFinanceNeighborhoodProbe {
|
|||
pub current_calendar_tuple_word_2_lane: SmpSaveDwordCandidate,
|
||||
pub absolute_counter_lane: SmpSaveDwordCandidate,
|
||||
pub absolute_counter_mirror_lane: SmpSaveDwordCandidate,
|
||||
pub stock_policy_raw_u8: u8,
|
||||
pub stock_policy_raw_hex: String,
|
||||
pub bond_policy_raw_u8: u8,
|
||||
pub bond_policy_raw_hex: String,
|
||||
pub bankruptcy_policy_raw_u8: u8,
|
||||
pub bankruptcy_policy_raw_hex: String,
|
||||
pub dividend_policy_raw_u8: u8,
|
||||
pub dividend_policy_raw_hex: String,
|
||||
pub dword_candidates: Vec<SmpSaveDwordCandidate>,
|
||||
pub evidence: Vec<String>,
|
||||
}
|
||||
|
|
@ -2256,6 +2268,14 @@ pub struct SmpLoadedWorldFinanceNeighborhoodState {
|
|||
pub absolute_counter_raw_hex: String,
|
||||
pub absolute_counter_mirror_raw_u32: u32,
|
||||
pub absolute_counter_mirror_raw_hex: String,
|
||||
pub stock_policy_raw_u8: u8,
|
||||
pub stock_policy_raw_hex: String,
|
||||
pub bond_policy_raw_u8: u8,
|
||||
pub bond_policy_raw_hex: String,
|
||||
pub bankruptcy_policy_raw_u8: u8,
|
||||
pub bankruptcy_policy_raw_hex: String,
|
||||
pub dividend_policy_raw_u8: u8,
|
||||
pub dividend_policy_raw_hex: String,
|
||||
pub labels: Vec<String>,
|
||||
pub relative_offsets: Vec<usize>,
|
||||
pub relative_offset_hex: Vec<String>,
|
||||
|
|
@ -3500,6 +3520,14 @@ fn derive_loaded_world_finance_neighborhood_state_from_probe(
|
|||
absolute_counter_raw_hex: probe.absolute_counter_lane.raw_u32_hex.clone(),
|
||||
absolute_counter_mirror_raw_u32: probe.absolute_counter_mirror_lane.raw_u32,
|
||||
absolute_counter_mirror_raw_hex: probe.absolute_counter_mirror_lane.raw_u32_hex.clone(),
|
||||
stock_policy_raw_u8: probe.stock_policy_raw_u8,
|
||||
stock_policy_raw_hex: probe.stock_policy_raw_hex.clone(),
|
||||
bond_policy_raw_u8: probe.bond_policy_raw_u8,
|
||||
bond_policy_raw_hex: probe.bond_policy_raw_hex.clone(),
|
||||
bankruptcy_policy_raw_u8: probe.bankruptcy_policy_raw_u8,
|
||||
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(),
|
||||
labels: probe
|
||||
.dword_candidates
|
||||
.iter()
|
||||
|
|
@ -9100,6 +9128,22 @@ fn parse_save_world_finance_neighborhood_probe(
|
|||
"absolute_calendar_counter_mirror",
|
||||
RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_MIRROR_RELATIVE_OFFSET,
|
||||
)?;
|
||||
let stock_policy_raw_u8 = read_u8_at(
|
||||
bytes,
|
||||
payload_offset + RT3_SAVE_WORLD_BLOCK_STOCK_POLICY_RELATIVE_OFFSET,
|
||||
)?;
|
||||
let bond_policy_raw_u8 = read_u8_at(
|
||||
bytes,
|
||||
payload_offset + RT3_SAVE_WORLD_BLOCK_BOND_POLICY_RELATIVE_OFFSET,
|
||||
)?;
|
||||
let bankruptcy_policy_raw_u8 = read_u8_at(
|
||||
bytes,
|
||||
payload_offset + RT3_SAVE_WORLD_BLOCK_BANKRUPTCY_POLICY_RELATIVE_OFFSET,
|
||||
)?;
|
||||
let dividend_policy_raw_u8 = read_u8_at(
|
||||
bytes,
|
||||
payload_offset + RT3_SAVE_WORLD_BLOCK_DIVIDEND_POLICY_RELATIVE_OFFSET,
|
||||
)?;
|
||||
let dword_candidates =
|
||||
build_save_world_finance_neighborhood_candidates(bytes, payload_offset)?;
|
||||
|
||||
|
|
@ -9119,6 +9163,14 @@ fn parse_save_world_finance_neighborhood_probe(
|
|||
current_calendar_tuple_word_2_lane,
|
||||
absolute_counter_lane,
|
||||
absolute_counter_mirror_lane,
|
||||
stock_policy_raw_u8,
|
||||
stock_policy_raw_hex: format!("0x{stock_policy_raw_u8:02x}"),
|
||||
bond_policy_raw_u8,
|
||||
bond_policy_raw_hex: format!("0x{bond_policy_raw_u8:02x}"),
|
||||
bankruptcy_policy_raw_u8,
|
||||
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}"),
|
||||
dword_candidates,
|
||||
evidence: vec![
|
||||
format!(
|
||||
|
|
@ -9133,6 +9185,13 @@ fn parse_save_world_finance_neighborhood_probe(
|
|||
RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_2_RELATIVE_OFFSET,
|
||||
RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_RELATIVE_OFFSET
|
||||
),
|
||||
format!(
|
||||
"payload +0x{:x}/+0x{:x}/+0x{:x}/+0x{:x} carry the stock, bond, bankruptcy, and dividend finance-policy bytes mirrored from scenario offsets 0x4a87/0x4a8b/0x4a8f/0x4a93",
|
||||
RT3_SAVE_WORLD_BLOCK_STOCK_POLICY_RELATIVE_OFFSET,
|
||||
RT3_SAVE_WORLD_BLOCK_BOND_POLICY_RELATIVE_OFFSET,
|
||||
RT3_SAVE_WORLD_BLOCK_BANKRUPTCY_POLICY_RELATIVE_OFFSET,
|
||||
RT3_SAVE_WORLD_BLOCK_DIVIDEND_POLICY_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(),
|
||||
],
|
||||
});
|
||||
|
|
@ -15868,6 +15927,10 @@ mod tests {
|
|||
bytes[payload_offset + relative_offset..payload_offset + relative_offset + 4]
|
||||
.copy_from_slice(&((index as u32) + 1).to_le_bytes());
|
||||
}
|
||||
bytes[payload_offset + RT3_SAVE_WORLD_BLOCK_STOCK_POLICY_RELATIVE_OFFSET] = 1;
|
||||
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;
|
||||
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());
|
||||
|
|
@ -15897,6 +15960,14 @@ mod tests {
|
|||
assert_eq!(probe.packed_year_word_raw_hex, "0x0001");
|
||||
assert_eq!(probe.partial_year_progress_raw_u8, 0);
|
||||
assert_eq!(probe.partial_year_progress_raw_hex, "0x00");
|
||||
assert_eq!(probe.stock_policy_raw_u8, 1);
|
||||
assert_eq!(probe.stock_policy_raw_hex, "0x01");
|
||||
assert_eq!(probe.bond_policy_raw_u8, 2);
|
||||
assert_eq!(probe.bond_policy_raw_hex, "0x02");
|
||||
assert_eq!(probe.bankruptcy_policy_raw_u8, 3);
|
||||
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.current_calendar_tuple_word_lane.value_i32, 1);
|
||||
assert_eq!(
|
||||
probe.current_calendar_tuple_word_2_lane.relative_offset_hex,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
CalendarPoint, RuntimeState, runtime_company_annual_finance_state,
|
||||
runtime_company_unassigned_share_pool,
|
||||
CalendarPoint, RuntimeState, runtime_company_annual_creditor_pressure_state,
|
||||
runtime_company_annual_finance_state, runtime_company_unassigned_share_pool,
|
||||
};
|
||||
|
||||
fn raw_u32_to_f32_text(raw: u32) -> String {
|
||||
|
|
@ -50,6 +50,14 @@ pub struct RuntimeSummary {
|
|||
pub world_restore_issue_3a_value: Option<u32>,
|
||||
pub world_restore_issue_37_multiplier_raw_u32: Option<u32>,
|
||||
pub world_restore_issue_37_multiplier_value_f32_text: Option<String>,
|
||||
pub world_restore_stock_issue_and_buyback_policy_raw_u8: Option<u8>,
|
||||
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_stock_issue_and_buyback_allowed: Option<bool>,
|
||||
pub world_restore_bond_issue_and_repayment_allowed: Option<bool>,
|
||||
pub world_restore_bankruptcy_allowed: Option<bool>,
|
||||
pub world_restore_dividend_adjustment_allowed: Option<bool>,
|
||||
pub world_restore_finance_neighborhood_count: usize,
|
||||
pub world_restore_finance_neighborhood_labels: Vec<String>,
|
||||
pub world_restore_economic_tuning_mirror_raw_u32: Option<u32>,
|
||||
|
|
@ -87,6 +95,16 @@ pub struct RuntimeSummary {
|
|||
pub selected_company_current_issue_age_absolute_counter_delta: 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>,
|
||||
pub selected_company_creditor_pressure_recent_peak_revenue: Option<i64>,
|
||||
pub selected_company_creditor_pressure_recent_three_year_net_profit_total: Option<i64>,
|
||||
pub selected_company_creditor_pressure_cash_floor: Option<i64>,
|
||||
pub selected_company_creditor_pressure_cash_plus_slot_12_total: Option<i64>,
|
||||
pub selected_company_creditor_pressure_share_price_floor: Option<i64>,
|
||||
pub selected_company_creditor_pressure_share_price_scalar: Option<i64>,
|
||||
pub selected_company_creditor_pressure_current_fuel_cost: Option<i64>,
|
||||
pub selected_company_creditor_pressure_current_fuel_cost_floor: Option<i64>,
|
||||
pub selected_company_creditor_pressure_eligible_for_bankruptcy_branch: Option<bool>,
|
||||
pub player_count: usize,
|
||||
pub chairman_profile_count: usize,
|
||||
pub active_chairman_profile_count: usize,
|
||||
|
|
@ -176,6 +194,10 @@ impl RuntimeSummary {
|
|||
let selected_company_annual_finance_state = state
|
||||
.selected_company_id
|
||||
.and_then(|company_id| runtime_company_annual_finance_state(state, company_id));
|
||||
let selected_company_creditor_pressure_state =
|
||||
state.selected_company_id.and_then(|company_id| {
|
||||
runtime_company_annual_creditor_pressure_state(state, company_id)
|
||||
});
|
||||
Self {
|
||||
calendar: state.calendar,
|
||||
calendar_projection_source: state.metadata.get("save_slice.calendar_source").cloned(),
|
||||
|
|
@ -255,6 +277,24 @@ impl RuntimeSummary {
|
|||
.world_restore
|
||||
.issue_37_multiplier_value_f32_text
|
||||
.clone(),
|
||||
world_restore_stock_issue_and_buyback_policy_raw_u8: state
|
||||
.world_restore
|
||||
.stock_issue_and_buyback_policy_raw_u8,
|
||||
world_restore_bond_issue_and_repayment_policy_raw_u8: state
|
||||
.world_restore
|
||||
.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_stock_issue_and_buyback_allowed: state
|
||||
.world_restore
|
||||
.stock_issue_and_buyback_allowed,
|
||||
world_restore_bond_issue_and_repayment_allowed: state
|
||||
.world_restore
|
||||
.bond_issue_and_repayment_allowed,
|
||||
world_restore_bankruptcy_allowed: state.world_restore.bankruptcy_allowed,
|
||||
world_restore_dividend_adjustment_allowed: state
|
||||
.world_restore
|
||||
.dividend_adjustment_allowed,
|
||||
world_restore_finance_neighborhood_count: state
|
||||
.world_restore
|
||||
.finance_neighborhood_candidates
|
||||
|
|
@ -372,6 +412,45 @@ impl RuntimeSummary {
|
|||
selected_company_chairman_bonus_amount: selected_company_market_state
|
||||
.map(|market_state| market_state.chairman_bonus_amount)
|
||||
.filter(|amount| *amount != 0),
|
||||
selected_company_creditor_pressure_recent_bad_net_profit_year_count:
|
||||
selected_company_creditor_pressure_state
|
||||
.as_ref()
|
||||
.map(|pressure_state| pressure_state.recent_bad_net_profit_year_count),
|
||||
selected_company_creditor_pressure_recent_peak_revenue:
|
||||
selected_company_creditor_pressure_state
|
||||
.as_ref()
|
||||
.and_then(|pressure_state| pressure_state.recent_peak_revenue),
|
||||
selected_company_creditor_pressure_recent_three_year_net_profit_total:
|
||||
selected_company_creditor_pressure_state
|
||||
.as_ref()
|
||||
.and_then(|pressure_state| pressure_state.recent_three_year_net_profit_total),
|
||||
selected_company_creditor_pressure_cash_floor: selected_company_creditor_pressure_state
|
||||
.as_ref()
|
||||
.and_then(|pressure_state| pressure_state.pressure_ladder_cash_floor),
|
||||
selected_company_creditor_pressure_cash_plus_slot_12_total:
|
||||
selected_company_creditor_pressure_state
|
||||
.as_ref()
|
||||
.and_then(|pressure_state| pressure_state.current_cash_plus_slot_12_total),
|
||||
selected_company_creditor_pressure_share_price_floor:
|
||||
selected_company_creditor_pressure_state
|
||||
.as_ref()
|
||||
.and_then(|pressure_state| pressure_state.support_adjusted_share_price_floor),
|
||||
selected_company_creditor_pressure_share_price_scalar:
|
||||
selected_company_creditor_pressure_state
|
||||
.as_ref()
|
||||
.and_then(|pressure_state| pressure_state.support_adjusted_share_price_scalar),
|
||||
selected_company_creditor_pressure_current_fuel_cost:
|
||||
selected_company_creditor_pressure_state
|
||||
.as_ref()
|
||||
.and_then(|pressure_state| pressure_state.current_fuel_cost),
|
||||
selected_company_creditor_pressure_current_fuel_cost_floor:
|
||||
selected_company_creditor_pressure_state
|
||||
.as_ref()
|
||||
.and_then(|pressure_state| pressure_state.current_fuel_cost_floor),
|
||||
selected_company_creditor_pressure_eligible_for_bankruptcy_branch:
|
||||
selected_company_creditor_pressure_state
|
||||
.as_ref()
|
||||
.map(|pressure_state| pressure_state.eligible_for_bankruptcy_branch),
|
||||
player_count: state.players.len(),
|
||||
chairman_profile_count: state.chairman_profiles.len(),
|
||||
active_chairman_profile_count: state
|
||||
|
|
@ -1217,6 +1296,14 @@ mod tests {
|
|||
issue_3a_value: Some(4),
|
||||
issue_37_multiplier_raw_u32: Some(0x3d75c28f),
|
||||
issue_37_multiplier_value_f32_text: Some("0.06".to_string()),
|
||||
stock_issue_and_buyback_policy_raw_u8: Some(0),
|
||||
bond_issue_and_repayment_policy_raw_u8: Some(1),
|
||||
bankruptcy_policy_raw_u8: Some(0),
|
||||
dividend_policy_raw_u8: Some(1),
|
||||
stock_issue_and_buyback_allowed: Some(true),
|
||||
bond_issue_and_repayment_allowed: Some(false),
|
||||
bankruptcy_allowed: Some(true),
|
||||
dividend_adjustment_allowed: Some(false),
|
||||
economic_tuning_mirror_raw_u32: Some(0x3f46dff5),
|
||||
economic_tuning_mirror_value_f32_text: Some("0.7766201".to_string()),
|
||||
economic_tuning_lane_raw_u32: vec![
|
||||
|
|
@ -1301,6 +1388,29 @@ mod tests {
|
|||
summary.world_restore_issue_37_multiplier_raw_u32,
|
||||
Some(0x3d75c28f)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.world_restore_stock_issue_and_buyback_policy_raw_u8,
|
||||
Some(0)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.world_restore_bond_issue_and_repayment_policy_raw_u8,
|
||||
Some(1)
|
||||
);
|
||||
assert_eq!(summary.world_restore_bankruptcy_policy_raw_u8, Some(0));
|
||||
assert_eq!(summary.world_restore_dividend_policy_raw_u8, Some(1));
|
||||
assert_eq!(
|
||||
summary.world_restore_stock_issue_and_buyback_allowed,
|
||||
Some(true)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.world_restore_bond_issue_and_repayment_allowed,
|
||||
Some(false)
|
||||
);
|
||||
assert_eq!(summary.world_restore_bankruptcy_allowed, Some(true));
|
||||
assert_eq!(
|
||||
summary.world_restore_dividend_adjustment_allowed,
|
||||
Some(false)
|
||||
);
|
||||
assert_eq!(
|
||||
summary
|
||||
.world_restore_issue_37_multiplier_value_f32_text
|
||||
|
|
@ -2177,4 +2287,153 @@ mod tests {
|
|||
assert_eq!(summary.selected_company_chairman_bonus_year, Some(1842));
|
||||
assert_eq!(summary.selected_company_chairman_bonus_amount, Some(750));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn summarizes_selected_company_creditor_pressure_branch_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();
|
||||
};
|
||||
let write_prior_year_value =
|
||||
|bits: &mut Vec<u64>, slot_id: u32, year_delta: u32, value: f64| {
|
||||
let index =
|
||||
(slot_id * crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN + year_delta) as usize;
|
||||
bits[index] = value.to_bits();
|
||||
};
|
||||
write_current_value(&mut year_stat_family_qword_bits, 0x09, -50_000.0);
|
||||
write_current_value(&mut year_stat_family_qword_bits, 0x0d, -700_000.0);
|
||||
write_current_value(&mut year_stat_family_qword_bits, 0x12, 0.0);
|
||||
write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 1, 100_000.0);
|
||||
write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 2, 90_000.0);
|
||||
write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 3, 80_000.0);
|
||||
write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 1, -115_000.0);
|
||||
write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 2, -110_000.0);
|
||||
write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 3, -110_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 {
|
||||
packed_year_word_raw_u16: Some(1845),
|
||||
partial_year_progress_raw_u8: Some(0x0c),
|
||||
bankruptcy_policy_raw_u8: Some(0),
|
||||
bankruptcy_allowed: Some(true),
|
||||
..RuntimeWorldRestoreState::default()
|
||||
},
|
||||
metadata: BTreeMap::new(),
|
||||
companies: vec![RuntimeCompany {
|
||||
company_id: 7,
|
||||
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: 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(7),
|
||||
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([(
|
||||
7,
|
||||
crate::RuntimeCompanyMarketState {
|
||||
founding_year: 1841,
|
||||
last_bankruptcy_year: 1832,
|
||||
cached_share_price_raw_u32: 25.0f32.to_bits(),
|
||||
year_stat_family_qword_bits,
|
||||
..crate::RuntimeCompanyMarketState::default()
|
||||
},
|
||||
)]),
|
||||
..RuntimeServiceState::default()
|
||||
},
|
||||
};
|
||||
|
||||
let summary = RuntimeSummary::from_state(&state);
|
||||
assert_eq!(
|
||||
summary.selected_company_creditor_pressure_recent_bad_net_profit_year_count,
|
||||
Some(3)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_creditor_pressure_recent_peak_revenue,
|
||||
Some(100_000)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_creditor_pressure_recent_three_year_net_profit_total,
|
||||
Some(-65_000)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_creditor_pressure_cash_floor,
|
||||
Some(-600_000)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_creditor_pressure_cash_plus_slot_12_total,
|
||||
Some(-700_000)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_creditor_pressure_share_price_floor,
|
||||
Some(20)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_creditor_pressure_share_price_scalar,
|
||||
Some(25)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_creditor_pressure_current_fuel_cost,
|
||||
Some(-50_000)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_creditor_pressure_current_fuel_cost_floor,
|
||||
Some(-48_000)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.selected_company_creditor_pressure_eligible_for_bankruptcy_branch,
|
||||
Some(true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,9 @@ The highest-value next passes are now:
|
|||
and last bankruptcy for later annual finance-policy rehosting; live bond-slot count now travels through that same owned annual-finance
|
||||
state for the stock-capital branch gate, and the grounded bond table now also contributes both
|
||||
the largest live bond principal and the chosen highest-coupon live bond principal into that same
|
||||
owner-state surface
|
||||
owner-state surface; the same fixed-world save block now also carries the raw stock, bond,
|
||||
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 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
|
||||
|
|
|
|||
|
|
@ -225,7 +225,10 @@ owned company market and annual-finance state, matching the stock-capital branch
|
|||
at least two live bonds. The same grounded bond table now also contributes both the largest live
|
||||
bond principal and the chosen highest-coupon live bond principal into owned company market and
|
||||
annual-finance state, so later stock-capital gates can extend a rehosted owner-state seam instead
|
||||
of guessing another finance leaf.
|
||||
of guessing another finance leaf. The same fixed-world save block now also carries the raw stock,
|
||||
bond, bankruptcy, and dividend finance-policy bytes, and the earliest annual creditor-pressure
|
||||
bankruptcy branch now runs as a pure runtime reader over owned annual-finance state, support-
|
||||
adjusted share price, and those policy bytes rather than staying in atlas prose only.
|
||||
|
||||
## Why This Boundary
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue