Rehost company unassigned share pool
This commit is contained in:
parent
8c4e7009c0
commit
c41a6b0e92
7 changed files with 189 additions and 13 deletions
|
|
@ -60,7 +60,10 @@ extend one shared reader family instead of hard-coding more direct field accesse
|
|||
stat-band windows are now widened to 16 dwords per root in save-slice/runtime state so later
|
||||
year-series finance closure has enough owned raw state to attach to. The matching world-side issue
|
||||
reader seam is now also rehosted for the grounded `0x37` investor-confidence lane on top of the
|
||||
save-native world-restore state. A checked-in
|
||||
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
|
||||
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,6 +92,8 @@ pub struct ExpectedRuntimeSummary {
|
|||
#[serde(default)]
|
||||
pub selected_company_outstanding_shares: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_company_unassigned_share_pool: Option<u32>,
|
||||
#[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>,
|
||||
|
|
@ -591,6 +593,14 @@ impl ExpectedRuntimeSummary {
|
|||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = self.selected_company_unassigned_share_pool {
|
||||
if actual.selected_company_unassigned_share_pool != Some(value) {
|
||||
mismatches.push(format!(
|
||||
"selected_company_unassigned_share_pool mismatch: expected {value}, got {:?}",
|
||||
actual.selected_company_unassigned_share_pool
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = &self.selected_company_cached_share_price_value_f32_text {
|
||||
if actual
|
||||
.selected_company_cached_share_price_value_f32_text
|
||||
|
|
|
|||
|
|
@ -61,7 +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_world_issue_state,
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -1838,6 +1838,20 @@ pub fn runtime_world_issue_state(state: &RuntimeState, issue_id: u32) -> Option<
|
|||
}
|
||||
}
|
||||
|
||||
pub fn runtime_company_unassigned_share_pool(state: &RuntimeState, company_id: u32) -> Option<u32> {
|
||||
let outstanding_shares = state
|
||||
.service_state
|
||||
.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>();
|
||||
Some(outstanding_shares.saturating_sub(assigned_shares))
|
||||
}
|
||||
|
||||
fn rounded_cached_share_price_i64(raw_u32: u32) -> Option<i64> {
|
||||
let value = f32::from_bits(raw_u32);
|
||||
if !value.is_finite() {
|
||||
|
|
@ -3889,4 +3903,102 @@ mod tests {
|
|||
assert_eq!(issue.multiplier_value_f32_text, "0.060000");
|
||||
assert_eq!(runtime_world_issue_state(&state, 0x39), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derives_company_unassigned_share_pool_from_market_state_and_holdings() {
|
||||
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,
|
||||
..RuntimeCompanyMarketState::default()
|
||||
},
|
||||
)]),
|
||||
..RuntimeServiceState::default()
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(runtime_company_unassigned_share_pool(&state, 4), Some(4_500));
|
||||
assert_eq!(runtime_company_unassigned_share_pool(&state, 99), None);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{CalendarPoint, RuntimeState};
|
||||
use crate::{runtime_company_unassigned_share_pool, CalendarPoint, RuntimeState};
|
||||
|
||||
fn raw_u32_to_f32_text(raw: u32) -> String {
|
||||
format!("{:.6}", f32::from_bits(raw))
|
||||
|
|
@ -47,6 +47,7 @@ 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_unassigned_share_pool: Option<u32>,
|
||||
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,
|
||||
|
|
@ -246,6 +247,9 @@ 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_unassigned_share_pool: state
|
||||
.selected_company_id
|
||||
.and_then(|company_id| runtime_company_unassigned_share_pool(state, company_id)),
|
||||
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
|
||||
|
|
@ -865,8 +869,18 @@ mod tests {
|
|||
selected_company_id: None,
|
||||
players: Vec::new(),
|
||||
selected_player_id: None,
|
||||
chairman_profiles: Vec::new(),
|
||||
selected_chairman_profile_id: None,
|
||||
chairman_profiles: vec![crate::RuntimeChairmanProfile {
|
||||
profile_id: 1,
|
||||
name: "Chairman One".to_string(),
|
||||
active: true,
|
||||
current_cash: 0,
|
||||
linked_company_id: Some(1),
|
||||
company_holdings: std::collections::BTreeMap::from([(1, 5_000)]),
|
||||
holdings_value_total: 0,
|
||||
net_worth_total: 0,
|
||||
purchasing_power_total: 0,
|
||||
}],
|
||||
selected_chairman_profile_id: Some(1),
|
||||
trains: Vec::new(),
|
||||
locomotive_catalog: vec![
|
||||
crate::RuntimeLocomotiveCatalogEntry {
|
||||
|
|
@ -1116,8 +1130,18 @@ mod tests {
|
|||
selected_company_id: None,
|
||||
players: Vec::new(),
|
||||
selected_player_id: None,
|
||||
chairman_profiles: Vec::new(),
|
||||
selected_chairman_profile_id: None,
|
||||
chairman_profiles: vec![crate::RuntimeChairmanProfile {
|
||||
profile_id: 1,
|
||||
name: "Chairman One".to_string(),
|
||||
active: true,
|
||||
current_cash: 0,
|
||||
linked_company_id: Some(1),
|
||||
company_holdings: std::collections::BTreeMap::from([(1, 5_000)]),
|
||||
holdings_value_total: 0,
|
||||
net_worth_total: 0,
|
||||
purchasing_power_total: 0,
|
||||
}],
|
||||
selected_chairman_profile_id: Some(1),
|
||||
trains: Vec::new(),
|
||||
locomotive_catalog: Vec::new(),
|
||||
cargo_catalog: Vec::new(),
|
||||
|
|
@ -1227,8 +1251,18 @@ mod tests {
|
|||
selected_company_id: None,
|
||||
players: Vec::new(),
|
||||
selected_player_id: None,
|
||||
chairman_profiles: Vec::new(),
|
||||
selected_chairman_profile_id: None,
|
||||
chairman_profiles: vec![crate::RuntimeChairmanProfile {
|
||||
profile_id: 1,
|
||||
name: "Chairman One".to_string(),
|
||||
active: true,
|
||||
current_cash: 0,
|
||||
linked_company_id: Some(1),
|
||||
company_holdings: std::collections::BTreeMap::from([(1, 5_000)]),
|
||||
holdings_value_total: 0,
|
||||
net_worth_total: 0,
|
||||
purchasing_power_total: 0,
|
||||
}],
|
||||
selected_chairman_profile_id: Some(1),
|
||||
trains: Vec::new(),
|
||||
locomotive_catalog: vec![
|
||||
crate::RuntimeLocomotiveCatalogEntry {
|
||||
|
|
@ -1855,8 +1889,18 @@ mod tests {
|
|||
selected_company_id: Some(1),
|
||||
players: Vec::new(),
|
||||
selected_player_id: None,
|
||||
chairman_profiles: Vec::new(),
|
||||
selected_chairman_profile_id: None,
|
||||
chairman_profiles: vec![crate::RuntimeChairmanProfile {
|
||||
profile_id: 1,
|
||||
name: "Chairman One".to_string(),
|
||||
active: true,
|
||||
current_cash: 0,
|
||||
linked_company_id: Some(1),
|
||||
company_holdings: std::collections::BTreeMap::from([(1, 5_000)]),
|
||||
holdings_value_total: 0,
|
||||
net_worth_total: 0,
|
||||
purchasing_power_total: 0,
|
||||
}],
|
||||
selected_chairman_profile_id: Some(1),
|
||||
trains: Vec::new(),
|
||||
locomotive_catalog: Vec::new(),
|
||||
cargo_catalog: Vec::new(),
|
||||
|
|
@ -1953,6 +1997,7 @@ 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_unassigned_share_pool, Some(15_000));
|
||||
assert_eq!(
|
||||
summary.selected_company_cached_share_price_value_f32_text,
|
||||
Some("40.000000".to_string())
|
||||
|
|
|
|||
|
|
@ -118,7 +118,9 @@ The highest-value next passes are now:
|
|||
owned runtime data instead of one more guessed save offset; the first runtime-side `0x2329`
|
||||
stat-family reader seam is now also rehosted for slots `0x0d` and `0x1d`, and the saved
|
||||
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
|
||||
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
|
||||
- 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
|
||||
|
|
|
|||
|
|
@ -203,7 +203,10 @@ the first grounded stat-band root windows at `[company+0x0cfb]`, `[company+0x0d7
|
|||
for slots `0x0d` and `0x1d`, so later finance readers can target saved owner state and one shared
|
||||
reader family directly. Those stat-band windows now carry 16 dwords per root in the save-slice and
|
||||
runtime-owned company market state, and the matching world-side issue reader seam is now rehosted
|
||||
for the grounded `0x37` lane over save-native world restore state.
|
||||
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.
|
||||
|
||||
## Why This Boundary
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue