Broaden chairman governance event coverage

This commit is contained in:
Jan Petykiewicz 2026-04-16 16:36:48 -07:00
commit f8350a48c5
18 changed files with 1939 additions and 13 deletions

View file

@ -405,6 +405,14 @@ fn apply_runtime_effects(
state.selected_chairman_profile_id = None;
}
if let Some(linked_company_id) = linked_company_id {
if let Some(company) = state
.companies
.iter_mut()
.find(|company| company.company_id == linked_company_id)
{
company.linked_chairman_profile_id = None;
mutated_company_ids.insert(linked_company_id);
}
for other in &mut state.chairman_profiles {
if other.profile_id != profile_id
&& other.linked_company_id == Some(linked_company_id)
@ -1222,6 +1230,9 @@ fn company_metric_value(company: &crate::RuntimeCompany, metric: RuntimeCompanyM
RuntimeCompanyMetric::TotalDebt => company.debt as i64,
RuntimeCompanyMetric::CreditRating => company.credit_rating_score.unwrap_or(0),
RuntimeCompanyMetric::PrimeRate => company.prime_rate.unwrap_or(0),
RuntimeCompanyMetric::BookValuePerShare => company.book_value_per_share,
RuntimeCompanyMetric::InvestorConfidence => company.investor_confidence,
RuntimeCompanyMetric::ManagementAttitude => company.management_attitude,
RuntimeCompanyMetric::TrackPiecesTotal => i64::from(company.track_piece_counts.total),
RuntimeCompanyMetric::TrackPiecesSingle => i64::from(company.track_piece_counts.single),
RuntimeCompanyMetric::TrackPiecesDouble => i64::from(company.track_piece_counts.double),
@ -1402,10 +1413,11 @@ mod tests {
use super::*;
use crate::{
CalendarPoint, RuntimeCompany, RuntimeCompanyControllerKind, RuntimeCompanyTarget,
RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate, RuntimePlayer,
RuntimeSaveProfileState, RuntimeServiceState, RuntimeTerritory, RuntimeTerritoryTarget,
RuntimeTrackPieceCounts, RuntimeTrain, RuntimeWorldRestoreState,
CalendarPoint, RuntimeChairmanMetric, RuntimeChairmanProfile, RuntimeChairmanTarget,
RuntimeCompany, RuntimeCompanyControllerKind, RuntimeCompanyTarget, RuntimeCondition,
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
RuntimePlayer, RuntimeSaveProfileState, RuntimeServiceState, RuntimeTerritory,
RuntimeTerritoryTarget, RuntimeTrackPieceCounts, RuntimeTrain, RuntimeWorldRestoreState,
};
fn state() -> RuntimeState {
@ -1430,6 +1442,12 @@ mod tests {
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,
}],
selected_company_id: None,
players: Vec::new(),
@ -1613,6 +1631,12 @@ mod tests {
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,
},
RuntimeCompany {
company_id: 2,
@ -1624,6 +1648,12 @@ mod tests {
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 {
@ -1824,6 +1854,12 @@ mod tests {
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,
},
RuntimeCompany {
company_id: 2,
@ -1835,6 +1871,12 @@ mod tests {
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,
},
],
selected_company_id: Some(1),
@ -1970,6 +2012,12 @@ mod tests {
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,
},
RuntimeCompany {
company_id: 2,
@ -1981,6 +2029,12 @@ mod tests {
track_piece_counts: RuntimeTrackPieceCounts::default(),
active: false,
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,
},
RuntimeCompany {
company_id: 3,
@ -1992,6 +2046,12 @@ mod tests {
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![
@ -2068,6 +2128,12 @@ mod tests {
track_piece_counts: RuntimeTrackPieceCounts::default(),
active: true,
available_track_laying_capacity: Some(8),
linked_chairman_profile_id: None,
book_value_per_share: 0,
investor_confidence: 0,
management_attitude: 0,
takeover_cooldown_year: None,
merger_cooldown_year: None,
}],
selected_company_id: Some(1),
event_runtime_records: vec![RuntimeEventRecord {
@ -2148,6 +2214,12 @@ mod tests {
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,
},
RuntimeCompany {
company_id: 2,
@ -2159,6 +2231,12 @@ mod tests {
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 {
@ -2203,6 +2281,12 @@ mod tests {
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,
},
RuntimeCompany {
company_id: 2,
@ -2214,6 +2298,12 @@ mod tests {
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,
},
],
territories: vec![
@ -2588,6 +2678,12 @@ mod tests {
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: 30,
@ -2982,6 +3078,12 @@ mod tests {
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,
},
RuntimeCompany {
company_id: 2,
@ -2993,6 +3095,12 @@ mod tests {
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,
},
],
selected_company_id: Some(1),
@ -3114,4 +3222,280 @@ mod tests {
assert!(!state.trains[1].retired);
assert!(!state.trains[2].retired);
}
#[test]
fn set_chairman_cash_supports_all_active_target() {
let mut state = RuntimeState {
chairman_profiles: vec![
RuntimeChairmanProfile {
profile_id: 1,
name: "Chairman One".to_string(),
active: true,
current_cash: 10,
linked_company_id: None,
company_holdings: BTreeMap::new(),
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: 20,
linked_company_id: None,
company_holdings: BTreeMap::new(),
holdings_value_total: 0,
net_worth_total: 0,
purchasing_power_total: 0,
},
RuntimeChairmanProfile {
profile_id: 3,
name: "Chairman Three".to_string(),
active: false,
current_cash: 30,
linked_company_id: None,
company_holdings: BTreeMap::new(),
holdings_value_total: 0,
net_worth_total: 0,
purchasing_power_total: 0,
},
],
event_runtime_records: vec![RuntimeEventRecord {
record_id: 93,
trigger_kind: 6,
active: true,
service_count: 0,
marks_collection_dirty: false,
one_shot: false,
has_fired: false,
conditions: Vec::new(),
effects: vec![RuntimeEffect::SetChairmanCash {
target: RuntimeChairmanTarget::AllActive,
value: 77,
}],
}],
..state()
};
execute_step_command(
&mut state,
&StepCommand::ServiceTriggerKind { trigger_kind: 6 },
)
.expect("all-active chairman cash effect should succeed");
assert_eq!(state.chairman_profiles[0].current_cash, 77);
assert_eq!(state.chairman_profiles[1].current_cash, 77);
assert_eq!(state.chairman_profiles[2].current_cash, 30);
}
#[test]
fn deactivate_chairman_clears_selected_and_company_links_for_ids_target() {
let mut state = RuntimeState {
companies: vec![
RuntimeCompany {
company_id: 1,
controller_kind: RuntimeCompanyControllerKind::Human,
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: Some(1),
book_value_per_share: 0,
investor_confidence: 0,
management_attitude: 0,
takeover_cooldown_year: None,
merger_cooldown_year: None,
},
RuntimeCompany {
company_id: 2,
controller_kind: RuntimeCompanyControllerKind::Ai,
current_cash: 80,
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: Some(2),
book_value_per_share: 0,
investor_confidence: 0,
management_attitude: 0,
takeover_cooldown_year: None,
merger_cooldown_year: None,
},
],
chairman_profiles: vec![
RuntimeChairmanProfile {
profile_id: 1,
name: "Chairman One".to_string(),
active: true,
current_cash: 10,
linked_company_id: Some(1),
company_holdings: BTreeMap::new(),
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: 20,
linked_company_id: Some(2),
company_holdings: BTreeMap::new(),
holdings_value_total: 0,
net_worth_total: 0,
purchasing_power_total: 0,
},
],
selected_chairman_profile_id: Some(2),
event_runtime_records: vec![RuntimeEventRecord {
record_id: 94,
trigger_kind: 6,
active: true,
service_count: 0,
marks_collection_dirty: false,
one_shot: false,
has_fired: false,
conditions: Vec::new(),
effects: vec![RuntimeEffect::DeactivateChairman {
target: RuntimeChairmanTarget::Ids { ids: vec![2] },
}],
}],
..state()
};
execute_step_command(
&mut state,
&StepCommand::ServiceTriggerKind { trigger_kind: 6 },
)
.expect("ids-target chairman deactivation should succeed");
assert!(state.chairman_profiles[0].active);
assert!(!state.chairman_profiles[1].active);
assert_eq!(state.chairman_profiles[1].linked_company_id, None);
assert_eq!(state.selected_chairman_profile_id, None);
assert_eq!(state.companies[0].linked_chairman_profile_id, Some(1));
assert_eq!(state.companies[1].linked_chairman_profile_id, None);
}
#[test]
fn company_governance_metric_conditions_gate_execution() {
let mut state = RuntimeState {
companies: vec![RuntimeCompany {
company_id: 1,
controller_kind: RuntimeCompanyControllerKind::Human,
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: 2620,
investor_confidence: 37,
management_attitude: 58,
takeover_cooldown_year: Some(1844),
merger_cooldown_year: Some(1845),
}],
selected_company_id: Some(1),
event_runtime_records: vec![RuntimeEventRecord {
record_id: 95,
trigger_kind: 6,
active: true,
service_count: 0,
marks_collection_dirty: false,
one_shot: false,
has_fired: false,
conditions: vec![RuntimeCondition::CompanyNumericThreshold {
target: RuntimeCompanyTarget::SelectedCompany,
metric: crate::RuntimeCompanyMetric::BookValuePerShare,
comparator: RuntimeConditionComparator::Eq,
value: 2620,
}],
effects: vec![RuntimeEffect::SetWorldFlag {
key: "world.book_value_gate_passed".to_string(),
value: true,
}],
}],
..state()
};
execute_step_command(
&mut state,
&StepCommand::ServiceTriggerKind { trigger_kind: 6 },
)
.expect("book-value company condition should gate execution");
assert_eq!(
state.world_flags.get("world.book_value_gate_passed"),
Some(&true)
);
}
#[test]
fn chairman_metric_conditions_support_all_active_target() {
let mut state = RuntimeState {
chairman_profiles: vec![
RuntimeChairmanProfile {
profile_id: 1,
name: "Chairman One".to_string(),
active: true,
current_cash: 20,
linked_company_id: None,
company_holdings: BTreeMap::new(),
holdings_value_total: 500,
net_worth_total: 700,
purchasing_power_total: 900,
},
RuntimeChairmanProfile {
profile_id: 2,
name: "Chairman Two".to_string(),
active: true,
current_cash: 30,
linked_company_id: None,
company_holdings: BTreeMap::new(),
holdings_value_total: 600,
net_worth_total: 800,
purchasing_power_total: 1000,
},
],
event_runtime_records: vec![RuntimeEventRecord {
record_id: 96,
trigger_kind: 6,
active: true,
service_count: 0,
marks_collection_dirty: false,
one_shot: false,
has_fired: false,
conditions: vec![RuntimeCondition::ChairmanNumericThreshold {
target: RuntimeChairmanTarget::AllActive,
metric: RuntimeChairmanMetric::PurchasingPowerTotal,
comparator: RuntimeConditionComparator::Ge,
value: 900,
}],
effects: vec![RuntimeEffect::SetWorldFlag {
key: "world.chairman_gate_passed".to_string(),
value: true,
}],
}],
..state()
};
execute_step_command(
&mut state,
&StepCommand::ServiceTriggerKind { trigger_kind: 6 },
)
.expect("all-active chairman condition should gate execution");
assert_eq!(
state.world_flags.get("world.chairman_gate_passed"),
Some(&true)
);
}
}