Add company market reader seam

This commit is contained in:
Jan Petykiewicz 2026-04-17 21:04:23 -07:00
commit 53f1078a84
5 changed files with 212 additions and 9 deletions

View file

@ -68,7 +68,10 @@ state too, deriving assigned shares, public float, and rounded cached share pric
company market reader instead of scattering more finance helpers across the runtime. A checked-in
The fixed-world finance neighborhood itself is now widened to 16 dwords rooted at `[world+0x11]`,
so future issue-`0x38/0x39` closure can build on a broader owned restore-state window rather than
another narrow one-off probe. A checked-in
another narrow one-off probe. The next company-side seam is now bundled too: a shared company
market reader now exposes outstanding shares, assigned shares, public float, rounded cached share
price, salary lanes, bonus amount, and issue-calendar words from the owned annual-finance state
instead of leaving that logic spread across summary helpers. 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

View file

@ -47,11 +47,12 @@ pub use runtime::{
RuntimeCargoCatalogEntry, RuntimeCargoClass, RuntimeCargoPriceTarget,
RuntimeCargoProductionTarget, RuntimeChairmanMetric, RuntimeChairmanProfile,
RuntimeChairmanTarget, RuntimeCompany, RuntimeCompanyAnnualFinanceState,
RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind, RuntimeCompanyMarketState,
RuntimeCompanyMetric, RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector,
RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount,
RuntimeCondition, RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord,
RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind, RuntimeCompanyMarketMetric,
RuntimeCompanyMarketState, RuntimeCompanyMetric, RuntimeCompanyStatBandCandidate,
RuntimeCompanyStatSelector, RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess,
RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition, RuntimeConditionComparator,
RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary,
RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary,
RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer,
@ -62,7 +63,8 @@ pub use runtime::{
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_annual_finance_state, runtime_company_assigned_share_pool,
runtime_company_stat_value, runtime_company_unassigned_share_pool, runtime_world_issue_state,
runtime_company_market_value, 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,

View file

@ -342,6 +342,20 @@ pub enum RuntimeCompanyMetric {
TrackPiecesNonElectric,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RuntimeCompanyMarketMetric {
OutstandingShares,
AssignedSharePool,
UnassignedSharePool,
CachedSharePrice,
ChairmanSalaryBaseline,
ChairmanSalaryCurrent,
ChairmanBonusAmount,
CurrentIssueCalendarWord,
PriorIssueCalendarWord,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RuntimeChairmanMetric {
@ -1910,6 +1924,41 @@ pub fn runtime_company_annual_finance_state(
})
}
pub fn runtime_company_market_value(
state: &RuntimeState,
company_id: u32,
metric: RuntimeCompanyMarketMetric,
) -> Option<i64> {
let annual_finance_state = runtime_company_annual_finance_state(state, company_id)?;
match metric {
RuntimeCompanyMarketMetric::OutstandingShares => {
Some(annual_finance_state.outstanding_shares as i64)
}
RuntimeCompanyMarketMetric::AssignedSharePool => {
Some(annual_finance_state.assigned_share_pool as i64)
}
RuntimeCompanyMarketMetric::UnassignedSharePool => {
Some(annual_finance_state.unassigned_share_pool as i64)
}
RuntimeCompanyMarketMetric::CachedSharePrice => annual_finance_state.cached_share_price,
RuntimeCompanyMarketMetric::ChairmanSalaryBaseline => {
Some(annual_finance_state.chairman_salary_baseline as i64)
}
RuntimeCompanyMarketMetric::ChairmanSalaryCurrent => {
Some(annual_finance_state.chairman_salary_current as i64)
}
RuntimeCompanyMarketMetric::ChairmanBonusAmount => {
Some(annual_finance_state.chairman_bonus_amount as i64)
}
RuntimeCompanyMarketMetric::CurrentIssueCalendarWord => {
Some(annual_finance_state.current_issue_calendar_word as i64)
}
RuntimeCompanyMarketMetric::PriorIssueCalendarWord => {
Some(annual_finance_state.prior_issue_calendar_word as i64)
}
}
}
fn rounded_cached_share_price_i64(raw_u32: u32) -> Option<i64> {
let value = f32::from_bits(raw_u32);
if !value.is_finite() {
@ -4191,4 +4240,149 @@ mod tests {
assert_eq!(runtime_company_assigned_share_pool(&state, 99), None);
assert_eq!(runtime_company_annual_finance_state(&state, 99), None);
}
#[test]
fn reads_company_market_metrics_from_annual_finance_reader() {
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: 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![RuntimeChairmanProfile {
profile_id: 1,
name: "Chairman One".to_string(),
active: true,
current_cash: 0,
linked_company_id: Some(7),
company_holdings: BTreeMap::from([(7, 12_000)]),
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([(
7,
RuntimeCompanyMarketState {
outstanding_shares: 20_000,
cached_share_price_raw_u32: 0x42200000,
chairman_salary_baseline: 18,
chairman_salary_current: 27,
chairman_bonus_year: 1843,
chairman_bonus_amount: 625,
founding_year: 1832,
last_bankruptcy_year: 0,
last_dividend_year: 1842,
current_issue_calendar_word: 9,
prior_issue_calendar_word: 8,
..RuntimeCompanyMarketState::default()
},
)]),
..RuntimeServiceState::default()
},
};
assert_eq!(
runtime_company_market_value(&state, 7, RuntimeCompanyMarketMetric::OutstandingShares),
Some(20_000)
);
assert_eq!(
runtime_company_market_value(&state, 7, RuntimeCompanyMarketMetric::AssignedSharePool),
Some(12_000)
);
assert_eq!(
runtime_company_market_value(&state, 7, RuntimeCompanyMarketMetric::UnassignedSharePool),
Some(8_000)
);
assert_eq!(
runtime_company_market_value(&state, 7, RuntimeCompanyMarketMetric::CachedSharePrice),
Some(40)
);
assert_eq!(
runtime_company_market_value(
&state,
7,
RuntimeCompanyMarketMetric::CurrentIssueCalendarWord
),
Some(9)
);
assert_eq!(
runtime_company_market_value(
&state,
7,
RuntimeCompanyMarketMetric::PriorIssueCalendarWord
),
Some(8)
);
assert_eq!(
runtime_company_market_value(
&state,
7,
RuntimeCompanyMarketMetric::ChairmanSalaryCurrent
),
Some(27)
);
assert_eq!(
runtime_company_market_value(
&state,
7,
RuntimeCompanyMarketMetric::ChairmanBonusAmount
),
Some(625)
);
assert_eq!(
runtime_company_market_value(&state, 99, RuntimeCompanyMarketMetric::OutstandingShares),
None
);
}
}

View file

@ -123,7 +123,8 @@ The highest-value next passes are now:
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 fixed-world
finance neighborhood is now widened to 16 dwords rooted at `[world+0x11]` so later issue-family
closure can target a broader owned restore-state window
closure can target a broader owned restore-state window; the same annual-finance state now also
feeds a shared company market reader for stock-capital, salary, bonus, and issue-calendar values
- 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

View file

@ -211,7 +211,10 @@ supports a bundled annual-finance reader seam for assigned shares, public float,
cached share price, which is a better base for later dividend / issue-calendar simulation than
scattered single-field helpers. The fixed-world finance neighborhood is now widened to 16 dwords
rooted at `[world+0x11]`, so later issue-`0x38/0x39` closure can build on a broader owned
restore-state window instead of another narrow probe.
restore-state window instead of another narrow probe. The same owned company annual-finance state
now also drives a shared company market reader seam for stock-capital, salary, bonus, and
issue-calendar values, which is a better base for shellless finance simulation than summary-only
helpers.
## Why This Boundary