Keep company cash and confiscation owner state in sync
This commit is contained in:
parent
41418b4044
commit
b322bed6ad
1 changed files with 235 additions and 24 deletions
|
|
@ -419,6 +419,44 @@ fn service_set_company_prime_rate_target(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn service_zero_company_current_cash(state: &mut RuntimeState, company_id: u32) -> bool {
|
||||||
|
let Some(current_cash) = crate::runtime::runtime_company_stat_value(
|
||||||
|
state,
|
||||||
|
company_id,
|
||||||
|
crate::RuntimeCompanyStatSelector {
|
||||||
|
family_id: crate::RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
|
||||||
|
slot_id: crate::RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
|
||||||
|
},
|
||||||
|
) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if current_cash == 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
service_post_company_stat_delta(
|
||||||
|
state,
|
||||||
|
company_id,
|
||||||
|
RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
|
||||||
|
-(current_cash as f64),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn service_clear_company_live_bonds(state: &mut RuntimeState, company_id: u32) -> bool {
|
||||||
|
let Some(market_state) = state
|
||||||
|
.service_state
|
||||||
|
.company_market_state
|
||||||
|
.get_mut(&company_id)
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
market_state.live_bond_slots.clear();
|
||||||
|
market_state.bond_count = 0;
|
||||||
|
market_state.largest_live_bond_principal = None;
|
||||||
|
market_state.highest_coupon_live_bond_principal = None;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn service_apply_company_bankruptcy(state: &mut RuntimeState, company_id: u32) -> bool {
|
fn service_apply_company_bankruptcy(state: &mut RuntimeState, company_id: u32) -> bool {
|
||||||
let Some(bankruptcy_year) = state
|
let Some(bankruptcy_year) = state
|
||||||
.world_restore
|
.world_restore
|
||||||
|
|
@ -486,24 +524,7 @@ fn service_apply_company_bankruptcy(state: &mut RuntimeState, company_id: u32) -
|
||||||
company_mutated = true;
|
company_mutated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(current_cash) = crate::runtime::runtime_company_stat_value(
|
company_mutated |= service_zero_company_current_cash(state, company_id);
|
||||||
state,
|
|
||||||
company_id,
|
|
||||||
crate::RuntimeCompanyStatSelector {
|
|
||||||
family_id: crate::RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
|
|
||||||
slot_id: crate::RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
if current_cash != 0 {
|
|
||||||
company_mutated |= service_post_company_stat_delta(
|
|
||||||
state,
|
|
||||||
company_id,
|
|
||||||
RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
|
|
||||||
-(current_cash as f64),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
company_mutated
|
company_mutated
|
||||||
}
|
}
|
||||||
|
|
@ -1533,6 +1554,8 @@ fn apply_runtime_effects(
|
||||||
RuntimeEffect::ConfiscateCompanyAssets { target } => {
|
RuntimeEffect::ConfiscateCompanyAssets { target } => {
|
||||||
let company_ids = resolve_company_target_ids(state, target, condition_context)?;
|
let company_ids = resolve_company_target_ids(state, target, condition_context)?;
|
||||||
for company_id in company_ids.iter().copied() {
|
for company_id in company_ids.iter().copied() {
|
||||||
|
let _ = service_zero_company_current_cash(state, company_id);
|
||||||
|
let _ = service_clear_company_live_bonds(state, company_id);
|
||||||
let company = state
|
let company = state
|
||||||
.companies
|
.companies
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
|
|
@ -1610,17 +1633,35 @@ fn apply_runtime_effects(
|
||||||
RuntimeEffect::AdjustCompanyCash { target, delta } => {
|
RuntimeEffect::AdjustCompanyCash { target, delta } => {
|
||||||
let company_ids = resolve_company_target_ids(state, target, condition_context)?;
|
let company_ids = resolve_company_target_ids(state, target, condition_context)?;
|
||||||
for company_id in company_ids {
|
for company_id in company_ids {
|
||||||
let company = state
|
let prior_cash = state
|
||||||
.companies
|
.companies
|
||||||
.iter_mut()
|
.iter()
|
||||||
.find(|company| company.company_id == company_id)
|
.find(|company| company.company_id == company_id)
|
||||||
|
.map(|company| company.current_cash)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
format!("missing company_id {company_id} while applying cash effect")
|
format!("missing company_id {company_id} while applying cash effect")
|
||||||
})?;
|
})?;
|
||||||
company.current_cash =
|
let next_cash = prior_cash.checked_add(*delta).ok_or_else(|| {
|
||||||
company.current_cash.checked_add(*delta).ok_or_else(|| {
|
format!("company_id {company_id} cash adjustment overflow")
|
||||||
format!("company_id {company_id} cash adjustment overflow")
|
})?;
|
||||||
})?;
|
if !service_post_company_stat_delta(
|
||||||
|
state,
|
||||||
|
company_id,
|
||||||
|
RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
|
||||||
|
*delta as f64,
|
||||||
|
false,
|
||||||
|
) {
|
||||||
|
let company = state
|
||||||
|
.companies
|
||||||
|
.iter_mut()
|
||||||
|
.find(|company| company.company_id == company_id)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
|
"missing company_id {company_id} while applying cash effect"
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
company.current_cash = next_cash;
|
||||||
|
}
|
||||||
mutated_company_ids.insert(company_id);
|
mutated_company_ids.insert(company_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4675,6 +4716,121 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adjust_company_cash_updates_owner_state_backed_current_cash() {
|
||||||
|
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
|
||||||
|
];
|
||||||
|
year_stat_family_qword_bits[(crate::RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH
|
||||||
|
* crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||||
|
as usize] = 100.0f64.to_bits();
|
||||||
|
|
||||||
|
let mut state = RuntimeState {
|
||||||
|
companies: vec![RuntimeCompany {
|
||||||
|
company_id: 1,
|
||||||
|
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||||
|
current_cash: 100,
|
||||||
|
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,
|
||||||
|
}],
|
||||||
|
event_runtime_records: vec![RuntimeEventRecord {
|
||||||
|
record_id: 12,
|
||||||
|
trigger_kind: 7,
|
||||||
|
active: true,
|
||||||
|
service_count: 0,
|
||||||
|
marks_collection_dirty: false,
|
||||||
|
one_shot: false,
|
||||||
|
has_fired: false,
|
||||||
|
conditions: Vec::new(),
|
||||||
|
effects: vec![RuntimeEffect::AdjustCompanyCash {
|
||||||
|
target: RuntimeCompanyTarget::Ids { ids: vec![1] },
|
||||||
|
delta: 25,
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
service_state: RuntimeServiceState {
|
||||||
|
company_market_state: BTreeMap::from([(
|
||||||
|
1,
|
||||||
|
crate::RuntimeCompanyMarketState {
|
||||||
|
year_stat_family_qword_bits,
|
||||||
|
special_stat_family_232a_qword_bits: vec![0u64; 0x20],
|
||||||
|
..crate::RuntimeCompanyMarketState::default()
|
||||||
|
},
|
||||||
|
)]),
|
||||||
|
..RuntimeServiceState::default()
|
||||||
|
},
|
||||||
|
calendar: crate::CalendarPoint {
|
||||||
|
year: 1830,
|
||||||
|
month_slot: 0,
|
||||||
|
phase_slot: 0,
|
||||||
|
tick_slot: 0,
|
||||||
|
},
|
||||||
|
world_flags: BTreeMap::new(),
|
||||||
|
save_profile: crate::RuntimeSaveProfileState::default(),
|
||||||
|
world_restore: crate::RuntimeWorldRestoreState::default(),
|
||||||
|
metadata: BTreeMap::new(),
|
||||||
|
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,
|
||||||
|
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(),
|
||||||
|
};
|
||||||
|
|
||||||
|
execute_step_command(
|
||||||
|
&mut state,
|
||||||
|
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||||
|
)
|
||||||
|
.expect("cash adjustment should apply through owner state");
|
||||||
|
|
||||||
|
assert_eq!(state.companies[0].current_cash, 125);
|
||||||
|
assert_eq!(
|
||||||
|
crate::runtime::runtime_company_stat_value(
|
||||||
|
&state,
|
||||||
|
1,
|
||||||
|
crate::RuntimeCompanyStatSelector {
|
||||||
|
family_id: crate::RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
|
||||||
|
slot_id: crate::RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Some(125)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn applies_named_locomotive_availability_effects() {
|
fn applies_named_locomotive_availability_effects() {
|
||||||
let mut state = RuntimeState {
|
let mut state = RuntimeState {
|
||||||
|
|
@ -6324,6 +6480,16 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn confiscate_company_assets_zeros_company_and_retires_owned_trains() {
|
fn confiscate_company_assets_zeros_company_and_retires_owned_trains() {
|
||||||
|
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
|
||||||
|
];
|
||||||
|
year_stat_family_qword_bits[(crate::RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH
|
||||||
|
* crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||||
|
as usize] = 50.0f64.to_bits();
|
||||||
|
|
||||||
let mut state = RuntimeState {
|
let mut state = RuntimeState {
|
||||||
companies: vec![
|
companies: vec![
|
||||||
RuntimeCompany {
|
RuntimeCompany {
|
||||||
|
|
@ -6393,6 +6559,26 @@ mod tests {
|
||||||
target: RuntimeCompanyTarget::SelectedCompany,
|
target: RuntimeCompanyTarget::SelectedCompany,
|
||||||
}],
|
}],
|
||||||
}],
|
}],
|
||||||
|
service_state: RuntimeServiceState {
|
||||||
|
company_market_state: BTreeMap::from([(
|
||||||
|
1,
|
||||||
|
crate::RuntimeCompanyMarketState {
|
||||||
|
year_stat_family_qword_bits,
|
||||||
|
special_stat_family_232a_qword_bits: vec![0u64; 0x20],
|
||||||
|
live_bond_slots: vec![crate::RuntimeCompanyBondSlot {
|
||||||
|
slot_index: 0,
|
||||||
|
principal: 20_000,
|
||||||
|
maturity_year: 1845,
|
||||||
|
coupon_rate_raw_u32: 0.05f32.to_bits(),
|
||||||
|
}],
|
||||||
|
bond_count: 1,
|
||||||
|
largest_live_bond_principal: Some(20_000),
|
||||||
|
highest_coupon_live_bond_principal: Some(20_000),
|
||||||
|
..crate::RuntimeCompanyMarketState::default()
|
||||||
|
},
|
||||||
|
)]),
|
||||||
|
..crate::RuntimeServiceState::default()
|
||||||
|
},
|
||||||
..state()
|
..state()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -6408,6 +6594,31 @@ mod tests {
|
||||||
assert_eq!(state.selected_company_id, None);
|
assert_eq!(state.selected_company_id, None);
|
||||||
assert!(state.trains[0].retired);
|
assert!(state.trains[0].retired);
|
||||||
assert!(!state.trains[1].retired);
|
assert!(!state.trains[1].retired);
|
||||||
|
assert_eq!(
|
||||||
|
crate::runtime::runtime_company_stat_value(
|
||||||
|
&state,
|
||||||
|
1,
|
||||||
|
crate::RuntimeCompanyStatSelector {
|
||||||
|
family_id: crate::RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
|
||||||
|
slot_id: crate::RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Some(0)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
state.service_state.company_market_state[&1]
|
||||||
|
.live_bond_slots
|
||||||
|
.is_empty()
|
||||||
|
);
|
||||||
|
assert_eq!(state.service_state.company_market_state[&1].bond_count, 0);
|
||||||
|
assert_eq!(
|
||||||
|
state.service_state.company_market_state[&1].largest_live_bond_principal,
|
||||||
|
None
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
state.service_state.company_market_state[&1].highest_coupon_live_bond_principal,
|
||||||
|
None
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue