Execute real packed event player deactivation
This commit is contained in:
parent
e44e0d5ac5
commit
991725dba8
10 changed files with 670 additions and 47 deletions
|
|
@ -128,7 +128,7 @@ struct RealGroupedEffectDescriptorMetadata {
|
|||
executable_in_runtime: bool,
|
||||
}
|
||||
|
||||
const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetadata; 11] = [
|
||||
const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetadata; 12] = [
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id: 1,
|
||||
label: "Player Cash",
|
||||
|
|
@ -201,6 +201,14 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad
|
|||
runtime_key: None,
|
||||
executable_in_runtime: true,
|
||||
},
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id: 14,
|
||||
label: "Deactivate Player",
|
||||
target_mask_bits: 0x02,
|
||||
parameter_family: "player_lifecycle_toggle",
|
||||
runtime_key: None,
|
||||
executable_in_runtime: true,
|
||||
},
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id: 15,
|
||||
label: "Retire Train",
|
||||
|
|
@ -2378,8 +2386,8 @@ fn parse_real_condition_row_summary(
|
|||
let comparator = ordinary_metadata
|
||||
.and_then(|_| decode_real_condition_comparator(subtype))
|
||||
.map(condition_comparator_label);
|
||||
let metric =
|
||||
ordinary_metadata.map(|metadata| real_ordinary_condition_metric_label(metadata, candidate_name_ref));
|
||||
let metric = ordinary_metadata
|
||||
.map(|metadata| real_ordinary_condition_metric_label(metadata, candidate_name_ref));
|
||||
let threshold = ordinary_metadata.and_then(|_| decode_real_condition_threshold(&flag_bytes));
|
||||
let requires_candidate_name_binding = ordinary_metadata.is_some_and(|metadata| {
|
||||
matches!(
|
||||
|
|
@ -2409,7 +2417,10 @@ fn parse_real_condition_row_summary(
|
|||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CandidateAvailability)
|
||||
) && candidate_name.is_none()
|
||||
}) {
|
||||
notes.push("candidate-availability condition row is missing its candidate-name side string".to_string());
|
||||
notes.push(
|
||||
"candidate-availability condition row is missing its candidate-name side string"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Some(SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index,
|
||||
|
|
@ -2507,7 +2518,9 @@ fn real_ordinary_condition_metric_label(
|
|||
) -> String {
|
||||
match metadata.kind {
|
||||
RealOrdinaryConditionKind::Numeric(_) => metadata.label.to_string(),
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::SpecialCondition { label }) => {
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::SpecialCondition {
|
||||
label,
|
||||
}) => {
|
||||
format!("Special Condition: {label}")
|
||||
}
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CandidateAvailability) => {
|
||||
|
|
@ -2522,7 +2535,9 @@ fn real_ordinary_condition_metric_label(
|
|||
}
|
||||
}
|
||||
|
||||
fn real_ordinary_condition_semantic_family(metadata: RealOrdinaryConditionMetadata) -> &'static str {
|
||||
fn real_ordinary_condition_semantic_family(
|
||||
metadata: RealOrdinaryConditionMetadata,
|
||||
) -> &'static str {
|
||||
match metadata.kind {
|
||||
RealOrdinaryConditionKind::Numeric(_) => "numeric_threshold",
|
||||
RealOrdinaryConditionKind::WorldState(_) => "world_state_threshold",
|
||||
|
|
@ -2685,16 +2700,17 @@ fn decode_real_condition_row(
|
|||
}
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Territory(metric)) => {
|
||||
negative_sentinel_scope
|
||||
.filter(|scope| scope.territory_scope_selector_is_0x63)
|
||||
.map(|_| RuntimeCondition::TerritoryNumericThreshold {
|
||||
target: RuntimeTerritoryTarget::AllTerritories,
|
||||
metric,
|
||||
comparator,
|
||||
value,
|
||||
})
|
||||
.filter(|scope| scope.territory_scope_selector_is_0x63)
|
||||
.map(|_| RuntimeCondition::TerritoryNumericThreshold {
|
||||
target: RuntimeTerritoryTarget::AllTerritories,
|
||||
metric,
|
||||
comparator,
|
||||
value,
|
||||
})
|
||||
}
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyTerritory(metric)) => {
|
||||
negative_sentinel_scope
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyTerritory(
|
||||
metric,
|
||||
)) => negative_sentinel_scope
|
||||
.filter(|scope| scope.territory_scope_selector_is_0x63)
|
||||
.map(|_| RuntimeCondition::CompanyTerritoryNumericThreshold {
|
||||
target: RuntimeCompanyTarget::ConditionTrueCompany,
|
||||
|
|
@ -2702,8 +2718,7 @@ fn decode_real_condition_row(
|
|||
metric,
|
||||
comparator,
|
||||
value,
|
||||
})
|
||||
}
|
||||
}),
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::SpecialCondition {
|
||||
label,
|
||||
}) => Some(RuntimeCondition::SpecialConditionThreshold {
|
||||
|
|
@ -2711,15 +2726,14 @@ fn decode_real_condition_row(
|
|||
comparator,
|
||||
value,
|
||||
}),
|
||||
RealOrdinaryConditionKind::WorldState(
|
||||
RealWorldConditionKind::CandidateAvailability,
|
||||
) => row.candidate_name.as_ref().map(|name| {
|
||||
RuntimeCondition::CandidateAvailabilityThreshold {
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CandidateAvailability) => row
|
||||
.candidate_name
|
||||
.as_ref()
|
||||
.map(|name| RuntimeCondition::CandidateAvailabilityThreshold {
|
||||
name: name.clone(),
|
||||
comparator,
|
||||
value,
|
||||
}
|
||||
}),
|
||||
}),
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::EconomicStatus) => {
|
||||
Some(RuntimeCondition::EconomicStatusCodeThreshold { comparator, value })
|
||||
}
|
||||
|
|
@ -2813,7 +2827,9 @@ fn runtime_candidate_availability_name(label: &str) -> String {
|
|||
.to_string()
|
||||
}
|
||||
|
||||
fn runtime_world_flag_key(descriptor_metadata: RealGroupedEffectDescriptorMetadata) -> Option<String> {
|
||||
fn runtime_world_flag_key(
|
||||
descriptor_metadata: RealGroupedEffectDescriptorMetadata,
|
||||
) -> Option<String> {
|
||||
descriptor_metadata.runtime_key.map(str::to_string)
|
||||
}
|
||||
|
||||
|
|
@ -2939,6 +2955,15 @@ fn decode_real_grouped_effect_action(
|
|||
return Some(RuntimeEffect::DeactivateCompany { target });
|
||||
}
|
||||
|
||||
if descriptor_metadata.executable_in_runtime
|
||||
&& descriptor_metadata.descriptor_id == 14
|
||||
&& row.row_shape == "bool_toggle"
|
||||
&& row.raw_scalar_value != 0
|
||||
{
|
||||
let target = real_grouped_player_target(target_scope_ordinal)?;
|
||||
return Some(RuntimeEffect::DeactivatePlayer { target });
|
||||
}
|
||||
|
||||
if descriptor_metadata.executable_in_runtime
|
||||
&& descriptor_metadata.descriptor_id == 16
|
||||
&& row.row_shape == "scalar_assignment"
|
||||
|
|
@ -3151,6 +3176,7 @@ fn runtime_effect_supported_for_save_import(effect: &RuntimeEffect) -> bool {
|
|||
| RuntimeEffect::SetSpecialCondition { .. }
|
||||
| RuntimeEffect::ConfiscateCompanyAssets { .. }
|
||||
| RuntimeEffect::DeactivateCompany { .. }
|
||||
| RuntimeEffect::DeactivatePlayer { .. }
|
||||
| RuntimeEffect::SetCompanyTrackLayingCapacity { .. }
|
||||
| RuntimeEffect::RetireTrains { .. }
|
||||
| RuntimeEffect::ActivateEventRecord { .. }
|
||||
|
|
@ -8715,8 +8741,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn decodes_real_candidate_availability_threshold_from_checked_in_world_condition_metadata() {
|
||||
let condition_row =
|
||||
build_real_condition_row_with_threshold(REAL_CANDIDATE_AVAILABILITY_CONDITION_TEMPLATE_ID, 0, 2, Some("Mogul"));
|
||||
let condition_row = build_real_condition_row_with_threshold(
|
||||
REAL_CANDIDATE_AVAILABILITY_CONDITION_TEMPLATE_ID,
|
||||
0,
|
||||
2,
|
||||
Some("Mogul"),
|
||||
);
|
||||
let record_body = build_real_event_record(
|
||||
[b"World", b"", b"", b"", b"", b""],
|
||||
Some(RealCompactControlSpec {
|
||||
|
|
@ -8839,6 +8869,90 @@ mod tests {
|
|||
assert!(metadata.executable_in_runtime);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn looks_up_checked_in_deactivate_player_descriptor_metadata() {
|
||||
let metadata =
|
||||
real_grouped_effect_descriptor_metadata(14).expect("descriptor metadata should exist");
|
||||
|
||||
assert_eq!(metadata.label, "Deactivate Player");
|
||||
assert_eq!(metadata.parameter_family, "player_lifecycle_toggle");
|
||||
assert_eq!(metadata.runtime_key, None);
|
||||
assert!(metadata.executable_in_runtime);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_real_deactivate_player_descriptor_from_checked_in_metadata() {
|
||||
let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
|
||||
descriptor_id: 14,
|
||||
opcode: 1,
|
||||
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"Players", 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: [1, 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("Deactivate Player")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[0]
|
||||
.parameter_family
|
||||
.as_deref(),
|
||||
Some("player_lifecycle_toggle")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].decoded_actions,
|
||||
vec![RuntimeEffect::DeactivatePlayer {
|
||||
target: RuntimePlayerTarget::SelectedPlayer,
|
||||
}]
|
||||
);
|
||||
assert!(summary.records[0].executable_import_ready);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_real_world_flag_descriptor_with_checked_in_runtime_key() {
|
||||
let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue