Execute real packed event world and train descriptors

This commit is contained in:
Jan Petykiewicz 2026-04-15 20:20:25 -07:00
commit e481274243
31 changed files with 3287 additions and 206 deletions

View file

@ -21,10 +21,12 @@ parallel packed executor. The first grounded condition-side unlock now exists fo
`raw_condition_id = -1` company scopes, and the first ordinary nonnegative condition batch now
executes too: numeric-threshold company finance, company track, aggregate territory track, and
company-territory track rows can import through overlay-backed runtime context. Exact
named-territory binding now executes, while descriptor `3` `Territory - Allow All` remains the
explicit parity-only descriptor frontier. 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.
named-territory binding now executes, and the runtime now also carries the minimal event-owned
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`
`Territory - Allow All` remains the explicit parity-only descriptor frontier. 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

View file

@ -176,6 +176,7 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,
@ -343,6 +344,7 @@ mod tests {
selected_company_id: Some(42),
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,

View file

@ -54,6 +54,8 @@ pub struct ExpectedRuntimeSummary {
#[serde(default)]
pub world_restore_ai_ignore_territories_at_startup_enabled: Option<bool>,
#[serde(default)]
pub world_restore_economic_status_code: Option<i32>,
#[serde(default)]
pub world_restore_absolute_counter_restore_kind: Option<String>,
#[serde(default)]
pub world_restore_absolute_counter_adjustment_context: Option<String>,
@ -66,6 +68,12 @@ pub struct ExpectedRuntimeSummary {
#[serde(default)]
pub player_count: Option<usize>,
#[serde(default)]
pub train_count: Option<usize>,
#[serde(default)]
pub active_train_count: Option<usize>,
#[serde(default)]
pub retired_train_count: Option<usize>,
#[serde(default)]
pub territory_count: Option<usize>,
#[serde(default)]
pub company_territory_track_count: Option<usize>,
@ -116,6 +124,16 @@ pub struct ExpectedRuntimeSummary {
#[serde(default)]
pub packed_event_blocked_territory_policy_descriptor_count: Option<usize>,
#[serde(default)]
pub packed_event_blocked_missing_train_context_count: Option<usize>,
#[serde(default)]
pub packed_event_blocked_missing_train_territory_context_count: Option<usize>,
#[serde(default)]
pub packed_event_blocked_confiscation_variant_count: Option<usize>,
#[serde(default)]
pub packed_event_blocked_retire_train_variant_count: Option<usize>,
#[serde(default)]
pub packed_event_blocked_retire_train_scope_count: Option<usize>,
#[serde(default)]
pub packed_event_blocked_structural_only_count: Option<usize>,
#[serde(default)]
pub event_runtime_record_count: Option<usize>,
@ -321,6 +339,14 @@ impl ExpectedRuntimeSummary {
));
}
}
if let Some(code) = self.world_restore_economic_status_code {
if actual.world_restore_economic_status_code != Some(code) {
mismatches.push(format!(
"world_restore_economic_status_code mismatch: expected {code}, got {:?}",
actual.world_restore_economic_status_code
));
}
}
if let Some(kind) = &self.world_restore_absolute_counter_restore_kind {
if actual.world_restore_absolute_counter_restore_kind.as_ref() != Some(kind) {
mismatches.push(format!(
@ -373,6 +399,30 @@ impl ExpectedRuntimeSummary {
));
}
}
if let Some(count) = self.train_count {
if actual.train_count != count {
mismatches.push(format!(
"train_count mismatch: expected {count}, got {}",
actual.train_count
));
}
}
if let Some(count) = self.active_train_count {
if actual.active_train_count != count {
mismatches.push(format!(
"active_train_count mismatch: expected {count}, got {}",
actual.active_train_count
));
}
}
if let Some(count) = self.retired_train_count {
if actual.retired_train_count != count {
mismatches.push(format!(
"retired_train_count mismatch: expected {count}, got {}",
actual.retired_train_count
));
}
}
if let Some(count) = self.territory_count {
if actual.territory_count != count {
mismatches.push(format!(
@ -573,6 +623,46 @@ impl ExpectedRuntimeSummary {
));
}
}
if let Some(count) = self.packed_event_blocked_missing_train_context_count {
if actual.packed_event_blocked_missing_train_context_count != count {
mismatches.push(format!(
"packed_event_blocked_missing_train_context_count mismatch: expected {count}, got {}",
actual.packed_event_blocked_missing_train_context_count
));
}
}
if let Some(count) = self.packed_event_blocked_missing_train_territory_context_count {
if actual.packed_event_blocked_missing_train_territory_context_count != count {
mismatches.push(format!(
"packed_event_blocked_missing_train_territory_context_count mismatch: expected {count}, got {}",
actual.packed_event_blocked_missing_train_territory_context_count
));
}
}
if let Some(count) = self.packed_event_blocked_confiscation_variant_count {
if actual.packed_event_blocked_confiscation_variant_count != count {
mismatches.push(format!(
"packed_event_blocked_confiscation_variant_count mismatch: expected {count}, got {}",
actual.packed_event_blocked_confiscation_variant_count
));
}
}
if let Some(count) = self.packed_event_blocked_retire_train_variant_count {
if actual.packed_event_blocked_retire_train_variant_count != count {
mismatches.push(format!(
"packed_event_blocked_retire_train_variant_count mismatch: expected {count}, got {}",
actual.packed_event_blocked_retire_train_variant_count
));
}
}
if let Some(count) = self.packed_event_blocked_retire_train_scope_count {
if actual.packed_event_blocked_retire_train_scope_count != count {
mismatches.push(format!(
"packed_event_blocked_retire_train_scope_count mismatch: expected {count}, got {}",
actual.packed_event_blocked_retire_train_scope_count
));
}
}
if let Some(count) = self.packed_event_blocked_structural_only_count {
if actual.packed_event_blocked_structural_only_count != count {
mismatches.push(format!(

File diff suppressed because it is too large Load diff

View file

@ -44,7 +44,7 @@ pub use runtime::{
RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer,
RuntimePlayerConditionTestScope, RuntimePlayerTarget, RuntimeSaveProfileState,
RuntimeServiceState, RuntimeState, RuntimeTerritory, RuntimeTerritoryMetric,
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts,
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain,
RuntimeWorldRestoreState,
};
pub use smp::{

View file

@ -96,6 +96,7 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,

View file

@ -83,6 +83,24 @@ pub struct RuntimePlayer {
pub controller_kind: RuntimeCompanyControllerKind,
}
fn runtime_train_default_active() -> bool {
true
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RuntimeTrain {
pub train_id: u32,
pub owner_company_id: u32,
#[serde(default)]
pub territory_id: Option<u32>,
#[serde(default)]
pub locomotive_name: Option<String>,
#[serde(default = "runtime_train_default_active")]
pub active: bool,
#[serde(default)]
pub retired: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum RuntimeCompanyTarget {
@ -213,6 +231,9 @@ pub enum RuntimeEffect {
key: String,
value: bool,
},
SetEconomicStatusCode {
value: i32,
},
SetCompanyCash {
target: RuntimeCompanyTarget,
value: i64,
@ -221,6 +242,9 @@ pub enum RuntimeEffect {
target: RuntimePlayerTarget,
value: i64,
},
ConfiscateCompanyAssets {
target: RuntimeCompanyTarget,
},
DeactivateCompany {
target: RuntimeCompanyTarget,
},
@ -228,6 +252,14 @@ pub enum RuntimeEffect {
target: RuntimeCompanyTarget,
value: Option<u32>,
},
RetireTrains {
#[serde(default)]
company_target: Option<RuntimeCompanyTarget>,
#[serde(default)]
territory_target: Option<RuntimeTerritoryTarget>,
#[serde(default)]
locomotive_name: Option<String>,
},
AdjustCompanyCash {
target: RuntimeCompanyTarget,
delta: i64,
@ -527,6 +559,8 @@ pub struct RuntimeWorldRestoreState {
#[serde(default)]
pub ai_ignore_territories_at_startup_enabled: Option<bool>,
#[serde(default)]
pub economic_status_code: Option<i32>,
#[serde(default)]
pub absolute_counter_restore_kind: Option<String>,
#[serde(default)]
pub absolute_counter_adjustment_context: Option<String>,
@ -552,6 +586,8 @@ pub struct RuntimeState {
#[serde(default)]
pub selected_player_id: Option<u32>,
#[serde(default)]
pub trains: Vec<RuntimeTrain>,
#[serde(default)]
pub territories: Vec<RuntimeTerritory>,
#[serde(default)]
pub company_territory_track_piece_counts: Vec<RuntimeCompanyTerritoryTrackPieceCount>,
@ -639,6 +675,42 @@ impl RuntimeState {
}
}
}
let mut seen_train_ids = BTreeSet::new();
for train in &self.trains {
if !seen_train_ids.insert(train.train_id) {
return Err(format!("duplicate train_id {}", train.train_id));
}
if !seen_company_ids.contains(&train.owner_company_id) {
return Err(format!(
"train_id {} references unknown owner_company_id {}",
train.train_id, train.owner_company_id
));
}
if let Some(territory_id) = train.territory_id {
if !seen_territory_ids.contains(&territory_id) {
return Err(format!(
"train_id {} references unknown territory_id {}",
train.train_id, territory_id
));
}
}
if train.retired && train.active {
return Err(format!(
"train_id {} cannot be active and retired at the same time",
train.train_id
));
}
if train
.locomotive_name
.as_deref()
.is_some_and(|value| value.trim().is_empty())
{
return Err(format!(
"train_id {} has an empty locomotive_name",
train.train_id
));
}
}
for entry in &self.company_territory_track_piece_counts {
if !seen_company_ids.contains(&entry.company_id) {
return Err(format!(
@ -1009,7 +1081,9 @@ fn validate_runtime_effect(
return Err("key must not be empty".to_string());
}
}
RuntimeEffect::SetEconomicStatusCode { .. } => {}
RuntimeEffect::SetCompanyCash { target, .. }
| RuntimeEffect::ConfiscateCompanyAssets { target }
| RuntimeEffect::DeactivateCompany { target }
| RuntimeEffect::SetCompanyTrackLayingCapacity { target, .. }
| RuntimeEffect::AdjustCompanyCash { target, .. }
@ -1019,6 +1093,30 @@ fn validate_runtime_effect(
RuntimeEffect::SetPlayerCash { target, .. } => {
validate_player_target(target, valid_player_ids)?;
}
RuntimeEffect::RetireTrains {
company_target,
territory_target,
locomotive_name,
} => {
if let Some(company_target) = company_target {
validate_company_target(company_target, valid_company_ids)?;
}
if let Some(territory_target) = territory_target {
validate_territory_target(territory_target, valid_territory_ids)?;
}
if company_target.is_none() && territory_target.is_none() && locomotive_name.is_none() {
return Err(
"retire_trains requires at least one company_target, territory_target, or locomotive_name filter"
.to_string(),
);
}
if locomotive_name
.as_deref()
.is_some_and(|value| value.trim().is_empty())
{
return Err("locomotive_name must not be empty".to_string());
}
}
RuntimeEffect::SetCandidateAvailability { name, .. } => {
if name.trim().is_empty() {
return Err("name must not be empty".to_string());
@ -1054,10 +1152,10 @@ fn validate_event_record_template(
for (condition_index, condition) in record.conditions.iter().enumerate() {
validate_runtime_condition(condition, valid_company_ids, valid_territory_ids).map_err(
|err| {
format!(
"template record_id={}.conditions[{condition_index}] {err}",
record.record_id
)
format!(
"template record_id={}.conditions[{condition_index}] {err}",
record.record_id
)
},
)?;
}
@ -1092,9 +1190,7 @@ fn validate_runtime_condition(
validate_territory_target(target, valid_territory_ids)
}
RuntimeCondition::CompanyTerritoryNumericThreshold {
target,
territory,
..
target, territory, ..
} => {
validate_company_target(target, valid_company_ids)?;
validate_territory_target(territory, valid_territory_ids)
@ -1216,6 +1312,7 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,
@ -1255,6 +1352,7 @@ mod tests {
disable_train_crashes_enabled: Some(false),
disable_train_crashes_and_breakdowns_enabled: Some(false),
ai_ignore_territories_at_startup_enabled: Some(false),
economic_status_code: None,
absolute_counter_restore_kind: Some(
"mode-adjusted-selected-year-lane".to_string(),
),
@ -1267,6 +1365,7 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,
@ -1306,6 +1405,7 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,
@ -1358,6 +1458,7 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,
@ -1410,6 +1511,7 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: Some(RuntimePackedEventCollectionSummary {
@ -1513,6 +1615,7 @@ mod tests {
selected_company_id: Some(2),
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,
@ -1552,6 +1655,209 @@ mod tests {
selected_company_id: Some(1),
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,
event_runtime_records: Vec::new(),
candidate_availability: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
assert!(state.validate().is_err());
}
#[test]
fn rejects_duplicate_train_ids() {
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![RuntimeCompany {
company_id: 1,
current_cash: 100,
debt: 0,
credit_rating_score: None,
prime_rate: None,
track_piece_counts: RuntimeTrackPieceCounts::default(),
active: true,
available_track_laying_capacity: None,
controller_kind: RuntimeCompanyControllerKind::Human,
}],
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: vec![
RuntimeTrain {
train_id: 7,
owner_company_id: 1,
territory_id: None,
locomotive_name: Some("Mikado".to_string()),
active: true,
retired: false,
},
RuntimeTrain {
train_id: 7,
owner_company_id: 1,
territory_id: None,
locomotive_name: Some("Orca".to_string()),
active: true,
retired: false,
},
],
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,
event_runtime_records: Vec::new(),
candidate_availability: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
assert!(state.validate().is_err());
}
#[test]
fn rejects_train_with_unknown_owner_company() {
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![RuntimeCompany {
company_id: 1,
current_cash: 100,
debt: 0,
credit_rating_score: None,
prime_rate: None,
track_piece_counts: RuntimeTrackPieceCounts::default(),
active: true,
available_track_laying_capacity: None,
controller_kind: RuntimeCompanyControllerKind::Human,
}],
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: vec![RuntimeTrain {
train_id: 7,
owner_company_id: 2,
territory_id: None,
locomotive_name: Some("Mikado".to_string()),
active: true,
retired: false,
}],
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,
event_runtime_records: Vec::new(),
candidate_availability: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
assert!(state.validate().is_err());
}
#[test]
fn rejects_train_with_unknown_territory() {
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![RuntimeCompany {
company_id: 1,
current_cash: 100,
debt: 0,
credit_rating_score: None,
prime_rate: None,
track_piece_counts: RuntimeTrackPieceCounts::default(),
active: true,
available_track_laying_capacity: None,
controller_kind: RuntimeCompanyControllerKind::Human,
}],
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: vec![RuntimeTrain {
train_id: 7,
owner_company_id: 1,
territory_id: Some(9),
locomotive_name: Some("Mikado".to_string()),
active: true,
retired: false,
}],
territories: vec![RuntimeTerritory {
territory_id: 1,
name: Some("Appalachia".to_string()),
track_piece_counts: RuntimeTrackPieceCounts::default(),
}],
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,
event_runtime_records: Vec::new(),
candidate_availability: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
assert!(state.validate().is_err());
}
#[test]
fn rejects_train_marked_active_and_retired() {
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![RuntimeCompany {
company_id: 1,
current_cash: 100,
debt: 0,
credit_rating_score: None,
prime_rate: None,
track_piece_counts: RuntimeTrackPieceCounts::default(),
active: true,
available_track_laying_capacity: None,
controller_kind: RuntimeCompanyControllerKind::Human,
}],
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: vec![RuntimeTrain {
train_id: 7,
owner_company_id: 1,
territory_id: None,
locomotive_name: Some("Mikado".to_string()),
active: true,
retired: true,
}],
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,

View file

@ -5,8 +5,8 @@ use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use crate::{
RuntimeCompanyConditionTestScope, RuntimeCompanyMetric, RuntimeCompanyTarget,
RuntimeCondition, RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecordTemplate,
RuntimeCompanyConditionTestScope, RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCondition,
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecordTemplate,
RuntimePlayerConditionTestScope, RuntimePlayerTarget, RuntimeTerritoryMetric,
RuntimeTerritoryTarget, RuntimeTrackMetric,
};
@ -154,14 +154,14 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad
label: "Economic Status",
target_mask_bits: 0x08,
parameter_family: "whole_game_state_enum",
executable_in_runtime: false,
executable_in_runtime: true,
},
RealGroupedEffectDescriptorMetadata {
descriptor_id: 9,
label: "Confiscate All",
target_mask_bits: 0x01,
parameter_family: "company_confiscation_variant",
executable_in_runtime: false,
executable_in_runtime: true,
},
RealGroupedEffectDescriptorMetadata {
descriptor_id: 13,
@ -175,7 +175,7 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad
label: "Retire Train",
target_mask_bits: 0x0d,
parameter_family: "company_or_territory_asset_toggle",
executable_in_runtime: false,
executable_in_runtime: true,
},
RealGroupedEffectDescriptorMetadata {
descriptor_id: 16,
@ -269,7 +269,9 @@ const REAL_ORDINARY_CONDITION_METADATA: [RealOrdinaryConditionMetadata; 22] = [
RealOrdinaryConditionMetadata {
raw_condition_id: 2316,
label: "Territory Transition Track Pieces",
metric: RealOrdinaryConditionMetric::Territory(RuntimeTerritoryMetric::TrackPiecesTransition),
metric: RealOrdinaryConditionMetric::Territory(
RuntimeTerritoryMetric::TrackPiecesTransition,
),
},
RealOrdinaryConditionMetadata {
raw_condition_id: 2317,
@ -279,7 +281,9 @@ const REAL_ORDINARY_CONDITION_METADATA: [RealOrdinaryConditionMetadata; 22] = [
RealOrdinaryConditionMetadata {
raw_condition_id: 2318,
label: "Territory Non-Electric Track Pieces",
metric: RealOrdinaryConditionMetric::Territory(RuntimeTerritoryMetric::TrackPiecesNonElectric),
metric: RealOrdinaryConditionMetric::Territory(
RuntimeTerritoryMetric::TrackPiecesNonElectric,
),
},
RealOrdinaryConditionMetadata {
raw_condition_id: 2323,
@ -2096,14 +2100,36 @@ fn parse_real_event_runtime_record_summary(
)?);
}
}
if let Some(control) = compact_control.as_ref() {
for row in &mut grouped_effect_rows {
if row.descriptor_id != 15
|| row.row_shape != "bool_toggle"
|| row.raw_scalar_value == 0
{
continue;
}
let company_target_present = control
.grouped_target_scope_ordinals_0x7fb
.get(row.group_index)
.copied()
.and_then(real_grouped_company_target)
.is_some();
let territory_target_present = control
.grouped_territory_selectors_0x80f
.get(row.group_index)
.is_some_and(|selector| *selector >= 0);
if !company_target_present && !territory_target_present {
row.notes
.push("retire train row is missing company and territory scope".to_string());
}
}
}
let negative_sentinel_scope = compact_control.as_ref().and_then(|control| {
derive_negative_sentinel_scope_summary(&standalone_condition_rows, control)
});
let decoded_conditions = decode_real_condition_rows(
&standalone_condition_rows,
negative_sentinel_scope.as_ref(),
);
let decoded_conditions =
decode_real_condition_rows(&standalone_condition_rows, negative_sentinel_scope.as_ref());
let decoded_actions = compact_control
.as_ref()
.map(|control| decode_real_grouped_effect_actions(&grouped_effect_rows, control))
@ -2253,7 +2279,10 @@ fn parse_real_condition_row_summary(
notes.push("condition row carries candidate-name side string".to_string());
}
if ordinary_metadata.is_none() && raw_condition_id >= 0 {
notes.push("ordinary condition id is not yet recovered in the checked-in condition table".to_string());
notes.push(
"ordinary condition id is not yet recovered in the checked-in condition table"
.to_string(),
);
}
Some(SmpLoadedPackedEventConditionRowSummary {
row_index,
@ -2462,33 +2491,31 @@ fn decode_real_condition_row(
let comparator = decode_real_condition_comparator(row.subtype)?;
let value = decode_real_condition_threshold(&row.flag_bytes)?;
match metadata.metric {
RealOrdinaryConditionMetric::Company(metric) => Some(RuntimeCondition::CompanyNumericThreshold {
target: RuntimeCompanyTarget::ConditionTrueCompany,
metric,
comparator,
value,
}),
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
.filter(|scope| scope.territory_scope_selector_is_0x63)
.map(|_| RuntimeCondition::CompanyTerritoryNumericThreshold {
target: RuntimeCompanyTarget::ConditionTrueCompany,
territory: RuntimeTerritoryTarget::AllTerritories,
metric,
comparator,
value,
})
RealOrdinaryConditionMetric::Company(metric) => {
Some(RuntimeCondition::CompanyNumericThreshold {
target: RuntimeCompanyTarget::ConditionTrueCompany,
metric,
comparator,
value,
})
}
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
.filter(|scope| scope.territory_scope_selector_is_0x63)
.map(|_| RuntimeCondition::CompanyTerritoryNumericThreshold {
target: RuntimeCompanyTarget::ConditionTrueCompany,
territory: RuntimeTerritoryTarget::AllTerritories,
metric,
comparator,
value,
}),
}
}
@ -2616,6 +2643,24 @@ fn decode_real_grouped_effect_action(
});
}
if descriptor_metadata.executable_in_runtime
&& descriptor_metadata.descriptor_id == 8
&& row.row_shape == "scalar_assignment"
{
return Some(RuntimeEffect::SetEconomicStatusCode {
value: row.raw_scalar_value,
});
}
if descriptor_metadata.executable_in_runtime
&& descriptor_metadata.descriptor_id == 9
&& row.row_shape == "bool_toggle"
&& row.raw_scalar_value != 0
{
let target = real_grouped_company_target(target_scope_ordinal)?;
return Some(RuntimeEffect::ConfiscateCompanyAssets { target });
}
if descriptor_metadata.executable_in_runtime
&& descriptor_metadata.descriptor_id == 13
&& row.row_shape == "bool_toggle"
@ -2637,6 +2682,30 @@ fn decode_real_grouped_effect_action(
});
}
if descriptor_metadata.executable_in_runtime
&& descriptor_metadata.descriptor_id == 15
&& row.row_shape == "bool_toggle"
&& row.raw_scalar_value != 0
{
let company_target = real_grouped_company_target(target_scope_ordinal);
let territory_target = compact_control
.grouped_territory_selectors_0x80f
.get(row.group_index)
.copied()
.filter(|selector| *selector >= 0)
.map(|selector| RuntimeTerritoryTarget::Ids {
ids: vec![selector as u32],
});
if company_target.is_none() && territory_target.is_none() {
return None;
}
return Some(RuntimeEffect::RetireTrains {
company_target,
territory_target,
locomotive_name: row.locomotive_name.clone(),
});
}
None
}
@ -2808,10 +2877,13 @@ fn parse_optional_u16_len_prefixed_string(
fn runtime_effect_supported_for_save_import(effect: &RuntimeEffect) -> bool {
match effect {
RuntimeEffect::SetWorldFlag { .. }
| RuntimeEffect::SetEconomicStatusCode { .. }
| RuntimeEffect::SetCandidateAvailability { .. }
| RuntimeEffect::SetSpecialCondition { .. }
| RuntimeEffect::ConfiscateCompanyAssets { .. }
| RuntimeEffect::DeactivateCompany { .. }
| RuntimeEffect::SetCompanyTrackLayingCapacity { .. }
| RuntimeEffect::RetireTrains { .. }
| RuntimeEffect::ActivateEventRecord { .. }
| RuntimeEffect::DeactivateEventRecord { .. }
| RuntimeEffect::RemoveEventRecord { .. } => true,

View file

@ -6,8 +6,7 @@ use crate::{
RuntimeCompanyControllerKind, RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCondition,
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecordTemplate, RuntimePlayerTarget,
RuntimeState, RuntimeSummary, RuntimeTerritoryMetric, RuntimeTerritoryTarget,
RuntimeTrackMetric, RuntimeTrackPieceCounts,
calendar::BoundaryEventKind,
RuntimeTrackMetric, RuntimeTrackPieceCounts, calendar::BoundaryEventKind,
};
const PERIODIC_TRIGGER_KIND_ORDER: [u8; 6] = [1, 0, 3, 2, 5, 4];
@ -312,6 +311,9 @@ fn apply_runtime_effects(
RuntimeEffect::SetWorldFlag { key, value } => {
state.world_flags.insert(key.clone(), *value);
}
RuntimeEffect::SetEconomicStatusCode { value } => {
state.world_restore.economic_status_code = Some(*value);
}
RuntimeEffect::SetCompanyCash { target, value } => {
let company_ids = resolve_company_target_ids(state, target, condition_context)?;
for company_id in company_ids {
@ -340,6 +342,28 @@ fn apply_runtime_effects(
mutated_player_ids.insert(player_id);
}
}
RuntimeEffect::ConfiscateCompanyAssets { target } => {
let company_ids = resolve_company_target_ids(state, target, condition_context)?;
for company_id in company_ids.iter().copied() {
let company = state
.companies
.iter_mut()
.find(|company| company.company_id == company_id)
.ok_or_else(|| {
format!(
"missing company_id {company_id} while applying confiscate effect"
)
})?;
company.current_cash = 0;
company.debt = 0;
company.active = false;
mutated_company_ids.insert(company_id);
if state.selected_company_id == Some(company_id) {
state.selected_company_id = None;
}
}
retire_matching_trains(&mut state.trains, Some(&company_ids), None, None);
}
RuntimeEffect::DeactivateCompany { target } => {
let company_ids = resolve_company_target_ids(state, target, condition_context)?;
for company_id in company_ids {
@ -375,6 +399,26 @@ fn apply_runtime_effects(
mutated_company_ids.insert(company_id);
}
}
RuntimeEffect::RetireTrains {
company_target,
territory_target,
locomotive_name,
} => {
let company_ids = company_target
.as_ref()
.map(|target| resolve_company_target_ids(state, target, condition_context))
.transpose()?;
let territory_ids = territory_target
.as_ref()
.map(|target| resolve_territory_target_ids(state, target))
.transpose()?;
retire_matching_trains(
&mut state.trains,
company_ids.as_ref(),
territory_ids.as_ref(),
locomotive_name.as_deref(),
);
}
RuntimeEffect::AdjustCompanyCash { target, delta } => {
let company_ids = resolve_company_target_ids(state, target, condition_context)?;
for company_id in company_ids {
@ -523,13 +567,17 @@ fn evaluate_record_conditions(
let matching = resolved
.into_iter()
.filter(|company_id| {
state.companies.iter().find(|company| company.company_id == *company_id).is_some_and(
|company| compare_condition_value(
company_metric_value(company, *metric),
*comparator,
*value,
),
)
state
.companies
.iter()
.find(|company| company.company_id == *company_id)
.is_some_and(|company| {
compare_condition_value(
company_metric_value(company, *metric),
*comparator,
*value,
)
})
})
.collect::<BTreeSet<_>>();
if matching.is_empty() {
@ -597,10 +645,7 @@ fn evaluate_record_conditions(
}))
}
fn intersect_company_matches(
company_matches: &mut Option<BTreeSet<u32>>,
next: BTreeSet<u32>,
) {
fn intersect_company_matches(company_matches: &mut Option<BTreeSet<u32>>, next: BTreeSet<u32>) {
match company_matches {
Some(existing) => {
existing.retain(|company_id| next.contains(company_id));
@ -790,7 +835,11 @@ fn resolve_player_target_ids(
if condition_context.matching_player_ids.is_empty() {
Err("target requires player condition-evaluation context".to_string())
} else {
Ok(condition_context.matching_player_ids.iter().copied().collect())
Ok(condition_context
.matching_player_ids
.iter()
.copied()
.collect())
}
}
}
@ -801,9 +850,11 @@ fn resolve_territory_target_ids(
target: &RuntimeTerritoryTarget,
) -> Result<Vec<u32>, String> {
match target {
RuntimeTerritoryTarget::AllTerritories => {
Ok(state.territories.iter().map(|territory| territory.territory_id).collect())
}
RuntimeTerritoryTarget::AllTerritories => Ok(state
.territories
.iter()
.map(|territory| territory.territory_id)
.collect()),
RuntimeTerritoryTarget::Ids { ids } => {
let known_ids = state
.territories
@ -812,7 +863,9 @@ fn resolve_territory_target_ids(
.collect::<BTreeSet<_>>();
for territory_id in ids {
if !known_ids.contains(territory_id) {
return Err(format!("territory target references unknown territory_id {territory_id}"));
return Err(format!(
"territory target references unknown territory_id {territory_id}"
));
}
}
Ok(ids.clone())
@ -832,9 +885,7 @@ fn company_metric_value(company: &crate::RuntimeCompany, metric: RuntimeCompanyM
RuntimeCompanyMetric::TrackPiecesTransition => {
i64::from(company.track_piece_counts.transition)
}
RuntimeCompanyMetric::TrackPiecesElectric => {
i64::from(company.track_piece_counts.electric)
}
RuntimeCompanyMetric::TrackPiecesElectric => i64::from(company.track_piece_counts.electric),
RuntimeCompanyMetric::TrackPiecesNonElectric => {
i64::from(company.track_piece_counts.non_electric)
}
@ -846,7 +897,8 @@ fn territory_metric_value(
territory_ids: &[u32],
metric: RuntimeTerritoryMetric,
) -> i64 {
state.territories
state
.territories
.iter()
.filter(|territory| territory_ids.contains(&territory.territory_id))
.map(|territory| {
@ -864,9 +916,12 @@ fn company_territory_metric_value(
territory_ids: &[u32],
metric: RuntimeTrackMetric,
) -> i64 {
state.company_territory_track_piece_counts
state
.company_territory_track_piece_counts
.iter()
.filter(|entry| entry.company_id == company_id && territory_ids.contains(&entry.territory_id))
.filter(|entry| {
entry.company_id == company_id && territory_ids.contains(&entry.territory_id)
})
.map(|entry| track_piece_metric_value(entry.track_piece_counts, metric))
.sum()
}
@ -920,6 +975,34 @@ fn apply_u64_delta(current: u64, delta: i64, company_id: u32) -> Result<u64, Str
}
}
fn retire_matching_trains(
trains: &mut [crate::RuntimeTrain],
company_ids: Option<&Vec<u32>>,
territory_ids: Option<&Vec<u32>>,
locomotive_name: Option<&str>,
) {
for train in trains.iter_mut() {
if !train.active || train.retired {
continue;
}
if company_ids.is_some_and(|company_ids| !company_ids.contains(&train.owner_company_id)) {
continue;
}
if territory_ids.is_some_and(|territory_ids| {
!train
.territory_id
.is_some_and(|territory_id| territory_ids.contains(&territory_id))
}) {
continue;
}
if locomotive_name.is_some_and(|name| train.locomotive_name.as_deref() != Some(name)) {
continue;
}
train.active = false;
train.retired = true;
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
@ -928,7 +1011,8 @@ mod tests {
use crate::{
CalendarPoint, RuntimeCompany, RuntimeCompanyControllerKind, RuntimeCompanyTarget,
RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate, RuntimeSaveProfileState,
RuntimeServiceState, RuntimeWorldRestoreState,
RuntimeServiceState, RuntimeTerritory, RuntimeTerritoryTarget, RuntimeTrackPieceCounts,
RuntimeTrain, RuntimeWorldRestoreState,
};
fn state() -> RuntimeState {
@ -957,6 +1041,7 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,
@ -1927,4 +2012,177 @@ mod tests {
assert!(result.is_err());
}
#[test]
fn applies_economic_status_code_effect() {
let mut state = RuntimeState {
event_runtime_records: vec![RuntimeEventRecord {
record_id: 90,
trigger_kind: 6,
active: true,
service_count: 0,
marks_collection_dirty: false,
one_shot: false,
has_fired: false,
conditions: Vec::new(),
effects: vec![RuntimeEffect::SetEconomicStatusCode { value: 3 }],
}],
..state()
};
execute_step_command(
&mut state,
&StepCommand::ServiceTriggerKind { trigger_kind: 6 },
)
.expect("economic-status effect should succeed");
assert_eq!(state.world_restore.economic_status_code, Some(3));
}
#[test]
fn confiscate_company_assets_zeros_company_and_retires_owned_trains() {
let mut state = RuntimeState {
companies: vec![
RuntimeCompany {
company_id: 1,
controller_kind: RuntimeCompanyControllerKind::Human,
current_cash: 50,
debt: 7,
credit_rating_score: None,
prime_rate: None,
track_piece_counts: RuntimeTrackPieceCounts::default(),
active: true,
available_track_laying_capacity: None,
},
RuntimeCompany {
company_id: 2,
controller_kind: RuntimeCompanyControllerKind::Ai,
current_cash: 80,
debt: 9,
credit_rating_score: None,
prime_rate: None,
track_piece_counts: RuntimeTrackPieceCounts::default(),
active: true,
available_track_laying_capacity: None,
},
],
selected_company_id: Some(1),
trains: vec![
RuntimeTrain {
train_id: 10,
owner_company_id: 1,
territory_id: None,
locomotive_name: Some("Mikado".to_string()),
active: true,
retired: false,
},
RuntimeTrain {
train_id: 11,
owner_company_id: 2,
territory_id: None,
locomotive_name: Some("Orca".to_string()),
active: true,
retired: false,
},
],
event_runtime_records: vec![RuntimeEventRecord {
record_id: 91,
trigger_kind: 6,
active: true,
service_count: 0,
marks_collection_dirty: false,
one_shot: false,
has_fired: false,
conditions: Vec::new(),
effects: vec![RuntimeEffect::ConfiscateCompanyAssets {
target: RuntimeCompanyTarget::SelectedCompany,
}],
}],
..state()
};
execute_step_command(
&mut state,
&StepCommand::ServiceTriggerKind { trigger_kind: 6 },
)
.expect("confiscation effect should succeed");
assert_eq!(state.companies[0].current_cash, 0);
assert_eq!(state.companies[0].debt, 0);
assert!(!state.companies[0].active);
assert_eq!(state.selected_company_id, None);
assert!(state.trains[0].retired);
assert!(!state.trains[1].retired);
}
#[test]
fn retire_trains_respects_company_territory_and_locomotive_filters() {
let mut state = RuntimeState {
territories: vec![
RuntimeTerritory {
territory_id: 7,
name: Some("Appalachia".to_string()),
track_piece_counts: RuntimeTrackPieceCounts::default(),
},
RuntimeTerritory {
territory_id: 8,
name: Some("Great Plains".to_string()),
track_piece_counts: RuntimeTrackPieceCounts::default(),
},
],
trains: vec![
RuntimeTrain {
train_id: 10,
owner_company_id: 1,
territory_id: Some(7),
locomotive_name: Some("Mikado".to_string()),
active: true,
retired: false,
},
RuntimeTrain {
train_id: 11,
owner_company_id: 1,
territory_id: Some(7),
locomotive_name: Some("Orca".to_string()),
active: true,
retired: false,
},
RuntimeTrain {
train_id: 12,
owner_company_id: 1,
territory_id: Some(8),
locomotive_name: Some("Mikado".to_string()),
active: true,
retired: false,
},
],
event_runtime_records: vec![RuntimeEventRecord {
record_id: 92,
trigger_kind: 6,
active: true,
service_count: 0,
marks_collection_dirty: false,
one_shot: false,
has_fired: false,
conditions: Vec::new(),
effects: vec![RuntimeEffect::RetireTrains {
company_target: Some(RuntimeCompanyTarget::SelectedCompany),
territory_target: Some(RuntimeTerritoryTarget::Ids { ids: vec![7] }),
locomotive_name: Some("Mikado".to_string()),
}],
}],
selected_company_id: Some(1),
..state()
};
execute_step_command(
&mut state,
&StepCommand::ServiceTriggerKind { trigger_kind: 6 },
)
.expect("retire-trains effect should succeed");
assert!(state.trains[0].retired);
assert!(!state.trains[1].retired);
assert!(!state.trains[2].retired);
}
}

View file

@ -24,12 +24,16 @@ pub struct RuntimeSummary {
pub world_restore_disable_train_crashes_enabled: Option<bool>,
pub world_restore_disable_train_crashes_and_breakdowns_enabled: Option<bool>,
pub world_restore_ai_ignore_territories_at_startup_enabled: Option<bool>,
pub world_restore_economic_status_code: Option<i32>,
pub world_restore_absolute_counter_restore_kind: Option<String>,
pub world_restore_absolute_counter_adjustment_context: Option<String>,
pub metadata_count: usize,
pub company_count: usize,
pub active_company_count: usize,
pub player_count: usize,
pub train_count: usize,
pub active_train_count: usize,
pub retired_train_count: usize,
pub territory_count: usize,
pub company_territory_track_count: usize,
pub packed_event_collection_present: bool,
@ -55,6 +59,11 @@ pub struct RuntimeSummary {
pub packed_event_blocked_missing_compact_control_count: usize,
pub packed_event_blocked_unmapped_real_descriptor_count: usize,
pub packed_event_blocked_territory_policy_descriptor_count: usize,
pub packed_event_blocked_missing_train_context_count: usize,
pub packed_event_blocked_missing_train_territory_context_count: usize,
pub packed_event_blocked_confiscation_variant_count: usize,
pub packed_event_blocked_retire_train_variant_count: usize,
pub packed_event_blocked_retire_train_scope_count: usize,
pub packed_event_blocked_structural_only_count: usize,
pub event_runtime_record_count: usize,
pub candidate_availability_count: usize,
@ -127,6 +136,7 @@ impl RuntimeSummary {
world_restore_ai_ignore_territories_at_startup_enabled: state
.world_restore
.ai_ignore_territories_at_startup_enabled,
world_restore_economic_status_code: state.world_restore.economic_status_code,
world_restore_absolute_counter_restore_kind: state
.world_restore
.absolute_counter_restore_kind
@ -143,6 +153,9 @@ impl RuntimeSummary {
.filter(|company| company.active)
.count(),
player_count: state.players.len(),
train_count: state.trains.len(),
active_train_count: state.trains.iter().filter(|train| train.active).count(),
retired_train_count: state.trains.iter().filter(|train| train.retired).count(),
territory_count: state.territories.len(),
company_territory_track_count: state.company_territory_track_piece_counts.len(),
packed_event_collection_present: state.packed_event_collection.is_some(),
@ -421,6 +434,73 @@ impl RuntimeSummary {
.count()
})
.unwrap_or(0),
packed_event_blocked_missing_train_context_count: state
.packed_event_collection
.as_ref()
.map(|summary| {
summary
.records
.iter()
.filter(|record| {
record.import_outcome.as_deref()
== Some("blocked_missing_train_context")
})
.count()
})
.unwrap_or(0),
packed_event_blocked_missing_train_territory_context_count: state
.packed_event_collection
.as_ref()
.map(|summary| {
summary
.records
.iter()
.filter(|record| {
record.import_outcome.as_deref()
== Some("blocked_missing_train_territory_context")
})
.count()
})
.unwrap_or(0),
packed_event_blocked_confiscation_variant_count: state
.packed_event_collection
.as_ref()
.map(|summary| {
summary
.records
.iter()
.filter(|record| {
record.import_outcome.as_deref() == Some("blocked_confiscation_variant")
})
.count()
})
.unwrap_or(0),
packed_event_blocked_retire_train_variant_count: state
.packed_event_collection
.as_ref()
.map(|summary| {
summary
.records
.iter()
.filter(|record| {
record.import_outcome.as_deref() == Some("blocked_retire_train_variant")
})
.count()
})
.unwrap_or(0),
packed_event_blocked_retire_train_scope_count: state
.packed_event_collection
.as_ref()
.map(|summary| {
summary
.records
.iter()
.filter(|record| {
record.import_outcome.as_deref() == Some("blocked_retire_train_scope")
})
.count()
})
.unwrap_or(0),
packed_event_blocked_structural_only_count: state
.packed_event_collection
.as_ref()
@ -481,8 +561,8 @@ mod tests {
use crate::{
CalendarPoint, RuntimeCompany, RuntimeCompanyControllerKind,
RuntimePackedEventCollectionSummary, RuntimePackedEventRecordSummary,
RuntimeTrackPieceCounts,
RuntimeSaveProfileState, RuntimeServiceState, RuntimeState, RuntimeWorldRestoreState,
RuntimeSaveProfileState, RuntimeServiceState, RuntimeState, RuntimeTrackPieceCounts,
RuntimeWorldRestoreState,
};
use super::RuntimeSummary;
@ -504,6 +584,7 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: Some(RuntimePackedEventCollectionSummary {
@ -731,6 +812,7 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
packed_event_collection: None,

View file

@ -91,6 +91,9 @@ The highest-value next passes are now:
track
- exact named-territory binding now executes too, while named-territory no-match cases remain the
explicit binding blocker frontier
- real descriptors `8` `Economic Status`, `9` `Confiscate All`, and `15` `Retire Train` now join
the executable batch through the same ordinary runtime path, backed by the opaque economic-status
lane and the minimal event-owned train roster
- descriptor `3` `Territory - Allow All` remains the explicit parity-only descriptor frontier, and
mixed supported/unsupported real rows still stay parity-only
- keep in mind that the current local `.gms` corpus still exports with no packed event collection,

View file

@ -40,13 +40,17 @@ Implemented today:
- exact named-territory binding now lowers candidate-name ordinary rows onto tracked territory
names, a minimal player runtime now carries selected-player and role context, and real descriptor
`1` = `Player Cash` now imports and executes through the ordinary runtime path
- descriptor `3` = `Territory - Allow All` now has an explicit parity-only frontier label instead
of hiding behind the generic unmapped bucket
- a minimal event-owned train surface and an opaque economic-status lane now exist in runtime
state, and real descriptors `8` = `Economic Status`, `9` = `Confiscate All`, and `15` =
`Retire Train` now import and execute through the ordinary runtime path when overlay context
supplies the required train ownership data
- descriptor `3` = `Territory - Allow All` remains the explicit parity-only descriptor frontier
instead of hiding behind the generic unmapped bucket
That means the next implementation work is breadth, not bootstrap. The recommended next slice is
broader ordinary condition-id coverage beyond numeric thresholds, wider real grouped-descriptor
coverage beyond the current company/player cash batch, and later executable territory-policy
mutation once those semantics are grounded strongly enough to avoid guessing.
broader real policy-descriptor coverage beyond `3/8/9/15`, wider ordinary condition-id coverage
beyond the current numeric-threshold batch, and richer train/runtime simulation only if later
descriptor families need more than the current event-owned roster.
## Why This Boundary

View file

@ -0,0 +1,32 @@
{
"format_version": 1,
"fixture_id": "packed-event-confiscate-all-false-save-slice-fixture",
"source": {
"kind": "captured-runtime",
"description": "Fixture keeping the unsupported FALSE Confiscate All variant explicit."
},
"state_save_slice_path": "packed-event-confiscate-all-false-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": 0,
"event_runtime_record_count": 0,
"packed_event_blocked_confiscation_variant_count": 1
},
"expected_state_fragment": {
"packed_event_collection": {
"records": [
{
"import_outcome": "blocked_confiscation_variant"
}
]
}
}
}

View file

@ -0,0 +1,101 @@
{
"format_version": 1,
"save_slice_id": "packed-event-confiscate-all-false-save-slice",
"source": {
"description": "Tracked save-slice document with an unsupported FALSE Confiscate All row.",
"original_save_filename": "captured-confiscate-all-false.gms",
"original_save_sha256": "confiscate-all-false-sample-sha256",
"notes": [
"tracked as JSON save-slice document rather than raw .smp",
"keeps the unsupported descriptor 9 variant 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": 28928,
"records_tag_offset": 29184,
"close_tag_offset": 29696,
"packed_state_version": 1001,
"packed_state_version_hex": "0x000003e9",
"live_id_bound": 20,
"live_record_count": 1,
"live_entry_ids": [20],
"decoded_record_count": 1,
"imported_runtime_record_count": 0,
"records": [
{
"record_index": 0,
"live_entry_id": 20,
"payload_offset": 29280,
"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": 99,
"grouped_mode_0x7f4": 2,
"one_shot_header_0x7f5": 0,
"modifier_flag_0x7f9": 1,
"modifier_flag_0x7fa": 0,
"grouped_target_scope_ordinals_0x7fb": [1, 1, 1, 1],
"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": 9,
"descriptor_label": "Confiscate All",
"target_mask_bits": 1,
"parameter_family": "company_confiscation_variant",
"opcode": 1,
"raw_scalar_value": 0,
"value_byte_0x09": 0,
"value_dword_0x0d": 0,
"value_byte_0x11": 0,
"value_byte_0x12": 0,
"value_word_0x14": 0,
"value_word_0x16": 0,
"row_shape": "bool_toggle",
"semantic_family": "bool_toggle",
"semantic_preview": "Set Confiscate All to FALSE",
"locomotive_name": null,
"notes": []
}
],
"decoded_conditions": [],
"decoded_actions": [],
"executable_import_ready": false,
"notes": [
"decoded from grounded real 0x4e9a row framing"
]
}
]
},
"notes": [
"unsupported real confiscate-all FALSE variant sample"
]
}
}

View file

@ -0,0 +1,81 @@
{
"format_version": 1,
"fixture_id": "packed-event-confiscate-all-overlay-fixture",
"source": {
"kind": "captured-runtime",
"description": "Fixture proving descriptor 9 Confiscate All imports and executes through the ordinary runtime path."
},
"state_import_path": "packed-event-confiscate-all-overlay.json",
"commands": [
{
"kind": "service_trigger_kind",
"trigger_kind": 7
}
],
"expected_summary": {
"calendar_projection_source": "base-snapshot-preserved",
"calendar_projection_is_placeholder": false,
"company_count": 2,
"active_company_count": 1,
"train_count": 3,
"active_train_count": 1,
"retired_train_count": 2,
"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,
"total_event_record_service_count": 1,
"total_trigger_dispatch_count": 1,
"total_company_cash": 90
},
"expected_state_fragment": {
"selected_company_id": null,
"companies": [
{
"company_id": 1,
"current_cash": 0,
"debt": 0,
"active": false
},
{
"company_id": 2,
"current_cash": 90,
"debt": 40,
"active": true
}
],
"trains": [
{
"train_id": 100,
"active": false,
"retired": true
},
{
"train_id": 101,
"active": false,
"retired": true
},
{
"train_id": 102,
"active": true,
"retired": false
}
],
"packed_event_collection": {
"records": [
{
"import_outcome": "imported",
"decoded_actions": [
{
"kind": "confiscate_company_assets",
"target": {
"kind": "selected_company"
}
}
]
}
]
}
}
}

View file

@ -0,0 +1,9 @@
{
"format_version": 1,
"import_id": "packed-event-confiscate-all-overlay",
"source": {
"description": "Overlay import combining world/train runtime context with the real Confiscate All descriptor sample."
},
"base_snapshot_path": "packed-event-world-train-overlay-base-snapshot.json",
"save_slice_path": "packed-event-confiscate-all-save-slice.json"
}

View file

@ -0,0 +1,108 @@
{
"format_version": 1,
"save_slice_id": "packed-event-confiscate-all-save-slice",
"source": {
"description": "Tracked save-slice document with a real Confiscate All row.",
"original_save_filename": "captured-confiscate-all.gms",
"original_save_sha256": "confiscate-all-sample-sha256",
"notes": [
"tracked as JSON save-slice document rather than raw .smp",
"proves descriptor 9 import into company liquidation plus owned-train retirement"
]
},
"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": 28928,
"records_tag_offset": 29184,
"close_tag_offset": 29696,
"packed_state_version": 1001,
"packed_state_version_hex": "0x000003e9",
"live_id_bound": 19,
"live_record_count": 1,
"live_entry_ids": [19],
"decoded_record_count": 1,
"imported_runtime_record_count": 1,
"records": [
{
"record_index": 0,
"live_entry_id": 19,
"payload_offset": 29280,
"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": 99,
"grouped_mode_0x7f4": 2,
"one_shot_header_0x7f5": 0,
"modifier_flag_0x7f9": 1,
"modifier_flag_0x7fa": 0,
"grouped_target_scope_ordinals_0x7fb": [1, 1, 1, 1],
"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": 9,
"descriptor_label": "Confiscate All",
"target_mask_bits": 1,
"parameter_family": "company_confiscation_variant",
"opcode": 1,
"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 Confiscate All to TRUE",
"locomotive_name": null,
"notes": []
}
],
"decoded_conditions": [],
"decoded_actions": [
{
"kind": "confiscate_company_assets",
"target": {
"kind": "selected_company"
}
}
],
"executable_import_ready": true,
"notes": [
"decoded from grounded real 0x4e9a row framing"
]
}
]
},
"notes": [
"real confiscate-all descriptor sample"
]
}
}

View file

@ -0,0 +1,48 @@
{
"format_version": 1,
"fixture_id": "packed-event-economic-status-overlay-fixture",
"source": {
"kind": "captured-runtime",
"description": "Fixture proving descriptor 8 Economic Status imports and executes through the ordinary runtime path."
},
"state_import_path": "packed-event-economic-status-overlay.json",
"commands": [
{
"kind": "service_trigger_kind",
"trigger_kind": 7
}
],
"expected_summary": {
"calendar_projection_source": "base-snapshot-preserved",
"calendar_projection_is_placeholder": false,
"company_count": 2,
"train_count": 3,
"territory_count": 2,
"world_restore_economic_status_code": 2,
"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,
"total_event_record_service_count": 1,
"total_trigger_dispatch_count": 1
},
"expected_state_fragment": {
"world_restore": {
"economic_status_code": 2
},
"packed_event_collection": {
"records": [
{
"import_outcome": "imported",
"decoded_actions": [
{
"kind": "set_economic_status_code",
"value": 2
}
]
}
]
}
}
}

View file

@ -0,0 +1,9 @@
{
"format_version": 1,
"import_id": "packed-event-economic-status-overlay",
"source": {
"description": "Overlay import combining world/train runtime context with the real Economic Status descriptor sample."
},
"base_snapshot_path": "packed-event-world-train-overlay-base-snapshot.json",
"save_slice_path": "packed-event-economic-status-save-slice.json"
}

View file

@ -0,0 +1,106 @@
{
"format_version": 1,
"save_slice_id": "packed-event-economic-status-save-slice",
"source": {
"description": "Tracked save-slice document with a real Economic Status row.",
"original_save_filename": "captured-economic-status.gms",
"original_save_sha256": "economic-status-sample-sha256",
"notes": [
"tracked as JSON save-slice document rather than raw .smp",
"proves descriptor 8 import into the opaque economic-status runtime lane"
]
},
"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": 28928,
"records_tag_offset": 29184,
"close_tag_offset": 29696,
"packed_state_version": 1001,
"packed_state_version_hex": "0x000003e9",
"live_id_bound": 18,
"live_record_count": 1,
"live_entry_ids": [18],
"decoded_record_count": 1,
"imported_runtime_record_count": 1,
"records": [
{
"record_index": 0,
"live_entry_id": 18,
"payload_offset": 29280,
"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": 99,
"grouped_mode_0x7f4": 2,
"one_shot_header_0x7f5": 0,
"modifier_flag_0x7f9": 1,
"modifier_flag_0x7fa": 0,
"grouped_target_scope_ordinals_0x7fb": [1, 1, 1, 1],
"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": 2,
"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 2",
"locomotive_name": null,
"notes": []
}
],
"decoded_conditions": [],
"decoded_actions": [
{
"kind": "set_economic_status_code",
"value": 2
}
],
"executable_import_ready": true,
"notes": [
"decoded from grounded real 0x4e9a row framing"
]
}
]
},
"notes": [
"real economic status descriptor sample"
]
}
}

View file

@ -21,7 +21,7 @@
"packed_event_record_count": 1,
"packed_event_decoded_record_count": 1,
"packed_event_imported_runtime_record_count": 0,
"packed_event_blocked_unmapped_real_descriptor_count": 1,
"packed_event_blocked_confiscation_variant_count": 1,
"event_runtime_record_count": 0,
"total_event_record_service_count": 0,
"total_trigger_dispatch_count": 1
@ -45,7 +45,7 @@
"packed_event_collection": {
"records": [
{
"import_outcome": "blocked_unmapped_real_descriptor"
"import_outcome": "blocked_confiscation_variant"
}
]
},

View file

@ -86,21 +86,21 @@
{
"group_index": 1,
"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": 2,
"descriptor_id": 9,
"descriptor_label": "Confiscate All",
"target_mask_bits": 1,
"parameter_family": "company_confiscation_variant",
"opcode": 1,
"raw_scalar_value": 0,
"value_byte_0x09": 0,
"value_dword_0x0d": 0,
"value_byte_0x11": 0,
"value_byte_0x12": 0,
"value_word_0x14": 0,
"value_word_0x16": 0,
"row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment",
"semantic_preview": "Set Economic Status to 2",
"row_shape": "bool_toggle",
"semantic_family": "bool_toggle",
"semantic_preview": "Set Confiscate All to FALSE",
"locomotive_name": null,
"notes": []
}

View file

@ -0,0 +1,64 @@
{
"format_version": 1,
"fixture_id": "packed-event-retire-train-company-overlay-fixture",
"source": {
"kind": "captured-runtime",
"description": "Fixture proving descriptor 15 Retire Train executes against company scope."
},
"state_import_path": "packed-event-retire-train-company-overlay.json",
"commands": [
{
"kind": "service_trigger_kind",
"trigger_kind": 7
}
],
"expected_summary": {
"calendar_projection_source": "base-snapshot-preserved",
"calendar_projection_is_placeholder": false,
"company_count": 2,
"train_count": 3,
"active_train_count": 1,
"retired_train_count": 2,
"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,
"total_event_record_service_count": 1,
"total_trigger_dispatch_count": 1
},
"expected_state_fragment": {
"trains": [
{
"train_id": 100,
"active": false,
"retired": true
},
{
"train_id": 101,
"active": false,
"retired": true
},
{
"train_id": 102,
"active": true,
"retired": false
}
],
"packed_event_collection": {
"records": [
{
"import_outcome": "imported",
"decoded_actions": [
{
"kind": "retire_trains",
"company_target": {
"kind": "selected_company"
}
}
]
}
]
}
}
}

View file

@ -0,0 +1,9 @@
{
"format_version": 1,
"import_id": "packed-event-retire-train-company-overlay",
"source": {
"description": "Overlay import combining world/train runtime context with the company-scoped Retire Train descriptor sample."
},
"base_snapshot_path": "packed-event-world-train-overlay-base-snapshot.json",
"save_slice_path": "packed-event-retire-train-company-save-slice.json"
}

View file

@ -0,0 +1,108 @@
{
"format_version": 1,
"save_slice_id": "packed-event-retire-train-company-save-slice",
"source": {
"description": "Tracked save-slice document with a real company-scoped Retire Train row.",
"original_save_filename": "captured-retire-train-company.gms",
"original_save_sha256": "retire-train-company-sample-sha256",
"notes": [
"tracked as JSON save-slice document rather than raw .smp",
"proves descriptor 15 import through company scope"
]
},
"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": 28928,
"records_tag_offset": 29184,
"close_tag_offset": 29696,
"packed_state_version": 1001,
"packed_state_version_hex": "0x000003e9",
"live_id_bound": 21,
"live_record_count": 1,
"live_entry_ids": [21],
"decoded_record_count": 1,
"imported_runtime_record_count": 1,
"records": [
{
"record_index": 0,
"live_entry_id": 21,
"payload_offset": 29280,
"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": 99,
"grouped_mode_0x7f4": 2,
"one_shot_header_0x7f5": 0,
"modifier_flag_0x7f9": 1,
"modifier_flag_0x7fa": 0,
"grouped_target_scope_ordinals_0x7fb": [1, 1, 1, 1],
"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": 15,
"descriptor_label": "Retire Train",
"target_mask_bits": 13,
"parameter_family": "company_or_territory_asset_toggle",
"opcode": 1,
"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 Retire Train to TRUE",
"locomotive_name": null,
"notes": []
}
],
"decoded_conditions": [],
"decoded_actions": [
{
"kind": "retire_trains",
"company_target": {
"kind": "selected_company"
}
}
],
"executable_import_ready": true,
"notes": [
"decoded from grounded real 0x4e9a row framing"
]
}
]
},
"notes": [
"real retire-train descriptor sample scoped by company"
]
}
}

View file

@ -0,0 +1,32 @@
{
"format_version": 1,
"fixture_id": "packed-event-retire-train-missing-scope-save-slice-fixture",
"source": {
"kind": "captured-runtime",
"description": "Fixture keeping the missing-scope Retire Train variant explicit."
},
"state_save_slice_path": "packed-event-retire-train-missing-scope-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": 0,
"event_runtime_record_count": 0,
"packed_event_blocked_retire_train_scope_count": 1
},
"expected_state_fragment": {
"packed_event_collection": {
"records": [
{
"import_outcome": "blocked_retire_train_scope"
}
]
}
}
}

View file

@ -0,0 +1,103 @@
{
"format_version": 1,
"save_slice_id": "packed-event-retire-train-missing-scope-save-slice",
"source": {
"description": "Tracked save-slice document with a Retire Train row missing both company and territory scope.",
"original_save_filename": "captured-retire-train-missing-scope.gms",
"original_save_sha256": "retire-train-missing-scope-sample-sha256",
"notes": [
"tracked as JSON save-slice document rather than raw .smp",
"keeps the missing-scope descriptor 15 variant 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": 28928,
"records_tag_offset": 29184,
"close_tag_offset": 29696,
"packed_state_version": 1001,
"packed_state_version_hex": "0x000003e9",
"live_id_bound": 23,
"live_record_count": 1,
"live_entry_ids": [23],
"decoded_record_count": 1,
"imported_runtime_record_count": 0,
"records": [
{
"record_index": 0,
"live_entry_id": 23,
"payload_offset": 29280,
"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": 99,
"grouped_mode_0x7f4": 2,
"one_shot_header_0x7f5": 0,
"modifier_flag_0x7f9": 1,
"modifier_flag_0x7fa": 0,
"grouped_target_scope_ordinals_0x7fb": [8, 1, 1, 1],
"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": 15,
"descriptor_label": "Retire Train",
"target_mask_bits": 13,
"parameter_family": "company_or_territory_asset_toggle",
"opcode": 1,
"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 Retire Train to TRUE",
"locomotive_name": "Mikado",
"notes": [
"retire train row is missing company and territory scope"
]
}
],
"decoded_conditions": [],
"decoded_actions": [],
"executable_import_ready": false,
"notes": [
"decoded from grounded real 0x4e9a row framing"
]
}
]
},
"notes": [
"unsupported real retire-train missing-scope sample"
]
}
}

View file

@ -0,0 +1,66 @@
{
"format_version": 1,
"fixture_id": "packed-event-retire-train-territory-overlay-fixture",
"source": {
"kind": "captured-runtime",
"description": "Fixture proving descriptor 15 Retire Train executes against territory and locomotive filters."
},
"state_import_path": "packed-event-retire-train-territory-overlay.json",
"commands": [
{
"kind": "service_trigger_kind",
"trigger_kind": 7
}
],
"expected_summary": {
"calendar_projection_source": "base-snapshot-preserved",
"calendar_projection_is_placeholder": false,
"company_count": 2,
"train_count": 3,
"active_train_count": 1,
"retired_train_count": 2,
"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,
"total_event_record_service_count": 1,
"total_trigger_dispatch_count": 1
},
"expected_state_fragment": {
"trains": [
{
"train_id": 100,
"active": false,
"retired": true
},
{
"train_id": 101,
"active": true,
"retired": false
},
{
"train_id": 102,
"active": false,
"retired": true
}
],
"packed_event_collection": {
"records": [
{
"import_outcome": "imported",
"decoded_actions": [
{
"kind": "retire_trains",
"territory_target": {
"kind": "ids",
"ids": [7]
},
"locomotive_name": "Mikado"
}
]
}
]
}
}
}

View file

@ -0,0 +1,9 @@
{
"format_version": 1,
"import_id": "packed-event-retire-train-territory-overlay",
"source": {
"description": "Overlay import combining world/train runtime context with the territory-scoped Retire Train descriptor sample."
},
"base_snapshot_path": "packed-event-world-train-overlay-base-snapshot.json",
"save_slice_path": "packed-event-retire-train-territory-save-slice.json"
}

View file

@ -0,0 +1,112 @@
{
"format_version": 1,
"save_slice_id": "packed-event-retire-train-territory-save-slice",
"source": {
"description": "Tracked save-slice document with a real territory- and locomotive-scoped Retire Train row.",
"original_save_filename": "captured-retire-train-territory.gms",
"original_save_sha256": "retire-train-territory-sample-sha256",
"notes": [
"tracked as JSON save-slice document rather than raw .smp",
"proves descriptor 15 import through territory selector plus locomotive-name filtering"
]
},
"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": 28928,
"records_tag_offset": 29184,
"close_tag_offset": 29696,
"packed_state_version": 1001,
"packed_state_version_hex": "0x000003e9",
"live_id_bound": 22,
"live_record_count": 1,
"live_entry_ids": [22],
"decoded_record_count": 1,
"imported_runtime_record_count": 1,
"records": [
{
"record_index": 0,
"live_entry_id": 22,
"payload_offset": 29280,
"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": 99,
"grouped_mode_0x7f4": 2,
"one_shot_header_0x7f5": 0,
"modifier_flag_0x7f9": 1,
"modifier_flag_0x7fa": 0,
"grouped_target_scope_ordinals_0x7fb": [8, 1, 1, 1],
"grouped_scope_checkboxes_0x7ff": [1, 0, 0, 0],
"summary_toggle_0x800": 1,
"grouped_territory_selectors_0x80f": [7, -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": 15,
"descriptor_label": "Retire Train",
"target_mask_bits": 13,
"parameter_family": "company_or_territory_asset_toggle",
"opcode": 1,
"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 Retire Train to TRUE",
"locomotive_name": "Mikado",
"notes": [
"grouped effect row carries locomotive-name side string"
]
}
],
"decoded_conditions": [],
"decoded_actions": [
{
"kind": "retire_trains",
"territory_target": {
"kind": "ids",
"ids": [7]
},
"locomotive_name": "Mikado"
}
],
"executable_import_ready": true,
"notes": [
"decoded from grounded real 0x4e9a row framing"
]
}
]
},
"notes": [
"real retire-train descriptor sample scoped by territory and locomotive name"
]
}
}

View file

@ -0,0 +1,81 @@
{
"format_version": 1,
"snapshot_id": "packed-event-world-train-overlay-base-snapshot",
"source": {
"description": "Base runtime snapshot supplying company, territory, and train context for real descriptor 8/9/15 overlays."
},
"state": {
"calendar": {
"year": 1845,
"month_slot": 2,
"phase_slot": 1,
"tick_slot": 3
},
"world_flags": {
"base.only": true
},
"metadata": {
"base.note": "world-and-train overlay context"
},
"companies": [
{
"company_id": 1,
"current_cash": 150,
"debt": 80,
"credit_rating_score": 650,
"prime_rate": 5,
"controller_kind": "human"
},
{
"company_id": 2,
"current_cash": 90,
"debt": 40,
"credit_rating_score": 480,
"prime_rate": 6,
"controller_kind": "ai"
}
],
"selected_company_id": 1,
"players": [],
"trains": [
{
"train_id": 100,
"owner_company_id": 1,
"territory_id": 7,
"locomotive_name": "Mikado"
},
{
"train_id": 101,
"owner_company_id": 1,
"territory_id": 8,
"locomotive_name": "Orca"
},
{
"train_id": 102,
"owner_company_id": 2,
"territory_id": 7,
"locomotive_name": "Mikado"
}
],
"territories": [
{
"territory_id": 7,
"name": "Appalachia"
},
{
"territory_id": 8,
"name": "Great Plains"
}
],
"company_territory_track_piece_counts": [],
"event_runtime_records": [],
"candidate_availability": {},
"special_conditions": {},
"service_state": {
"periodic_boundary_calls": 0,
"trigger_dispatch_counts": {},
"total_event_record_services": 0,
"dirty_rerun_count": 0
}
}
}