Unlock negative-sentinel company condition scopes
This commit is contained in:
parent
780e739daa
commit
087ebf1097
18 changed files with 1315 additions and 79 deletions
|
|
@ -4,7 +4,10 @@ use std::path::Path;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::{RuntimeCompanyTarget, RuntimeEffect, RuntimeEventRecordTemplate};
|
||||
use crate::{
|
||||
RuntimeCompanyConditionTestScope, RuntimeCompanyTarget, RuntimeEffect,
|
||||
RuntimeEventRecordTemplate, RuntimePlayerConditionTestScope,
|
||||
};
|
||||
|
||||
pub const SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION: u32 = 0x03ec;
|
||||
const PREAMBLE_U32_WORD_COUNT: usize = 16;
|
||||
|
|
@ -1312,6 +1315,8 @@ pub struct SmpLoadedPackedEventRecordSummary {
|
|||
#[serde(default)]
|
||||
pub standalone_condition_rows: Vec<SmpLoadedPackedEventConditionRowSummary>,
|
||||
#[serde(default)]
|
||||
pub negative_sentinel_scope: Option<SmpLoadedPackedEventNegativeSentinelScopeSummary>,
|
||||
#[serde(default)]
|
||||
pub grouped_effect_row_counts: Vec<usize>,
|
||||
#[serde(default)]
|
||||
pub grouped_effect_rows: Vec<SmpLoadedPackedEventGroupedEffectRowSummary>,
|
||||
|
|
@ -1323,6 +1328,15 @@ pub struct SmpLoadedPackedEventRecordSummary {
|
|||
pub notes: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SmpLoadedPackedEventNegativeSentinelScopeSummary {
|
||||
pub company_test_scope: RuntimeCompanyConditionTestScope,
|
||||
pub player_test_scope: RuntimePlayerConditionTestScope,
|
||||
pub territory_scope_selector_is_0x63: bool,
|
||||
#[serde(default)]
|
||||
pub source_row_indexes: Vec<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SmpLoadedPackedEventCompactControlSummary {
|
||||
pub mode_byte_0x7ef: u8,
|
||||
|
|
@ -1836,6 +1850,7 @@ fn parse_synthetic_event_runtime_record_summary(
|
|||
text_bands,
|
||||
standalone_condition_row_count,
|
||||
standalone_condition_rows: Vec::new(),
|
||||
negative_sentinel_scope: None,
|
||||
grouped_effect_row_counts,
|
||||
grouped_effect_rows: Vec::new(),
|
||||
decoded_actions,
|
||||
|
|
@ -1940,6 +1955,9 @@ fn parse_real_event_runtime_record_summary(
|
|||
}
|
||||
}
|
||||
|
||||
let negative_sentinel_scope = compact_control.as_ref().and_then(|control| {
|
||||
derive_negative_sentinel_scope_summary(&standalone_condition_rows, control)
|
||||
});
|
||||
let decoded_actions = compact_control
|
||||
.as_ref()
|
||||
.map(|control| decode_real_grouped_effect_actions(&grouped_effect_rows, control))
|
||||
|
|
@ -1968,6 +1986,7 @@ fn parse_real_event_runtime_record_summary(
|
|||
text_bands,
|
||||
standalone_condition_row_count,
|
||||
standalone_condition_rows,
|
||||
negative_sentinel_scope,
|
||||
grouped_effect_row_counts,
|
||||
grouped_effect_rows,
|
||||
decoded_actions,
|
||||
|
|
@ -2074,6 +2093,49 @@ fn parse_real_condition_row_summary(
|
|||
})
|
||||
}
|
||||
|
||||
fn derive_negative_sentinel_scope_summary(
|
||||
rows: &[SmpLoadedPackedEventConditionRowSummary],
|
||||
control: &SmpLoadedPackedEventCompactControlSummary,
|
||||
) -> Option<SmpLoadedPackedEventNegativeSentinelScopeSummary> {
|
||||
let source_row_indexes = rows
|
||||
.iter()
|
||||
.filter(|row| row.raw_condition_id == -1)
|
||||
.map(|row| row.row_index)
|
||||
.collect::<Vec<_>>();
|
||||
if source_row_indexes.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(SmpLoadedPackedEventNegativeSentinelScopeSummary {
|
||||
company_test_scope: decode_company_condition_test_scope(control.modifier_flag_0x7f9)?,
|
||||
player_test_scope: decode_player_condition_test_scope(control.modifier_flag_0x7fa)?,
|
||||
territory_scope_selector_is_0x63: control.primary_selector_0x7f0 == 0x63,
|
||||
source_row_indexes,
|
||||
})
|
||||
}
|
||||
|
||||
fn decode_company_condition_test_scope(value: u8) -> Option<RuntimeCompanyConditionTestScope> {
|
||||
match value {
|
||||
0 => Some(RuntimeCompanyConditionTestScope::Disabled),
|
||||
1 => Some(RuntimeCompanyConditionTestScope::AllCompanies),
|
||||
2 => Some(RuntimeCompanyConditionTestScope::SelectedCompanyOnly),
|
||||
3 => Some(RuntimeCompanyConditionTestScope::AiCompaniesOnly),
|
||||
4 => Some(RuntimeCompanyConditionTestScope::HumanCompaniesOnly),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_player_condition_test_scope(value: u8) -> Option<RuntimePlayerConditionTestScope> {
|
||||
match value {
|
||||
0 => Some(RuntimePlayerConditionTestScope::Disabled),
|
||||
1 => Some(RuntimePlayerConditionTestScope::AllPlayers),
|
||||
2 => Some(RuntimePlayerConditionTestScope::SelectedPlayerOnly),
|
||||
3 => Some(RuntimePlayerConditionTestScope::AiPlayersOnly),
|
||||
4 => Some(RuntimePlayerConditionTestScope::HumanPlayersOnly),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_real_grouped_effect_row_summary(
|
||||
row_bytes: &[u8],
|
||||
group_index: usize,
|
||||
|
|
@ -2484,6 +2546,7 @@ fn build_unsupported_event_runtime_record_summaries(
|
|||
text_bands: Vec::new(),
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: Vec::new(),
|
||||
negative_sentinel_scope: None,
|
||||
grouped_effect_row_counts: vec![0, 0, 0, 0],
|
||||
grouped_effect_rows: Vec::new(),
|
||||
decoded_actions: Vec::new(),
|
||||
|
|
@ -7603,6 +7666,7 @@ mod tests {
|
|||
assert_eq!(summary.records[0].text_bands[0].preview, "Alpha");
|
||||
assert_eq!(summary.records[0].standalone_condition_row_count, 0);
|
||||
assert_eq!(summary.records[0].standalone_condition_rows.len(), 0);
|
||||
assert!(summary.records[0].negative_sentinel_scope.is_none());
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_row_counts,
|
||||
vec![0, 0, 0, 0]
|
||||
|
|
@ -7682,6 +7746,20 @@ mod tests {
|
|||
.as_deref(),
|
||||
Some("AutoPlant")
|
||||
);
|
||||
let negative_sentinel_scope = summary.records[0]
|
||||
.negative_sentinel_scope
|
||||
.as_ref()
|
||||
.expect("negative-sentinel scope summary should decode");
|
||||
assert_eq!(
|
||||
negative_sentinel_scope.company_test_scope,
|
||||
RuntimeCompanyConditionTestScope::SelectedCompanyOnly
|
||||
);
|
||||
assert_eq!(
|
||||
negative_sentinel_scope.player_test_scope,
|
||||
RuntimePlayerConditionTestScope::AiPlayersOnly
|
||||
);
|
||||
assert!(!negative_sentinel_scope.territory_scope_selector_is_0x63);
|
||||
assert_eq!(negative_sentinel_scope.source_row_indexes, vec![0]);
|
||||
assert_eq!(summary.records[0].grouped_effect_rows.len(), 1);
|
||||
assert_eq!(summary.records[0].grouped_effect_rows[0].opcode, 8);
|
||||
assert_eq!(
|
||||
|
|
@ -7725,6 +7803,63 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_negative_sentinel_scope_modifiers_and_territory_marker() {
|
||||
for (value, expected) in [
|
||||
(0, RuntimeCompanyConditionTestScope::Disabled),
|
||||
(1, RuntimeCompanyConditionTestScope::AllCompanies),
|
||||
(2, RuntimeCompanyConditionTestScope::SelectedCompanyOnly),
|
||||
(3, RuntimeCompanyConditionTestScope::AiCompaniesOnly),
|
||||
(4, RuntimeCompanyConditionTestScope::HumanCompaniesOnly),
|
||||
] {
|
||||
assert_eq!(decode_company_condition_test_scope(value), Some(expected));
|
||||
}
|
||||
for (value, expected) in [
|
||||
(0, RuntimePlayerConditionTestScope::Disabled),
|
||||
(1, RuntimePlayerConditionTestScope::AllPlayers),
|
||||
(2, RuntimePlayerConditionTestScope::SelectedPlayerOnly),
|
||||
(3, RuntimePlayerConditionTestScope::AiPlayersOnly),
|
||||
(4, RuntimePlayerConditionTestScope::HumanPlayersOnly),
|
||||
] {
|
||||
assert_eq!(decode_player_condition_test_scope(value), Some(expected));
|
||||
}
|
||||
|
||||
let rows = vec![SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 0,
|
||||
raw_condition_id: -1,
|
||||
subtype: 4,
|
||||
flag_bytes: vec![0x30; 25],
|
||||
candidate_name: Some("AutoPlant".to_string()),
|
||||
notes: vec![],
|
||||
}];
|
||||
let summary = derive_negative_sentinel_scope_summary(
|
||||
&rows,
|
||||
&SmpLoadedPackedEventCompactControlSummary {
|
||||
mode_byte_0x7ef: 6,
|
||||
primary_selector_0x7f0: 0x63,
|
||||
grouped_mode_0x7f4: 2,
|
||||
one_shot_header_0x7f5: 1,
|
||||
modifier_flag_0x7f9: 4,
|
||||
modifier_flag_0x7fa: 2,
|
||||
grouped_target_scope_ordinals_0x7fb: vec![0, 1, 2, 3],
|
||||
grouped_scope_checkboxes_0x7ff: vec![1, 0, 0, 0],
|
||||
summary_toggle_0x800: 1,
|
||||
grouped_territory_selectors_0x80f: vec![-1, -1, -1, -1],
|
||||
},
|
||||
)
|
||||
.expect("negative sentinel summary should derive");
|
||||
assert_eq!(
|
||||
summary.company_test_scope,
|
||||
RuntimeCompanyConditionTestScope::HumanCompaniesOnly
|
||||
);
|
||||
assert_eq!(
|
||||
summary.player_test_scope,
|
||||
RuntimePlayerConditionTestScope::SelectedPlayerOnly
|
||||
);
|
||||
assert!(summary.territory_scope_selector_is_0x63);
|
||||
assert_eq!(summary.source_row_indexes, vec![0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classifies_real_grouped_row_semantic_families() {
|
||||
let grouped_rows = vec![
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue