Recover whole-game packed event conditions
This commit is contained in:
parent
a3f9a73766
commit
5d779263d2
7 changed files with 414 additions and 117 deletions
|
|
@ -214,127 +214,192 @@ enum RealOrdinaryConditionMetric {
|
|||
CompanyTerritory(RuntimeTrackMetric),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum RealWorldConditionKind {
|
||||
SpecialCondition { label: &'static str },
|
||||
CandidateAvailability,
|
||||
EconomicStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum RealOrdinaryConditionKind {
|
||||
Numeric(RealOrdinaryConditionMetric),
|
||||
WorldState(RealWorldConditionKind),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: i32,
|
||||
label: &'static str,
|
||||
metric: RealOrdinaryConditionMetric,
|
||||
kind: RealOrdinaryConditionKind,
|
||||
}
|
||||
|
||||
const REAL_ORDINARY_CONDITION_METADATA: [RealOrdinaryConditionMetadata; 22] = [
|
||||
const REAL_CANDIDATE_AVAILABILITY_CONDITION_TEMPLATE_ID: i32 = 435;
|
||||
|
||||
const REAL_ORDINARY_CONDITION_METADATA: [RealOrdinaryConditionMetadata; 24] = [
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 1802,
|
||||
label: "Current Cash",
|
||||
metric: RealOrdinaryConditionMetric::Company(RuntimeCompanyMetric::CurrentCash),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(
|
||||
RuntimeCompanyMetric::CurrentCash,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 951,
|
||||
label: "Total Debt",
|
||||
metric: RealOrdinaryConditionMetric::Company(RuntimeCompanyMetric::TotalDebt),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(
|
||||
RuntimeCompanyMetric::TotalDebt,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2366,
|
||||
label: "Credit Rating",
|
||||
metric: RealOrdinaryConditionMetric::Company(RuntimeCompanyMetric::CreditRating),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(
|
||||
RuntimeCompanyMetric::CreditRating,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2368,
|
||||
label: "Prime Rate",
|
||||
metric: RealOrdinaryConditionMetric::Company(RuntimeCompanyMetric::PrimeRate),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(
|
||||
RuntimeCompanyMetric::PrimeRate,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2293,
|
||||
label: "Company Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::Company(RuntimeCompanyMetric::TrackPiecesTotal),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(
|
||||
RuntimeCompanyMetric::TrackPiecesTotal,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2294,
|
||||
label: "Company Single Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::Company(RuntimeCompanyMetric::TrackPiecesSingle),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(
|
||||
RuntimeCompanyMetric::TrackPiecesSingle,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2295,
|
||||
label: "Company Double Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::Company(RuntimeCompanyMetric::TrackPiecesDouble),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(
|
||||
RuntimeCompanyMetric::TrackPiecesDouble,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2296,
|
||||
label: "Company Transition Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::Company(RuntimeCompanyMetric::TrackPiecesTransition),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(
|
||||
RuntimeCompanyMetric::TrackPiecesTransition,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2297,
|
||||
label: "Company Electric Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::Company(RuntimeCompanyMetric::TrackPiecesElectric),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(
|
||||
RuntimeCompanyMetric::TrackPiecesElectric,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2298,
|
||||
label: "Company Non-Electric Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::Company(RuntimeCompanyMetric::TrackPiecesNonElectric),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(
|
||||
RuntimeCompanyMetric::TrackPiecesNonElectric,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2313,
|
||||
label: "Territory Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::Territory(RuntimeTerritoryMetric::TrackPiecesTotal),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Territory(
|
||||
RuntimeTerritoryMetric::TrackPiecesTotal,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2314,
|
||||
label: "Territory Single Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::Territory(RuntimeTerritoryMetric::TrackPiecesSingle),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Territory(
|
||||
RuntimeTerritoryMetric::TrackPiecesSingle,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2315,
|
||||
label: "Territory Double Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::Territory(RuntimeTerritoryMetric::TrackPiecesDouble),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Territory(
|
||||
RuntimeTerritoryMetric::TrackPiecesDouble,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2316,
|
||||
label: "Territory Transition Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::Territory(
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Territory(
|
||||
RuntimeTerritoryMetric::TrackPiecesTransition,
|
||||
),
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2317,
|
||||
label: "Territory Electric Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::Territory(RuntimeTerritoryMetric::TrackPiecesElectric),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Territory(
|
||||
RuntimeTerritoryMetric::TrackPiecesElectric,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2318,
|
||||
label: "Territory Non-Electric Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::Territory(
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Territory(
|
||||
RuntimeTerritoryMetric::TrackPiecesNonElectric,
|
||||
),
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2323,
|
||||
label: "Company-Territory Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::CompanyTerritory(RuntimeTrackMetric::Total),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyTerritory(
|
||||
RuntimeTrackMetric::Total,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2324,
|
||||
label: "Company-Territory Single Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::CompanyTerritory(RuntimeTrackMetric::Single),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyTerritory(
|
||||
RuntimeTrackMetric::Single,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2325,
|
||||
label: "Company-Territory Double Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::CompanyTerritory(RuntimeTrackMetric::Double),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyTerritory(
|
||||
RuntimeTrackMetric::Double,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2326,
|
||||
label: "Company-Territory Transition Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::CompanyTerritory(RuntimeTrackMetric::Transition),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyTerritory(
|
||||
RuntimeTrackMetric::Transition,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2327,
|
||||
label: "Company-Territory Electric Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::CompanyTerritory(RuntimeTrackMetric::Electric),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyTerritory(
|
||||
RuntimeTrackMetric::Electric,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2328,
|
||||
label: "Company-Territory Non-Electric Track Pieces",
|
||||
metric: RealOrdinaryConditionMetric::CompanyTerritory(RuntimeTrackMetric::NonElectric),
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyTerritory(
|
||||
RuntimeTrackMetric::NonElectric,
|
||||
)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_CANDIDATE_AVAILABILITY_CONDITION_TEMPLATE_ID,
|
||||
label: "%1 Avail.",
|
||||
kind: RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CandidateAvailability),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 2350,
|
||||
label: "Economic Status",
|
||||
kind: RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::EconomicStatus),
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -633,6 +698,15 @@ const KNOWN_TAG_DEFINITIONS: [KnownTagDefinition; 4] = [
|
|||
},
|
||||
];
|
||||
|
||||
fn known_special_condition_definition_for_label_id(
|
||||
label_id: u32,
|
||||
) -> Option<KnownSpecialConditionDefinition> {
|
||||
KNOWN_SPECIAL_CONDITION_DEFINITIONS
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|definition| !definition.hidden && definition.label_id == label_id)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SmpKnownTagHit {
|
||||
pub tag_id: u16,
|
||||
|
|
@ -2286,17 +2360,22 @@ fn parse_real_condition_row_summary(
|
|||
let flag_bytes = row_bytes
|
||||
.get(5..PACKED_EVENT_REAL_CONDITION_ROW_LEN)?
|
||||
.to_vec();
|
||||
let candidate_name_display = candidate_name.clone();
|
||||
let candidate_name_ref = candidate_name_display.as_deref();
|
||||
let ordinary_metadata = real_ordinary_condition_metadata(raw_condition_id);
|
||||
let comparator = ordinary_metadata
|
||||
.and_then(|_| decode_real_condition_comparator(subtype))
|
||||
.map(condition_comparator_label);
|
||||
let metric = ordinary_metadata.map(|metadata| metadata.label.to_string());
|
||||
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!(
|
||||
metadata.metric,
|
||||
RealOrdinaryConditionMetric::Territory(_)
|
||||
| RealOrdinaryConditionMetric::CompanyTerritory(_)
|
||||
metadata.kind,
|
||||
RealOrdinaryConditionKind::Numeric(
|
||||
RealOrdinaryConditionMetric::Territory(_)
|
||||
| RealOrdinaryConditionMetric::CompanyTerritory(_)
|
||||
)
|
||||
) && candidate_name.is_some()
|
||||
});
|
||||
let mut notes = Vec::new();
|
||||
|
|
@ -2312,6 +2391,14 @@ fn parse_real_condition_row_summary(
|
|||
.to_string(),
|
||||
);
|
||||
}
|
||||
if ordinary_metadata.is_some_and(|metadata| {
|
||||
matches!(
|
||||
metadata.kind,
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CandidateAvailability)
|
||||
) && candidate_name.is_none()
|
||||
}) {
|
||||
notes.push("candidate-availability condition row is missing its candidate-name side string".to_string());
|
||||
}
|
||||
Some(SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index,
|
||||
raw_condition_id,
|
||||
|
|
@ -2320,13 +2407,16 @@ fn parse_real_condition_row_summary(
|
|||
candidate_name,
|
||||
comparator,
|
||||
metric,
|
||||
semantic_family: ordinary_metadata.map(|_| "numeric_threshold".to_string()),
|
||||
semantic_family: ordinary_metadata
|
||||
.map(|metadata| real_ordinary_condition_semantic_family(metadata).to_string()),
|
||||
semantic_preview: ordinary_metadata.and_then(|metadata| {
|
||||
threshold.map(|value| {
|
||||
let comparator_text = decode_real_condition_comparator(subtype)
|
||||
.map(condition_comparator_symbol)
|
||||
.unwrap_or("?");
|
||||
format!("Test {} {} {}", metadata.label, comparator_text, value)
|
||||
let metric_label =
|
||||
real_ordinary_condition_metric_label(metadata, candidate_name_ref);
|
||||
format!("Test {} {} {}", metric_label, comparator_text, value)
|
||||
})
|
||||
}),
|
||||
requires_candidate_name_binding,
|
||||
|
|
@ -2384,6 +2474,47 @@ fn real_ordinary_condition_metadata(
|
|||
.iter()
|
||||
.copied()
|
||||
.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,
|
||||
},
|
||||
),
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn real_ordinary_condition_metric_label(
|
||||
metadata: RealOrdinaryConditionMetadata,
|
||||
candidate_name: Option<&str>,
|
||||
) -> String {
|
||||
match metadata.kind {
|
||||
RealOrdinaryConditionKind::Numeric(_) => metadata.label.to_string(),
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::SpecialCondition { label }) => {
|
||||
format!("Special Condition: {label}")
|
||||
}
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CandidateAvailability) => {
|
||||
match candidate_name {
|
||||
Some(name) => format!("Candidate Availability: {name}"),
|
||||
None => "Candidate Availability".to_string(),
|
||||
}
|
||||
}
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::EconomicStatus) => {
|
||||
"Economic Status".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn real_ordinary_condition_semantic_family(metadata: RealOrdinaryConditionMetadata) -> &'static str {
|
||||
match metadata.kind {
|
||||
RealOrdinaryConditionKind::Numeric(_) => "numeric_threshold",
|
||||
RealOrdinaryConditionKind::WorldState(_) => "world_state_threshold",
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_real_condition_comparator(subtype: u8) -> Option<RuntimeConditionComparator> {
|
||||
|
|
@ -2531,8 +2662,8 @@ fn decode_real_condition_row(
|
|||
let metadata = real_ordinary_condition_metadata(row.raw_condition_id)?;
|
||||
let comparator = decode_real_condition_comparator(row.subtype)?;
|
||||
let value = decode_real_condition_threshold(&row.flag_bytes)?;
|
||||
match metadata.metric {
|
||||
RealOrdinaryConditionMetric::Company(metric) => {
|
||||
match metadata.kind {
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(metric)) => {
|
||||
Some(RuntimeCondition::CompanyNumericThreshold {
|
||||
target: RuntimeCompanyTarget::ConditionTrueCompany,
|
||||
metric,
|
||||
|
|
@ -2540,15 +2671,18 @@ fn decode_real_condition_row(
|
|||
value,
|
||||
})
|
||||
}
|
||||
RealOrdinaryConditionMetric::Territory(metric) => negative_sentinel_scope
|
||||
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,
|
||||
}),
|
||||
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,
|
||||
|
|
@ -2556,7 +2690,27 @@ fn decode_real_condition_row(
|
|||
metric,
|
||||
comparator,
|
||||
value,
|
||||
}),
|
||||
})
|
||||
}
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::SpecialCondition {
|
||||
label,
|
||||
}) => Some(RuntimeCondition::SpecialConditionThreshold {
|
||||
label: label.to_string(),
|
||||
comparator,
|
||||
value,
|
||||
}),
|
||||
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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7925,6 +8079,17 @@ mod tests {
|
|||
bytes
|
||||
}
|
||||
|
||||
fn build_real_condition_row_with_threshold(
|
||||
raw_condition_id: i32,
|
||||
subtype: u8,
|
||||
threshold: i32,
|
||||
candidate_name: Option<&str>,
|
||||
) -> Vec<u8> {
|
||||
let mut bytes = build_real_condition_row(raw_condition_id, subtype, 0, candidate_name);
|
||||
bytes[5..9].copy_from_slice(&threshold.to_le_bytes());
|
||||
bytes
|
||||
}
|
||||
|
||||
struct RealGroupedEffectRowSpec<'a> {
|
||||
descriptor_id: u32,
|
||||
opcode: u8,
|
||||
|
|
@ -8460,6 +8625,180 @@ mod tests {
|
|||
assert!(summary.records[0].executable_import_ready);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_real_special_condition_threshold_from_checked_in_world_condition_metadata() {
|
||||
let condition_row = build_real_condition_row_with_threshold(3835, 0, 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("Special Condition: Use Wartime Cargos")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].standalone_condition_rows[0]
|
||||
.semantic_family
|
||||
.as_deref(),
|
||||
Some("world_state_threshold")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].decoded_conditions,
|
||||
vec![RuntimeCondition::SpecialConditionThreshold {
|
||||
label: "Use Wartime Cargos".to_string(),
|
||||
comparator: RuntimeConditionComparator::Ge,
|
||||
value: 1,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[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 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("Candidate Availability: Mogul")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].decoded_conditions,
|
||||
vec![RuntimeCondition::CandidateAvailabilityThreshold {
|
||||
name: "Mogul".to_string(),
|
||||
comparator: RuntimeConditionComparator::Ge,
|
||||
value: 2,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_real_economic_status_threshold_from_checked_in_world_condition_metadata() {
|
||||
let condition_row = build_real_condition_row_with_threshold(2350, 0, 4, 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("Economic Status")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].decoded_conditions,
|
||||
vec![RuntimeCondition::EconomicStatusCodeThreshold {
|
||||
comparator: RuntimeConditionComparator::Ge,
|
||||
value: 4,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_real_world_flag_descriptor_parity_only_with_checked_in_metadata() {
|
||||
let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue