Implement whole-game packed event conditions

This commit is contained in:
Jan Petykiewicz 2026-04-15 21:41:40 -07:00
commit cc54a00e25
16 changed files with 1184 additions and 42 deletions

View file

@ -25,9 +25,13 @@ named-territory binding now executes, and the runtime now also carries the minim
train roster and opaque economic-status lane needed for real descriptors `8` `Economic Status`, `9` train roster and opaque economic-status lane needed for real descriptors `8` `Economic Status`, `9`
`Confiscate All`, and `15` `Retire Train` to execute through the same path. Descriptor `3` `Confiscate All`, and `15` `Retire Train` to execute through the same path. Descriptor `3`
`Territory - Allow All` now executes too, reinterpreted as company-to-territory access rights `Territory - Allow All` now executes too, reinterpreted as company-to-territory access rights
rather than a territory-owned policy bit. Shell purchase-flow and selected-profile parity remain rather than a territory-owned policy bit. Whole-game ordinary-condition execution now exists too:
out of scope. Mixed supported/unsupported real rows still stay parity-only. The PE32 hook remains special-condition thresholds, candidate-availability thresholds, and economic-status-code
useful as capture and integration tooling, but it is no longer the main execution milestone. thresholds now gate imported runtime records through the same service path, with explicit unmapped
world-condition and world-descriptor frontier buckets where current checked-in metadata still
stops. Shell purchase-flow and selected-profile parity remain out of scope. Mixed
supported/unsupported real rows still stay parity-only. The PE32 hook remains useful as capture
and integration tooling, but it is no longer the main execution milestone.
## Project Docs ## Project Docs

View file

@ -118,10 +118,14 @@ pub struct ExpectedRuntimeSummary {
#[serde(default)] #[serde(default)]
pub packed_event_blocked_unmapped_ordinary_condition_count: Option<usize>, pub packed_event_blocked_unmapped_ordinary_condition_count: Option<usize>,
#[serde(default)] #[serde(default)]
pub packed_event_blocked_unmapped_world_condition_count: Option<usize>,
#[serde(default)]
pub packed_event_blocked_missing_compact_control_count: Option<usize>, pub packed_event_blocked_missing_compact_control_count: Option<usize>,
#[serde(default)] #[serde(default)]
pub packed_event_blocked_unmapped_real_descriptor_count: Option<usize>, pub packed_event_blocked_unmapped_real_descriptor_count: Option<usize>,
#[serde(default)] #[serde(default)]
pub packed_event_blocked_unmapped_world_descriptor_count: Option<usize>,
#[serde(default)]
pub packed_event_blocked_territory_access_variant_count: Option<usize>, pub packed_event_blocked_territory_access_variant_count: Option<usize>,
#[serde(default)] #[serde(default)]
pub packed_event_blocked_territory_access_scope_count: Option<usize>, pub packed_event_blocked_territory_access_scope_count: Option<usize>,
@ -601,6 +605,14 @@ impl ExpectedRuntimeSummary {
)); ));
} }
} }
if let Some(count) = self.packed_event_blocked_unmapped_world_condition_count {
if actual.packed_event_blocked_unmapped_world_condition_count != count {
mismatches.push(format!(
"packed_event_blocked_unmapped_world_condition_count mismatch: expected {count}, got {}",
actual.packed_event_blocked_unmapped_world_condition_count
));
}
}
if let Some(count) = self.packed_event_blocked_missing_compact_control_count { if let Some(count) = self.packed_event_blocked_missing_compact_control_count {
if actual.packed_event_blocked_missing_compact_control_count != count { if actual.packed_event_blocked_missing_compact_control_count != count {
mismatches.push(format!( mismatches.push(format!(
@ -617,6 +629,14 @@ impl ExpectedRuntimeSummary {
)); ));
} }
} }
if let Some(count) = self.packed_event_blocked_unmapped_world_descriptor_count {
if actual.packed_event_blocked_unmapped_world_descriptor_count != count {
mismatches.push(format!(
"packed_event_blocked_unmapped_world_descriptor_count mismatch: expected {count}, got {}",
actual.packed_event_blocked_unmapped_world_descriptor_count
));
}
}
if let Some(count) = self.packed_event_blocked_territory_access_variant_count { if let Some(count) = self.packed_event_blocked_territory_access_variant_count {
if actual.packed_event_blocked_territory_access_variant_count != count { if actual.packed_event_blocked_territory_access_variant_count != count {
mismatches.push(format!( mismatches.push(format!(

View file

@ -132,6 +132,7 @@ enum ImportBlocker {
MissingTerritoryContext, MissingTerritoryContext,
NamedTerritoryBinding, NamedTerritoryBinding,
UnmappedOrdinaryCondition, UnmappedOrdinaryCondition,
UnmappedWorldCondition,
MissingTrainContext, MissingTrainContext,
MissingTrainTerritoryContext, MissingTrainTerritoryContext,
} }
@ -923,7 +924,11 @@ fn packed_record_condition_scope_import_blocker(
.count(); .count();
if ordinary_condition_row_count != 0 { if ordinary_condition_row_count != 0 {
if ordinary_condition_row_count != record.decoded_conditions.len() { if ordinary_condition_row_count != record.decoded_conditions.len() {
return Some(ImportBlocker::UnmappedOrdinaryCondition); return Some(if record_has_world_state_condition_rows(record) {
ImportBlocker::UnmappedWorldCondition
} else {
ImportBlocker::UnmappedOrdinaryCondition
});
} }
if (!company_context.has_territory_context) if (!company_context.has_territory_context)
&& (record && (record
@ -1205,6 +1210,30 @@ fn lower_condition_targets_in_condition(
comparator: *comparator, comparator: *comparator,
value: *value, value: *value,
}, },
RuntimeCondition::SpecialConditionThreshold {
label,
comparator,
value,
} => RuntimeCondition::SpecialConditionThreshold {
label: label.clone(),
comparator: *comparator,
value: *value,
},
RuntimeCondition::CandidateAvailabilityThreshold {
name,
comparator,
value,
} => RuntimeCondition::CandidateAvailabilityThreshold {
name: name.clone(),
comparator: *comparator,
value: *value,
},
RuntimeCondition::EconomicStatusCodeThreshold { comparator, value } => {
RuntimeCondition::EconomicStatusCodeThreshold {
comparator: *comparator,
value: *value,
}
}
}) })
} }
@ -1281,7 +1310,10 @@ fn condition_uses_condition_true_company(condition: &RuntimeCondition) -> bool {
| RuntimeCondition::CompanyTerritoryNumericThreshold { target, .. } => { | RuntimeCondition::CompanyTerritoryNumericThreshold { target, .. } => {
matches!(target, RuntimeCompanyTarget::ConditionTrueCompany) matches!(target, RuntimeCompanyTarget::ConditionTrueCompany)
} }
RuntimeCondition::TerritoryNumericThreshold { .. } => false, RuntimeCondition::TerritoryNumericThreshold { .. }
| RuntimeCondition::SpecialConditionThreshold { .. }
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
| RuntimeCondition::EconomicStatusCodeThreshold { .. } => false,
} }
} }
@ -1351,8 +1383,11 @@ fn smp_runtime_effect_to_runtime_effect(
territory, territory,
value, value,
} => { } => {
if !company_target_allowed_for_import(target, company_context, allow_condition_true_company) if !company_target_allowed_for_import(
{ target,
company_context,
allow_condition_true_company,
) {
Err(company_target_import_error_message(target, company_context)) Err(company_target_import_error_message(target, company_context))
} else if territory_target_import_blocker(territory, company_context).is_some() { } else if territory_target_import_blocker(territory, company_context).is_some() {
Err("packed effect requires territory runtime context".to_string()) Err("packed effect requires territory runtime context".to_string())
@ -1657,6 +1692,9 @@ fn company_target_import_error_message(
Some(ImportBlocker::UnmappedOrdinaryCondition) => { Some(ImportBlocker::UnmappedOrdinaryCondition) => {
"packed ordinary condition is not yet mapped".to_string() "packed ordinary condition is not yet mapped".to_string()
} }
Some(ImportBlocker::UnmappedWorldCondition) => {
"packed whole-game condition is not yet mapped".to_string()
}
Some(ImportBlocker::MissingTrainContext) => { Some(ImportBlocker::MissingTrainContext) => {
"packed effect requires runtime train context".to_string() "packed effect requires runtime train context".to_string()
} }
@ -1788,12 +1826,23 @@ fn determine_packed_event_import_outcome(
{ {
return "blocked_retire_train_variant".to_string(); return "blocked_retire_train_variant".to_string();
} }
if record
.grouped_effect_rows
.iter()
.any(real_grouped_row_is_world_state_family)
{
return "blocked_unmapped_world_descriptor".to_string();
}
return if record return if record
.standalone_condition_rows .standalone_condition_rows
.iter() .iter()
.any(|row| row.raw_condition_id >= 0) .any(|row| row.raw_condition_id >= 0)
{ {
"blocked_unmapped_ordinary_condition".to_string() if record_has_world_state_condition_rows(record) {
"blocked_unmapped_world_condition".to_string()
} else {
"blocked_unmapped_ordinary_condition".to_string()
}
} else { } else {
"blocked_unmapped_real_descriptor".to_string() "blocked_unmapped_real_descriptor".to_string()
}; };
@ -1814,6 +1863,58 @@ fn determine_packed_event_import_outcome(
"blocked_unsupported_decode".to_string() "blocked_unsupported_decode".to_string()
} }
fn record_has_world_state_condition_rows(record: &SmpLoadedPackedEventRecordSummary) -> bool {
record
.decoded_conditions
.iter()
.any(runtime_condition_is_world_state)
|| record
.standalone_condition_rows
.iter()
.any(ordinary_condition_row_is_world_state_family)
}
fn runtime_condition_is_world_state(condition: &RuntimeCondition) -> bool {
matches!(
condition,
RuntimeCondition::SpecialConditionThreshold { .. }
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
| RuntimeCondition::EconomicStatusCodeThreshold { .. }
)
}
fn ordinary_condition_row_is_world_state_family(
row: &SmpLoadedPackedEventConditionRowSummary,
) -> bool {
row.metric.as_deref().is_some_and(|metric| {
metric.contains("Special Condition")
|| metric.contains("Candidate Availability")
|| metric.contains("Economic Status")
|| metric.contains("World Flag")
}) || row
.semantic_family
.as_deref()
.is_some_and(|family| family == "world_state_threshold" || family == "world_flag_equals")
}
fn real_grouped_row_is_world_state_family(
row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> bool {
row.target_mask_bits == Some(0x08)
|| row.parameter_family.as_deref().is_some_and(|family| {
family.starts_with("whole_game_")
|| family.starts_with("special_condition")
|| family.starts_with("candidate_availability")
|| family.starts_with("world_flag")
})
|| row.descriptor_label.as_deref().is_some_and(|label| {
label.contains("Special Condition")
|| label.contains("Candidate Availability")
|| label.contains("World Flag")
|| label == "Economic Status"
})
}
fn packed_record_company_target_import_blocker( fn packed_record_company_target_import_blocker(
record: &SmpLoadedPackedEventRecordSummary, record: &SmpLoadedPackedEventRecordSummary,
company_context: &ImportRuntimeContext, company_context: &ImportRuntimeContext,
@ -1858,6 +1959,9 @@ fn runtime_condition_company_target_import_blocker(
target, territory, .. target, territory, ..
} => company_target_import_blocker(target, company_context) } => company_target_import_blocker(target, company_context)
.or_else(|| territory_target_import_blocker(territory, company_context)), .or_else(|| territory_target_import_blocker(territory, company_context)),
RuntimeCondition::SpecialConditionThreshold { .. }
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
| RuntimeCondition::EconomicStatusCodeThreshold { .. } => None,
} }
} }
@ -1896,6 +2000,7 @@ fn company_target_import_outcome(blocker: ImportBlocker) -> &'static str {
ImportBlocker::MissingTerritoryContext => "blocked_missing_territory_context", ImportBlocker::MissingTerritoryContext => "blocked_missing_territory_context",
ImportBlocker::NamedTerritoryBinding => "blocked_named_territory_binding", ImportBlocker::NamedTerritoryBinding => "blocked_named_territory_binding",
ImportBlocker::UnmappedOrdinaryCondition => "blocked_unmapped_ordinary_condition", ImportBlocker::UnmappedOrdinaryCondition => "blocked_unmapped_ordinary_condition",
ImportBlocker::UnmappedWorldCondition => "blocked_unmapped_world_condition",
ImportBlocker::MissingTrainContext => "blocked_missing_train_context", ImportBlocker::MissingTrainContext => "blocked_missing_train_context",
ImportBlocker::MissingTrainTerritoryContext => "blocked_missing_train_territory_context", ImportBlocker::MissingTrainTerritoryContext => "blocked_missing_train_territory_context",
} }
@ -4621,8 +4726,10 @@ mod tests {
grouped_effect_row_counts: vec![1, 0, 0, 0], grouped_effect_row_counts: vec![1, 0, 0, 0],
grouped_effect_rows: vec![real_territory_access_row( grouped_effect_rows: vec![real_territory_access_row(
true, true,
vec!["territory access row is missing company or territory scope" vec![
.to_string()], "territory access row is missing company or territory scope"
.to_string(),
],
)], )],
decoded_conditions: Vec::new(), decoded_conditions: Vec::new(),
decoded_actions: vec![], decoded_actions: vec![],

View file

@ -39,14 +39,13 @@ pub use runtime::{
RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess, RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess,
RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition, RuntimeConditionComparator, RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition, RuntimeConditionComparator,
RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
RuntimePackedEventCollectionSummary, RuntimePackedEventCollectionSummary, RuntimePackedEventCompactControlSummary,
RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary, RuntimePackedEventConditionRowSummary, RuntimePackedEventGroupedEffectRowSummary,
RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary, RuntimePackedEventNegativeSentinelScopeSummary, RuntimePackedEventRecordSummary,
RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer, RuntimePackedEventTextBandSummary, RuntimePlayer, RuntimePlayerConditionTestScope,
RuntimePlayerConditionTestScope, RuntimePlayerTarget, RuntimeSaveProfileState, RuntimePlayerTarget, RuntimeSaveProfileState, RuntimeServiceState, RuntimeState,
RuntimeServiceState, RuntimeState, RuntimeTerritory, RuntimeTerritoryMetric, RuntimeTerritory, RuntimeTerritoryMetric, RuntimeTerritoryTarget, RuntimeTrackMetric,
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain, RuntimeTrackPieceCounts, RuntimeTrain, RuntimeWorldRestoreState,
RuntimeWorldRestoreState,
}; };
pub use smp::{ pub use smp::{
SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION, SmpAlignedRuntimeRuleBandLane, SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION, SmpAlignedRuntimeRuleBandLane,

View file

@ -228,6 +228,20 @@ pub enum RuntimeCondition {
comparator: RuntimeConditionComparator, comparator: RuntimeConditionComparator,
value: i64, value: i64,
}, },
SpecialConditionThreshold {
label: String,
comparator: RuntimeConditionComparator,
value: i64,
},
CandidateAvailabilityThreshold {
name: String,
comparator: RuntimeConditionComparator,
value: i64,
},
EconomicStatusCodeThreshold {
comparator: RuntimeConditionComparator,
value: i64,
},
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -1235,6 +1249,21 @@ fn validate_runtime_condition(
validate_company_target(target, valid_company_ids)?; validate_company_target(target, valid_company_ids)?;
validate_territory_target(territory, valid_territory_ids) validate_territory_target(territory, valid_territory_ids)
} }
RuntimeCondition::SpecialConditionThreshold { label, .. } => {
if label.trim().is_empty() {
Err("label must not be empty".to_string())
} else {
Ok(())
}
}
RuntimeCondition::CandidateAvailabilityThreshold { name, .. } => {
if name.trim().is_empty() {
Err("name must not be empty".to_string())
} else {
Ok(())
}
}
RuntimeCondition::EconomicStatusCodeThreshold { .. } => Ok(()),
} }
} }

View file

@ -2126,9 +2126,8 @@ fn parse_real_event_runtime_record_summary(
&& row.raw_scalar_value != 0 && row.raw_scalar_value != 0
&& (!company_target_present || !territory_target_present) && (!company_target_present || !territory_target_present)
{ {
row.notes.push( row.notes
"territory access row is missing company or territory scope".to_string(), .push("territory access row is missing company or territory scope".to_string());
);
} }
} }
} }
@ -2927,18 +2926,20 @@ fn runtime_effect_supported_for_save_import(effect: &RuntimeEffect) -> bool {
), ),
RuntimeEffect::SetCompanyTerritoryAccess { RuntimeEffect::SetCompanyTerritoryAccess {
target, territory, .. target, territory, ..
} => matches!( } => {
target, matches!(
RuntimeCompanyTarget::AllActive target,
| RuntimeCompanyTarget::Ids { .. } RuntimeCompanyTarget::AllActive
| RuntimeCompanyTarget::HumanCompanies | RuntimeCompanyTarget::Ids { .. }
| RuntimeCompanyTarget::AiCompanies | RuntimeCompanyTarget::HumanCompanies
| RuntimeCompanyTarget::SelectedCompany | RuntimeCompanyTarget::AiCompanies
| RuntimeCompanyTarget::ConditionTrueCompany | RuntimeCompanyTarget::SelectedCompany
) && matches!( | RuntimeCompanyTarget::ConditionTrueCompany
territory, ) && matches!(
RuntimeTerritoryTarget::AllTerritories | RuntimeTerritoryTarget::Ids { .. } territory,
), RuntimeTerritoryTarget::AllTerritories | RuntimeTerritoryTarget::Ids { .. }
)
}
RuntimeEffect::SetCompanyCash { target, .. } RuntimeEffect::SetCompanyCash { target, .. }
| RuntimeEffect::AdjustCompanyCash { target, .. } | RuntimeEffect::AdjustCompanyCash { target, .. }
| RuntimeEffect::AdjustCompanyDebt { target, .. } => matches!( | RuntimeEffect::AdjustCompanyDebt { target, .. } => matches!(
@ -2961,7 +2962,10 @@ fn runtime_condition_supported_for_save_import(condition: &RuntimeCondition) ->
match condition { match condition {
RuntimeCondition::CompanyNumericThreshold { .. } RuntimeCondition::CompanyNumericThreshold { .. }
| RuntimeCondition::TerritoryNumericThreshold { .. } | RuntimeCondition::TerritoryNumericThreshold { .. }
| RuntimeCondition::CompanyTerritoryNumericThreshold { .. } => true, | RuntimeCondition::CompanyTerritoryNumericThreshold { .. }
| RuntimeCondition::SpecialConditionThreshold { .. }
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
| RuntimeCondition::EconomicStatusCodeThreshold { .. } => true,
} }
} }

View file

@ -651,6 +651,46 @@ fn evaluate_record_conditions(
return Ok(None); return Ok(None);
} }
} }
RuntimeCondition::SpecialConditionThreshold {
label,
comparator,
value,
} => {
let actual = state
.special_conditions
.get(label)
.copied()
.map(i64::from)
.unwrap_or(0);
if !compare_condition_value(actual, *comparator, *value) {
return Ok(None);
}
}
RuntimeCondition::CandidateAvailabilityThreshold {
name,
comparator,
value,
} => {
let actual = state
.candidate_availability
.get(name)
.copied()
.map(i64::from)
.unwrap_or(0);
if !compare_condition_value(actual, *comparator, *value) {
return Ok(None);
}
}
RuntimeCondition::EconomicStatusCodeThreshold { comparator, value } => {
let actual = state
.world_restore
.economic_status_code
.map(i64::from)
.unwrap_or(0);
if !compare_condition_value(actual, *comparator, *value) {
return Ok(None);
}
}
} }
} }
@ -1039,7 +1079,8 @@ fn set_company_territory_access_pairs(
} }
} else { } else {
access_entries.retain(|entry| { access_entries.retain(|entry| {
!(company_ids.contains(&entry.company_id) && territory_ids.contains(&entry.territory_id)) !(company_ids.contains(&entry.company_id)
&& territory_ids.contains(&entry.territory_id))
}); });
} }
} }
@ -1785,6 +1826,78 @@ mod tests {
assert!(error.contains("condition-evaluation context")); assert!(error.contains("condition-evaluation context"));
} }
#[test]
fn evaluates_world_state_conditions_before_effects_run() {
let mut state = RuntimeState {
world_restore: RuntimeWorldRestoreState {
economic_status_code: Some(3),
..RuntimeWorldRestoreState::default()
},
candidate_availability: BTreeMap::from([(String::from("Mogul"), 2)]),
special_conditions: BTreeMap::from([(String::from("Use Wartime Cargos"), 1)]),
event_runtime_records: vec![
RuntimeEventRecord {
record_id: 23,
trigger_kind: 7,
active: true,
service_count: 0,
marks_collection_dirty: false,
one_shot: false,
has_fired: false,
conditions: vec![
RuntimeCondition::SpecialConditionThreshold {
label: "Use Wartime Cargos".to_string(),
comparator: RuntimeConditionComparator::Ge,
value: 1,
},
RuntimeCondition::CandidateAvailabilityThreshold {
name: "Mogul".to_string(),
comparator: RuntimeConditionComparator::Eq,
value: 2,
},
RuntimeCondition::EconomicStatusCodeThreshold {
comparator: RuntimeConditionComparator::Eq,
value: 3,
},
],
effects: vec![RuntimeEffect::SetWorldFlag {
key: "world_condition_passed".to_string(),
value: true,
}],
},
RuntimeEventRecord {
record_id: 24,
trigger_kind: 7,
active: true,
service_count: 0,
marks_collection_dirty: false,
one_shot: false,
has_fired: false,
conditions: vec![RuntimeCondition::SpecialConditionThreshold {
label: "Disable Cargo Economy".to_string(),
comparator: RuntimeConditionComparator::Gt,
value: 0,
}],
effects: vec![RuntimeEffect::SetWorldFlag {
key: "world_condition_failed".to_string(),
value: true,
}],
},
],
..state()
};
let result = execute_step_command(
&mut state,
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
)
.expect("world-state conditions should evaluate successfully");
assert_eq!(result.service_events[0].serviced_record_ids, vec![23]);
assert_eq!(state.world_flags.get("world_condition_passed"), Some(&true));
assert_eq!(state.world_flags.get("world_condition_failed"), None);
}
#[test] #[test]
fn one_shot_record_only_fires_once() { fn one_shot_record_only_fires_once() {
let mut state = RuntimeState { let mut state = RuntimeState {

View file

@ -56,8 +56,10 @@ pub struct RuntimeSummary {
pub packed_event_blocked_missing_territory_context_count: usize, pub packed_event_blocked_missing_territory_context_count: usize,
pub packed_event_blocked_named_territory_binding_count: usize, pub packed_event_blocked_named_territory_binding_count: usize,
pub packed_event_blocked_unmapped_ordinary_condition_count: usize, pub packed_event_blocked_unmapped_ordinary_condition_count: usize,
pub packed_event_blocked_unmapped_world_condition_count: usize,
pub packed_event_blocked_missing_compact_control_count: usize, pub packed_event_blocked_missing_compact_control_count: usize,
pub packed_event_blocked_unmapped_real_descriptor_count: usize, pub packed_event_blocked_unmapped_real_descriptor_count: usize,
pub packed_event_blocked_unmapped_world_descriptor_count: usize,
pub packed_event_blocked_territory_access_variant_count: usize, pub packed_event_blocked_territory_access_variant_count: usize,
pub packed_event_blocked_territory_access_scope_count: usize, pub packed_event_blocked_territory_access_scope_count: usize,
pub packed_event_blocked_missing_train_context_count: usize, pub packed_event_blocked_missing_train_context_count: usize,
@ -393,6 +395,20 @@ impl RuntimeSummary {
.count() .count()
}) })
.unwrap_or(0), .unwrap_or(0),
packed_event_blocked_unmapped_world_condition_count: state
.packed_event_collection
.as_ref()
.map(|summary| {
summary
.records
.iter()
.filter(|record| {
record.import_outcome.as_deref()
== Some("blocked_unmapped_world_condition")
})
.count()
})
.unwrap_or(0),
packed_event_blocked_missing_compact_control_count: state packed_event_blocked_missing_compact_control_count: state
.packed_event_collection .packed_event_collection
.as_ref() .as_ref()
@ -421,6 +437,20 @@ impl RuntimeSummary {
.count() .count()
}) })
.unwrap_or(0), .unwrap_or(0),
packed_event_blocked_unmapped_world_descriptor_count: state
.packed_event_collection
.as_ref()
.map(|summary| {
summary
.records
.iter()
.filter(|record| {
record.import_outcome.as_deref()
== Some("blocked_unmapped_world_descriptor")
})
.count()
})
.unwrap_or(0),
packed_event_blocked_territory_access_variant_count: state packed_event_blocked_territory_access_variant_count: state
.packed_event_collection .packed_event_collection
.as_ref() .as_ref()
@ -843,4 +873,107 @@ mod tests {
assert_eq!(summary.company_count, 2); assert_eq!(summary.company_count, 2);
assert_eq!(summary.active_company_count, 1); assert_eq!(summary.active_company_count, 1);
} }
#[test]
fn counts_world_frontier_buckets_separately() {
let state = RuntimeState {
calendar: CalendarPoint {
year: 1830,
month_slot: 0,
phase_slot: 0,
tick_slot: 0,
},
world_flags: BTreeMap::new(),
save_profile: RuntimeSaveProfileState::default(),
world_restore: RuntimeWorldRestoreState::default(),
metadata: BTreeMap::new(),
companies: Vec::new(),
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
company_territory_access: Vec::new(),
packed_event_collection: Some(RuntimePackedEventCollectionSummary {
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()),
packed_state_version: 0x3e9,
packed_state_version_hex: "0x000003e9".to_string(),
live_id_bound: 2,
live_record_count: 2,
live_entry_ids: vec![21, 22],
decoded_record_count: 2,
imported_runtime_record_count: 0,
records: vec![
RuntimePackedEventRecordSummary {
record_index: 0,
live_entry_id: 21,
payload_offset: Some(0x7202),
payload_len: Some(96),
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: None,
compact_control: None,
text_bands: Vec::new(),
standalone_condition_row_count: 0,
standalone_condition_rows: Vec::new(),
negative_sentinel_scope: None,
grouped_effect_row_counts: vec![1, 0, 0, 0],
grouped_effect_rows: Vec::new(),
grouped_company_targets: Vec::new(),
decoded_conditions: Vec::new(),
decoded_actions: Vec::new(),
executable_import_ready: false,
import_outcome: Some("blocked_unmapped_world_descriptor".to_string()),
notes: Vec::new(),
},
RuntimePackedEventRecordSummary {
record_index: 1,
live_entry_id: 22,
payload_offset: Some(0x7242),
payload_len: Some(96),
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: None,
compact_control: None,
text_bands: Vec::new(),
standalone_condition_row_count: 1,
standalone_condition_rows: Vec::new(),
negative_sentinel_scope: None,
grouped_effect_row_counts: vec![0, 0, 0, 0],
grouped_effect_rows: Vec::new(),
grouped_company_targets: Vec::new(),
decoded_conditions: Vec::new(),
decoded_actions: Vec::new(),
executable_import_ready: false,
import_outcome: Some("blocked_unmapped_world_condition".to_string()),
notes: Vec::new(),
},
],
}),
event_runtime_records: Vec::new(),
candidate_availability: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
let summary = RuntimeSummary::from_state(&state);
assert_eq!(
summary.packed_event_blocked_unmapped_world_descriptor_count,
1
);
assert_eq!(
summary.packed_event_blocked_unmapped_world_condition_count,
1
);
}
} }

View file

@ -97,6 +97,10 @@ The highest-value next passes are now:
- descriptor `3` `Territory - Allow All` now executes as company-to-territory access rights through - descriptor `3` `Territory - Allow All` now executes as company-to-territory access rights through
the same ordinary runtime path; shell purchase-flow parity remains out of scope, and mixed the same ordinary runtime path; shell purchase-flow parity remains out of scope, and mixed
supported/unsupported real rows still stay parity-only supported/unsupported real rows still stay parity-only
- whole-game ordinary-condition execution now exists too: special-condition thresholds,
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
- keep in mind that the current local `.gms` corpus still exports with no packed event collection, - keep in mind that the current local `.gms` corpus still exports with no packed event collection,
so real descriptor mapping needs to stay plumbing-first until better captures exist so real descriptor mapping needs to stay plumbing-first until better captures exist
- use `rrt-hook` primarily as optional capture or integration tooling, not as the first execution - use `rrt-hook` primarily as optional capture or integration tooling, not as the first execution

View file

@ -47,11 +47,16 @@ Implemented today:
- descriptor `3` = `Territory - Allow All` now imports and executes too, reinterpreted as - descriptor `3` = `Territory - Allow All` now imports and executes too, reinterpreted as
company-to-territory access rights instead of a territory-owned policy bit; shell purchase-flow company-to-territory access rights instead of a territory-owned policy bit; shell purchase-flow
and selected-profile parity still remain outside the runtime surface and selected-profile parity still remain outside the runtime surface
- the first whole-game ordinary-condition batch now executes too: special-condition thresholds,
candidate-availability thresholds, and economic-status-code thresholds now gate runtime records
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
That means the next implementation work is breadth, not bootstrap. The recommended next slice is 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, broader real grouped-descriptor and ordinary condition-id coverage beyond the current access,
world, train, player, and numeric-threshold batches, plus richer runtime ownership only where a whole-game, train, player, and numeric-threshold batches, plus richer runtime ownership only where
later descriptor family needs more than the current event-owned roster. a later descriptor or condition family needs more than the current event-owned roster.
## Why This Boundary ## Why This Boundary
@ -396,8 +401,8 @@ Checked-in fixture families already include:
## Next Slice ## Next Slice
The recommended next implementation slice is broader ordinary-condition and grouped-descriptor The recommended next implementation slice is broader ordinary-condition and grouped-descriptor
breadth on top of the now-stable numeric-threshold, overlay-context, named-territory, player, breadth on top of the now-stable numeric-threshold, whole-game-state, overlay-context,
world/train, and company-territory-access batches. named-territory, player, world/train, and company-territory-access batches.
Target behavior: Target behavior:
@ -405,9 +410,9 @@ Target behavior:
company finance, company track, aggregate territory track, and company-territory track numeric company finance, company track, aggregate territory track, and company-territory track numeric
thresholds all pass through parse, semantic summary, overlay-backed import, and ordinary trigger thresholds all pass through parse, semantic summary, overlay-backed import, and ordinary trigger
execution execution
- extend ordinary condition coverage beyond numeric thresholds only when comparator semantics, - extend ordinary condition coverage beyond the current numeric-threshold and whole-game-state
runtime ownership, and binding rules are grounded enough to lower honestly into the normalized families only when comparator semantics, runtime ownership, and binding rules are grounded
runtime path enough to lower honestly into the normalized runtime path
- keep named-territory ordinary rows on exact case-sensitive binding until captured evidence - keep named-territory ordinary rows on exact case-sensitive binding until captured evidence
justifies alias tables or fuzzier matching justifies alias tables or fuzzier matching
- keep player-owned condition scope within the minimal event runtime model until later slices need - keep player-owned condition scope within the minimal event runtime model until later slices need
@ -430,6 +435,9 @@ Fixture work for that slice:
- preserve the new ordinary-condition tracked overlays for executable company finance, company - preserve the new ordinary-condition tracked overlays for executable company finance, company
track, aggregate territory track, and company-territory track thresholds track, aggregate territory track, and company-territory track thresholds
- preserve the new whole-game tracked save-slice fixtures for executable special-condition and
candidate-availability/economic-status batches, plus the parity-only world-condition and
world-descriptor frontier sample
- preserve the named-territory no-match tracked overlay as the explicit binding blocker frontier - preserve the named-territory no-match tracked overlay as the explicit binding blocker frontier
- preserve the territory-access tracked overlays and parity samples so descriptor `3` access-rights - preserve the territory-access tracked overlays and parity samples so descriptor `3` access-rights
execution does not regress while other grouped families widen execution does not regress while other grouped families widen

