Execute real packed event world and train descriptors
This commit is contained in:
parent
ca208f74e0
commit
e481274243
31 changed files with 3287 additions and 206 deletions
10
README.md
10
README.md
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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::{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,24 +2491,23 @@ 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 {
|
||||
RealOrdinaryConditionMetric::Company(metric) => {
|
||||
Some(RuntimeCondition::CompanyNumericThreshold {
|
||||
target: RuntimeCompanyTarget::ConditionTrueCompany,
|
||||
metric,
|
||||
comparator,
|
||||
value,
|
||||
}),
|
||||
RealOrdinaryConditionMetric::Territory(metric) => {
|
||||
negative_sentinel_scope
|
||||
})
|
||||
}
|
||||
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
|
||||
}),
|
||||
RealOrdinaryConditionMetric::CompanyTerritory(metric) => negative_sentinel_scope
|
||||
.filter(|scope| scope.territory_scope_selector_is_0x63)
|
||||
.map(|_| RuntimeCondition::CompanyTerritoryNumericThreshold {
|
||||
target: RuntimeCompanyTarget::ConditionTrueCompany,
|
||||
|
|
@ -2487,8 +2515,7 @@ fn decode_real_condition_row(
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -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,14 +567,18 @@ 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(
|
||||
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() {
|
||||
return Ok(None);
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
108
fixtures/runtime/packed-event-confiscate-all-save-slice.json
Normal file
108
fixtures/runtime/packed-event-confiscate-all-save-slice.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
106
fixtures/runtime/packed-event-economic-status-save-slice.json
Normal file
106
fixtures/runtime/packed-event-economic-status-save-slice.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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": []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue