Broaden whole-game packed event coverage

This commit is contained in:
Jan Petykiewicz 2026-04-16 08:28:50 -07:00
commit b89463d227
19 changed files with 1874 additions and 27 deletions

View file

@ -1240,6 +1240,10 @@ fn lower_condition_targets_in_condition(
value: *value,
}
}
RuntimeCondition::WorldFlagEquals { key, value } => RuntimeCondition::WorldFlagEquals {
key: key.clone(),
value: *value,
},
})
}
@ -1319,7 +1323,8 @@ fn condition_uses_condition_true_company(condition: &RuntimeCondition) -> bool {
RuntimeCondition::TerritoryNumericThreshold { .. }
| RuntimeCondition::SpecialConditionThreshold { .. }
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
| RuntimeCondition::EconomicStatusCodeThreshold { .. } => false,
| RuntimeCondition::EconomicStatusCodeThreshold { .. }
| RuntimeCondition::WorldFlagEquals { .. } => false,
}
}
@ -1899,6 +1904,7 @@ fn runtime_condition_is_world_state(condition: &RuntimeCondition) -> bool {
RuntimeCondition::SpecialConditionThreshold { .. }
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
| RuntimeCondition::EconomicStatusCodeThreshold { .. }
| RuntimeCondition::WorldFlagEquals { .. }
)
}
@ -1980,7 +1986,8 @@ fn runtime_condition_company_target_import_blocker(
.or_else(|| territory_target_import_blocker(territory, company_context)),
RuntimeCondition::SpecialConditionThreshold { .. }
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
| RuntimeCondition::EconomicStatusCodeThreshold { .. } => None,
| RuntimeCondition::EconomicStatusCodeThreshold { .. }
| RuntimeCondition::WorldFlagEquals { .. } => None,
}
}
@ -2844,12 +2851,16 @@ mod tests {
}
}
fn real_world_flag_row(enabled: bool) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary {
fn real_world_flag_row(
descriptor_id: u32,
label: &str,
enabled: bool,
) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary {
crate::SmpLoadedPackedEventGroupedEffectRowSummary {
group_index: 0,
row_index: 0,
descriptor_id: 110,
descriptor_label: Some("Disable Stock Buying and Selling".to_string()),
descriptor_id,
descriptor_label: Some(label.to_string()),
target_mask_bits: Some(0x08),
parameter_family: Some("world_flag_toggle".to_string()),
opcode: 0,
@ -2863,7 +2874,7 @@ mod tests {
row_shape: "bool_toggle".to_string(),
semantic_family: Some("bool_toggle".to_string()),
semantic_preview: Some(format!(
"Set Disable Stock Buying and Selling to {}",
"Set {label} to {}",
if enabled { "TRUE" } else { "FALSE" }
)),
locomotive_name: None,
@ -5658,7 +5669,11 @@ mod tests {
standalone_condition_rows: vec![],
negative_sentinel_scope: None,
grouped_effect_row_counts: vec![1, 0, 0, 0],
grouped_effect_rows: vec![real_world_flag_row(true)],
grouped_effect_rows: vec![real_world_flag_row(
110,
"Disable Stock Buying and Selling",
true,
)],
decoded_conditions: Vec::new(),
decoded_actions: vec![RuntimeEffect::SetWorldFlag {
key: "world.disable_stock_buying_and_selling".to_string(),
@ -5751,7 +5766,11 @@ mod tests {
standalone_condition_rows: vec![],
negative_sentinel_scope: None,
grouped_effect_row_counts: vec![1, 0, 0, 0],
grouped_effect_rows: vec![real_world_flag_row(false)],
grouped_effect_rows: vec![real_world_flag_row(
110,
"Disable Stock Buying and Selling",
false,
)],
decoded_conditions: Vec::new(),
decoded_actions: vec![RuntimeEffect::SetWorldFlag {
key: "world.disable_stock_buying_and_selling".to_string(),
@ -5790,6 +5809,366 @@ mod tests {
);
}
#[test]
fn overlays_real_world_flag_condition_into_executable_runtime_record() {
let mut base_state = state();
base_state
.world_flags
.insert("world.disable_stock_buying_and_selling".to_string(), true);
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,
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: 27,
live_record_count: 1,
live_entry_ids: vec![27],
decoded_record_count: 1,
imported_runtime_record_count: 1,
records: vec![crate::SmpLoadedPackedEventRecordSummary {
record_index: 0,
live_entry_id: 27,
payload_offset: Some(0x7200),
payload_len: Some(152),
decode_status: "executable".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: packed_text_bands(),
standalone_condition_row_count: 1,
standalone_condition_rows: vec![
crate::SmpLoadedPackedEventConditionRowSummary {
row_index: 0,
raw_condition_id: 2535,
subtype: 0,
flag_bytes: vec![
1, 0, 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: None,
comparator: Some("eq".to_string()),
metric: Some(
"World Flag: Disable Stock Buying and Selling".to_string(),
),
semantic_family: Some("world_flag_equals".to_string()),
semantic_preview: Some(
"Test Disable Stock Buying and Selling == TRUE".to_string(),
),
requires_candidate_name_binding: false,
notes: vec![
"checked-in whole-game condition metadata sample".to_string(),
],
},
],
negative_sentinel_scope: None,
grouped_effect_row_counts: vec![1, 0, 0, 0],
grouped_effect_rows: vec![crate::SmpLoadedPackedEventGroupedEffectRowSummary {
group_index: 0,
row_index: 0,
descriptor_id: 109,
descriptor_label: Some("Turbo Diesel Availability".to_string()),
target_mask_bits: Some(0x08),
parameter_family: Some("candidate_availability_scalar".to_string()),
opcode: 3,
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: "scalar_assignment".to_string(),
semantic_family: Some("scalar_assignment".to_string()),
semantic_preview: Some("Set Turbo Diesel Availability to 1".to_string()),
locomotive_name: None,
notes: vec!["checked-in whole-game grouped-effect sample".to_string()],
}],
decoded_conditions: vec![RuntimeCondition::WorldFlagEquals {
key: "world.disable_stock_buying_and_selling".to_string(),
value: true,
}],
decoded_actions: vec![RuntimeEffect::SetCandidateAvailability {
name: "Turbo Diesel".to_string(),
value: 1,
}],
executable_import_ready: true,
notes: vec!["world-flag 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,
"real-world-flag-condition-overlay",
None,
)
.expect("overlay import should project");
crate::execute_step_command(
&mut import.state,
&crate::StepCommand::ServiceTriggerKind { trigger_kind: 7 },
)
.expect("trigger service should execute");
assert_eq!(
import.state.candidate_availability.get("Turbo Diesel"),
Some(&1)
);
}
#[test]
fn overlays_recovered_world_toggle_batch_into_executable_runtime_record() {
let base_state = state();
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,
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: 25,
live_record_count: 1,
live_entry_ids: vec![25],
decoded_record_count: 1,
imported_runtime_record_count: 1,
records: vec![crate::SmpLoadedPackedEventRecordSummary {
record_index: 0,
live_entry_id: 25,
payload_offset: Some(0x7200),
payload_len: Some(168),
decode_status: "executable".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: packed_text_bands(),
standalone_condition_row_count: 0,
standalone_condition_rows: vec![],
negative_sentinel_scope: None,
grouped_effect_row_counts: vec![3, 0, 0, 0],
grouped_effect_rows: vec![
real_world_flag_row(111, "Disable Margin Buying/Short Selling Stock", true),
real_world_flag_row(120, "Disable All Track Building", true),
real_world_flag_row(131, "Disable Starting Any Companies", false),
],
decoded_conditions: Vec::new(),
decoded_actions: vec![
RuntimeEffect::SetWorldFlag {
key: "world.disable_margin_buying_short_selling_stock".to_string(),
value: true,
},
RuntimeEffect::SetWorldFlag {
key: "world.disable_all_track_building".to_string(),
value: true,
},
RuntimeEffect::SetWorldFlag {
key: "world.disable_starting_any_companies".to_string(),
value: false,
},
],
executable_import_ready: true,
notes: vec!["decoded from grounded real 0x4e9a row framing".to_string()],
}],
}),
notes: vec![],
};
let mut import = project_save_slice_overlay_to_runtime_state_import(
&base_state,
&save_slice,
"real-world-toggle-batch-overlay",
None,
)
.expect("overlay import should project");
crate::execute_step_command(
&mut import.state,
&crate::StepCommand::ServiceTriggerKind { trigger_kind: 7 },
)
.expect("trigger service should execute");
assert_eq!(
import
.state
.world_flags
.get("world.disable_margin_buying_short_selling_stock"),
Some(&true)
);
assert_eq!(
import
.state
.world_flags
.get("world.disable_all_track_building"),
Some(&true)
);
assert_eq!(
import
.state
.world_flags
.get("world.disable_starting_any_companies"),
Some(&false)
);
}
#[test]
fn overlays_recovered_late_world_toggle_batch_into_executable_runtime_record() {
let base_state = state();
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,
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: 26,
live_record_count: 1,
live_entry_ids: vec![26],
decoded_record_count: 1,
imported_runtime_record_count: 1,
records: vec![crate::SmpLoadedPackedEventRecordSummary {
record_index: 0,
live_entry_id: 26,
payload_offset: Some(0x7200),
payload_len: Some(184),
decode_status: "executable".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: packed_text_bands(),
standalone_condition_row_count: 0,
standalone_condition_rows: vec![],
negative_sentinel_scope: None,
grouped_effect_row_counts: vec![5, 0, 0, 0],
grouped_effect_rows: vec![
real_world_flag_row(139, "Use Bio-Accelerator Cars", true),
real_world_flag_row(140, "Disable Cargo Economy", true),
real_world_flag_row(142, "Disable Train Crashes", false),
real_world_flag_row(143, "Disable Train Crashes AND Breakdowns", true),
real_world_flag_row(144, "AI Ignore Territories At Startup", true),
],
decoded_conditions: Vec::new(),
decoded_actions: vec![
RuntimeEffect::SetWorldFlag {
key: "world.use_bio_accelerator_cars".to_string(),
value: true,
},
RuntimeEffect::SetWorldFlag {
key: "world.disable_cargo_economy".to_string(),
value: true,
},
RuntimeEffect::SetWorldFlag {
key: "world.disable_train_crashes".to_string(),
value: false,
},
RuntimeEffect::SetWorldFlag {
key: "world.disable_train_crashes_and_breakdowns".to_string(),
value: true,
},
RuntimeEffect::SetWorldFlag {
key: "world.ai_ignore_territories_at_startup".to_string(),
value: true,
},
],
executable_import_ready: true,
notes: vec!["decoded from grounded real 0x4e9a row framing".to_string()],
}],
}),
notes: vec![],
};
let mut import = project_save_slice_overlay_to_runtime_state_import(
&base_state,
&save_slice,
"real-late-world-toggle-batch-overlay",
None,
)
.expect("overlay import should project");
crate::execute_step_command(
&mut import.state,
&crate::StepCommand::ServiceTriggerKind { trigger_kind: 7 },
)
.expect("trigger service should execute");
assert_eq!(
import
.state
.world_flags
.get("world.use_bio_accelerator_cars"),
Some(&true)
);
assert_eq!(
import.state.world_flags.get("world.disable_cargo_economy"),
Some(&true)
);
assert_eq!(
import.state.world_flags.get("world.disable_train_crashes"),
Some(&false)
);
assert_eq!(
import
.state
.world_flags
.get("world.disable_train_crashes_and_breakdowns"),
Some(&true)
);
assert_eq!(
import
.state
.world_flags
.get("world.ai_ignore_territories_at_startup"),
Some(&true)
);
}
#[test]
fn overlays_real_confiscate_all_descriptor_into_executable_runtime_record() {
let base_state = RuntimeState {

View file

@ -242,6 +242,10 @@ pub enum RuntimeCondition {
comparator: RuntimeConditionComparator,
value: i64,
},
WorldFlagEquals {
key: String,
value: bool,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -1268,6 +1272,13 @@ fn validate_runtime_condition(
}
}
RuntimeCondition::EconomicStatusCodeThreshold { .. } => Ok(()),
RuntimeCondition::WorldFlagEquals { key, .. } => {
if key.trim().is_empty() {
Err("key must not be empty".to_string())
} else {
Ok(())
}
}
}
}

View file

@ -239,6 +239,7 @@ enum RealWorldConditionKind {
SpecialCondition { label: &'static str },
CandidateAvailability,
EconomicStatus,
WorldFlag { key: &'static str },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -2499,14 +2500,26 @@ fn real_ordinary_condition_metadata(
.find(|metadata| metadata.raw_condition_id == raw_condition_id)
.or_else(|| {
known_special_condition_definition_for_label_id(raw_condition_id as u32).map(
|definition| RealOrdinaryConditionMetadata {
raw_condition_id,
label: definition.label,
kind: RealOrdinaryConditionKind::WorldState(
RealWorldConditionKind::SpecialCondition {
label: definition.label,
},
),
|definition| {
let kind = if let Some(world_toggle) =
real_grouped_effect_descriptor_metadata(110 + definition.slot_index as u32)
.filter(|metadata| metadata.parameter_family == "world_flag_toggle")
{
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::WorldFlag {
key: world_toggle.runtime_key.unwrap_or(world_toggle.label),
})
} else {
RealOrdinaryConditionKind::WorldState(
RealWorldConditionKind::SpecialCondition {
label: definition.label,
},
)
};
RealOrdinaryConditionMetadata {
raw_condition_id,
label: definition.label,
kind,
}
},
)
})
@ -2532,6 +2545,9 @@ fn real_ordinary_condition_metric_label(
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::EconomicStatus) => {
"Economic Status".to_string()
}
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::WorldFlag { .. }) => {
format!("World Flag: {}", metadata.label)
}
}
}
@ -2540,6 +2556,9 @@ fn real_ordinary_condition_semantic_family(
) -> &'static str {
match metadata.kind {
RealOrdinaryConditionKind::Numeric(_) => "numeric_threshold",
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::WorldFlag { .. }) => {
"world_flag_equals"
}
RealOrdinaryConditionKind::WorldState(_) => "world_state_threshold",
}
}
@ -2737,9 +2756,28 @@ fn decode_real_condition_row(
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::EconomicStatus) => {
Some(RuntimeCondition::EconomicStatusCodeThreshold { comparator, value })
}
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::WorldFlag { key }) => {
decode_world_flag_condition(comparator, value, key)
}
}
}
fn decode_world_flag_condition(
comparator: RuntimeConditionComparator,
value: i64,
key: &'static str,
) -> Option<RuntimeCondition> {
let bool_value = match (comparator, value) {
(RuntimeConditionComparator::Eq, 0) | (RuntimeConditionComparator::Ne, 1) => false,
(RuntimeConditionComparator::Eq, 1) | (RuntimeConditionComparator::Ne, 0) => true,
_ => return None,
};
Some(RuntimeCondition::WorldFlagEquals {
key: key.to_string(),
value: bool_value,
})
}
fn real_grouped_effect_descriptor_metadata(
descriptor_id: u32,
) -> Option<RealGroupedEffectDescriptorMetadata> {
@ -2747,6 +2785,28 @@ fn real_grouped_effect_descriptor_metadata(
.iter()
.copied()
.find(|metadata| metadata.descriptor_id == descriptor_id)
.or_else(|| special_condition_world_toggle_descriptor_metadata(descriptor_id))
}
fn special_condition_world_toggle_descriptor_metadata(
descriptor_id: u32,
) -> Option<RealGroupedEffectDescriptorMetadata> {
let slot_index = descriptor_id.checked_sub(110)? as usize;
if !(1..=34).contains(&slot_index) || matches!(slot_index, 12 | 31) {
return None;
}
let definition = KNOWN_SPECIAL_CONDITION_DEFINITIONS.get(slot_index)?;
if definition.hidden {
return None;
}
Some(RealGroupedEffectDescriptorMetadata {
descriptor_id,
label: definition.label,
target_mask_bits: 0x08,
parameter_family: "world_flag_toggle",
runtime_key: None,
executable_in_runtime: true,
})
}
fn classify_real_grouped_effect_semantic_family(
@ -2830,7 +2890,32 @@ fn runtime_candidate_availability_name(label: &str) -> String {
fn runtime_world_flag_key(
descriptor_metadata: RealGroupedEffectDescriptorMetadata,
) -> Option<String> {
descriptor_metadata.runtime_key.map(str::to_string)
descriptor_metadata
.runtime_key
.map(str::to_string)
.or_else(|| {
(descriptor_metadata.parameter_family == "world_flag_toggle")
.then(|| runtime_world_flag_key_from_label(descriptor_metadata.label))
})
}
fn runtime_world_flag_key_from_label(label: &str) -> String {
let mut key = String::with_capacity(label.len() + 6);
key.push_str("world.");
let mut last_was_underscore = false;
for ch in label.chars() {
if ch.is_ascii_alphanumeric() {
key.push(ch.to_ascii_lowercase());
last_was_underscore = false;
} else if !last_was_underscore {
key.push('_');
last_was_underscore = true;
}
}
while key.ends_with('_') {
key.pop();
}
key
}
fn decode_real_grouped_effect_actions(
@ -2928,7 +3013,7 @@ fn decode_real_grouped_effect_action(
}
if descriptor_metadata.executable_in_runtime
&& descriptor_metadata.descriptor_id == 110
&& descriptor_metadata.parameter_family == "world_flag_toggle"
&& row.row_shape == "bool_toggle"
{
return Some(RuntimeEffect::SetWorldFlag {
@ -3232,7 +3317,8 @@ fn runtime_condition_supported_for_save_import(condition: &RuntimeCondition) ->
| RuntimeCondition::CompanyTerritoryNumericThreshold { .. }
| RuntimeCondition::SpecialConditionThreshold { .. }
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
| RuntimeCondition::EconomicStatusCodeThreshold { .. } => true,
| RuntimeCondition::EconomicStatusCodeThreshold { .. }
| RuntimeCondition::WorldFlagEquals { .. } => true,
}
}
@ -8855,6 +8941,67 @@ mod tests {
);
}
#[test]
fn decodes_real_world_flag_condition_from_checked_in_world_condition_metadata() {
let condition_row = build_real_condition_row_with_threshold(2535, 4, 1, None);
let record_body = build_real_event_record(
[b"World", b"", b"", b"", b"", b""],
Some(RealCompactControlSpec {
mode_byte_0x7ef: 7,
primary_selector_0x7f0: 0,
grouped_mode_0x7f4: 2,
one_shot_header_0x7f5: 0,
modifier_flag_0x7f9: 0,
modifier_flag_0x7fa: 0,
grouped_target_scope_ordinals_0x7fb: [0, 0, 0, 0],
grouped_scope_checkboxes_0x7ff: [0, 0, 0, 0],
summary_toggle_0x800: 1,
grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
}),
&[condition_row],
[&[], &[], &[], &[]],
);
let mut bytes = Vec::new();
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
for word in header_words {
bytes.extend_from_slice(&word.to_le_bytes());
}
bytes.extend_from_slice(&[0x00, 0x00]);
bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
bytes.extend_from_slice(&record_body);
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes());
let report = inspect_smp_bytes(&bytes);
let summary = report
.event_runtime_collection_summary
.as_ref()
.expect("event runtime collection summary should parse");
assert_eq!(
summary.records[0].standalone_condition_rows[0]
.metric
.as_deref(),
Some("World Flag: Disable Stock Buying and Selling")
);
assert_eq!(
summary.records[0].standalone_condition_rows[0]
.semantic_family
.as_deref(),
Some("world_flag_equals")
);
assert_eq!(
summary.records[0].decoded_conditions,
vec![RuntimeCondition::WorldFlagEquals {
key: "world.disable_stock_buying_and_selling".to_string(),
value: true,
}]
);
}
#[test]
fn looks_up_checked_in_world_flag_descriptor_metadata() {
let metadata =
@ -8869,6 +9016,34 @@ mod tests {
assert!(metadata.executable_in_runtime);
}
#[test]
fn looks_up_recovered_world_toggle_descriptor_metadata() {
let metadata =
real_grouped_effect_descriptor_metadata(111).expect("descriptor metadata should exist");
assert_eq!(metadata.label, "Disable Margin Buying/Short Selling Stock");
assert_eq!(metadata.parameter_family, "world_flag_toggle");
assert_eq!(
runtime_world_flag_key(metadata),
Some("world.disable_margin_buying_short_selling_stock".to_string())
);
assert!(metadata.executable_in_runtime);
}
#[test]
fn looks_up_recovered_late_world_toggle_descriptor_metadata() {
let metadata =
real_grouped_effect_descriptor_metadata(143).expect("descriptor metadata should exist");
assert_eq!(metadata.label, "Disable Train Crashes AND Breakdowns");
assert_eq!(metadata.parameter_family, "world_flag_toggle");
assert_eq!(
runtime_world_flag_key(metadata),
Some("world.disable_train_crashes_and_breakdowns".to_string())
);
assert!(metadata.executable_in_runtime);
}
#[test]
fn looks_up_checked_in_deactivate_player_descriptor_metadata() {
let metadata =
@ -9027,6 +9202,154 @@ mod tests {
assert!(summary.records[0].executable_import_ready);
}
#[test]
fn decodes_recovered_world_toggle_descriptor_family() {
let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
descriptor_id: 131,
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,
locomotive_name: None,
});
let group0_rows = vec![grouped_row];
let record_body = build_real_event_record(
[b"World", b"", b"", b"", b"", b""],
Some(RealCompactControlSpec {
mode_byte_0x7ef: 7,
primary_selector_0x7f0: 0,
grouped_mode_0x7f4: 2,
one_shot_header_0x7f5: 0,
modifier_flag_0x7f9: 0,
modifier_flag_0x7fa: 0,
grouped_target_scope_ordinals_0x7fb: [0, 0, 0, 0],
grouped_scope_checkboxes_0x7ff: [1, 0, 0, 0],
summary_toggle_0x800: 1,
grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
}),
&[],
[&group0_rows, &[], &[], &[]],
);
let mut bytes = Vec::new();
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
for word in header_words {
bytes.extend_from_slice(&word.to_le_bytes());
}
bytes.extend_from_slice(&[0x00, 0x00]);
bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
bytes.extend_from_slice(&record_body);
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes());
let report = inspect_smp_bytes(&bytes);
let summary = report
.event_runtime_collection_summary
.as_ref()
.expect("event runtime collection summary should parse");
assert_eq!(
summary.records[0].grouped_effect_rows[0]
.descriptor_label
.as_deref(),
Some("Disable Starting Any Companies")
);
assert_eq!(
summary.records[0].grouped_effect_rows[0]
.parameter_family
.as_deref(),
Some("world_flag_toggle")
);
assert_eq!(
summary.records[0].decoded_actions,
vec![RuntimeEffect::SetWorldFlag {
key: "world.disable_starting_any_companies".to_string(),
value: true,
}]
);
assert!(summary.records[0].executable_import_ready);
}
#[test]
fn decodes_recovered_late_world_toggle_descriptor_family() {
let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
descriptor_id: 144,
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,
locomotive_name: None,
});
let group0_rows = vec![grouped_row];
let record_body = build_real_event_record(
[b"World", b"", b"", b"", b"", b""],
Some(RealCompactControlSpec {
mode_byte_0x7ef: 7,
primary_selector_0x7f0: 0,
grouped_mode_0x7f4: 2,
one_shot_header_0x7f5: 0,
modifier_flag_0x7f9: 0,
modifier_flag_0x7fa: 0,
grouped_target_scope_ordinals_0x7fb: [0, 0, 0, 0],
grouped_scope_checkboxes_0x7ff: [1, 0, 0, 0],
summary_toggle_0x800: 1,
grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
}),
&[],
[&group0_rows, &[], &[], &[]],
);
let mut bytes = Vec::new();
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
for word in header_words {
bytes.extend_from_slice(&word.to_le_bytes());
}
bytes.extend_from_slice(&[0x00, 0x00]);
bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
bytes.extend_from_slice(&record_body);
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes());
let report = inspect_smp_bytes(&bytes);
let summary = report
.event_runtime_collection_summary
.as_ref()
.expect("event runtime collection summary should parse");
assert_eq!(
summary.records[0].grouped_effect_rows[0]
.descriptor_label
.as_deref(),
Some("AI Ignore Territories At Startup")
);
assert_eq!(
summary.records[0].grouped_effect_rows[0]
.parameter_family
.as_deref(),
Some("world_flag_toggle")
);
assert_eq!(
summary.records[0].decoded_actions,
vec![RuntimeEffect::SetWorldFlag {
key: "world.ai_ignore_territories_at_startup".to_string(),
value: true,
}]
);
assert!(summary.records[0].executable_import_ready);
}
#[test]
fn decodes_negative_sentinel_scope_modifiers_and_territory_marker() {
for (value, expected) in [

View file

@ -710,6 +710,12 @@ fn evaluate_record_conditions(
return Ok(None);
}
}
RuntimeCondition::WorldFlagEquals { key, value } => {
let actual = state.world_flags.get(key).copied().unwrap_or(false);
if actual != *value {
return Ok(None);
}
}
}
}
@ -1889,6 +1895,10 @@ mod tests {
economic_status_code: Some(3),
..RuntimeWorldRestoreState::default()
},
world_flags: BTreeMap::from([(
String::from("world.disable_stock_buying_and_selling"),
true,
)]),
candidate_availability: BTreeMap::from([(String::from("Mogul"), 2)]),
special_conditions: BTreeMap::from([(String::from("Use Wartime Cargos"), 1)]),
event_runtime_records: vec![
@ -1915,6 +1925,10 @@ mod tests {
comparator: RuntimeConditionComparator::Eq,
value: 3,
},
RuntimeCondition::WorldFlagEquals {
key: "world.disable_stock_buying_and_selling".to_string(),
value: true,
},
],
effects: vec![RuntimeEffect::SetWorldFlag {
key: "world_condition_passed".to_string(),