View file

@ -0,0 +1,81 @@
{
"format_version": 1,
"fixture_id": "packed-event-world-condition-gated-save-slice-fixture",
"source": {
"kind": "captured-runtime",
"description": "Fixture proving whole-game ordinary conditions gate imported world-state effects."
},
"state_save_slice_path": "packed-event-world-condition-gated-save-slice.json",
"commands": [
{
"kind": "service_trigger_kind",
"trigger_kind": 6
},
{
"kind": "service_trigger_kind",
"trigger_kind": 7
}
],
"expected_summary": {
"packed_event_collection_present": true,
"packed_event_record_count": 2,
"packed_event_decoded_record_count": 2,
"packed_event_imported_runtime_record_count": 2,
"event_runtime_record_count": 2,
"candidate_availability_count": 2,
"zero_candidate_availability_count": 0,
"special_condition_count": 1,
"enabled_special_condition_count": 1,
"world_restore_economic_status_code": 4,
"total_event_record_service_count": 2,
"total_trigger_dispatch_count": 2
},
"expected_state_fragment": {
"world_restore": {
"economic_status_code": 4
},
"candidate_availability": {
"Mogul": 2,
"Turbo Diesel": 1
},
"packed_event_collection": {
"records": [
{
"import_outcome": "imported"
},
{
"import_outcome": "imported",
"decoded_conditions": [
{
"kind": "special_condition_threshold",
"label": "Use Wartime Cargos",
"comparator": "ge",
"value": 1
},
{
"kind": "candidate_availability_threshold",
"name": "Mogul",
"comparator": "ge",
"value": 2
},
{
"kind": "economic_status_code_threshold",
"comparator": "ge",
"value": 4
}
]
}
]
},
"event_runtime_records": [
{
"record_id": 41,
"service_count": 1
},
{
"record_id": 44,
"service_count": 1
}
]
}
}

View file

@ -0,0 +1,297 @@
{
"format_version": 1,
"save_slice_id": "packed-event-world-condition-gated-save-slice",
"source": {
"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"
]
},
"save_slice": {
"file_extension_hint": "gms",
"container_profile_family": "rt3-classic-save-container-v1",
"mechanism_family": "classic-save-rehydrate-v1",
"mechanism_confidence": "grounded",
"trailer_family": null,
"bridge_family": null,
"profile": null,
"candidate_availability_table": {
"source_kind": "save-bridge-secondary-block",
"semantic_family": "scenario-named-candidate-availability-table",
"header_offset": 27248,
"entries_offset": 27345,
"entries_end_offset": 27432,
"observed_entry_count": 2,
"zero_availability_count": 1,
"zero_availability_names": [
"Turbo Diesel"
],
"footer_progress_hex_words": [
"0x000032dc",
"0x00003714"
],
"entries": [
{
"index": 0,
"offset": 27345,
"text": "Mogul",
"availability_dword": 2,
"availability_dword_hex": "0x00000002",
"trailer_word": 2,
"trailer_word_hex": "0x00000002"
},
{
"index": 1,
"offset": 27373,
"text": "Turbo Diesel",
"availability_dword": 0,
"availability_dword_hex": "0x00000000",
"trailer_word": 0,
"trailer_word_hex": "0x00000000"
}
]
},
"special_conditions_table": {
"source_kind": "save-fixed-special-conditions-range",
"table_offset": 3428,
"table_len": 144,
"enabled_visible_count": 1,
"enabled_visible_labels": [
"Use Wartime Cargos"
],
"entries": [
{
"slot_index": 0,
"hidden": false,
"label_id": 2874,
"help_id": 2875,
"label": "Use Wartime Cargos",
"value": 1,
"value_hex": "0x00000001"
},
{
"slot_index": 35,
"hidden": true,
"label_id": 3,
"help_id": 3,
"label": "Hidden sentinel",
"value": 1,
"value_hex": "0x00000001"
}
]
},
"event_runtime_collection": {
"source_kind": "packed-event-runtime-collection",
"mechanism_family": "classic-save-rehydrate-v1",
"mechanism_confidence": "grounded",
"container_profile_family": "rt3-classic-save-container-v1",
"metadata_tag_offset": 30528,
"records_tag_offset": 30784,
"close_tag_offset": 31616,
"packed_state_version": 1001,
"packed_state_version_hex": "0x000003e9",
"live_id_bound": 44,
"live_record_count": 2,
"live_entry_ids": [41, 44],
"decoded_record_count": 2,
"imported_runtime_record_count": 2,
"records": [
{
"record_index": 0,
"live_entry_id": 41,
"payload_offset": 30848,
"payload_len": 96,
"decode_status": "parity_only",
"payload_family": "real_packed_v1",
"trigger_kind": 6,
"one_shot": false,
"compact_control": {
"mode_byte_0x7ef": 6,
"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": [1, 0, 0, 0],
"summary_toggle_0x800": 1,
"grouped_territory_selectors_0x80f": [-1, -1, -1, -1]
},
"text_bands": [],
"standalone_condition_row_count": 0,
"standalone_condition_rows": [],
"negative_sentinel_scope": null,
"grouped_effect_row_counts": [1, 0, 0, 0],
"grouped_effect_rows": [
{
"group_index": 0,
"row_index": 0,
"descriptor_id": 8,
"descriptor_label": "Economic Status",
"target_mask_bits": 8,
"parameter_family": "whole_game_state_enum",
"opcode": 3,
"raw_scalar_value": 4,
"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": "scalar_assignment",
"semantic_family": "scalar_assignment",
"semantic_preview": "Set Economic Status to 4",
"locomotive_name": null,
"notes": []
}
],
"decoded_conditions": [],
"decoded_actions": [
{
"kind": "set_economic_status_code",
"value": 4
}
],
"executable_import_ready": true,
"notes": [
"world-side setup record"
]
},
{
"record_index": 1,
"live_entry_id": 44,
"payload_offset": 30976,
"payload_len": 184,
"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": [1, 0, 0, 0],
"summary_toggle_0x800": 1,
"grouped_territory_selectors_0x80f": [-1, -1, -1, -1]
},
"text_bands": [],
"standalone_condition_row_count": 3,
"standalone_condition_rows": [
{
"row_index": 0,
"raw_condition_id": 3901,
"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: Use Wartime Cargos",
"semantic_family": "world_state_threshold",
"semantic_preview": "Test Use Wartime Cargos >= 1",
"requires_candidate_name_binding": false,
"notes": [
"tracked whole-game condition sample"
]
},
{
"row_index": 1,
"raw_condition_id": 3902,
"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,
"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"
]
},
{
"row_index": 2,
"raw_condition_id": 3903,
"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,
"comparator": "ge",
"metric": "Economic Status",
"semantic_family": "world_state_threshold",
"semantic_preview": "Test Economic Status >= 4",
"requires_candidate_name_binding": false,
"notes": [
"tracked whole-game condition sample"
]
}
],
"negative_sentinel_scope": null,
"grouped_effect_row_counts": [1, 0, 0, 0],
"grouped_effect_rows": [
{
"group_index": 0,
"row_index": 0,
"descriptor_id": 109,
"descriptor_label": "Turbo Diesel Availability",
"target_mask_bits": 8,
"parameter_family": "candidate_availability_scalar",
"opcode": 3,
"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,
"row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment",
"semantic_preview": "Set Turbo Diesel Availability to 1",
"locomotive_name": null,
"notes": [
"tracked whole-game grouped-effect sample"
]
}
],
"decoded_conditions": [
{
"kind": "special_condition_threshold",
"label": "Use Wartime Cargos",
"comparator": "ge",
"value": 1
},
{
"kind": "candidate_availability_threshold",
"name": "Mogul",
"comparator": "ge",
"value": 2
},
{
"kind": "economic_status_code_threshold",
"comparator": "ge",
"value": 4
}
],
"decoded_actions": [
{
"kind": "set_candidate_availability",
"name": "Turbo Diesel",
"value": 1
}
],
"executable_import_ready": true,
"notes": [
"whole-game ordinary conditions gate a whole-game effect"
]
}
]
},
"notes": [
"whole-game condition-gated effect sample"
]
}
}

View file

@ -0,0 +1,36 @@
{
"format_version": 1,
"fixture_id": "packed-event-world-parity-save-slice-fixture",
"source": {
"kind": "captured-runtime",
"description": "Fixture keeping the current whole-game descriptor and condition frontier explicit."
},
"state_save_slice_path": "packed-event-world-parity-save-slice.json",
"commands": [
{
"kind": "service_trigger_kind",
"trigger_kind": 7
}
],
"expected_summary": {
"packed_event_collection_present": true,
"packed_event_record_count": 2,
"packed_event_decoded_record_count": 2,
"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
},
"expected_state_fragment": {
"packed_event_collection": {
"records": [
{
"import_outcome": "blocked_unmapped_world_descriptor"
},
{
"import_outcome": "blocked_unmapped_world_condition"
}
]
}
}
}

View file

@ -0,0 +1,153 @@
{
"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.",
"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"
]
},
"save_slice": {
"file_extension_hint": "gms",
"container_profile_family": "rt3-classic-save-container-v1",
"mechanism_family": "classic-save-rehydrate-v1",
"mechanism_confidence": "grounded",
"trailer_family": null,
"bridge_family": null,
"profile": null,
"candidate_availability_table": null,
"special_conditions_table": null,
"event_runtime_collection": {
"source_kind": "packed-event-runtime-collection",
"mechanism_family": "classic-save-rehydrate-v1",
"mechanism_confidence": "grounded",
"container_profile_family": "rt3-classic-save-container-v1",
"metadata_tag_offset": 32000,
"records_tag_offset": 32256,
"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,
"imported_runtime_record_count": 0,
"records": [
{
"record_index": 0,
"live_entry_id": 49,
"payload_offset": 32320,
"payload_len": 112,
"decode_status": "parity_only",
"payload_family": "real_packed_v1",
"trigger_kind": 6,
"one_shot": false,
"compact_control": {
"mode_byte_0x7ef": 6,
"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": [1, 0, 0, 0],
"summary_toggle_0x800": 1,
"grouped_territory_selectors_0x80f": [-1, -1, -1, -1]
},
"text_bands": [],
"standalone_condition_row_count": 0,
"standalone_condition_rows": [],
"negative_sentinel_scope": null,
"grouped_effect_row_counts": [1, 0, 0, 0],
"grouped_effect_rows": [
{
"group_index": 0,
"row_index": 0,
"descriptor_id": 110,
"descriptor_label": "Disable Stock Buying and Selling",
"target_mask_bits": 8,
"parameter_family": "world_flag_toggle",
"opcode": 0,
"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,
"row_shape": "bool_toggle",
"semantic_family": "bool_toggle",
"semantic_preview": "Set Disable Stock Buying and Selling to TRUE",
"locomotive_name": null,
"notes": [
"recovered whole-game descriptor family without a checked-in executable mapping yet"
]
}
],
"decoded_conditions": [],
"decoded_actions": [],
"executable_import_ready": false,
"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"
]
}
]
},
"notes": [
"whole-game parity frontier sample"
]
}
}

View file

@ -0,0 +1,45 @@
{
"format_version": 1,
"fixture_id": "packed-event-world-special-condition-save-slice-fixture",
"source": {
"kind": "captured-runtime",
"description": "Fixture proving a whole-game special-condition effect imports and executes through the ordinary runtime path."
},
"state_save_slice_path": "packed-event-world-special-condition-save-slice.json",
"commands": [
{
"kind": "service_trigger_kind",
"trigger_kind": 7
}
],
"expected_summary": {
"packed_event_collection_present": true,
"packed_event_record_count": 1,
"packed_event_decoded_record_count": 1,
"packed_event_imported_runtime_record_count": 1,
"event_runtime_record_count": 1,
"special_condition_count": 1,
"enabled_special_condition_count": 1,
"total_event_record_service_count": 1,
"total_trigger_dispatch_count": 1
},
"expected_state_fragment": {
"special_conditions": {
"Use Wartime Cargos": 1
},
"packed_event_collection": {
"records": [
{
"import_outcome": "imported",
"decoded_actions": [
{
"kind": "set_special_condition",
"label": "Use Wartime Cargos",
"value": 1
}
]
}
]
}
}
}

View file

@ -0,0 +1,109 @@
{
"format_version": 1,
"save_slice_id": "packed-event-world-special-condition-save-slice",
"source": {
"description": "Tracked save-slice document with a whole-game special-condition effect row.",
"original_save_filename": "captured-world-special-condition.gms",
"original_save_sha256": "world-special-condition-sample-sha256",
"notes": [
"tracked as JSON save-slice document rather than raw .smp",
"proves whole-game special-condition effects import through the ordinary runtime path"
]
},
"save_slice": {
"file_extension_hint": "gms",
"container_profile_family": "rt3-classic-save-container-v1",
"mechanism_family": "classic-save-rehydrate-v1",
"mechanism_confidence": "grounded",
"trailer_family": null,
"bridge_family": null,
"profile": null,
"candidate_availability_table": null,
"special_conditions_table": null,
"event_runtime_collection": {
"source_kind": "packed-event-runtime-collection",
"mechanism_family": "classic-save-rehydrate-v1",
"mechanism_confidence": "grounded",
"container_profile_family": "rt3-classic-save-container-v1",
"metadata_tag_offset": 30000,
"records_tag_offset": 30256,
"close_tag_offset": 30720,
"packed_state_version": 1001,
"packed_state_version_hex": "0x000003e9",
"live_id_bound": 12,
"live_record_count": 1,
"live_entry_ids": [12],
"decoded_record_count": 1,
"imported_runtime_record_count": 1,
"records": [
{
"record_index": 0,
"live_entry_id": 12,
"payload_offset": 30336,
"payload_len": 112,
"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": [1, 0, 0, 0],
"summary_toggle_0x800": 1,
"grouped_territory_selectors_0x80f": [-1, -1, -1, -1]
},
"text_bands": [],
"standalone_condition_row_count": 0,
"standalone_condition_rows": [],
"negative_sentinel_scope": null,
"grouped_effect_row_counts": [1, 0, 0, 0],
"grouped_effect_rows": [
{
"group_index": 0,
"row_index": 0,
"descriptor_id": 108,
"descriptor_label": "Use Wartime Cargos",
"target_mask_bits": 8,
"parameter_family": "special_condition_scalar",
"opcode": 3,
"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,
"row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment",
"semantic_preview": "Set Use Wartime Cargos to 1",
"locomotive_name": null,
"notes": [
"tracked world-side descriptor sample"
]
}
],
"decoded_conditions": [],
"decoded_actions": [
{
"kind": "set_special_condition",
"label": "Use Wartime Cargos",
"value": 1
}
],
"executable_import_ready": true,
"notes": [
"tracked whole-game grouped-effect import sample"
]
}
]
},
"notes": [
"whole-game special-condition effect sample"
]
}
}