Execute real packed event player deactivation
This commit is contained in:
parent
e44e0d5ac5
commit
991725dba8
10 changed files with 670 additions and 47 deletions
|
|
@ -1051,6 +1051,12 @@ fn lower_condition_targets_in_effect(
|
|||
)?,
|
||||
value: *value,
|
||||
},
|
||||
RuntimeEffect::DeactivatePlayer { target } => RuntimeEffect::DeactivatePlayer {
|
||||
target: lower_condition_true_player_target_in_player_target(
|
||||
target,
|
||||
lowered_player_target,
|
||||
)?,
|
||||
},
|
||||
RuntimeEffect::SetCompanyTerritoryAccess {
|
||||
target,
|
||||
territory,
|
||||
|
|
@ -1378,6 +1384,19 @@ fn smp_runtime_effect_to_runtime_effect(
|
|||
Err(player_target_import_error_message(target, company_context))
|
||||
}
|
||||
}
|
||||
RuntimeEffect::DeactivatePlayer { target } => {
|
||||
if player_target_allowed_for_import(
|
||||
target,
|
||||
company_context,
|
||||
allow_condition_true_player,
|
||||
) {
|
||||
Ok(RuntimeEffect::DeactivatePlayer {
|
||||
target: target.clone(),
|
||||
})
|
||||
} else {
|
||||
Err(player_target_import_error_message(target, company_context))
|
||||
}
|
||||
}
|
||||
RuntimeEffect::SetCompanyTerritoryAccess {
|
||||
target,
|
||||
territory,
|
||||
|
|
@ -2075,6 +2094,7 @@ fn runtime_effect_uses_condition_true_company(effect: &RuntimeEffect) -> bool {
|
|||
RuntimeEffect::SetWorldFlag { .. }
|
||||
| RuntimeEffect::SetEconomicStatusCode { .. }
|
||||
| RuntimeEffect::SetPlayerCash { .. }
|
||||
| RuntimeEffect::DeactivatePlayer { .. }
|
||||
| RuntimeEffect::SetCandidateAvailability { .. }
|
||||
| RuntimeEffect::SetSpecialCondition { .. }
|
||||
| RuntimeEffect::ActivateEventRecord { .. }
|
||||
|
|
@ -2088,6 +2108,9 @@ fn runtime_effect_uses_condition_true_player(effect: &RuntimeEffect) -> bool {
|
|||
RuntimeEffect::SetPlayerCash { target, .. } => {
|
||||
matches!(target, RuntimePlayerTarget::ConditionTruePlayer)
|
||||
}
|
||||
RuntimeEffect::DeactivatePlayer { target } => {
|
||||
matches!(target, RuntimePlayerTarget::ConditionTruePlayer)
|
||||
}
|
||||
RuntimeEffect::AppendEventRecord { record } => record
|
||||
.effects
|
||||
.iter()
|
||||
|
|
@ -2119,7 +2142,8 @@ fn runtime_effect_company_target_import_blocker(
|
|||
company_target_import_blocker(target, company_context)
|
||||
}
|
||||
}
|
||||
RuntimeEffect::SetPlayerCash { target, .. } => {
|
||||
RuntimeEffect::SetPlayerCash { target, .. }
|
||||
| RuntimeEffect::DeactivatePlayer { target } => {
|
||||
player_target_import_blocker(target, company_context)
|
||||
}
|
||||
RuntimeEffect::RetireTrains {
|
||||
|
|
@ -2685,6 +2709,35 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn real_deactivate_player_row(
|
||||
enabled: bool,
|
||||
) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
crate::SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
group_index: 0,
|
||||
row_index: 0,
|
||||
descriptor_id: 14,
|
||||
descriptor_label: Some("Deactivate Player".to_string()),
|
||||
target_mask_bits: Some(0x02),
|
||||
parameter_family: Some("player_lifecycle_toggle".to_string()),
|
||||
opcode: 1,
|
||||
raw_scalar_value: if enabled { 1 } else { 0 },
|
||||
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".to_string(),
|
||||
semantic_family: Some("bool_toggle".to_string()),
|
||||
semantic_preview: Some(format!(
|
||||
"Set Deactivate Player to {}",
|
||||
if enabled { "TRUE" } else { "FALSE" }
|
||||
)),
|
||||
locomotive_name: None,
|
||||
notes: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn real_territory_access_row(
|
||||
enabled: bool,
|
||||
notes: Vec<String>,
|
||||
|
|
@ -4938,6 +4991,208 @@ mod tests {
|
|||
assert_eq!(import.state.selected_company_id, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlays_real_deactivate_player_descriptor_into_executable_runtime_record() {
|
||||
let base_state = RuntimeState {
|
||||
players: vec![
|
||||
crate::RuntimePlayer {
|
||||
player_id: 7,
|
||||
current_cash: 500,
|
||||
active: true,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
},
|
||||
crate::RuntimePlayer {
|
||||
player_id: 8,
|
||||
current_cash: 250,
|
||||
active: true,
|
||||
controller_kind: RuntimeCompanyControllerKind::Ai,
|
||||
},
|
||||
],
|
||||
selected_player_id: Some(7),
|
||||
..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: 18,
|
||||
live_record_count: 1,
|
||||
live_entry_ids: vec![18],
|
||||
decoded_record_count: 1,
|
||||
imported_runtime_record_count: 0,
|
||||
records: vec![crate::SmpLoadedPackedEventRecordSummary {
|
||||
record_index: 0,
|
||||
live_entry_id: 18,
|
||||
payload_offset: Some(0x7202),
|
||||
payload_len: Some(120),
|
||||
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(crate::SmpLoadedPackedEventCompactControlSummary {
|
||||
mode_byte_0x7ef: 7,
|
||||
primary_selector_0x7f0: 0x63,
|
||||
grouped_mode_0x7f4: 2,
|
||||
one_shot_header_0x7f5: 0,
|
||||
modifier_flag_0x7f9: 0,
|
||||
modifier_flag_0x7fa: 2,
|
||||
grouped_target_scope_ordinals_0x7fb: vec![0, 0, 0, 0],
|
||||
grouped_scope_checkboxes_0x7ff: vec![1, 0, 0, 0],
|
||||
summary_toggle_0x800: 1,
|
||||
grouped_territory_selectors_0x80f: vec![-1, -1, -1, -1],
|
||||
}),
|
||||
text_bands: packed_text_bands(),
|
||||
standalone_condition_row_count: 1,
|
||||
standalone_condition_rows: real_condition_rows(),
|
||||
negative_sentinel_scope: Some(
|
||||
crate::SmpLoadedPackedEventNegativeSentinelScopeSummary {
|
||||
company_test_scope: RuntimeCompanyConditionTestScope::Disabled,
|
||||
player_test_scope: RuntimePlayerConditionTestScope::SelectedPlayerOnly,
|
||||
territory_scope_selector_is_0x63: false,
|
||||
source_row_indexes: vec![0],
|
||||
},
|
||||
),
|
||||
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
||||
grouped_effect_rows: vec![real_deactivate_player_row(true)],
|
||||
decoded_conditions: Vec::new(),
|
||||
decoded_actions: vec![RuntimeEffect::DeactivatePlayer {
|
||||
target: RuntimePlayerTarget::ConditionTruePlayer,
|
||||
}],
|
||||
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-deactivate-player-overlay",
|
||||
None,
|
||||
)
|
||||
.expect("overlay import should project");
|
||||
|
||||
assert_eq!(import.state.event_runtime_records.len(), 1);
|
||||
execute_step_command(
|
||||
&mut import.state,
|
||||
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||
)
|
||||
.expect("real deactivate-player descriptor should execute");
|
||||
|
||||
assert!(!import.state.players[0].active);
|
||||
assert!(import.state.players[1].active);
|
||||
assert_eq!(import.state.selected_player_id, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_real_deactivate_player_false_row_parity_only() {
|
||||
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: 19,
|
||||
live_record_count: 1,
|
||||
live_entry_ids: vec![19],
|
||||
decoded_record_count: 1,
|
||||
imported_runtime_record_count: 0,
|
||||
records: vec![crate::SmpLoadedPackedEventRecordSummary {
|
||||
record_index: 0,
|
||||
live_entry_id: 19,
|
||||
payload_offset: Some(0x7202),
|
||||
payload_len: Some(120),
|
||||
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(crate::SmpLoadedPackedEventCompactControlSummary {
|
||||
mode_byte_0x7ef: 7,
|
||||
primary_selector_0x7f0: 0x63,
|
||||
grouped_mode_0x7f4: 2,
|
||||
one_shot_header_0x7f5: 0,
|
||||
modifier_flag_0x7f9: 0,
|
||||
modifier_flag_0x7fa: 2,
|
||||
grouped_target_scope_ordinals_0x7fb: vec![0, 0, 0, 0],
|
||||
grouped_scope_checkboxes_0x7ff: vec![1, 0, 0, 0],
|
||||
summary_toggle_0x800: 1,
|
||||
grouped_territory_selectors_0x80f: vec![-1, -1, -1, -1],
|
||||
}),
|
||||
text_bands: packed_text_bands(),
|
||||
standalone_condition_row_count: 1,
|
||||
standalone_condition_rows: real_condition_rows(),
|
||||
negative_sentinel_scope: Some(
|
||||
crate::SmpLoadedPackedEventNegativeSentinelScopeSummary {
|
||||
company_test_scope: RuntimeCompanyConditionTestScope::Disabled,
|
||||
player_test_scope: RuntimePlayerConditionTestScope::SelectedPlayerOnly,
|
||||
territory_scope_selector_is_0x63: false,
|
||||
source_row_indexes: vec![0],
|
||||
},
|
||||
),
|
||||
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
||||
grouped_effect_rows: vec![real_deactivate_player_row(false)],
|
||||
decoded_conditions: Vec::new(),
|
||||
decoded_actions: vec![],
|
||||
executable_import_ready: false,
|
||||
notes: vec!["decoded from grounded real 0x4e9a row framing".to_string()],
|
||||
}],
|
||||
}),
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
let import = project_save_slice_to_runtime_state_import(
|
||||
&save_slice,
|
||||
"real-deactivate-player-false",
|
||||
None,
|
||||
)
|
||||
.expect("save slice should project");
|
||||
|
||||
assert!(import.state.event_runtime_records.is_empty());
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.packed_event_collection
|
||||
.as_ref()
|
||||
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
|
||||
Some("blocked_unmapped_real_descriptor")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_real_deactivate_company_false_row_parity_only() {
|
||||
let save_slice = SmpLoadedSaveSlice {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue