Recover whole-game packed event conditions

This commit is contained in:
Jan Petykiewicz 2026-04-15 22:38:13 -07:00
commit 5d779263d2
7 changed files with 414 additions and 117 deletions

View file

@ -27,7 +27,10 @@ train roster and opaque economic-status lane needed for real descriptors `8` `Ec
`Territory - Allow All` now executes too, reinterpreted as company-to-territory access rights
rather than a territory-owned policy bit. Whole-game ordinary-condition execution now exists too:
special-condition thresholds, candidate-availability thresholds, and economic-status-code
thresholds now gate imported runtime records through the same service path, and checked-in
thresholds now gate imported runtime records through the same service path, and that world-side
condition batch now decodes from checked-in metadata instead of fixture-only ids: real
special-condition label ids, real economic-status ids, and the recovered `%1 Avail.` candidate
template plus candidate-name side strings all lower into the runtime condition model. Checked-in
whole-game descriptor metadata now drives the first real world-side effect batch too:
special-condition and candidate-availability setters import natively while world-flag rows remain
parity-only until keyed mapping is grounded. Explicit unmapped world-condition and

View file

@ -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 {

View file

@ -101,6 +101,9 @@ The highest-value next passes are now:
candidate-availability thresholds, and economic-status-code thresholds now gate imported runtime
records, and the packed-event frontier now reports explicit unmapped world-condition and
world-descriptor buckets
- that whole-game condition batch is now metadata-driven too: special-condition label ids,
economic-status, and the generic `%1 Avail.` candidate-availability template plus candidate-name
side strings all decode through checked-in world-condition metadata instead of fixture-only ids
- the first real whole-game grouped-descriptor batch is now metadata-driven too: checked-in
descriptor metadata covers special-condition and candidate-availability setters, while the
current world-flag family stays parity-only until keyed flag identity is grounded well enough

View file

@ -52,6 +52,11 @@ Implemented today:
through the same service path, and whole-game parity frontiers now report explicit unmapped
world-condition and world-descriptor buckets rather than falling back to the generic ordinary
or descriptor counts
- checked-in whole-game condition metadata now drives that same world-side condition batch too:
special-condition thresholds use the real special-condition label ids, economic-status thresholds
use the checked-in world label id, and candidate-availability thresholds now decode through the
recovered `%1 Avail.` template plus the packed candidate-name side string instead of fixture-only
placeholder ids
- checked-in whole-game grouped-descriptor metadata now drives the first real world-side effect
batch too: real special-condition and candidate-availability setter rows now decode and import
through the ordinary runtime path, while world-flag rows remain parity-only until keyed flag
@ -59,10 +64,10 @@ Implemented today:
That means the next implementation work is breadth, not bootstrap. The recommended next slice is
broader real grouped-descriptor and ordinary condition-id coverage beyond the current access,
whole-game, train, player, and numeric-threshold batches, with the whole-game frontier now
centered on still-unmapped world-flag families and any later state families that need stronger
checked-in descriptor or key recovery. Richer runtime ownership should still be added only where a
later descriptor or condition family needs more than the current event-owned roster.
whole-game, train, player, and numeric-threshold batches, with the world-side frontier now
centered primarily on still-unmapped world-flag families and any later state families that need
stronger checked-in descriptor or key recovery. Richer runtime ownership should still be added only
where a later descriptor or condition family needs more than the current event-owned roster.
## Why This Boundary

View file

@ -5,11 +5,11 @@
"description": "Tracked save-slice document with whole-game conditions gating a whole-game effect.",
"original_save_filename": "captured-world-condition-gated.gms",
"original_save_sha256": "world-condition-gated-sample-sha256",
"notes": [
"tracked as JSON save-slice document rather than raw .smp",
"proves whole-game ordinary conditions gate imported runtime effects",
"whole-game grouped descriptor ids now line up with the checked-in metadata table"
]
"notes": [
"tracked as JSON save-slice document rather than raw .smp",
"proves whole-game ordinary conditions gate imported runtime effects",
"whole-game condition ids now line up with the checked-in metadata tables instead of fixture-only placeholders"
]
},
"save_slice": {
"file_extension_hint": "gms",
@ -187,7 +187,7 @@
"standalone_condition_rows": [
{
"row_index": 0,
"raw_condition_id": 3901,
"raw_condition_id": 3835,
"subtype": 0,
"flag_bytes": [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": null,
@ -197,27 +197,27 @@
"semantic_preview": "Test Use Wartime Cargos >= 1",
"requires_candidate_name_binding": false,
"notes": [
"tracked whole-game condition sample"
"checked-in whole-game condition metadata sample"
]
},
{
"row_index": 1,
"raw_condition_id": 3902,
"raw_condition_id": 435,
"subtype": 0,
"flag_bytes": [2, 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": null,
"candidate_name": "Mogul",
"comparator": "ge",
"metric": "Candidate Availability: Mogul",
"semantic_family": "world_state_threshold",
"semantic_preview": "Test Mogul >= 2",
"requires_candidate_name_binding": false,
"notes": [
"tracked whole-game condition sample"
"checked-in whole-game condition metadata sample with candidate-name side string"
]
},
{
"row_index": 2,
"raw_condition_id": 3903,
"raw_condition_id": 2350,
"subtype": 0,
"flag_bytes": [4, 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": null,
@ -227,7 +227,7 @@
"semantic_preview": "Test Economic Status >= 4",
"requires_candidate_name_binding": false,
"notes": [
"tracked whole-game condition sample"
"checked-in whole-game condition metadata sample"
]
}
],

View file

@ -14,21 +14,18 @@
],
"expected_summary": {
"packed_event_collection_present": true,
"packed_event_record_count": 2,
"packed_event_decoded_record_count": 2,
"packed_event_record_count": 1,
"packed_event_decoded_record_count": 1,
"packed_event_imported_runtime_record_count": 0,
"event_runtime_record_count": 0,
"packed_event_blocked_unmapped_world_descriptor_count": 1,
"packed_event_blocked_unmapped_world_condition_count": 1
"packed_event_blocked_unmapped_world_condition_count": 0
},
"expected_state_fragment": {
"packed_event_collection": {
"records": [
{
"import_outcome": "blocked_unmapped_world_descriptor"
},
{
"import_outcome": "blocked_unmapped_world_condition"
}
]
}

View file

@ -2,12 +2,12 @@
"format_version": 1,
"save_slice_id": "packed-event-world-parity-save-slice",
"source": {
"description": "Tracked save-slice document preserving the current whole-game descriptor and condition frontier.",
"description": "Tracked save-slice document preserving the remaining whole-game descriptor frontier.",
"original_save_filename": "captured-world-parity.gms",
"original_save_sha256": "world-parity-sample-sha256",
"notes": [
"tracked as JSON save-slice document rather than raw .smp",
"keeps one unmapped world descriptor and one unmapped world condition explicit",
"keeps the still-unmapped world descriptor family explicit after the first real whole-game condition batch moved to checked-in metadata",
"whole-game world-flag descriptor identity is checked in, but keyed runtime mapping remains parity-only"
]
},
@ -31,10 +31,10 @@
"close_tag_offset": 33024,
"packed_state_version": 1001,
"packed_state_version_hex": "0x000003e9",
"live_id_bound": 52,
"live_record_count": 2,
"live_entry_ids": [49, 52],
"decoded_record_count": 2,
"live_id_bound": 49,
"live_record_count": 1,
"live_entry_ids": [49],
"decoded_record_count": 1,
"imported_runtime_record_count": 0,
"records": [
{
@ -94,56 +94,6 @@
"notes": [
"world-side descriptor remains parity-only"
]
},
{
"record_index": 1,
"live_entry_id": 52,
"payload_offset": 32464,
"payload_len": 120,
"decode_status": "parity_only",
"payload_family": "real_packed_v1",
"trigger_kind": 7,
"one_shot": false,
"compact_control": {
"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]
},
"text_bands": [],
"standalone_condition_row_count": 1,
"standalone_condition_rows": [
{
"row_index": 0,
"raw_condition_id": 3904,
"subtype": 0,
"flag_bytes": [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": null,
"comparator": "ge",
"metric": "Special Condition: Disable Stock Buying and Selling",
"semantic_family": "world_state_threshold",
"semantic_preview": "Test Disable Stock Buying and Selling >= 1",
"requires_candidate_name_binding": false,
"notes": [
"recovered world-side ordinary condition family without a checked-in executable mapping yet"
]
}
],
"negative_sentinel_scope": null,
"grouped_effect_row_counts": [0, 0, 0, 0],
"grouped_effect_rows": [],
"decoded_conditions": [],
"decoded_actions": [],
"executable_import_ready": false,
"notes": [
"world-side ordinary condition remains parity-only"
]
}
]
},