Broaden chairman governance event coverage
This commit is contained in:
parent
86cf89b26c
commit
f8350a48c5
18 changed files with 1939 additions and 13 deletions
|
|
@ -21,6 +21,10 @@ still exercise the same runtime without a parallel packed executor. The first gr
|
|||
chairman-profile runtime slice now exists too: overlay-backed selected-chairman context plus the
|
||||
hidden grouped target-subject lane let those same real descriptors `1` and `14` execute on
|
||||
selected-chairman scope, while wider chairman target scopes remain explicit parity. The first grounded
|
||||
chairman and governance condition batch is grounded too: selected-chairman cash / holdings / net
|
||||
worth / purchasing-power thresholds and company book-value-per-share thresholds now import through
|
||||
the normal event-service path, while wider chairman target scopes and the still-unpinned investor-
|
||||
confidence / management-attitude condition ids remain explicit frontier. The first grounded
|
||||
condition-side unlock now exists for negative-sentinel `raw_condition_id = -1` company scopes, and
|
||||
the first ordinary nonnegative condition batch now executes too: numeric-threshold company
|
||||
finance, company track, aggregate territory track, and company-territory track rows can import
|
||||
|
|
|
|||
|
|
@ -4495,6 +4495,14 @@ mod tests {
|
|||
let chairman_scope_parity_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
|
||||
"../../fixtures/runtime/packed-event-chairman-scope-parity-save-slice-fixture.json",
|
||||
);
|
||||
let chairman_condition_overlay_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../fixtures/runtime/packed-event-chairman-condition-overlay-fixture.json");
|
||||
let company_governance_condition_overlay_fixture = PathBuf::from(env!(
|
||||
"CARGO_MANIFEST_DIR"
|
||||
))
|
||||
.join(
|
||||
"../../fixtures/runtime/packed-event-company-governance-condition-overlay-fixture.json",
|
||||
);
|
||||
|
||||
run_runtime_summarize_fixture(&parity_fixture)
|
||||
.expect("save-slice-backed parity fixture should summarize");
|
||||
|
|
@ -4544,6 +4552,10 @@ mod tests {
|
|||
.expect("save-slice-backed chairman missing-context fixture should summarize");
|
||||
run_runtime_summarize_fixture(&chairman_scope_parity_fixture)
|
||||
.expect("save-slice-backed chairman scope parity fixture should summarize");
|
||||
run_runtime_summarize_fixture(&chairman_condition_overlay_fixture)
|
||||
.expect("overlay-backed chairman condition fixture should summarize");
|
||||
run_runtime_summarize_fixture(&company_governance_condition_overlay_fixture)
|
||||
.expect("overlay-backed company governance condition fixture should summarize");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -351,6 +351,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(42),
|
||||
players: Vec::new(),
|
||||
|
|
|
|||
|
|
@ -78,6 +78,12 @@ pub struct ExpectedRuntimeSummary {
|
|||
#[serde(default)]
|
||||
pub selected_chairman_profile_id: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub linked_chairman_company_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub company_takeover_cooldown_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub company_merger_cooldown_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub train_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub active_train_count: Option<usize>,
|
||||
|
|
@ -473,6 +479,30 @@ impl ExpectedRuntimeSummary {
|
|||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.linked_chairman_company_count {
|
||||
if actual.linked_chairman_company_count != count {
|
||||
mismatches.push(format!(
|
||||
"linked_chairman_company_count mismatch: expected {count}, got {}",
|
||||
actual.linked_chairman_company_count
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.company_takeover_cooldown_count {
|
||||
if actual.company_takeover_cooldown_count != count {
|
||||
mismatches.push(format!(
|
||||
"company_takeover_cooldown_count mismatch: expected {count}, got {}",
|
||||
actual.company_takeover_cooldown_count
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.company_merger_cooldown_count {
|
||||
if actual.company_merger_cooldown_count != count {
|
||||
mismatches.push(format!(
|
||||
"company_merger_cooldown_count mismatch: expected {count}, got {}",
|
||||
actual.company_merger_cooldown_count
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.train_count {
|
||||
if actual.train_count != count {
|
||||
mismatches.push(format!(
|
||||
|
|
|
|||
|
|
@ -3335,6 +3335,16 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn selected_chairman_negative_sentinel_scope()
|
||||
-> crate::SmpLoadedPackedEventNegativeSentinelScopeSummary {
|
||||
crate::SmpLoadedPackedEventNegativeSentinelScopeSummary {
|
||||
company_test_scope: RuntimeCompanyConditionTestScope::Disabled,
|
||||
player_test_scope: RuntimePlayerConditionTestScope::SelectedPlayerOnly,
|
||||
territory_scope_selector_is_0x63: false,
|
||||
source_row_indexes: vec![0],
|
||||
}
|
||||
}
|
||||
|
||||
fn real_grouped_rows() -> Vec<crate::SmpLoadedPackedEventGroupedEffectRowSummary> {
|
||||
vec![crate::SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
group_index: 0,
|
||||
|
|
@ -4814,6 +4824,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,
|
||||
},
|
||||
crate::RuntimeCompany {
|
||||
company_id: 2,
|
||||
|
|
@ -4825,6 +4841,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),
|
||||
|
|
@ -5023,6 +5045,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,
|
||||
},
|
||||
crate::RuntimeCompany {
|
||||
company_id: 2,
|
||||
|
|
@ -5034,6 +5062,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,
|
||||
},
|
||||
crate::RuntimeCompany {
|
||||
company_id: 3,
|
||||
|
|
@ -5045,6 +5079,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(3),
|
||||
|
|
@ -6638,6 +6678,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(42),
|
||||
players: Vec::new(),
|
||||
|
|
@ -6799,6 +6845,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(42),
|
||||
territories: vec![crate::RuntimeTerritory {
|
||||
|
|
@ -7102,6 +7154,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(42),
|
||||
..state()
|
||||
|
|
@ -7500,6 +7558,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(42),
|
||||
..state()
|
||||
|
|
@ -8804,6 +8868,375 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlays_selected_chairman_conditions_into_imported_runtime_records() {
|
||||
let base_state = RuntimeState {
|
||||
calendar: CalendarPoint {
|
||||
year: 1840,
|
||||
month_slot: 1,
|
||||
phase_slot: 2,
|
||||
tick_slot: 3,
|
||||
},
|
||||
world_flags: BTreeMap::new(),
|
||||
save_profile: RuntimeSaveProfileState::default(),
|
||||
world_restore: RuntimeWorldRestoreState::default(),
|
||||
metadata: BTreeMap::new(),
|
||||
companies: vec![crate::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: 2620,
|
||||
investor_confidence: 37,
|
||||
management_attitude: 58,
|
||||
takeover_cooldown_year: Some(1839),
|
||||
merger_cooldown_year: Some(1838),
|
||||
}],
|
||||
selected_company_id: Some(1),
|
||||
players: Vec::new(),
|
||||
selected_player_id: None,
|
||||
chairman_profiles: vec![
|
||||
crate::RuntimeChairmanProfile {
|
||||
profile_id: 1,
|
||||
name: "Chairman One".to_string(),
|
||||
active: true,
|
||||
current_cash: 500,
|
||||
linked_company_id: Some(1),
|
||||
company_holdings: BTreeMap::new(),
|
||||
holdings_value_total: 700,
|
||||
net_worth_total: 1200,
|
||||
purchasing_power_total: 1500,
|
||||
},
|
||||
crate::RuntimeChairmanProfile {
|
||||
profile_id: 2,
|
||||
name: "Chairman Two".to_string(),
|
||||
active: true,
|
||||
current_cash: 200,
|
||||
linked_company_id: None,
|
||||
company_holdings: BTreeMap::new(),
|
||||
holdings_value_total: 400,
|
||||
net_worth_total: 600,
|
||||
purchasing_power_total: 800,
|
||||
},
|
||||
],
|
||||
selected_chairman_profile_id: Some(1),
|
||||
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(),
|
||||
cargo_production_overrides: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
let save_slice = SmpLoadedSaveSlice {
|
||||
file_extension_hint: Some("gms".to_string()),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
trailer_family: None,
|
||||
bridge_family: None,
|
||||
profile: None,
|
||||
candidate_availability_table: None,
|
||||
named_locomotive_availability_table: None,
|
||||
locomotive_catalog: None,
|
||||
cargo_catalog: None,
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
|
||||
source_kind: "packed-event-runtime-collection".to_string(),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
metadata_tag_offset: 0x7100,
|
||||
records_tag_offset: 0x7200,
|
||||
close_tag_offset: 0x7600,
|
||||
packed_state_version: 0x3e9,
|
||||
packed_state_version_hex: "0x000003e9".to_string(),
|
||||
live_id_bound: 71,
|
||||
live_record_count: 1,
|
||||
live_entry_ids: vec![71],
|
||||
decoded_record_count: 1,
|
||||
imported_runtime_record_count: 0,
|
||||
records: vec![crate::SmpLoadedPackedEventRecordSummary {
|
||||
record_index: 0,
|
||||
live_entry_id: 71,
|
||||
payload_offset: Some(0x7202),
|
||||
payload_len: Some(136),
|
||||
decode_status: "parity_only".to_string(),
|
||||
payload_family: "real_packed_v1".to_string(),
|
||||
trigger_kind: Some(6),
|
||||
active: None,
|
||||
marks_collection_dirty: None,
|
||||
one_shot: Some(false),
|
||||
compact_control: Some(real_compact_control()),
|
||||
text_bands: vec![],
|
||||
standalone_condition_row_count: 1,
|
||||
standalone_condition_rows: vec![
|
||||
crate::SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 0,
|
||||
raw_condition_id: 2218,
|
||||
subtype: 4,
|
||||
flag_bytes: {
|
||||
let mut bytes = vec![0; 25];
|
||||
bytes[0..4].copy_from_slice(&500_i32.to_le_bytes());
|
||||
bytes
|
||||
},
|
||||
candidate_name: None,
|
||||
comparator: Some("eq".to_string()),
|
||||
metric: Some("Player Cash".to_string()),
|
||||
semantic_family: Some("numeric_threshold".to_string()),
|
||||
semantic_preview: Some("Test Player Cash == 500".to_string()),
|
||||
recovered_cargo_slot: None,
|
||||
recovered_cargo_class: None,
|
||||
requires_candidate_name_binding: false,
|
||||
notes: vec![],
|
||||
},
|
||||
],
|
||||
negative_sentinel_scope: Some(selected_chairman_negative_sentinel_scope()),
|
||||
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
||||
grouped_effect_rows: vec![real_world_flag_row(
|
||||
110,
|
||||
"Disable Stock Buying and Selling",
|
||||
true,
|
||||
)],
|
||||
decoded_conditions: vec![RuntimeCondition::ChairmanNumericThreshold {
|
||||
target: RuntimeChairmanTarget::SelectedChairman,
|
||||
metric: crate::RuntimeChairmanMetric::CurrentCash,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 500,
|
||||
}],
|
||||
decoded_actions: vec![RuntimeEffect::SetWorldFlag {
|
||||
key: "world.chairman_condition_imported".to_string(),
|
||||
value: true,
|
||||
}],
|
||||
executable_import_ready: true,
|
||||
notes: vec!["chairman metric condition gates a world-side effect".to_string()],
|
||||
}],
|
||||
}),
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
let mut import = project_save_slice_overlay_to_runtime_state_import(
|
||||
&base_state,
|
||||
&save_slice,
|
||||
"chairman-condition-overlay",
|
||||
None,
|
||||
)
|
||||
.expect("overlay import should project");
|
||||
|
||||
assert_eq!(import.state.event_runtime_records.len(), 1);
|
||||
assert_eq!(
|
||||
import.state.event_runtime_records[0].conditions,
|
||||
vec![RuntimeCondition::ChairmanNumericThreshold {
|
||||
target: RuntimeChairmanTarget::SelectedChairman,
|
||||
metric: crate::RuntimeChairmanMetric::CurrentCash,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 500,
|
||||
}]
|
||||
);
|
||||
|
||||
crate::execute_step_command(
|
||||
&mut import.state,
|
||||
&crate::StepCommand::ServiceTriggerKind { trigger_kind: 6 },
|
||||
)
|
||||
.expect("chairman-gated trigger should execute");
|
||||
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.world_flags
|
||||
.get("world.chairman_condition_imported"),
|
||||
Some(&true)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlays_book_value_conditions_into_imported_runtime_records() {
|
||||
let base_state = RuntimeState {
|
||||
calendar: CalendarPoint {
|
||||
year: 1840,
|
||||
month_slot: 1,
|
||||
phase_slot: 2,
|
||||
tick_slot: 3,
|
||||
},
|
||||
world_flags: BTreeMap::new(),
|
||||
save_profile: RuntimeSaveProfileState::default(),
|
||||
world_restore: RuntimeWorldRestoreState::default(),
|
||||
metadata: BTreeMap::new(),
|
||||
companies: vec![crate::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(1839),
|
||||
merger_cooldown_year: Some(1838),
|
||||
}],
|
||||
selected_company_id: Some(1),
|
||||
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(),
|
||||
cargo_production_overrides: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
let save_slice = SmpLoadedSaveSlice {
|
||||
file_extension_hint: Some("gms".to_string()),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
trailer_family: None,
|
||||
bridge_family: None,
|
||||
profile: None,
|
||||
candidate_availability_table: None,
|
||||
named_locomotive_availability_table: None,
|
||||
locomotive_catalog: None,
|
||||
cargo_catalog: None,
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
|
||||
source_kind: "packed-event-runtime-collection".to_string(),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
metadata_tag_offset: 0x7100,
|
||||
records_tag_offset: 0x7200,
|
||||
close_tag_offset: 0x7600,
|
||||
packed_state_version: 0x3e9,
|
||||
packed_state_version_hex: "0x000003e9".to_string(),
|
||||
live_id_bound: 72,
|
||||
live_record_count: 1,
|
||||
live_entry_ids: vec![72],
|
||||
decoded_record_count: 1,
|
||||
imported_runtime_record_count: 0,
|
||||
records: vec![crate::SmpLoadedPackedEventRecordSummary {
|
||||
record_index: 0,
|
||||
live_entry_id: 72,
|
||||
payload_offset: Some(0x7202),
|
||||
payload_len: Some(136),
|
||||
decode_status: "parity_only".to_string(),
|
||||
payload_family: "real_packed_v1".to_string(),
|
||||
trigger_kind: Some(7),
|
||||
active: None,
|
||||
marks_collection_dirty: None,
|
||||
one_shot: Some(false),
|
||||
compact_control: Some(real_compact_control()),
|
||||
text_bands: vec![],
|
||||
standalone_condition_row_count: 1,
|
||||
standalone_condition_rows: vec![
|
||||
crate::SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 0,
|
||||
raw_condition_id: 2620,
|
||||
subtype: 4,
|
||||
flag_bytes: {
|
||||
let mut bytes = vec![0; 25];
|
||||
bytes[0..4].copy_from_slice(&2620_i32.to_le_bytes());
|
||||
bytes
|
||||
},
|
||||
candidate_name: None,
|
||||
comparator: Some("eq".to_string()),
|
||||
metric: Some("Book Value Per Share".to_string()),
|
||||
semantic_family: Some("numeric_threshold".to_string()),
|
||||
semantic_preview: Some("Test Book Value Per Share == 2620".to_string()),
|
||||
recovered_cargo_slot: None,
|
||||
recovered_cargo_class: None,
|
||||
requires_candidate_name_binding: false,
|
||||
notes: vec![],
|
||||
},
|
||||
],
|
||||
negative_sentinel_scope: Some(company_negative_sentinel_scope(
|
||||
RuntimeCompanyConditionTestScope::SelectedCompanyOnly,
|
||||
)),
|
||||
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
||||
grouped_effect_rows: vec![real_world_flag_row(
|
||||
110,
|
||||
"Disable Stock Buying and Selling",
|
||||
true,
|
||||
)],
|
||||
decoded_conditions: vec![RuntimeCondition::CompanyNumericThreshold {
|
||||
target: RuntimeCompanyTarget::ConditionTrueCompany,
|
||||
metric: crate::RuntimeCompanyMetric::BookValuePerShare,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 2620,
|
||||
}],
|
||||
decoded_actions: vec![RuntimeEffect::SetWorldFlag {
|
||||
key: "world.book_value_condition_imported".to_string(),
|
||||
value: true,
|
||||
}],
|
||||
executable_import_ready: true,
|
||||
notes: vec![
|
||||
"book value per share condition gates a world-side effect".to_string(),
|
||||
],
|
||||
}],
|
||||
}),
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
let mut import = project_save_slice_overlay_to_runtime_state_import(
|
||||
&base_state,
|
||||
&save_slice,
|
||||
"company-book-value-condition-overlay",
|
||||
None,
|
||||
)
|
||||
.expect("overlay import should project");
|
||||
|
||||
assert_eq!(import.state.event_runtime_records.len(), 1);
|
||||
assert_eq!(
|
||||
import.state.event_runtime_records[0].conditions,
|
||||
vec![RuntimeCondition::CompanyNumericThreshold {
|
||||
target: RuntimeCompanyTarget::SelectedCompany,
|
||||
metric: crate::RuntimeCompanyMetric::BookValuePerShare,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 2620,
|
||||
}]
|
||||
);
|
||||
|
||||
crate::execute_step_command(
|
||||
&mut import.state,
|
||||
&crate::StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||
)
|
||||
.expect("company-governance trigger should execute");
|
||||
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.world_flags
|
||||
.get("world.book_value_condition_imported"),
|
||||
Some(&true)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlays_recovered_world_toggle_batch_into_executable_runtime_record() {
|
||||
let base_state = state();
|
||||
|
|
@ -9058,6 +9491,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,
|
||||
},
|
||||
crate::RuntimeCompany {
|
||||
company_id: 7,
|
||||
|
|
@ -9069,6 +9508,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(42),
|
||||
|
|
@ -9272,6 +9717,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(42),
|
||||
..state()
|
||||
|
|
@ -9377,6 +9828,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,
|
||||
},
|
||||
crate::RuntimeCompany {
|
||||
company_id: 7,
|
||||
|
|
@ -9388,6 +9845,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(42),
|
||||
|
|
@ -9533,6 +9996,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(42),
|
||||
trains: vec![
|
||||
|
|
@ -9767,6 +10236,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(42),
|
||||
trains: vec![RuntimeTrain {
|
||||
|
|
@ -9886,6 +10361,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(42),
|
||||
..state()
|
||||
|
|
@ -10004,6 +10485,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(42),
|
||||
players: Vec::new(),
|
||||
|
|
@ -10190,6 +10677,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(42),
|
||||
players: Vec::new(),
|
||||
|
|
|
|||
|
|
@ -33,6 +33,18 @@ pub struct RuntimeCompany {
|
|||
#[serde(default)]
|
||||
pub controller_kind: RuntimeCompanyControllerKind,
|
||||
#[serde(default)]
|
||||
pub linked_chairman_profile_id: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub book_value_per_share: i64,
|
||||
#[serde(default)]
|
||||
pub investor_confidence: i64,
|
||||
#[serde(default)]
|
||||
pub management_attitude: i64,
|
||||
#[serde(default)]
|
||||
pub takeover_cooldown_year: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub merger_cooldown_year: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub track_piece_counts: RuntimeTrackPieceCounts,
|
||||
}
|
||||
|
||||
|
|
@ -235,6 +247,9 @@ pub enum RuntimeCompanyMetric {
|
|||
TotalDebt,
|
||||
CreditRating,
|
||||
PrimeRate,
|
||||
BookValuePerShare,
|
||||
InvestorConfidence,
|
||||
ManagementAttitude,
|
||||
TrackPiecesTotal,
|
||||
TrackPiecesSingle,
|
||||
TrackPiecesDouble,
|
||||
|
|
@ -918,6 +933,46 @@ impl RuntimeState {
|
|||
));
|
||||
}
|
||||
}
|
||||
for company in &self.companies {
|
||||
if let Some(linked_chairman_profile_id) = company.linked_chairman_profile_id {
|
||||
let linked_profile = self
|
||||
.chairman_profiles
|
||||
.iter()
|
||||
.find(|profile| profile.profile_id == linked_chairman_profile_id)
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"company {} references unknown linked_chairman_profile_id {}",
|
||||
company.company_id, linked_chairman_profile_id
|
||||
)
|
||||
})?;
|
||||
if linked_profile.linked_company_id != Some(company.company_id) {
|
||||
return Err(format!(
|
||||
"company {} linked_chairman_profile_id {} must point back through linked_company_id",
|
||||
company.company_id, linked_chairman_profile_id
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
for chairman in &self.chairman_profiles {
|
||||
if let Some(linked_company_id) = chairman.linked_company_id {
|
||||
let linked_company = self
|
||||
.companies
|
||||
.iter()
|
||||
.find(|company| company.company_id == linked_company_id)
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"chairman_profile {} references unknown linked_company_id {}",
|
||||
chairman.profile_id, linked_company_id
|
||||
)
|
||||
})?;
|
||||
if linked_company.linked_chairman_profile_id != Some(chairman.profile_id) {
|
||||
return Err(format!(
|
||||
"chairman_profile {} linked_company_id {} must point back through linked_chairman_profile_id",
|
||||
chairman.profile_id, linked_company_id
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(selected_player_id) = self.selected_player_id {
|
||||
if !seen_player_ids.contains(&selected_player_id) {
|
||||
return Err(format!(
|
||||
|
|
@ -1772,6 +1827,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||
},
|
||||
RuntimeCompany {
|
||||
|
|
@ -1783,6 +1844,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||
},
|
||||
],
|
||||
|
|
@ -1895,6 +1962,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||
}],
|
||||
selected_company_id: None,
|
||||
|
|
@ -1956,6 +2029,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||
}],
|
||||
selected_company_id: None,
|
||||
|
|
@ -2129,6 +2208,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
}],
|
||||
selected_company_id: Some(2),
|
||||
|
|
@ -2177,6 +2262,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
}],
|
||||
selected_company_id: Some(1),
|
||||
|
|
@ -2225,6 +2316,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
}],
|
||||
selected_company_id: None,
|
||||
|
|
@ -2290,6 +2387,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
}],
|
||||
selected_company_id: None,
|
||||
|
|
@ -2345,6 +2448,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
}],
|
||||
selected_company_id: None,
|
||||
|
|
@ -2404,6 +2513,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
}],
|
||||
selected_company_id: None,
|
||||
|
|
@ -2459,6 +2574,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
}],
|
||||
selected_company_id: None,
|
||||
|
|
@ -2520,6 +2641,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
}],
|
||||
selected_company_id: None,
|
||||
|
|
@ -2575,6 +2702,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
}],
|
||||
selected_company_id: None,
|
||||
|
|
@ -2607,4 +2740,122 @@ mod tests {
|
|||
|
||||
assert!(state.validate().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_company_with_unknown_linked_chairman_profile() {
|
||||
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: 1,
|
||||
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(9),
|
||||
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::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(),
|
||||
cargo_production_overrides: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
||||
assert!(state.validate().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_mismatched_company_chairman_back_links() {
|
||||
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: 1,
|
||||
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,
|
||||
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: 0,
|
||||
linked_company_id: None,
|
||||
company_holdings: BTreeMap::new(),
|
||||
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(),
|
||||
cargo_production_overrides: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
||||
assert!(state.validate().is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ use serde::{Deserialize, Serialize};
|
|||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::{
|
||||
RuntimeCargoClass, RuntimeChairmanTarget, RuntimeCompanyConditionTestScope,
|
||||
RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCondition, RuntimeConditionComparator,
|
||||
RuntimeEffect, RuntimeEventRecordTemplate, RuntimePlayerConditionTestScope,
|
||||
RuntimePlayerTarget, RuntimeTerritoryMetric, RuntimeTerritoryTarget, RuntimeTrackMetric,
|
||||
RuntimeCargoClass, RuntimeChairmanMetric, RuntimeChairmanTarget,
|
||||
RuntimeCompanyConditionTestScope, RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCondition,
|
||||
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecordTemplate,
|
||||
RuntimePlayerConditionTestScope, RuntimePlayerTarget, RuntimeTerritoryMetric,
|
||||
RuntimeTerritoryTarget, RuntimeTrackMetric,
|
||||
};
|
||||
|
||||
pub const SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION: u32 = 0x03ec;
|
||||
|
|
@ -232,6 +233,7 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum RealOrdinaryConditionMetric {
|
||||
Company(RuntimeCompanyMetric),
|
||||
Chairman(RuntimeChairmanMetric),
|
||||
Territory(RuntimeTerritoryMetric),
|
||||
CompanyTerritory(RuntimeTrackMetric),
|
||||
}
|
||||
|
|
@ -344,6 +346,11 @@ const KNOWN_CARGO_SLOT_DEFINITIONS: [KnownCargoSlotDefinition; 11] = [
|
|||
];
|
||||
|
||||
const REAL_CANDIDATE_AVAILABILITY_CONDITION_TEMPLATE_ID: i32 = 435;
|
||||
const REAL_CHAIRMAN_CASH_CONDITION_ID: i32 = 2218;
|
||||
const REAL_CHAIRMAN_HOLDINGS_TOTAL_CONDITION_ID: i32 = 2239;
|
||||
const REAL_CHAIRMAN_NET_WORTH_CONDITION_ID: i32 = 2240;
|
||||
const REAL_CHAIRMAN_PURCHASING_POWER_CONDITION_ID: i32 = 1247;
|
||||
const REAL_BOOK_VALUE_PER_SHARE_CONDITION_ID: i32 = 2620;
|
||||
const REAL_CARGO_PRODUCTION_CONDITION_TEMPLATE_ID: i32 = 200;
|
||||
const REAL_NAMED_LOCOMOTIVE_AVAILABILITY_CONDITION_ID: i32 = 2422;
|
||||
const REAL_NAMED_LOCOMOTIVE_COST_CONDITION_ID: i32 = 2423;
|
||||
|
|
@ -354,7 +361,7 @@ const REAL_OTHER_CARGO_PRODUCTION_TOTAL_CONDITION_ID: i32 = 2421;
|
|||
const REAL_LIMITED_TRACK_BUILDING_AMOUNT_CONDITION_ID: i32 = 2547;
|
||||
const REAL_TERRITORY_ACCESS_COST_CONDITION_ID: i32 = 1516;
|
||||
|
||||
const REAL_ORDINARY_CONDITION_METADATA: [RealOrdinaryConditionMetadata; 33] = [
|
||||
const REAL_ORDINARY_CONDITION_METADATA: [RealOrdinaryConditionMetadata; 38] = [
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 1802,
|
||||
label: "Current Cash",
|
||||
|
|
@ -362,6 +369,34 @@ const REAL_ORDINARY_CONDITION_METADATA: [RealOrdinaryConditionMetadata; 33] = [
|
|||
RuntimeCompanyMetric::CurrentCash,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_CHAIRMAN_CASH_CONDITION_ID,
|
||||
label: "Player Cash",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Chairman(
|
||||
RuntimeChairmanMetric::CurrentCash,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_CHAIRMAN_HOLDINGS_TOTAL_CONDITION_ID,
|
||||
label: "Player Stock Value",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Chairman(
|
||||
RuntimeChairmanMetric::HoldingsValueTotal,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_CHAIRMAN_NET_WORTH_CONDITION_ID,
|
||||
label: "Player Net Worth",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Chairman(
|
||||
RuntimeChairmanMetric::NetWorthTotal,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_CHAIRMAN_PURCHASING_POWER_CONDITION_ID,
|
||||
label: "Purchasing Power",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Chairman(
|
||||
RuntimeChairmanMetric::PurchasingPowerTotal,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 951,
|
||||
label: "Total Debt",
|
||||
|
|
@ -383,6 +418,13 @@ const REAL_ORDINARY_CONDITION_METADATA: [RealOrdinaryConditionMetadata; 33] = [
|
|||
RuntimeCompanyMetric::PrimeRate,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_BOOK_VALUE_PER_SHARE_CONDITION_ID,
|
||||
label: "Book Value Per Share",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(
|
||||
RuntimeCompanyMetric::BookValuePerShare,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2293,
|
||||
label: "Company Track Pieces",
|
||||
|
|
@ -3258,6 +3300,18 @@ fn decode_real_condition_row(
|
|||
value,
|
||||
})
|
||||
}
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Chairman(metric)) => {
|
||||
negative_sentinel_scope.and_then(|scope| {
|
||||
real_condition_chairman_target(scope).map(|target| {
|
||||
RuntimeCondition::ChairmanNumericThreshold {
|
||||
target,
|
||||
metric,
|
||||
comparator,
|
||||
value,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Territory(metric)) => {
|
||||
negative_sentinel_scope
|
||||
.filter(|scope| scope.territory_scope_selector_is_0x63)
|
||||
|
|
@ -3369,6 +3423,20 @@ fn decode_world_flag_condition(
|
|||
})
|
||||
}
|
||||
|
||||
fn real_condition_chairman_target(
|
||||
scope: &SmpLoadedPackedEventNegativeSentinelScopeSummary,
|
||||
) -> Option<RuntimeChairmanTarget> {
|
||||
match scope.player_test_scope {
|
||||
RuntimePlayerConditionTestScope::AllPlayers => Some(RuntimeChairmanTarget::AllActive),
|
||||
RuntimePlayerConditionTestScope::SelectedPlayerOnly => {
|
||||
Some(RuntimeChairmanTarget::SelectedChairman)
|
||||
}
|
||||
RuntimePlayerConditionTestScope::Disabled
|
||||
| RuntimePlayerConditionTestScope::AiPlayersOnly
|
||||
| RuntimePlayerConditionTestScope::HumanPlayersOnly => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn real_grouped_effect_descriptor_metadata(
|
||||
descriptor_id: u32,
|
||||
) -> Option<RealGroupedEffectDescriptorMetadata> {
|
||||
|
|
@ -10397,6 +10465,102 @@ mod tests {
|
|||
assert_eq!(access_cost.label, "Access Rights Cost:");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn looks_up_checked_in_chairman_and_governance_condition_metadata() {
|
||||
let chairman_cash = real_ordinary_condition_metadata(REAL_CHAIRMAN_CASH_CONDITION_ID)
|
||||
.expect("chairman cash condition metadata should exist");
|
||||
assert_eq!(chairman_cash.label, "Player Cash");
|
||||
|
||||
let holdings = real_ordinary_condition_metadata(REAL_CHAIRMAN_HOLDINGS_TOTAL_CONDITION_ID)
|
||||
.expect("chairman holdings condition metadata should exist");
|
||||
assert_eq!(holdings.label, "Player Stock Value");
|
||||
|
||||
let net_worth = real_ordinary_condition_metadata(REAL_CHAIRMAN_NET_WORTH_CONDITION_ID)
|
||||
.expect("chairman net worth condition metadata should exist");
|
||||
assert_eq!(net_worth.label, "Player Net Worth");
|
||||
|
||||
let purchasing_power =
|
||||
real_ordinary_condition_metadata(REAL_CHAIRMAN_PURCHASING_POWER_CONDITION_ID)
|
||||
.expect("chairman purchasing-power condition metadata should exist");
|
||||
assert_eq!(purchasing_power.label, "Purchasing Power");
|
||||
|
||||
let book_value = real_ordinary_condition_metadata(REAL_BOOK_VALUE_PER_SHARE_CONDITION_ID)
|
||||
.expect("book value condition metadata should exist");
|
||||
assert_eq!(book_value.label, "Book Value Per Share");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_chairman_cash_condition_from_selected_player_scope() {
|
||||
let row = SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 0,
|
||||
raw_condition_id: REAL_CHAIRMAN_CASH_CONDITION_ID,
|
||||
subtype: 4,
|
||||
flag_bytes: {
|
||||
let mut bytes = vec![0; 25];
|
||||
bytes[0..4].copy_from_slice(&500_i32.to_le_bytes());
|
||||
bytes
|
||||
},
|
||||
candidate_name: None,
|
||||
comparator: Some("eq".to_string()),
|
||||
metric: Some("Player Cash".to_string()),
|
||||
semantic_family: Some("numeric_threshold".to_string()),
|
||||
semantic_preview: Some("Test Player Cash == 500".to_string()),
|
||||
recovered_cargo_slot: None,
|
||||
recovered_cargo_class: None,
|
||||
requires_candidate_name_binding: false,
|
||||
notes: vec![],
|
||||
};
|
||||
let negative_scope = SmpLoadedPackedEventNegativeSentinelScopeSummary {
|
||||
company_test_scope: RuntimeCompanyConditionTestScope::Disabled,
|
||||
player_test_scope: RuntimePlayerConditionTestScope::SelectedPlayerOnly,
|
||||
territory_scope_selector_is_0x63: false,
|
||||
source_row_indexes: vec![0],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
decode_real_condition_row(&row, Some(&negative_scope)),
|
||||
Some(RuntimeCondition::ChairmanNumericThreshold {
|
||||
target: RuntimeChairmanTarget::SelectedChairman,
|
||||
metric: RuntimeChairmanMetric::CurrentCash,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 500,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_book_value_per_share_condition_to_company_metric() {
|
||||
let row = SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 0,
|
||||
raw_condition_id: REAL_BOOK_VALUE_PER_SHARE_CONDITION_ID,
|
||||
subtype: 4,
|
||||
flag_bytes: {
|
||||
let mut bytes = vec![0; 25];
|
||||
bytes[0..4].copy_from_slice(&2620_i32.to_le_bytes());
|
||||
bytes
|
||||
},
|
||||
candidate_name: None,
|
||||
comparator: Some("eq".to_string()),
|
||||
metric: Some("Book Value Per Share".to_string()),
|
||||
semantic_family: Some("numeric_threshold".to_string()),
|
||||
semantic_preview: Some("Test Book Value Per Share == 2620".to_string()),
|
||||
recovered_cargo_slot: None,
|
||||
recovered_cargo_class: None,
|
||||
requires_candidate_name_binding: false,
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
decode_real_condition_row(&row, None),
|
||||
Some(RuntimeCondition::CompanyNumericThreshold {
|
||||
target: RuntimeCompanyTarget::ConditionTrueCompany,
|
||||
metric: RuntimeCompanyMetric::BookValuePerShare,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 2620,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn looks_up_checked_in_world_flag_descriptor_metadata() {
|
||||
let metadata =
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ pub struct RuntimeSummary {
|
|||
pub chairman_profile_count: usize,
|
||||
pub active_chairman_profile_count: usize,
|
||||
pub selected_chairman_profile_id: Option<u32>,
|
||||
pub linked_chairman_company_count: usize,
|
||||
pub company_takeover_cooldown_count: usize,
|
||||
pub company_merger_cooldown_count: usize,
|
||||
pub train_count: usize,
|
||||
pub active_train_count: usize,
|
||||
pub retired_train_count: usize,
|
||||
|
|
@ -181,6 +184,21 @@ impl RuntimeSummary {
|
|||
.filter(|profile| profile.active)
|
||||
.count(),
|
||||
selected_chairman_profile_id: state.selected_chairman_profile_id,
|
||||
linked_chairman_company_count: state
|
||||
.companies
|
||||
.iter()
|
||||
.filter(|company| company.linked_chairman_profile_id.is_some())
|
||||
.count(),
|
||||
company_takeover_cooldown_count: state
|
||||
.companies
|
||||
.iter()
|
||||
.filter(|company| company.takeover_cooldown_year.is_some())
|
||||
.count(),
|
||||
company_merger_cooldown_count: state
|
||||
.companies
|
||||
.iter()
|
||||
.filter(|company| company.merger_cooldown_year.is_some())
|
||||
.count(),
|
||||
train_count: state.trains.len(),
|
||||
active_train_count: state.trains.iter().filter(|train| train.active).count(),
|
||||
retired_train_count: state.trains.iter().filter(|train| train.retired).count(),
|
||||
|
|
@ -934,6 +952,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,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
},
|
||||
RuntimeCompany {
|
||||
|
|
@ -945,6 +969,12 @@ mod tests {
|
|||
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||
active: false,
|
||||
available_track_laying_capacity: Some(7),
|
||||
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::Ai,
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -87,6 +87,11 @@ The highest-value next passes are now:
|
|||
- the first chairman-targeted real grouped rows now execute too through that same path when the
|
||||
hidden grouped target-subject lane resolves to selected-chairman scope; broader chairman target
|
||||
scopes stay parity-only under `blocked_chairman_target_scope`
|
||||
- chairman runtime ownership is broader now too: selected-chairman condition rows for chairman
|
||||
cash, holdings value, net worth, and purchasing power import through the same service path, and
|
||||
the first grounded company governance condition family now executes too via book-value-per-share
|
||||
thresholds; wider chairman target ordinals and the still-unpinned investor-confidence /
|
||||
management-attitude condition ids remain frontier
|
||||
- widen real packed-event executable coverage descriptor by descriptor after identity, target mask,
|
||||
and normalized effect semantics are all grounded, not just after row framing is parsed
|
||||
- the first grounded condition-side unlock now exists for negative-sentinel `raw_condition_id = -1`
|
||||
|
|
|
|||
|
|
@ -46,6 +46,13 @@ Implemented today:
|
|||
`1` = `Player Cash` and `14` = `Deactivate Player` now also import and execute through the
|
||||
hidden grouped target-subject lane when it resolves to selected-chairman scope, while broader
|
||||
chairman target scopes remain explicit parity on `blocked_chairman_target_scope`
|
||||
- chairman governance state is broader now too: companies carry explicit chairman links plus
|
||||
book-value-per-share, investor-confidence, management-attitude, and takeover/merger cooldown
|
||||
lanes, and the first grounded chairman/control-transfer ordinary-condition batch now imports and
|
||||
executes through the same path for selected-chairman cash / holdings / net-worth /
|
||||
purchasing-power thresholds plus company book-value-per-share thresholds; wider chairman target
|
||||
ordinals and the still-unpinned investor-confidence / management-attitude condition ids remain
|
||||
parity frontier
|
||||
- a minimal event-owned train surface and an opaque economic-status lane now exist in runtime
|
||||
state, and real descriptors `8` = `Economic Status`, `9` = `Confiscate All`, and `15` =
|
||||
`Retire Train` now import and execute through the ordinary runtime path when overlay context
|
||||
|
|
@ -116,10 +123,11 @@ Implemented today:
|
|||
|
||||
That means the next implementation work is breadth, not bootstrap. The recommended next slice is
|
||||
broader real grouped-descriptor and ordinary condition-id coverage beyond the current access,
|
||||
whole-game toggle, train, player, chairman selected-scope, numeric-threshold, named locomotive
|
||||
availability, named locomotive cost, world scalar override, and world-scalar condition batches.
|
||||
Richer runtime ownership should still be added only where a later descriptor or condition family
|
||||
needs more than the current event-owned roster.
|
||||
whole-game toggle, train, player, chairman selected-scope grouped effects, grounded
|
||||
chairman/governance conditions, numeric-threshold, named locomotive availability, named locomotive
|
||||
cost, world scalar override, and world-scalar condition batches. Richer runtime ownership should
|
||||
still be added only where a later descriptor or condition family needs more than the current
|
||||
event-owned roster.
|
||||
|
||||
## Why This Boundary
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"fixture_id": "packed-event-chairman-condition-overlay-fixture",
|
||||
"source": {
|
||||
"kind": "captured-runtime",
|
||||
"description": "Fixture proving grounded chairman metric conditions import and execute through selected-chairman context."
|
||||
},
|
||||
"state_import_path": "packed-event-chairman-condition-overlay.json",
|
||||
"commands": [
|
||||
{
|
||||
"kind": "service_trigger_kind",
|
||||
"trigger_kind": 6
|
||||
}
|
||||
],
|
||||
"expected_summary": {
|
||||
"calendar_projection_source": "base-snapshot-preserved",
|
||||
"calendar_projection_is_placeholder": false,
|
||||
"company_count": 2,
|
||||
"chairman_profile_count": 2,
|
||||
"active_chairman_profile_count": 2,
|
||||
"selected_chairman_profile_id": 1,
|
||||
"linked_chairman_company_count": 2,
|
||||
"company_takeover_cooldown_count": 1,
|
||||
"company_merger_cooldown_count": 1,
|
||||
"packed_event_collection_present": true,
|
||||
"packed_event_record_count": 1,
|
||||
"packed_event_decoded_record_count": 1,
|
||||
"packed_event_imported_runtime_record_count": 1,
|
||||
"event_runtime_record_count": 1,
|
||||
"world_flag_count": 10,
|
||||
"total_event_record_service_count": 1,
|
||||
"total_trigger_dispatch_count": 1
|
||||
},
|
||||
"expected_state_fragment": {
|
||||
"world_flags": {
|
||||
"world.chairman_condition_imported": true
|
||||
},
|
||||
"packed_event_collection": {
|
||||
"records": [
|
||||
{
|
||||
"import_outcome": "imported",
|
||||
"decoded_conditions": [
|
||||
{
|
||||
"kind": "chairman_numeric_threshold",
|
||||
"target": {
|
||||
"kind": "selected_chairman"
|
||||
},
|
||||
"metric": "current_cash",
|
||||
"comparator": "eq",
|
||||
"value": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"import_id": "packed-event-chairman-condition-overlay",
|
||||
"source": {
|
||||
"description": "Overlay import combining chairman runtime context with a grounded chairman-metric condition sample."
|
||||
},
|
||||
"base_snapshot_path": "packed-event-chairman-overlay-base-snapshot.json",
|
||||
"save_slice_path": "packed-event-chairman-condition-save-slice.json"
|
||||
}
|
||||
190
fixtures/runtime/packed-event-chairman-condition-save-slice.json
Normal file
190
fixtures/runtime/packed-event-chairman-condition-save-slice.json
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"save_slice_id": "packed-event-chairman-condition-save-slice",
|
||||
"source": {
|
||||
"description": "Tracked save-slice document proving grounded chairman metric conditions import and execute through selected-chairman context.",
|
||||
"original_save_filename": "captured-chairman-condition.gms",
|
||||
"original_save_sha256": "chairman-condition-sample-sha256",
|
||||
"notes": [
|
||||
"tracked as JSON save-slice document rather than raw .smp",
|
||||
"pins the first grounded chairman ordinary-condition family"
|
||||
]
|
||||
},
|
||||
"save_slice": {
|
||||
"file_extension_hint": "gms",
|
||||
"container_profile_family": "rt3-classic-save-container-v1",
|
||||
"mechanism_family": "classic-save-rehydrate-v1",
|
||||
"mechanism_confidence": "grounded",
|
||||
"trailer_family": null,
|
||||
"bridge_family": null,
|
||||
"profile": null,
|
||||
"candidate_availability_table": null,
|
||||
"named_locomotive_availability_table": null,
|
||||
"cargo_catalog": null,
|
||||
"special_conditions_table": null,
|
||||
"event_runtime_collection": {
|
||||
"source_kind": "packed-event-runtime-collection",
|
||||
"mechanism_family": "classic-save-rehydrate-v1",
|
||||
"mechanism_confidence": "grounded",
|
||||
"container_profile_family": "rt3-classic-save-container-v1",
|
||||
"metadata_tag_offset": 28928,
|
||||
"records_tag_offset": 29184,
|
||||
"close_tag_offset": 29696,
|
||||
"packed_state_version": 1001,
|
||||
"packed_state_version_hex": "0x000003e9",
|
||||
"live_id_bound": 71,
|
||||
"live_record_count": 1,
|
||||
"live_entry_ids": [
|
||||
71
|
||||
],
|
||||
"decoded_record_count": 1,
|
||||
"imported_runtime_record_count": 0,
|
||||
"records": [
|
||||
{
|
||||
"record_index": 0,
|
||||
"live_entry_id": 71,
|
||||
"payload_offset": 29186,
|
||||
"payload_len": 136,
|
||||
"decode_status": "executable",
|
||||
"payload_family": "real_packed_v1",
|
||||
"trigger_kind": 6,
|
||||
"active": null,
|
||||
"marks_collection_dirty": null,
|
||||
"one_shot": false,
|
||||
"compact_control": {
|
||||
"mode_byte_0x7ef": 6,
|
||||
"primary_selector_0x7f0": 99,
|
||||
"grouped_mode_0x7f4": 2,
|
||||
"one_shot_header_0x7f5": 1,
|
||||
"modifier_flag_0x7f9": 1,
|
||||
"modifier_flag_0x7fa": 0,
|
||||
"grouped_target_scope_ordinals_0x7fb": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"grouped_scope_checkboxes_0x7ff": [
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0
|
||||
],
|
||||
"summary_toggle_0x800": 1,
|
||||
"grouped_territory_selectors_0x80f": [
|
||||
-1,
|
||||
10,
|
||||
-1,
|
||||
22
|
||||
]
|
||||
},
|
||||
"text_bands": [],
|
||||
"standalone_condition_row_count": 1,
|
||||
"standalone_condition_rows": [
|
||||
{
|
||||
"row_index": 0,
|
||||
"raw_condition_id": 2218,
|
||||
"subtype": 4,
|
||||
"flag_bytes": [
|
||||
244,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"candidate_name": null,
|
||||
"comparator": "eq",
|
||||
"metric": "Player Cash",
|
||||
"semantic_family": "numeric_threshold",
|
||||
"semantic_preview": "Test Player Cash == 500",
|
||||
"requires_candidate_name_binding": false,
|
||||
"notes": []
|
||||
}
|
||||
],
|
||||
"negative_sentinel_scope": {
|
||||
"company_test_scope": "disabled",
|
||||
"player_test_scope": "selected_player_only",
|
||||
"territory_scope_selector_is_0x63": false,
|
||||
"source_row_indexes": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"grouped_effect_row_counts": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"group_index": 0,
|
||||
"row_index": 0,
|
||||
"descriptor_id": 110,
|
||||
"descriptor_label": "Disable Stock Buying and Selling",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_flag_toggle",
|
||||
"opcode": 0,
|
||||
"raw_scalar_value": 1,
|
||||
"value_byte_0x09": 0,
|
||||
"value_dword_0x0d": 0,
|
||||
"value_byte_0x11": 0,
|
||||
"value_byte_0x12": 0,
|
||||
"value_word_0x14": 0,
|
||||
"value_word_0x16": 0,
|
||||
"row_shape": "bool_toggle",
|
||||
"semantic_family": "bool_toggle",
|
||||
"semantic_preview": "Set Disable Stock Buying and Selling to TRUE",
|
||||
"locomotive_name": null,
|
||||
"notes": []
|
||||
}
|
||||
],
|
||||
"decoded_conditions": [
|
||||
{
|
||||
"kind": "chairman_numeric_threshold",
|
||||
"target": {
|
||||
"kind": "selected_chairman"
|
||||
},
|
||||
"metric": "current_cash",
|
||||
"comparator": "eq",
|
||||
"value": 500
|
||||
}
|
||||
],
|
||||
"decoded_actions": [
|
||||
{
|
||||
"kind": "set_world_flag",
|
||||
"key": "world.chairman_condition_imported",
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"executable_import_ready": true,
|
||||
"notes": [
|
||||
"chairman metric condition gates a world-side effect"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"notes": [
|
||||
"real chairman ordinary-condition sample"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,12 @@
|
|||
"credit_rating_score": 650,
|
||||
"prime_rate": 5,
|
||||
"controller_kind": "human",
|
||||
"linked_chairman_profile_id": 1,
|
||||
"book_value_per_share": 2620,
|
||||
"investor_confidence": 37,
|
||||
"management_attitude": 58,
|
||||
"takeover_cooldown_year": 1839,
|
||||
"merger_cooldown_year": 1838,
|
||||
"track_piece_counts": {
|
||||
"total": 20,
|
||||
"single": 5,
|
||||
|
|
@ -41,6 +47,12 @@
|
|||
"credit_rating_score": 480,
|
||||
"prime_rate": 6,
|
||||
"controller_kind": "ai",
|
||||
"linked_chairman_profile_id": 2,
|
||||
"book_value_per_share": 1400,
|
||||
"investor_confidence": 22,
|
||||
"management_attitude": 31,
|
||||
"takeover_cooldown_year": null,
|
||||
"merger_cooldown_year": null,
|
||||
"track_piece_counts": {
|
||||
"total": 8,
|
||||
"single": 2,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"fixture_id": "packed-event-company-governance-condition-overlay-fixture",
|
||||
"source": {
|
||||
"kind": "captured-runtime",
|
||||
"description": "Fixture proving grounded company governance conditions import and execute."
|
||||
},
|
||||
"state_import_path": "packed-event-company-governance-condition-overlay.json",
|
||||
"commands": [
|
||||
{
|
||||
"kind": "service_trigger_kind",
|
||||
"trigger_kind": 7
|
||||
}
|
||||
],
|
||||
"expected_summary": {
|
||||
"calendar_projection_source": "base-snapshot-preserved",
|
||||
"calendar_projection_is_placeholder": false,
|
||||
"company_count": 2,
|
||||
"chairman_profile_count": 2,
|
||||
"active_chairman_profile_count": 2,
|
||||
"selected_chairman_profile_id": 1,
|
||||
"linked_chairman_company_count": 2,
|
||||
"company_takeover_cooldown_count": 1,
|
||||
"company_merger_cooldown_count": 1,
|
||||
"packed_event_collection_present": true,
|
||||
"packed_event_record_count": 1,
|
||||
"packed_event_decoded_record_count": 1,
|
||||
"packed_event_imported_runtime_record_count": 1,
|
||||
"event_runtime_record_count": 1,
|
||||
"world_flag_count": 10,
|
||||
"total_event_record_service_count": 1,
|
||||
"total_trigger_dispatch_count": 1
|
||||
},
|
||||
"expected_state_fragment": {
|
||||
"world_flags": {
|
||||
"world.book_value_condition_imported": true
|
||||
},
|
||||
"packed_event_collection": {
|
||||
"records": [
|
||||
{
|
||||
"import_outcome": "imported",
|
||||
"decoded_conditions": [
|
||||
{
|
||||
"kind": "company_numeric_threshold",
|
||||
"target": {
|
||||
"kind": "selected_company"
|
||||
},
|
||||
"metric": "book_value_per_share",
|
||||
"comparator": "eq",
|
||||
"value": 2620
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"event_runtime_records": [
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"kind": "company_numeric_threshold",
|
||||
"target": {
|
||||
"kind": "selected_company"
|
||||
},
|
||||
"metric": "book_value_per_share",
|
||||
"comparator": "eq",
|
||||
"value": 2620
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"import_id": "packed-event-company-governance-condition-overlay",
|
||||
"source": {
|
||||
"description": "Overlay import combining company governance runtime context with a grounded book-value-per-share condition sample."
|
||||
},
|
||||
"base_snapshot_path": "packed-event-chairman-overlay-base-snapshot.json",
|
||||
"save_slice_path": "packed-event-company-governance-condition-save-slice.json"
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"save_slice_id": "packed-event-company-governance-condition-save-slice",
|
||||
"source": {
|
||||
"description": "Tracked save-slice document proving grounded company governance conditions import and execute.",
|
||||
"original_save_filename": "captured-company-governance-condition.gms",
|
||||
"original_save_sha256": "company-governance-condition-sample-sha256",
|
||||
"notes": [
|
||||
"tracked as JSON save-slice document rather than raw .smp",
|
||||
"pins the first grounded company control-transfer condition family"
|
||||
]
|
||||
},
|
||||
"save_slice": {
|
||||
"file_extension_hint": "gms",
|
||||
"container_profile_family": "rt3-classic-save-container-v1",
|
||||
"mechanism_family": "classic-save-rehydrate-v1",
|
||||
"mechanism_confidence": "grounded",
|
||||
"trailer_family": null,
|
||||
"bridge_family": null,
|
||||
"profile": null,
|
||||
"candidate_availability_table": null,
|
||||
"named_locomotive_availability_table": null,
|
||||
"cargo_catalog": null,
|
||||
"special_conditions_table": null,
|
||||
"event_runtime_collection": {
|
||||
"source_kind": "packed-event-runtime-collection",
|
||||
"mechanism_family": "classic-save-rehydrate-v1",
|
||||
"mechanism_confidence": "grounded",
|
||||
"container_profile_family": "rt3-classic-save-container-v1",
|
||||
"metadata_tag_offset": 28928,
|
||||
"records_tag_offset": 29184,
|
||||
"close_tag_offset": 29696,
|
||||
"packed_state_version": 1001,
|
||||
"packed_state_version_hex": "0x000003e9",
|
||||
"live_id_bound": 72,
|
||||
"live_record_count": 1,
|
||||
"live_entry_ids": [
|
||||
72
|
||||
],
|
||||
"decoded_record_count": 1,
|
||||
"imported_runtime_record_count": 0,
|
||||
"records": [
|
||||
{
|
||||
"record_index": 0,
|
||||
"live_entry_id": 72,
|
||||
"payload_offset": 29186,
|
||||
"payload_len": 136,
|
||||
"decode_status": "executable",
|
||||
"payload_family": "real_packed_v1",
|
||||
"trigger_kind": 7,
|
||||
"active": null,
|
||||
"marks_collection_dirty": null,
|
||||
"one_shot": false,
|
||||
"compact_control": {
|
||||
"mode_byte_0x7ef": 6,
|
||||
"primary_selector_0x7f0": 99,
|
||||
"grouped_mode_0x7f4": 2,
|
||||
"one_shot_header_0x7f5": 1,
|
||||
"modifier_flag_0x7f9": 1,
|
||||
"modifier_flag_0x7fa": 0,
|
||||
"grouped_target_scope_ordinals_0x7fb": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"grouped_scope_checkboxes_0x7ff": [
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0
|
||||
],
|
||||
"summary_toggle_0x800": 1,
|
||||
"grouped_territory_selectors_0x80f": [
|
||||
-1,
|
||||
10,
|
||||
-1,
|
||||
22
|
||||
]
|
||||
},
|
||||
"text_bands": [],
|
||||
"standalone_condition_row_count": 1,
|
||||
"standalone_condition_rows": [
|
||||
{
|
||||
"row_index": 0,
|
||||
"raw_condition_id": 2620,
|
||||
"subtype": 4,
|
||||
"flag_bytes": [
|
||||
60,
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"candidate_name": null,
|
||||
"comparator": "eq",
|
||||
"metric": "Book Value Per Share",
|
||||
"semantic_family": "numeric_threshold",
|
||||
"semantic_preview": "Test Book Value Per Share == 2620",
|
||||
"requires_candidate_name_binding": false,
|
||||
"notes": []
|
||||
}
|
||||
],
|
||||
"negative_sentinel_scope": {
|
||||
"company_test_scope": "selected_company_only",
|
||||
"player_test_scope": "disabled",
|
||||
"territory_scope_selector_is_0x63": false,
|
||||
"source_row_indexes": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"grouped_effect_row_counts": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"group_index": 0,
|
||||
"row_index": 0,
|
||||
"descriptor_id": 110,
|
||||
"descriptor_label": "Disable Stock Buying and Selling",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_flag_toggle",
|
||||
"opcode": 0,
|
||||
"raw_scalar_value": 1,
|
||||
"value_byte_0x09": 0,
|
||||
"value_dword_0x0d": 0,
|
||||
"value_byte_0x11": 0,
|
||||
"value_byte_0x12": 0,
|
||||
"value_word_0x14": 0,
|
||||
"value_word_0x16": 0,
|
||||
"row_shape": "bool_toggle",
|
||||
"semantic_family": "bool_toggle",
|
||||
"semantic_preview": "Set Disable Stock Buying and Selling to TRUE",
|
||||
"locomotive_name": null,
|
||||
"notes": []
|
||||
}
|
||||
],
|
||||
"decoded_conditions": [
|
||||
{
|
||||
"kind": "company_numeric_threshold",
|
||||
"target": {
|
||||
"kind": "condition_true_company"
|
||||
},
|
||||
"metric": "book_value_per_share",
|
||||
"comparator": "eq",
|
||||
"value": 2620
|
||||
}
|
||||
],
|
||||
"decoded_actions": [
|
||||
{
|
||||
"kind": "set_world_flag",
|
||||
"key": "world.book_value_condition_imported",
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"executable_import_ready": true,
|
||||
"notes": [
|
||||
"book value per share condition gates a world-side effect"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"notes": [
|
||||
"real company-governance ordinary-condition sample"
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue