Refresh chairman wealth from company market state
This commit is contained in:
parent
5198f80cd9
commit
26a7a34ad0
3 changed files with 343 additions and 12 deletions
|
|
@ -315,6 +315,8 @@ pub fn project_save_slice_to_runtime_state_import(
|
||||||
..RuntimeServiceState::default()
|
..RuntimeServiceState::default()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
let mut state = state;
|
||||||
|
state.refresh_derived_market_state();
|
||||||
state.validate()?;
|
state.validate()?;
|
||||||
|
|
||||||
Ok(RuntimeStateImport {
|
Ok(RuntimeStateImport {
|
||||||
|
|
@ -424,6 +426,8 @@ pub fn project_save_slice_overlay_to_runtime_state_import(
|
||||||
..base_state.service_state.clone()
|
..base_state.service_state.clone()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
let mut state = state;
|
||||||
|
state.refresh_derived_market_state();
|
||||||
state.validate()?;
|
state.validate()?;
|
||||||
|
|
||||||
Ok(RuntimeStateImport {
|
Ok(RuntimeStateImport {
|
||||||
|
|
|
||||||
|
|
@ -1716,6 +1716,61 @@ impl RuntimeState {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn refresh_derived_market_state(&mut self) {
|
||||||
|
let company_share_prices = self
|
||||||
|
.service_state
|
||||||
|
.company_market_state
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(company_id, market_state)| {
|
||||||
|
rounded_cached_share_price_i64(market_state.cached_share_price_raw_u32)
|
||||||
|
.map(|share_price| (*company_id, share_price))
|
||||||
|
})
|
||||||
|
.collect::<BTreeMap<_, _>>();
|
||||||
|
|
||||||
|
for profile in &mut self.chairman_profiles {
|
||||||
|
let preserved_threshold_adjusted_holdings_component = profile
|
||||||
|
.purchasing_power_total
|
||||||
|
.saturating_sub(profile.current_cash)
|
||||||
|
.max(0);
|
||||||
|
if let Some(holdings_value_total) = derive_runtime_chairman_holdings_share_price_total(
|
||||||
|
&profile.company_holdings,
|
||||||
|
&company_share_prices,
|
||||||
|
) {
|
||||||
|
profile.holdings_value_total = holdings_value_total;
|
||||||
|
}
|
||||||
|
profile.net_worth_total = profile
|
||||||
|
.current_cash
|
||||||
|
.saturating_add(profile.holdings_value_total);
|
||||||
|
profile.purchasing_power_total = profile
|
||||||
|
.current_cash
|
||||||
|
.saturating_add(preserved_threshold_adjusted_holdings_component)
|
||||||
|
.max(profile.net_worth_total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rounded_cached_share_price_i64(raw_u32: u32) -> Option<i64> {
|
||||||
|
let value = f32::from_bits(raw_u32);
|
||||||
|
if !value.is_finite() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if value < i64::MIN as f32 || value > i64::MAX as f32 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(value.round() as i64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn derive_runtime_chairman_holdings_share_price_total(
|
||||||
|
holdings_by_company: &BTreeMap<u32, u32>,
|
||||||
|
company_share_prices: &BTreeMap<u32, i64>,
|
||||||
|
) -> Option<i64> {
|
||||||
|
let mut total = 0i64;
|
||||||
|
for (company_id, units) in holdings_by_company {
|
||||||
|
let share_price = *company_share_prices.get(company_id)?;
|
||||||
|
total = total.checked_add((*units as i64).checked_mul(share_price)?)?;
|
||||||
|
}
|
||||||
|
Some(total)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_runtime_effect(
|
fn validate_runtime_effect(
|
||||||
|
|
@ -3372,4 +3427,209 @@ mod tests {
|
||||||
|
|
||||||
assert!(state.validate().is_err());
|
assert!(state.validate().is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn refreshes_chairman_totals_from_company_market_state() {
|
||||||
|
let mut 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: 1,
|
||||||
|
current_cash: 0,
|
||||||
|
debt: 0,
|
||||||
|
credit_rating_score: None,
|
||||||
|
prime_rate: None,
|
||||||
|
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||||
|
active: true,
|
||||||
|
available_track_laying_capacity: None,
|
||||||
|
linked_chairman_profile_id: None,
|
||||||
|
book_value_per_share: 0,
|
||||||
|
investor_confidence: 0,
|
||||||
|
management_attitude: 0,
|
||||||
|
takeover_cooldown_year: None,
|
||||||
|
merger_cooldown_year: None,
|
||||||
|
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||||
|
},
|
||||||
|
RuntimeCompany {
|
||||||
|
company_id: 2,
|
||||||
|
current_cash: 0,
|
||||||
|
debt: 0,
|
||||||
|
credit_rating_score: None,
|
||||||
|
prime_rate: None,
|
||||||
|
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||||
|
active: true,
|
||||||
|
available_track_laying_capacity: None,
|
||||||
|
linked_chairman_profile_id: None,
|
||||||
|
book_value_per_share: 0,
|
||||||
|
investor_confidence: 0,
|
||||||
|
management_attitude: 0,
|
||||||
|
takeover_cooldown_year: None,
|
||||||
|
merger_cooldown_year: None,
|
||||||
|
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
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: 100,
|
||||||
|
linked_company_id: Some(1),
|
||||||
|
company_holdings: BTreeMap::from([(1, 2), (2, 3)]),
|
||||||
|
holdings_value_total: 0,
|
||||||
|
net_worth_total: 0,
|
||||||
|
purchasing_power_total: 400,
|
||||||
|
}],
|
||||||
|
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([
|
||||||
|
(
|
||||||
|
1,
|
||||||
|
RuntimeCompanyMarketState {
|
||||||
|
cached_share_price_raw_u32: 0x41200000,
|
||||||
|
..RuntimeCompanyMarketState::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
2,
|
||||||
|
RuntimeCompanyMarketState {
|
||||||
|
cached_share_price_raw_u32: 0x41a00000,
|
||||||
|
..RuntimeCompanyMarketState::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
..RuntimeServiceState::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
state.refresh_derived_market_state();
|
||||||
|
|
||||||
|
assert_eq!(state.chairman_profiles[0].holdings_value_total, 80);
|
||||||
|
assert_eq!(state.chairman_profiles[0].net_worth_total, 180);
|
||||||
|
assert_eq!(state.chairman_profiles[0].purchasing_power_total, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn refreshes_chairman_purchasing_power_when_cash_changes() {
|
||||||
|
let mut 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: 1,
|
||||||
|
current_cash: 0,
|
||||||
|
debt: 0,
|
||||||
|
credit_rating_score: None,
|
||||||
|
prime_rate: None,
|
||||||
|
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||||
|
active: true,
|
||||||
|
available_track_laying_capacity: None,
|
||||||
|
linked_chairman_profile_id: None,
|
||||||
|
book_value_per_share: 0,
|
||||||
|
investor_confidence: 0,
|
||||||
|
management_attitude: 0,
|
||||||
|
takeover_cooldown_year: None,
|
||||||
|
merger_cooldown_year: None,
|
||||||
|
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||||
|
}],
|
||||||
|
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: 50,
|
||||||
|
linked_company_id: Some(1),
|
||||||
|
company_holdings: BTreeMap::from([(1, 2)]),
|
||||||
|
holdings_value_total: 20,
|
||||||
|
net_worth_total: 70,
|
||||||
|
purchasing_power_total: 130,
|
||||||
|
}],
|
||||||
|
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([(
|
||||||
|
1,
|
||||||
|
RuntimeCompanyMarketState {
|
||||||
|
cached_share_price_raw_u32: 0x41200000,
|
||||||
|
..RuntimeCompanyMarketState::default()
|
||||||
|
},
|
||||||
|
)]),
|
||||||
|
..RuntimeServiceState::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
state.chairman_profiles[0].current_cash = 80;
|
||||||
|
|
||||||
|
state.refresh_derived_market_state();
|
||||||
|
|
||||||
|
assert_eq!(state.chairman_profiles[0].holdings_value_total, 20);
|
||||||
|
assert_eq!(state.chairman_profiles[0].net_worth_total, 100);
|
||||||
|
assert_eq!(state.chairman_profiles[0].purchasing_power_total, 130);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ pub fn execute_step_command(
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
state.refresh_derived_market_state();
|
||||||
let final_summary = RuntimeSummary::from_state(state);
|
let final_summary = RuntimeSummary::from_state(state);
|
||||||
|
|
||||||
Ok(StepResult {
|
Ok(StepResult {
|
||||||
|
|
@ -363,7 +364,14 @@ fn apply_runtime_effects(
|
||||||
"missing chairman profile_id {profile_id} while applying cash effect"
|
"missing chairman profile_id {profile_id} while applying cash effect"
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
let preserved_threshold_adjusted_holdings_component = chairman
|
||||||
|
.purchasing_power_total
|
||||||
|
.saturating_sub(chairman.current_cash)
|
||||||
|
.max(0);
|
||||||
chairman.current_cash = *value;
|
chairman.current_cash = *value;
|
||||||
|
chairman.purchasing_power_total = chairman
|
||||||
|
.current_cash
|
||||||
|
.saturating_add(preserved_threshold_adjusted_holdings_component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RuntimeEffect::SetCompanyGovernanceScalar {
|
RuntimeEffect::SetCompanyGovernanceScalar {
|
||||||
|
|
@ -3790,28 +3798,64 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn set_chairman_cash_supports_all_active_target() {
|
fn set_chairman_cash_supports_all_active_target() {
|
||||||
let mut state = RuntimeState {
|
let mut state = RuntimeState {
|
||||||
|
companies: vec![
|
||||||
|
RuntimeCompany {
|
||||||
|
company_id: 1,
|
||||||
|
current_cash: 0,
|
||||||
|
debt: 0,
|
||||||
|
credit_rating_score: None,
|
||||||
|
prime_rate: None,
|
||||||
|
active: true,
|
||||||
|
available_track_laying_capacity: None,
|
||||||
|
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||||
|
linked_chairman_profile_id: Some(1),
|
||||||
|
book_value_per_share: 0,
|
||||||
|
investor_confidence: 0,
|
||||||
|
management_attitude: 0,
|
||||||
|
takeover_cooldown_year: None,
|
||||||
|
merger_cooldown_year: None,
|
||||||
|
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||||
|
},
|
||||||
|
RuntimeCompany {
|
||||||
|
company_id: 2,
|
||||||
|
current_cash: 0,
|
||||||
|
debt: 0,
|
||||||
|
credit_rating_score: None,
|
||||||
|
prime_rate: None,
|
||||||
|
active: true,
|
||||||
|
available_track_laying_capacity: None,
|
||||||
|
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||||
|
linked_chairman_profile_id: Some(2),
|
||||||
|
book_value_per_share: 0,
|
||||||
|
investor_confidence: 0,
|
||||||
|
management_attitude: 0,
|
||||||
|
takeover_cooldown_year: None,
|
||||||
|
merger_cooldown_year: None,
|
||||||
|
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||||
|
},
|
||||||
|
],
|
||||||
chairman_profiles: vec![
|
chairman_profiles: vec![
|
||||||
RuntimeChairmanProfile {
|
RuntimeChairmanProfile {
|
||||||
profile_id: 1,
|
profile_id: 1,
|
||||||
name: "Chairman One".to_string(),
|
name: "Chairman One".to_string(),
|
||||||
active: true,
|
active: true,
|
||||||
current_cash: 10,
|
current_cash: 10,
|
||||||
linked_company_id: None,
|
linked_company_id: Some(1),
|
||||||
company_holdings: BTreeMap::new(),
|
company_holdings: BTreeMap::from([(1, 2)]),
|
||||||
holdings_value_total: 0,
|
holdings_value_total: 20,
|
||||||
net_worth_total: 0,
|
net_worth_total: 30,
|
||||||
purchasing_power_total: 0,
|
purchasing_power_total: 70,
|
||||||
},
|
},
|
||||||
RuntimeChairmanProfile {
|
RuntimeChairmanProfile {
|
||||||
profile_id: 2,
|
profile_id: 2,
|
||||||
name: "Chairman Two".to_string(),
|
name: "Chairman Two".to_string(),
|
||||||
active: true,
|
active: true,
|
||||||
current_cash: 20,
|
current_cash: 20,
|
||||||
linked_company_id: None,
|
linked_company_id: Some(2),
|
||||||
company_holdings: BTreeMap::new(),
|
company_holdings: BTreeMap::from([(2, 3)]),
|
||||||
holdings_value_total: 0,
|
holdings_value_total: 60,
|
||||||
net_worth_total: 0,
|
net_worth_total: 80,
|
||||||
purchasing_power_total: 0,
|
purchasing_power_total: 110,
|
||||||
},
|
},
|
||||||
RuntimeChairmanProfile {
|
RuntimeChairmanProfile {
|
||||||
profile_id: 3,
|
profile_id: 3,
|
||||||
|
|
@ -3821,8 +3865,8 @@ mod tests {
|
||||||
linked_company_id: None,
|
linked_company_id: None,
|
||||||
company_holdings: BTreeMap::new(),
|
company_holdings: BTreeMap::new(),
|
||||||
holdings_value_total: 0,
|
holdings_value_total: 0,
|
||||||
net_worth_total: 0,
|
net_worth_total: 30,
|
||||||
purchasing_power_total: 0,
|
purchasing_power_total: 30,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
event_runtime_records: vec![RuntimeEventRecord {
|
event_runtime_records: vec![RuntimeEventRecord {
|
||||||
|
|
@ -3839,6 +3883,25 @@ mod tests {
|
||||||
value: 77,
|
value: 77,
|
||||||
}],
|
}],
|
||||||
}],
|
}],
|
||||||
|
service_state: RuntimeServiceState {
|
||||||
|
company_market_state: BTreeMap::from([
|
||||||
|
(
|
||||||
|
1,
|
||||||
|
crate::RuntimeCompanyMarketState {
|
||||||
|
cached_share_price_raw_u32: 0x41200000,
|
||||||
|
..crate::RuntimeCompanyMarketState::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
2,
|
||||||
|
crate::RuntimeCompanyMarketState {
|
||||||
|
cached_share_price_raw_u32: 0x41a00000,
|
||||||
|
..crate::RuntimeCompanyMarketState::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
..RuntimeServiceState::default()
|
||||||
|
},
|
||||||
..state()
|
..state()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -3851,6 +3914,10 @@ mod tests {
|
||||||
assert_eq!(state.chairman_profiles[0].current_cash, 77);
|
assert_eq!(state.chairman_profiles[0].current_cash, 77);
|
||||||
assert_eq!(state.chairman_profiles[1].current_cash, 77);
|
assert_eq!(state.chairman_profiles[1].current_cash, 77);
|
||||||
assert_eq!(state.chairman_profiles[2].current_cash, 30);
|
assert_eq!(state.chairman_profiles[2].current_cash, 30);
|
||||||
|
assert_eq!(state.chairman_profiles[0].net_worth_total, 97);
|
||||||
|
assert_eq!(state.chairman_profiles[0].purchasing_power_total, 137);
|
||||||
|
assert_eq!(state.chairman_profiles[1].net_worth_total, 137);
|
||||||
|
assert_eq!(state.chairman_profiles[1].purchasing_power_total, 167);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue