Rehost company annual finance reader
This commit is contained in:
parent
c41a6b0e92
commit
4599976b17
7 changed files with 249 additions and 17 deletions
|
|
@ -63,7 +63,9 @@ reader seam is now also rehosted for the grounded `0x37` investor-confidence lan
|
|||
save-native world-restore state. The selected-company summary path now also exposes the
|
||||
unassigned share pool derived from outstanding shares minus chairman-held shares, so later
|
||||
dividend / stock-capital logic can extend one owned market reader instead of another ad hoc
|
||||
counter. A checked-in
|
||||
counter. The next bundled annual-finance reader seam is now rehosted on top of that same market
|
||||
state too, deriving assigned shares, public float, and rounded cached share price from one shared
|
||||
company market reader instead of scattering more finance helpers across the runtime. A checked-in
|
||||
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
|
||||
|
|
|
|||
|
|
@ -92,8 +92,12 @@ pub struct ExpectedRuntimeSummary {
|
|||
#[serde(default)]
|
||||
pub selected_company_outstanding_shares: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_company_assigned_share_pool: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_company_unassigned_share_pool: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_company_cached_share_price: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub selected_company_cached_share_price_value_f32_text: Option<String>,
|
||||
#[serde(default)]
|
||||
pub selected_company_mutable_support_scalar_value_f32_text: Option<String>,
|
||||
|
|
@ -593,6 +597,14 @@ impl ExpectedRuntimeSummary {
|
|||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = self.selected_company_assigned_share_pool {
|
||||
if actual.selected_company_assigned_share_pool != Some(value) {
|
||||
mismatches.push(format!(
|
||||
"selected_company_assigned_share_pool mismatch: expected {value}, got {:?}",
|
||||
actual.selected_company_assigned_share_pool
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = self.selected_company_unassigned_share_pool {
|
||||
if actual.selected_company_unassigned_share_pool != Some(value) {
|
||||
mismatches.push(format!(
|
||||
|
|
@ -601,6 +613,14 @@ impl ExpectedRuntimeSummary {
|
|||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = self.selected_company_cached_share_price {
|
||||
if actual.selected_company_cached_share_price != Some(value) {
|
||||
mismatches.push(format!(
|
||||
"selected_company_cached_share_price mismatch: expected {value}, got {:?}",
|
||||
actual.selected_company_cached_share_price
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = &self.selected_company_cached_share_price_value_f32_text {
|
||||
if actual
|
||||
.selected_company_cached_share_price_value_f32_text
|
||||
|
|
|
|||
|
|
@ -46,12 +46,12 @@ pub use pk4::{
|
|||
pub use runtime::{
|
||||
RuntimeCargoCatalogEntry, RuntimeCargoClass, RuntimeCargoPriceTarget,
|
||||
RuntimeCargoProductionTarget, RuntimeChairmanMetric, RuntimeChairmanProfile,
|
||||
RuntimeChairmanTarget, RuntimeCompany, RuntimeCompanyConditionTestScope,
|
||||
RuntimeCompanyControllerKind, RuntimeCompanyMarketState, RuntimeCompanyMetric,
|
||||
RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector, RuntimeCompanyTarget,
|
||||
RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition,
|
||||
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
|
||||
RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
|
||||
RuntimeChairmanTarget, RuntimeCompany, RuntimeCompanyAnnualFinanceState,
|
||||
RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind, RuntimeCompanyMarketState,
|
||||
RuntimeCompanyMetric, RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector,
|
||||
RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount,
|
||||
RuntimeCondition, RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord,
|
||||
RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
|
||||
RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary,
|
||||
RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary,
|
||||
RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer,
|
||||
|
|
@ -61,8 +61,8 @@ pub use runtime::{
|
|||
RuntimeWorldFinanceNeighborhoodCandidate, RuntimeWorldIssueState, RuntimeWorldRestoreState,
|
||||
RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER, RUNTIME_COMPANY_STAT_SLOT_BOOK_VALUE_PER_SHARE,
|
||||
RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH, RUNTIME_WORLD_ISSUE_INVESTOR_CONFIDENCE,
|
||||
runtime_company_stat_value, runtime_company_unassigned_share_pool,
|
||||
runtime_world_issue_state,
|
||||
runtime_company_annual_finance_state, runtime_company_assigned_share_pool,
|
||||
runtime_company_stat_value, runtime_company_unassigned_share_pool, runtime_world_issue_state,
|
||||
};
|
||||
pub use smp::{
|
||||
SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION, SmpAlignedRuntimeRuleBandLane,
|
||||
|
|
|
|||
|
|
@ -92,6 +92,27 @@ pub struct RuntimeCompanyMarketState {
|
|||
pub stat_band_root_1c47_candidates: Vec<RuntimeCompanyStatBandCandidate>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RuntimeCompanyAnnualFinanceState {
|
||||
pub company_id: u32,
|
||||
pub outstanding_shares: u32,
|
||||
pub assigned_share_pool: u32,
|
||||
pub unassigned_share_pool: u32,
|
||||
#[serde(default)]
|
||||
pub cached_share_price: Option<i64>,
|
||||
pub chairman_salary_baseline: u32,
|
||||
pub chairman_salary_current: u32,
|
||||
pub chairman_bonus_year: u32,
|
||||
pub chairman_bonus_amount: i32,
|
||||
pub founding_year: u32,
|
||||
pub last_bankruptcy_year: u32,
|
||||
pub last_dividend_year: u32,
|
||||
pub current_issue_calendar_word: u32,
|
||||
pub prior_issue_calendar_word: u32,
|
||||
pub city_connection_latch: bool,
|
||||
pub linked_transit_latch: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
pub struct RuntimeTrackPieceCounts {
|
||||
#[serde(default)]
|
||||
|
|
@ -1844,14 +1865,51 @@ pub fn runtime_company_unassigned_share_pool(state: &RuntimeState, company_id: u
|
|||
.company_market_state
|
||||
.get(&company_id)?
|
||||
.outstanding_shares;
|
||||
let assigned_shares = state
|
||||
.chairman_profiles
|
||||
.iter()
|
||||
.filter_map(|profile| profile.company_holdings.get(&company_id).copied())
|
||||
.sum::<u32>();
|
||||
let assigned_shares = runtime_company_assigned_share_pool(state, company_id)?;
|
||||
Some(outstanding_shares.saturating_sub(assigned_shares))
|
||||
}
|
||||
|
||||
pub fn runtime_company_assigned_share_pool(state: &RuntimeState, company_id: u32) -> Option<u32> {
|
||||
state
|
||||
.service_state
|
||||
.company_market_state
|
||||
.get(&company_id)?;
|
||||
Some(
|
||||
state
|
||||
.chairman_profiles
|
||||
.iter()
|
||||
.filter_map(|profile| profile.company_holdings.get(&company_id).copied())
|
||||
.sum::<u32>(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn runtime_company_annual_finance_state(
|
||||
state: &RuntimeState,
|
||||
company_id: u32,
|
||||
) -> Option<RuntimeCompanyAnnualFinanceState> {
|
||||
let market_state = state.service_state.company_market_state.get(&company_id)?;
|
||||
let assigned_share_pool = runtime_company_assigned_share_pool(state, company_id)?;
|
||||
let unassigned_share_pool = runtime_company_unassigned_share_pool(state, company_id)?;
|
||||
Some(RuntimeCompanyAnnualFinanceState {
|
||||
company_id,
|
||||
outstanding_shares: market_state.outstanding_shares,
|
||||
assigned_share_pool,
|
||||
unassigned_share_pool,
|
||||
cached_share_price: rounded_cached_share_price_i64(market_state.cached_share_price_raw_u32),
|
||||
chairman_salary_baseline: market_state.chairman_salary_baseline,
|
||||
chairman_salary_current: market_state.chairman_salary_current,
|
||||
chairman_bonus_year: market_state.chairman_bonus_year,
|
||||
chairman_bonus_amount: market_state.chairman_bonus_amount,
|
||||
founding_year: market_state.founding_year,
|
||||
last_bankruptcy_year: market_state.last_bankruptcy_year,
|
||||
last_dividend_year: market_state.last_dividend_year,
|
||||
current_issue_calendar_word: market_state.current_issue_calendar_word,
|
||||
prior_issue_calendar_word: market_state.prior_issue_calendar_word,
|
||||
city_connection_latch: market_state.city_connection_latch,
|
||||
linked_transit_latch: market_state.linked_transit_latch,
|
||||
})
|
||||
}
|
||||
|
||||
fn rounded_cached_share_price_i64(raw_u32: u32) -> Option<i64> {
|
||||
let value = f32::from_bits(raw_u32);
|
||||
if !value.is_finite() {
|
||||
|
|
@ -4001,4 +4059,136 @@ mod tests {
|
|||
assert_eq!(runtime_company_unassigned_share_pool(&state, 4), Some(4_500));
|
||||
assert_eq!(runtime_company_unassigned_share_pool(&state, 99), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derives_company_annual_finance_state_from_owned_runtime_market_state() {
|
||||
let state = RuntimeState {
|
||||
calendar: CalendarPoint {
|
||||
year: 1830,
|
||||
month_slot: 0,
|
||||
phase_slot: 0,
|
||||
tick_slot: 0,
|
||||
},
|
||||
world_flags: BTreeMap::new(),
|
||||
save_profile: RuntimeSaveProfileState::default(),
|
||||
world_restore: RuntimeWorldRestoreState::default(),
|
||||
metadata: BTreeMap::new(),
|
||||
companies: vec![RuntimeCompany {
|
||||
company_id: 4,
|
||||
current_cash: 0,
|
||||
debt: 0,
|
||||
credit_rating_score: None,
|
||||
prime_rate: None,
|
||||
active: true,
|
||||
available_track_laying_capacity: None,
|
||||
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||
linked_chairman_profile_id: None,
|
||||
book_value_per_share: 0,
|
||||
investor_confidence: 0,
|
||||
management_attitude: 0,
|
||||
takeover_cooldown_year: None,
|
||||
merger_cooldown_year: None,
|
||||
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||
}],
|
||||
selected_company_id: None,
|
||||
players: Vec::new(),
|
||||
selected_player_id: None,
|
||||
chairman_profiles: vec![
|
||||
RuntimeChairmanProfile {
|
||||
profile_id: 1,
|
||||
name: "Chairman One".to_string(),
|
||||
active: true,
|
||||
current_cash: 0,
|
||||
linked_company_id: Some(4),
|
||||
company_holdings: BTreeMap::from([(4, 8_000)]),
|
||||
holdings_value_total: 0,
|
||||
net_worth_total: 0,
|
||||
purchasing_power_total: 0,
|
||||
},
|
||||
RuntimeChairmanProfile {
|
||||
profile_id: 2,
|
||||
name: "Chairman Two".to_string(),
|
||||
active: true,
|
||||
current_cash: 0,
|
||||
linked_company_id: None,
|
||||
company_holdings: BTreeMap::from([(4, 7_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([(
|
||||
4,
|
||||
RuntimeCompanyMarketState {
|
||||
outstanding_shares: 20_000,
|
||||
cached_share_price_raw_u32: 0x42200000,
|
||||
chairman_salary_baseline: 24,
|
||||
chairman_salary_current: 30,
|
||||
chairman_bonus_year: 1842,
|
||||
chairman_bonus_amount: 750,
|
||||
founding_year: 1831,
|
||||
last_bankruptcy_year: 0,
|
||||
last_dividend_year: 1841,
|
||||
current_issue_calendar_word: 5,
|
||||
prior_issue_calendar_word: 4,
|
||||
city_connection_latch: true,
|
||||
linked_transit_latch: false,
|
||||
..RuntimeCompanyMarketState::default()
|
||||
},
|
||||
)]),
|
||||
..RuntimeServiceState::default()
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(runtime_company_assigned_share_pool(&state, 4), Some(15_500));
|
||||
assert_eq!(
|
||||
runtime_company_annual_finance_state(&state, 4),
|
||||
Some(RuntimeCompanyAnnualFinanceState {
|
||||
company_id: 4,
|
||||
outstanding_shares: 20_000,
|
||||
assigned_share_pool: 15_500,
|
||||
unassigned_share_pool: 4_500,
|
||||
cached_share_price: Some(40),
|
||||
chairman_salary_baseline: 24,
|
||||
chairman_salary_current: 30,
|
||||
chairman_bonus_year: 1842,
|
||||
chairman_bonus_amount: 750,
|
||||
founding_year: 1831,
|
||||
last_bankruptcy_year: 0,
|
||||
last_dividend_year: 1841,
|
||||
current_issue_calendar_word: 5,
|
||||
prior_issue_calendar_word: 4,
|
||||
city_connection_latch: true,
|
||||
linked_transit_latch: false,
|
||||
})
|
||||
);
|
||||
assert_eq!(runtime_company_assigned_share_pool(&state, 99), None);
|
||||
assert_eq!(runtime_company_annual_finance_state(&state, 99), None);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{runtime_company_unassigned_share_pool, CalendarPoint, RuntimeState};
|
||||
use crate::{
|
||||
runtime_company_annual_finance_state, runtime_company_unassigned_share_pool, CalendarPoint,
|
||||
RuntimeState,
|
||||
};
|
||||
|
||||
fn raw_u32_to_f32_text(raw: u32) -> String {
|
||||
format!("{:.6}", f32::from_bits(raw))
|
||||
|
|
@ -47,7 +50,9 @@ pub struct RuntimeSummary {
|
|||
pub active_company_count: usize,
|
||||
pub company_market_state_owner_count: usize,
|
||||
pub selected_company_outstanding_shares: Option<u32>,
|
||||
pub selected_company_assigned_share_pool: Option<u32>,
|
||||
pub selected_company_unassigned_share_pool: Option<u32>,
|
||||
pub selected_company_cached_share_price: Option<i64>,
|
||||
pub selected_company_cached_share_price_value_f32_text: Option<String>,
|
||||
pub selected_company_mutable_support_scalar_value_f32_text: Option<String>,
|
||||
pub selected_company_stat_band_root_0cfb_count: usize,
|
||||
|
|
@ -142,6 +147,9 @@ impl RuntimeSummary {
|
|||
let selected_company_market_state = state
|
||||
.selected_company_id
|
||||
.and_then(|company_id| state.service_state.company_market_state.get(&company_id));
|
||||
let selected_company_annual_finance_state = state
|
||||
.selected_company_id
|
||||
.and_then(|company_id| runtime_company_annual_finance_state(state, company_id));
|
||||
Self {
|
||||
calendar: state.calendar,
|
||||
calendar_projection_source: state.metadata.get("save_slice.calendar_source").cloned(),
|
||||
|
|
@ -247,9 +255,15 @@ impl RuntimeSummary {
|
|||
company_market_state_owner_count: state.service_state.company_market_state.len(),
|
||||
selected_company_outstanding_shares: selected_company_market_state
|
||||
.map(|market_state| market_state.outstanding_shares),
|
||||
selected_company_assigned_share_pool: selected_company_annual_finance_state
|
||||
.as_ref()
|
||||
.map(|finance_state| finance_state.assigned_share_pool),
|
||||
selected_company_unassigned_share_pool: state
|
||||
.selected_company_id
|
||||
.and_then(|company_id| runtime_company_unassigned_share_pool(state, company_id)),
|
||||
selected_company_cached_share_price: selected_company_annual_finance_state
|
||||
.as_ref()
|
||||
.and_then(|finance_state| finance_state.cached_share_price),
|
||||
selected_company_cached_share_price_value_f32_text: selected_company_market_state
|
||||
.map(|market_state| raw_u32_to_f32_text(market_state.cached_share_price_raw_u32)),
|
||||
selected_company_mutable_support_scalar_value_f32_text: selected_company_market_state
|
||||
|
|
@ -1997,7 +2011,9 @@ mod tests {
|
|||
let summary = RuntimeSummary::from_state(&state);
|
||||
assert_eq!(summary.company_market_state_owner_count, 1);
|
||||
assert_eq!(summary.selected_company_outstanding_shares, Some(20_000));
|
||||
assert_eq!(summary.selected_company_assigned_share_pool, Some(5_000));
|
||||
assert_eq!(summary.selected_company_unassigned_share_pool, Some(15_000));
|
||||
assert_eq!(summary.selected_company_cached_share_price, Some(40));
|
||||
assert_eq!(
|
||||
summary.selected_company_cached_share_price_value_f32_text,
|
||||
Some("40.000000".to_string())
|
||||
|
|
|
|||
|
|
@ -120,7 +120,8 @@ The highest-value next passes are now:
|
|||
stat-band windows themselves now carry 16 dwords per root; the matching world-side issue reader
|
||||
seam is now rehosted for the grounded `0x37` lane, and selected-company summaries now expose the
|
||||
unassigned share pool derived from outstanding shares minus chairman-held shares for later annual
|
||||
finance logic
|
||||
finance logic; that same owned company market state now also backs a bundled annual-finance
|
||||
reader seam for assigned shares, public float, and rounded cached share price
|
||||
- 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
|
||||
|
|
|
|||
|
|
@ -206,7 +206,10 @@ runtime-owned company market state, and the matching world-side issue reader sea
|
|||
for the grounded `0x37` lane over save-native world restore state. The selected-company summary
|
||||
surface now also carries the unassigned share pool derived from outstanding shares minus
|
||||
chairman-held shares, so later dividend / stock-capital work can extend a shared owned-state
|
||||
reader instead of guessing another finance leaf.
|
||||
reader instead of guessing another finance leaf. The same owned company market state now also
|
||||
supports a bundled annual-finance reader seam for assigned shares, public float, and rounded
|
||||
cached share price, which is a better base for later dividend / issue-calendar simulation than
|
||||
scattered single-field helpers.
|
||||
|
||||
## Why This Boundary
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue