Rehost company credit rating reader

This commit is contained in:
Jan Petykiewicz 2026-04-17 23:26:59 -07:00
commit 3306763bad
4 changed files with 438 additions and 19 deletions

View file

@ -46,18 +46,18 @@ pub use pk4::{
pub use runtime::{
RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER, RUNTIME_COMPANY_STAT_FAMILY_SPECIAL_232A,
RUNTIME_COMPANY_STAT_SLOT_BOOK_VALUE_PER_SHARE, RUNTIME_COMPANY_STAT_SLOT_COUNT,
RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH, RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN,
RUNTIME_WORLD_ISSUE_CREDIT_MARKET, RUNTIME_WORLD_ISSUE_INVESTOR_CONFIDENCE,
RUNTIME_WORLD_ISSUE_MANAGEMENT_ATTITUDE, RUNTIME_WORLD_ISSUE_PRIME_RATE,
RuntimeCargoCatalogEntry, RuntimeCargoClass, RuntimeCargoPriceTarget,
RuntimeCargoProductionTarget, RuntimeChairmanMetric, RuntimeChairmanProfile,
RuntimeChairmanTarget, RuntimeCompany, RuntimeCompanyAnnualFinanceState,
RuntimeCompanyBondSlot, RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind,
RuntimeCompanyMarketMetric, RuntimeCompanyMarketState, RuntimeCompanyMetric,
RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector, RuntimeCompanyTarget,
RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition,
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
RUNTIME_COMPANY_STAT_SLOT_CREDIT_RATING, RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN, RUNTIME_WORLD_ISSUE_CREDIT_MARKET,
RUNTIME_WORLD_ISSUE_INVESTOR_CONFIDENCE, RUNTIME_WORLD_ISSUE_MANAGEMENT_ATTITUDE,
RUNTIME_WORLD_ISSUE_PRIME_RATE, RuntimeCargoCatalogEntry, RuntimeCargoClass,
RuntimeCargoPriceTarget, RuntimeCargoProductionTarget, RuntimeChairmanMetric,
RuntimeChairmanProfile, RuntimeChairmanTarget, RuntimeCompany,
RuntimeCompanyAnnualFinanceState, RuntimeCompanyBondSlot, RuntimeCompanyConditionTestScope,
RuntimeCompanyControllerKind, RuntimeCompanyMarketMetric, RuntimeCompanyMarketState,
RuntimeCompanyMetric, RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector,
RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount,
RuntimeCondition, RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord,
RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary,
RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary,
RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer,
@ -66,11 +66,11 @@ pub use runtime::{
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain,
RuntimeWorldFinanceNeighborhoodCandidate, RuntimeWorldIssueState, RuntimeWorldRestoreState,
runtime_company_annual_finance_state, runtime_company_assigned_share_pool,
runtime_company_average_live_bond_coupon, runtime_company_market_value,
runtime_company_prime_rate, runtime_company_stat_value, runtime_company_stat_value_f64,
runtime_company_unassigned_share_pool, runtime_world_issue_opinion_multiplier,
runtime_world_issue_opinion_term_sum_raw, runtime_world_issue_state,
runtime_world_prime_rate_baseline,
runtime_company_average_live_bond_coupon, runtime_company_credit_rating,
runtime_company_market_value, runtime_company_prime_rate, runtime_company_stat_value,
runtime_company_stat_value_f64, runtime_company_unassigned_share_pool,
runtime_world_issue_opinion_multiplier, runtime_world_issue_opinion_term_sum_raw,
runtime_world_issue_state, runtime_world_prime_rate_baseline,
};
pub use smp::{
SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION, SmpAlignedRuntimeRuleBandLane,

View file

@ -439,6 +439,7 @@ pub const RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER: u32 = 0x2329;
pub const RUNTIME_COMPANY_STAT_FAMILY_SPECIAL_232A: u32 = 0x232a;
pub const RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH: u32 = 0x0d;
pub const RUNTIME_COMPANY_STAT_SLOT_BOOK_VALUE_PER_SHARE: u32 = 0x1d;
pub const RUNTIME_COMPANY_STAT_SLOT_CREDIT_RATING: u32 = 0x19;
pub const RUNTIME_COMPANY_STAT_SLOT_COUNT: u32 = 0x2b;
pub const RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN: u32 = 11;
pub const RUNTIME_WORLD_ISSUE_INVESTOR_CONFIDENCE: u32 = 0x37;
@ -2021,6 +2022,9 @@ fn runtime_company_control_transfer_stat_value_f64(
runtime_company_current_stat_value_f64(state, company_id, slot_id)
}
}
RUNTIME_COMPANY_STAT_SLOT_CREDIT_RATING => {
runtime_company_credit_rating(state, company_id).map(|value| value as f64)
}
RUNTIME_COMPANY_STAT_SLOT_BOOK_VALUE_PER_SHARE => Some(company.book_value_per_share as f64),
_ => None,
}
@ -2301,6 +2305,146 @@ pub fn runtime_company_prime_rate(state: &RuntimeState, company_id: u32) -> Opti
runtime_round_f64_to_i64(baseline + (raw_issue_sum as f64) * 0.01)
}
fn runtime_credit_rating_profitability_ladder(ratio: f64) -> f64 {
if !ratio.is_finite() || ratio <= 0.0 {
0.0
} else if ratio < 1.0 {
1.0 + ratio * 4.0
} else if ratio < 2.0 {
3.0 + ratio * 2.0
} else if ratio < 5.0 {
5.0 + ratio
} else {
10.0
}
}
fn runtime_credit_rating_burden_ladder(ratio: f64) -> f64 {
if !ratio.is_finite() || ratio > 1.0 {
0.0
} else if ratio > 0.75 {
16.0 - ratio * 16.0
} else if ratio > 0.5 {
13.0 - ratio * 12.0
} else if ratio > 0.25 {
11.0 - ratio * 8.0
} else if ratio > 0.1 {
10.0 - ratio * 4.0
} else {
10.0
}
}
fn runtime_world_credit_market_scale(state: &RuntimeState) -> Option<f64> {
const ISSUE_38_SCALE_TABLE: [f64; 8] = [0.8, 0.9, 1.0, 1.1, 1.2, 0.9, 0.95, 1.0];
let index = state.world_restore.issue_38_value? as usize;
ISSUE_38_SCALE_TABLE.get(index).copied()
}
pub fn runtime_company_credit_rating(state: &RuntimeState, company_id: u32) -> Option<i64> {
let company = state
.companies
.iter()
.find(|company| company.company_id == company_id)?;
if let Some(credit_rating_score) = company.credit_rating_score {
return Some(credit_rating_score);
}
let annual_finance_state = runtime_company_annual_finance_state(state, company_id)?;
if annual_finance_state.outstanding_shares == 0 {
return Some(-512);
}
let mut weighted_recent_profit_total = 0.0f64;
let mut weighted_recent_profit_weight = 0.0f64;
for (index, (net_profit, fuel_cost)) in annual_finance_state
.trailing_full_year_net_profits
.iter()
.zip(annual_finance_state.trailing_full_year_fuel_costs.iter())
.take(4)
.enumerate()
{
let weight = (4 - index) as f64;
weighted_recent_profit_total += (*net_profit - *fuel_cost) as f64 * weight;
weighted_recent_profit_weight += weight;
}
let weighted_recent_profit = if weighted_recent_profit_weight > 0.0 {
weighted_recent_profit_total / weighted_recent_profit_weight
} else {
0.0
};
let current_slot_12 = runtime_company_control_transfer_stat_value_f64(state, company_id, 0x12)?;
let current_slot_30 = runtime_company_derived_stat_value_f64(
state,
company_id,
RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
0x30,
)?;
let current_slot_31 = runtime_company_derived_stat_value_f64(
state,
company_id,
RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
0x31,
)?;
let average_live_bond_coupon = runtime_company_average_live_bond_coupon(state, company_id)?;
let mut finance_pressure = average_live_bond_coupon * current_slot_12;
if company.current_cash > 0 {
let prime_baseline = runtime_world_prime_rate_baseline(state)?;
let raw_issue_39 = runtime_world_issue_opinion_term_sum_raw(
state,
RUNTIME_WORLD_ISSUE_PRIME_RATE,
company.linked_chairman_profile_id,
Some(company_id),
None,
)? as f64;
finance_pressure +=
company.current_cash as f64 * (prime_baseline + raw_issue_39 * 0.01 + 0.03);
}
let profitability_ratio = if finance_pressure < 0.0 {
weighted_recent_profit / (-finance_pressure)
} else {
10.0
};
let mut profitability_score = runtime_credit_rating_profitability_ladder(profitability_ratio);
if let Some(years_since_founding) = annual_finance_state.years_since_founding {
if years_since_founding < 5 {
let missing_years = (5 - years_since_founding) as f64;
profitability_score += (10.0 - profitability_score) * missing_years * 0.1;
}
}
if current_slot_31 > 1_000_000.0 {
profitability_score += current_slot_31 / 100_000.0 - 10.0;
}
let burden_ratio = if current_slot_30 > 0.0 {
(weighted_recent_profit - current_slot_12) / current_slot_30
} else {
1.0
};
let burden_score = runtime_credit_rating_burden_ladder(burden_ratio);
let mut rating =
(profitability_score * burden_score / 10.0 + profitability_score + burden_score) / 3.0;
rating *= runtime_world_credit_market_scale(state)?;
if let Some(years_since_last_bankruptcy) = annual_finance_state.years_since_last_bankruptcy {
if years_since_last_bankruptcy < 15 {
rating *= years_since_last_bankruptcy as f64 * 0.0666;
}
}
let raw_issue_38 = runtime_world_issue_opinion_term_sum_raw(
state,
RUNTIME_WORLD_ISSUE_CREDIT_MARKET,
company.linked_chairman_profile_id,
Some(company_id),
None,
)? as f64;
runtime_round_f64_to_i64((rating + raw_issue_38 + 0.5).clamp(0.0, 10.0))
}
pub fn runtime_company_average_live_bond_coupon(
state: &RuntimeState,
company_id: u32,
@ -4520,6 +4664,7 @@ mod tests {
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState {
world_issue_opinion_base_terms_raw_i32: vec![0; 0x3b],
company_market_state: BTreeMap::from([(
7,
RuntimeCompanyMarketState {
@ -4691,6 +4836,7 @@ mod tests {
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState {
world_issue_opinion_base_terms_raw_i32: vec![0; 0x3b],
company_market_state: BTreeMap::from([(
7,
RuntimeCompanyMarketState {
@ -4883,6 +5029,7 @@ mod tests {
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState {
world_issue_opinion_base_terms_raw_i32: vec![0; 0x3b],
company_market_state: BTreeMap::from([(
7,
RuntimeCompanyMarketState {
@ -5406,6 +5553,171 @@ mod tests {
assert_eq!(runtime_company_prime_rate(&state, 7), Some(7));
}
#[test]
fn derives_company_credit_rating_from_rehosted_finance_state() {
let mut year_stat_family_qword_bits = vec![
0u64;
(RUNTIME_COMPANY_STAT_SLOT_COUNT * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
as usize
];
year_stat_family_qword_bits[(0x12 * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize] =
20.0f64.to_bits();
year_stat_family_qword_bits[(0x01 * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN + 1) as usize] =
100.0f64.to_bits();
year_stat_family_qword_bits[(0x09 * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN + 1) as usize] =
0.0f64.to_bits();
let state = RuntimeState {
calendar: CalendarPoint {
year: 1835,
month_slot: 0,
phase_slot: 0,
tick_slot: 0,
},
world_flags: BTreeMap::new(),
save_profile: RuntimeSaveProfileState::default(),
world_restore: RuntimeWorldRestoreState {
issue_37_value: Some(5.0f32.to_bits()),
issue_38_value: Some(2),
packed_year_word_raw_u16: Some(1835),
..RuntimeWorldRestoreState::default()
},
metadata: BTreeMap::new(),
companies: vec![RuntimeCompany {
company_id: 7,
current_cash: 100,
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::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,
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 {
world_issue_opinion_base_terms_raw_i32: vec![0; 0x3b],
company_market_state: BTreeMap::from([(
7,
RuntimeCompanyMarketState {
outstanding_shares: 10_000,
founding_year: 1830,
last_bankruptcy_year: 1800,
year_stat_family_qword_bits,
live_bond_slots: vec![RuntimeCompanyBondSlot {
slot_index: 0,
principal: 100_000,
coupon_rate_raw_u32: 0.05f32.to_bits(),
}],
..RuntimeCompanyMarketState::default()
},
)]),
..RuntimeServiceState::default()
},
};
let annual_finance_state =
runtime_company_annual_finance_state(&state, 7).expect("annual finance state");
assert_eq!(
annual_finance_state.trailing_full_year_net_profits,
vec![100, 0, 0, 0]
);
assert_eq!(
runtime_company_control_transfer_stat_value_f64(&state, 7, 0x12),
Some(20.0)
);
assert_eq!(
runtime_company_derived_stat_value_f64(
&state,
7,
RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
0x30,
),
Some(100.0)
);
assert_eq!(
runtime_company_derived_stat_value_f64(
&state,
7,
RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
0x31,
),
Some(120.0)
);
let average_live_bond_coupon =
runtime_company_average_live_bond_coupon(&state, 7).expect("average coupon");
assert!((average_live_bond_coupon - 0.05).abs() < 1e-6);
assert_eq!(runtime_world_prime_rate_baseline(&state), Some(5.0));
assert_eq!(
runtime_world_issue_opinion_term_sum_raw(
&state,
RUNTIME_WORLD_ISSUE_PRIME_RATE,
None,
Some(7),
None,
),
Some(0)
);
assert_eq!(
runtime_world_issue_opinion_term_sum_raw(
&state,
RUNTIME_WORLD_ISSUE_CREDIT_MARKET,
None,
Some(7),
None,
),
Some(0)
);
assert_eq!(runtime_world_credit_market_scale(&state), Some(1.0));
assert_eq!(runtime_company_credit_rating(&state, 7), Some(10));
assert_eq!(
runtime_company_stat_value(
&state,
7,
RuntimeCompanyStatSelector {
family_id: RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
slot_id: RUNTIME_COMPANY_STAT_SLOT_CREDIT_RATING,
},
),
Some(10)
);
}
#[test]
fn computes_weighted_average_live_bond_coupon_from_owned_market_slots() {
let state = RuntimeState {

View file

@ -16439,6 +16439,14 @@ mod tests {
u8::from(city_connection_latch);
bytes[record_offset + SAVE_COMPANY_RECORD_LINKED_TRANSIT_LATCH_OFFSET] =
u8::from(linked_transit_latch);
let current_cash: f64 = if index == 0 { 125_000.0 } else { -25_000.0 };
let current_cash_slot_offset = record_offset
+ SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_OFFSET
+ (crate::RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH as usize
* crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN as usize
* 8);
bytes[current_cash_slot_offset..current_cash_slot_offset + 8]
.copy_from_slice(&current_cash.to_bits().to_le_bytes());
}
let header_probe = parse_save_company_collection_header_probe(
@ -16484,6 +16492,7 @@ mod tests {
assert_eq!(roster.selected_company_id, Some(2));
assert_eq!(roster.entries.len(), 2);
assert_eq!(roster.entries[0].company_id, 1);
assert_eq!(roster.entries[0].current_cash, 125_000);
assert_eq!(roster.entries[0].linked_chairman_profile_id, Some(1));
assert_eq!(roster.entries[0].debt, 1_550_000);
assert_eq!(roster.entries[0].available_track_laying_capacity, Some(603));
@ -16546,6 +16555,7 @@ mod tests {
"0xd77"
);
assert_eq!(roster.entries[1].company_id, 2);
assert_eq!(roster.entries[1].current_cash, -25_000);
assert_eq!(roster.entries[1].linked_chairman_profile_id, Some(2));
assert_eq!(roster.entries[1].debt, 500_000);
assert_eq!(roster.entries[1].available_track_laying_capacity, None);

View file

@ -8,7 +8,7 @@ use crate::{
RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCondition, RuntimeConditionComparator,
RuntimeEffect, RuntimeEventRecordTemplate, RuntimePlayerTarget, RuntimeState, RuntimeSummary,
RuntimeTerritoryMetric, RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts,
calendar::BoundaryEventKind, runtime_company_prime_rate,
calendar::BoundaryEventKind, runtime_company_credit_rating, runtime_company_prime_rate,
};
const PERIODIC_TRIGGER_KIND_ORDER: [u8; 6] = [1, 0, 3, 2, 5, 4];
@ -1516,7 +1516,9 @@ fn company_metric_value(
match metric {
RuntimeCompanyMetric::CurrentCash => company.current_cash,
RuntimeCompanyMetric::TotalDebt => company.debt as i64,
RuntimeCompanyMetric::CreditRating => company.credit_rating_score.unwrap_or(0),
RuntimeCompanyMetric::CreditRating => {
runtime_company_credit_rating(state, company.company_id).unwrap_or(0)
}
RuntimeCompanyMetric::PrimeRate => {
runtime_company_prime_rate(state, company.company_id).unwrap_or(0)
}
@ -4142,6 +4144,101 @@ mod tests {
);
}
#[test]
fn derived_credit_rating_condition_reads_rehosted_finance_owner_state() {
let mut year_stat_family_qword_bits = vec![
0u64;
(crate::RUNTIME_COMPANY_STAT_SLOT_COUNT * crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
as usize
];
year_stat_family_qword_bits
[(0x12 * crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize] = 20.0f64.to_bits();
year_stat_family_qword_bits
[(0x01 * crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN + 1) as usize] =
100.0f64.to_bits();
year_stat_family_qword_bits
[(0x09 * crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN + 1) as usize] = 0.0f64.to_bits();
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: 0,
investor_confidence: 37,
management_attitude: 58,
takeover_cooldown_year: Some(1844),
merger_cooldown_year: Some(1845),
}],
selected_company_id: Some(1),
world_restore: RuntimeWorldRestoreState {
issue_37_value: Some(5.0f32.to_bits()),
issue_38_value: Some(2),
packed_year_word_raw_u16: Some(1835),
..RuntimeWorldRestoreState::default()
},
service_state: RuntimeServiceState {
world_issue_opinion_base_terms_raw_i32: vec![0; 0x3b],
company_market_state: BTreeMap::from([(
1,
crate::RuntimeCompanyMarketState {
outstanding_shares: 10_000,
founding_year: 1830,
last_bankruptcy_year: 1800,
year_stat_family_qword_bits,
live_bond_slots: vec![crate::RuntimeCompanyBondSlot {
slot_index: 0,
principal: 100_000,
coupon_rate_raw_u32: 0.05f32.to_bits(),
}],
..crate::RuntimeCompanyMarketState::default()
},
)]),
..RuntimeServiceState::default()
},
event_runtime_records: vec![RuntimeEventRecord {
record_id: 196,
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::CreditRating,
comparator: RuntimeConditionComparator::Eq,
value: 10,
}],
effects: vec![RuntimeEffect::SetWorldFlag {
key: "world.derived_credit_rating_gate_passed".to_string(),
value: true,
}],
}],
..state()
};
execute_step_command(
&mut state,
&StepCommand::ServiceTriggerKind { trigger_kind: 6 },
)
.expect("derived credit-rating company condition should gate execution");
assert_eq!(
state
.world_flags
.get("world.derived_credit_rating_gate_passed"),
Some(&true)
);
}
#[test]
fn chairman_metric_conditions_support_all_active_target() {
let mut state = RuntimeState {