Execute locomotive cost packed event descriptors
This commit is contained in:
parent
e2174713a9
commit
09039d24e4
18 changed files with 785 additions and 21 deletions
19
README.md
19
README.md
|
|
@ -53,16 +53,19 @@ executable too: descriptors `454..456` (`All Steam/Diesel/Electric Locos Avail.`
|
|||
through checked-in metadata into keyed `world_flags`, while the wider locomotive availability/cost
|
||||
scalar bands are now split more cleanly: the boolean `0/1` availability subset can import through
|
||||
an overlay-backed `RuntimeState.locomotive_catalog` into
|
||||
`RuntimeState.named_locomotive_availability`, while non-boolean availability payloads plus the
|
||||
locomotive-cost/cargo-production/territory-access-cost families now surface as recovered,
|
||||
metadata-rich parity rows with checked-in slot labels and locomotive ids where grounded, but they
|
||||
still remain non-executable.
|
||||
The runtime still carries the save-owned named locomotive availability table directly too:
|
||||
`RuntimeState.named_locomotive_availability`, while non-boolean availability payloads still remain
|
||||
parity-only. The runtime still carries the save-owned named locomotive availability table directly
|
||||
too:
|
||||
checked-in save-slice documents can populate `RuntimeState.named_locomotive_availability`, and
|
||||
imported runtime effects can mutate that map through the ordinary event-service path without
|
||||
needing full Trainbuy or live-locomotive parity. Explicit unmapped world-condition and
|
||||
world-descriptor frontier buckets still remain where current checked-in metadata stops. Shell
|
||||
purchase-flow and selected-profile parity remain out of scope. Mixed supported/unsupported real
|
||||
needing full Trainbuy or live-locomotive parity. A parallel event-owned named locomotive cost map
|
||||
now exists too: recovered locomotive-cost descriptors from bands `352..451` and `475..500` can
|
||||
import through the same overlay-backed locomotive catalog into
|
||||
`RuntimeState.named_locomotive_cost`, while cargo-production and territory-access-cost rows remain
|
||||
metadata-rich parity-only families until a later slice grounds an honest landing surface. Explicit
|
||||
unmapped world-condition and world-descriptor frontier buckets still remain where current
|
||||
checked-in metadata stops. Shell purchase-flow, Trainbuy refresh, cached locomotive-rating
|
||||
recomputation, and selected-profile parity remain out of scope. Mixed supported/unsupported real
|
||||
rows still stay parity-only. The PE32 hook remains useful as capture and integration tooling, but
|
||||
it is no longer the main execution milestone.
|
||||
|
||||
|
|
|
|||
|
|
@ -4463,6 +4463,8 @@ mod tests {
|
|||
let overlay_locomotive_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
|
||||
"../../fixtures/runtime/packed-event-locomotive-availability-overlay-fixture.json",
|
||||
);
|
||||
let overlay_locomotive_cost_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../fixtures/runtime/packed-event-locomotive-cost-overlay-fixture.json");
|
||||
let scalar_band_parity_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
|
||||
"../../fixtures/runtime/packed-event-world-scalar-band-parity-save-slice-fixture.json",
|
||||
);
|
||||
|
|
@ -4490,6 +4492,8 @@ mod tests {
|
|||
);
|
||||
run_runtime_summarize_fixture(&overlay_locomotive_fixture)
|
||||
.expect("overlay-backed locomotive availability fixture should summarize");
|
||||
run_runtime_summarize_fixture(&overlay_locomotive_cost_fixture)
|
||||
.expect("overlay-backed locomotive cost fixture should summarize");
|
||||
run_runtime_summarize_fixture(&scalar_band_parity_fixture)
|
||||
.expect("save-slice-backed recovered scalar-band parity fixture should summarize");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
},
|
||||
|
|
@ -357,6 +358,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -158,6 +158,8 @@ pub struct ExpectedRuntimeSummary {
|
|||
#[serde(default)]
|
||||
pub zero_named_locomotive_availability_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub named_locomotive_cost_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub special_condition_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub enabled_special_condition_count: Option<usize>,
|
||||
|
|
@ -775,6 +777,14 @@ impl ExpectedRuntimeSummary {
|
|||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.named_locomotive_cost_count {
|
||||
if actual.named_locomotive_cost_count != count {
|
||||
mismatches.push(format!(
|
||||
"named_locomotive_cost_count mismatch: expected {count}, got {}",
|
||||
actual.named_locomotive_cost_count
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.special_condition_count {
|
||||
if actual.special_condition_count != count {
|
||||
mismatches.push(format!(
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ struct SaveSliceProjection {
|
|||
event_runtime_records: Vec<RuntimeEventRecord>,
|
||||
candidate_availability: BTreeMap<String, u32>,
|
||||
named_locomotive_availability: BTreeMap<String, u32>,
|
||||
named_locomotive_cost: BTreeMap<String, u32>,
|
||||
special_conditions: BTreeMap<String, u32>,
|
||||
}
|
||||
|
||||
|
|
@ -249,6 +250,7 @@ pub fn project_save_slice_to_runtime_state_import(
|
|||
event_runtime_records: projection.event_runtime_records,
|
||||
candidate_availability: projection.candidate_availability,
|
||||
named_locomotive_availability: projection.named_locomotive_availability,
|
||||
named_locomotive_cost: projection.named_locomotive_cost,
|
||||
special_conditions: projection.special_conditions,
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -308,6 +310,7 @@ pub fn project_save_slice_overlay_to_runtime_state_import(
|
|||
event_runtime_records: projection.event_runtime_records,
|
||||
candidate_availability: projection.candidate_availability,
|
||||
named_locomotive_availability: projection.named_locomotive_availability,
|
||||
named_locomotive_cost: base_state.named_locomotive_cost.clone(),
|
||||
special_conditions: projection.special_conditions,
|
||||
service_state: base_state.service_state.clone(),
|
||||
};
|
||||
|
|
@ -636,6 +639,8 @@ fn project_save_slice_components(
|
|||
}
|
||||
}
|
||||
|
||||
let named_locomotive_cost = BTreeMap::new();
|
||||
|
||||
for (index, note) in save_slice.notes.iter().enumerate() {
|
||||
metadata.insert(format!("save_slice.note.{index}"), note.clone());
|
||||
}
|
||||
|
|
@ -649,6 +654,7 @@ fn project_save_slice_components(
|
|||
event_runtime_records,
|
||||
candidate_availability,
|
||||
named_locomotive_availability,
|
||||
named_locomotive_cost,
|
||||
special_conditions,
|
||||
})
|
||||
}
|
||||
|
|
@ -972,6 +978,10 @@ fn lower_contextual_real_grouped_effects(
|
|||
|
||||
let mut effects = Vec::with_capacity(record.grouped_effect_rows.len());
|
||||
for row in &record.grouped_effect_rows {
|
||||
if let Some(effect) = lower_contextual_locomotive_cost_effect(row, company_context)? {
|
||||
effects.push(effect);
|
||||
continue;
|
||||
}
|
||||
if let Some(effect) = lower_contextual_locomotive_availability_effect(row, company_context)?
|
||||
{
|
||||
effects.push(effect);
|
||||
|
|
@ -1022,6 +1032,33 @@ fn lower_contextual_locomotive_availability_effect(
|
|||
}))
|
||||
}
|
||||
|
||||
fn lower_contextual_locomotive_cost_effect(
|
||||
row: &SmpLoadedPackedEventGroupedEffectRowSummary,
|
||||
company_context: &ImportRuntimeContext,
|
||||
) -> Result<Option<RuntimeEffect>, ImportBlocker> {
|
||||
if row.parameter_family.as_deref() != Some("locomotive_cost_scalar") {
|
||||
return Ok(None);
|
||||
}
|
||||
if row.row_shape != "scalar_assignment" {
|
||||
return Ok(None);
|
||||
}
|
||||
let value = u32::try_from(row.raw_scalar_value).ok();
|
||||
let Some(value) = value else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(locomotive_id) = row.recovered_locomotive_id else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(name) = company_context
|
||||
.locomotive_catalog_names_by_id
|
||||
.get(&locomotive_id)
|
||||
.cloned()
|
||||
else {
|
||||
return Err(ImportBlocker::MissingLocomotiveCatalogContext);
|
||||
};
|
||||
Ok(Some(RuntimeEffect::SetNamedLocomotiveCost { name, value }))
|
||||
}
|
||||
|
||||
fn packed_record_condition_scope_import_blocker(
|
||||
record: &SmpLoadedPackedEventRecordSummary,
|
||||
company_context: &ImportRuntimeContext,
|
||||
|
|
@ -1251,6 +1288,12 @@ fn lower_condition_targets_in_effect(
|
|||
value: *value,
|
||||
}
|
||||
}
|
||||
RuntimeEffect::SetNamedLocomotiveCost { name, value } => {
|
||||
RuntimeEffect::SetNamedLocomotiveCost {
|
||||
name: name.clone(),
|
||||
value: *value,
|
||||
}
|
||||
}
|
||||
RuntimeEffect::SetSpecialCondition { label, value } => RuntimeEffect::SetSpecialCondition {
|
||||
label: label.clone(),
|
||||
value: *value,
|
||||
|
|
@ -1686,6 +1729,12 @@ fn smp_runtime_effect_to_runtime_effect(
|
|||
value: *value,
|
||||
})
|
||||
}
|
||||
RuntimeEffect::SetNamedLocomotiveCost { name, value } => {
|
||||
Ok(RuntimeEffect::SetNamedLocomotiveCost {
|
||||
name: name.clone(),
|
||||
value: *value,
|
||||
})
|
||||
}
|
||||
RuntimeEffect::SetSpecialCondition { label, value } => {
|
||||
Ok(RuntimeEffect::SetSpecialCondition {
|
||||
label: label.clone(),
|
||||
|
|
@ -2247,6 +2296,7 @@ fn runtime_effect_uses_condition_true_company(effect: &RuntimeEffect) -> bool {
|
|||
| RuntimeEffect::DeactivatePlayer { .. }
|
||||
| RuntimeEffect::SetCandidateAvailability { .. }
|
||||
| RuntimeEffect::SetNamedLocomotiveAvailability { .. }
|
||||
| RuntimeEffect::SetNamedLocomotiveCost { .. }
|
||||
| RuntimeEffect::SetSpecialCondition { .. }
|
||||
| RuntimeEffect::ActivateEventRecord { .. }
|
||||
| RuntimeEffect::DeactivateEventRecord { .. }
|
||||
|
|
@ -2325,6 +2375,7 @@ fn runtime_effect_company_target_import_blocker(
|
|||
| RuntimeEffect::SetEconomicStatusCode { .. }
|
||||
| RuntimeEffect::SetCandidateAvailability { .. }
|
||||
| RuntimeEffect::SetNamedLocomotiveAvailability { .. }
|
||||
| RuntimeEffect::SetNamedLocomotiveCost { .. }
|
||||
| RuntimeEffect::SetSpecialCondition { .. }
|
||||
| RuntimeEffect::ActivateEventRecord { .. }
|
||||
| RuntimeEffect::DeactivateEventRecord { .. }
|
||||
|
|
@ -2665,6 +2716,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
}
|
||||
|
|
@ -5041,6 +5093,7 @@ mod tests {
|
|||
("Locomotive 10".to_string(), 0),
|
||||
("Locomotive 112".to_string(), 1),
|
||||
]),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -5144,7 +5197,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_recovered_locomotive_cost_rows_parity_only() {
|
||||
fn blocks_recovered_locomotive_cost_rows_without_catalog_context_lower_band() {
|
||||
let save_slice = SmpLoadedSaveSlice {
|
||||
file_extension_hint: Some("gms".to_string()),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
|
|
@ -5192,7 +5245,9 @@ mod tests {
|
|||
decoded_conditions: Vec::new(),
|
||||
decoded_actions: vec![],
|
||||
executable_import_ready: false,
|
||||
notes: vec!["locomotive cost rows remain metadata-only".to_string()],
|
||||
notes: vec![
|
||||
"scalar locomotive cost row still needs catalog context".to_string(),
|
||||
],
|
||||
}],
|
||||
}),
|
||||
notes: vec![],
|
||||
|
|
@ -5200,7 +5255,289 @@ mod tests {
|
|||
|
||||
let import = project_save_slice_to_runtime_state_import(
|
||||
&save_slice,
|
||||
"packed-events-locomotive-cost-frontier",
|
||||
"packed-events-locomotive-cost-frontier-lower-band",
|
||||
None,
|
||||
)
|
||||
.expect("save slice should project");
|
||||
|
||||
assert!(import.state.event_runtime_records.is_empty());
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.packed_event_collection
|
||||
.as_ref()
|
||||
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
|
||||
Some("blocked_missing_locomotive_catalog_context")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blocks_recovered_locomotive_cost_rows_without_catalog_context() {
|
||||
let save_slice = SmpLoadedSaveSlice {
|
||||
file_extension_hint: Some("gms".to_string()),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
trailer_family: None,
|
||||
bridge_family: None,
|
||||
profile: None,
|
||||
candidate_availability_table: None,
|
||||
named_locomotive_availability_table: None,
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
|
||||
source_kind: "packed-event-runtime-collection".to_string(),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
metadata_tag_offset: 0x7100,
|
||||
records_tag_offset: 0x7200,
|
||||
close_tag_offset: 0x7600,
|
||||
packed_state_version: 0x3e9,
|
||||
packed_state_version_hex: "0x000003e9".to_string(),
|
||||
live_id_bound: 35,
|
||||
live_record_count: 1,
|
||||
live_entry_ids: vec![35],
|
||||
decoded_record_count: 1,
|
||||
imported_runtime_record_count: 0,
|
||||
records: vec![crate::SmpLoadedPackedEventRecordSummary {
|
||||
record_index: 0,
|
||||
live_entry_id: 35,
|
||||
payload_offset: Some(0x7202),
|
||||
payload_len: Some(96),
|
||||
decode_status: "parity_only".to_string(),
|
||||
payload_family: "real_packed_v1".to_string(),
|
||||
trigger_kind: Some(7),
|
||||
active: None,
|
||||
marks_collection_dirty: None,
|
||||
one_shot: Some(false),
|
||||
compact_control: Some(real_compact_control()),
|
||||
text_bands: vec![],
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: vec![],
|
||||
negative_sentinel_scope: None,
|
||||
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
||||
grouped_effect_rows: vec![real_locomotive_cost_row(352, 250000)],
|
||||
decoded_conditions: Vec::new(),
|
||||
decoded_actions: vec![],
|
||||
executable_import_ready: false,
|
||||
notes: vec![
|
||||
"scalar locomotive cost row still needs catalog context".to_string(),
|
||||
],
|
||||
}],
|
||||
}),
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
let import = project_save_slice_to_runtime_state_import(
|
||||
&save_slice,
|
||||
"packed-events-locomotive-cost-missing-catalog",
|
||||
None,
|
||||
)
|
||||
.expect("save slice should project");
|
||||
|
||||
assert!(import.state.event_runtime_records.is_empty());
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.packed_event_collection
|
||||
.as_ref()
|
||||
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
|
||||
Some("blocked_missing_locomotive_catalog_context")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlays_scalar_locomotive_cost_rows_into_named_cost_effects() {
|
||||
let base_state = RuntimeState {
|
||||
calendar: CalendarPoint {
|
||||
year: 1845,
|
||||
month_slot: 2,
|
||||
phase_slot: 1,
|
||||
tick_slot: 3,
|
||||
},
|
||||
world_flags: BTreeMap::new(),
|
||||
save_profile: RuntimeSaveProfileState::default(),
|
||||
world_restore: RuntimeWorldRestoreState::default(),
|
||||
metadata: BTreeMap::new(),
|
||||
companies: Vec::new(),
|
||||
selected_company_id: None,
|
||||
players: Vec::new(),
|
||||
selected_player_id: None,
|
||||
trains: Vec::new(),
|
||||
locomotive_catalog: vec![
|
||||
crate::RuntimeLocomotiveCatalogEntry {
|
||||
locomotive_id: 1,
|
||||
name: "Locomotive 1".to_string(),
|
||||
},
|
||||
crate::RuntimeLocomotiveCatalogEntry {
|
||||
locomotive_id: 101,
|
||||
name: "Locomotive 101".to_string(),
|
||||
},
|
||||
],
|
||||
territories: Vec::new(),
|
||||
company_territory_track_piece_counts: Vec::new(),
|
||||
company_territory_access: Vec::new(),
|
||||
packed_event_collection: None,
|
||||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::from([
|
||||
("Locomotive 1".to_string(), 100000),
|
||||
("Locomotive 101".to_string(), 200000),
|
||||
]),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
let save_slice = SmpLoadedSaveSlice {
|
||||
file_extension_hint: Some("gms".to_string()),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
trailer_family: None,
|
||||
bridge_family: None,
|
||||
profile: None,
|
||||
candidate_availability_table: None,
|
||||
named_locomotive_availability_table: None,
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
|
||||
source_kind: "packed-event-runtime-collection".to_string(),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
metadata_tag_offset: 0x7100,
|
||||
records_tag_offset: 0x7200,
|
||||
close_tag_offset: 0x7600,
|
||||
packed_state_version: 0x3e9,
|
||||
packed_state_version_hex: "0x000003e9".to_string(),
|
||||
live_id_bound: 36,
|
||||
live_record_count: 1,
|
||||
live_entry_ids: vec![36],
|
||||
decoded_record_count: 1,
|
||||
imported_runtime_record_count: 0,
|
||||
records: vec![crate::SmpLoadedPackedEventRecordSummary {
|
||||
record_index: 0,
|
||||
live_entry_id: 36,
|
||||
payload_offset: Some(0x7202),
|
||||
payload_len: Some(120),
|
||||
decode_status: "parity_only".to_string(),
|
||||
payload_family: "real_packed_v1".to_string(),
|
||||
trigger_kind: Some(7),
|
||||
active: None,
|
||||
marks_collection_dirty: None,
|
||||
one_shot: Some(false),
|
||||
compact_control: Some(real_compact_control()),
|
||||
text_bands: vec![],
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: vec![],
|
||||
negative_sentinel_scope: None,
|
||||
grouped_effect_row_counts: vec![2, 0, 0, 0],
|
||||
grouped_effect_rows: vec![
|
||||
real_locomotive_cost_row(352, 250000),
|
||||
real_locomotive_cost_row(475, 325000),
|
||||
],
|
||||
decoded_conditions: Vec::new(),
|
||||
decoded_actions: vec![],
|
||||
executable_import_ready: false,
|
||||
notes: vec![
|
||||
"scalar locomotive cost rows use overlay catalog context".to_string(),
|
||||
],
|
||||
}],
|
||||
}),
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
let mut import = project_save_slice_overlay_to_runtime_state_import(
|
||||
&base_state,
|
||||
&save_slice,
|
||||
"overlay-locomotive-cost",
|
||||
None,
|
||||
)
|
||||
.expect("overlay import should project");
|
||||
|
||||
assert_eq!(import.state.event_runtime_records.len(), 1);
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.packed_event_collection
|
||||
.as_ref()
|
||||
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
|
||||
Some("imported")
|
||||
);
|
||||
|
||||
execute_step_command(
|
||||
&mut import.state,
|
||||
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||
)
|
||||
.expect("overlay-imported locomotive cost record should run");
|
||||
|
||||
assert_eq!(
|
||||
import.state.named_locomotive_cost.get("Locomotive 1"),
|
||||
Some(&250000)
|
||||
);
|
||||
assert_eq!(
|
||||
import.state.named_locomotive_cost.get("Locomotive 101"),
|
||||
Some(&325000)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_negative_locomotive_cost_rows_parity_only() {
|
||||
let save_slice = SmpLoadedSaveSlice {
|
||||
file_extension_hint: Some("gms".to_string()),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
trailer_family: None,
|
||||
bridge_family: None,
|
||||
profile: None,
|
||||
candidate_availability_table: None,
|
||||
named_locomotive_availability_table: None,
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
|
||||
source_kind: "packed-event-runtime-collection".to_string(),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
metadata_tag_offset: 0x7100,
|
||||
records_tag_offset: 0x7200,
|
||||
close_tag_offset: 0x7600,
|
||||
packed_state_version: 0x3e9,
|
||||
packed_state_version_hex: "0x000003e9".to_string(),
|
||||
live_id_bound: 37,
|
||||
live_record_count: 1,
|
||||
live_entry_ids: vec![37],
|
||||
decoded_record_count: 1,
|
||||
imported_runtime_record_count: 0,
|
||||
records: vec![crate::SmpLoadedPackedEventRecordSummary {
|
||||
record_index: 0,
|
||||
live_entry_id: 37,
|
||||
payload_offset: Some(0x7202),
|
||||
payload_len: Some(96),
|
||||
decode_status: "parity_only".to_string(),
|
||||
payload_family: "real_packed_v1".to_string(),
|
||||
trigger_kind: Some(7),
|
||||
active: None,
|
||||
marks_collection_dirty: None,
|
||||
one_shot: Some(false),
|
||||
compact_control: Some(real_compact_control()),
|
||||
text_bands: vec![],
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: vec![],
|
||||
negative_sentinel_scope: None,
|
||||
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
||||
grouped_effect_rows: vec![real_locomotive_cost_row(352, -1)],
|
||||
decoded_conditions: Vec::new(),
|
||||
decoded_actions: vec![],
|
||||
executable_import_ready: false,
|
||||
notes: vec!["negative locomotive cost rows remain parity-only".to_string()],
|
||||
}],
|
||||
}),
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
let import = project_save_slice_to_runtime_state_import(
|
||||
&save_slice,
|
||||
"packed-events-negative-locomotive-cost-frontier",
|
||||
None,
|
||||
)
|
||||
.expect("save slice should project");
|
||||
|
|
@ -5398,6 +5735,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -8257,6 +8595,7 @@ mod tests {
|
|||
}],
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState {
|
||||
periodic_boundary_calls: 9,
|
||||
|
|
@ -8426,6 +8765,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -317,6 +317,10 @@ pub enum RuntimeEffect {
|
|||
name: String,
|
||||
value: bool,
|
||||
},
|
||||
SetNamedLocomotiveCost {
|
||||
name: String,
|
||||
value: u32,
|
||||
},
|
||||
SetSpecialCondition {
|
||||
label: String,
|
||||
value: u32,
|
||||
|
|
@ -653,6 +657,8 @@ pub struct RuntimeState {
|
|||
#[serde(default)]
|
||||
pub named_locomotive_availability: BTreeMap<String, u32>,
|
||||
#[serde(default)]
|
||||
pub named_locomotive_cost: BTreeMap<String, u32>,
|
||||
#[serde(default)]
|
||||
pub special_conditions: BTreeMap<String, u32>,
|
||||
#[serde(default)]
|
||||
pub service_state: RuntimeServiceState,
|
||||
|
|
@ -1162,6 +1168,11 @@ impl RuntimeState {
|
|||
return Err("named_locomotive_availability contains an empty key".to_string());
|
||||
}
|
||||
}
|
||||
for key in self.named_locomotive_cost.keys() {
|
||||
if key.trim().is_empty() {
|
||||
return Err("named_locomotive_cost contains an empty key".to_string());
|
||||
}
|
||||
}
|
||||
for key in self.special_conditions.keys() {
|
||||
if key.trim().is_empty() {
|
||||
return Err("special_conditions contains an empty key".to_string());
|
||||
|
|
@ -1238,6 +1249,11 @@ fn validate_runtime_effect(
|
|||
return Err("name must not be empty".to_string());
|
||||
}
|
||||
}
|
||||
RuntimeEffect::SetNamedLocomotiveCost { name, .. } => {
|
||||
if name.trim().is_empty() {
|
||||
return Err("name must not be empty".to_string());
|
||||
}
|
||||
}
|
||||
RuntimeEffect::SetSpecialCondition { label, .. } => {
|
||||
if label.trim().is_empty() {
|
||||
return Err("label must not be empty".to_string());
|
||||
|
|
@ -1459,6 +1475,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -1516,6 +1533,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -1572,6 +1590,7 @@ mod tests {
|
|||
}],
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -1638,6 +1657,7 @@ mod tests {
|
|||
}],
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -1735,6 +1755,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -1778,6 +1799,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -1821,6 +1843,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -1881,6 +1904,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -1931,6 +1955,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -1985,6 +2010,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -2035,6 +2061,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -2091,6 +2118,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -2141,6 +2169,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -2191,6 +2220,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3489,6 +3489,7 @@ fn runtime_effect_supported_for_save_import(effect: &RuntimeEffect) -> bool {
|
|||
| RuntimeEffect::SetEconomicStatusCode { .. }
|
||||
| RuntimeEffect::SetCandidateAvailability { .. }
|
||||
| RuntimeEffect::SetNamedLocomotiveAvailability { .. }
|
||||
| RuntimeEffect::SetNamedLocomotiveCost { .. }
|
||||
| RuntimeEffect::SetSpecialCondition { .. }
|
||||
| RuntimeEffect::ConfiscateCompanyAssets { .. }
|
||||
| RuntimeEffect::DeactivateCompany { .. }
|
||||
|
|
|
|||
|
|
@ -495,6 +495,9 @@ fn apply_runtime_effects(
|
|||
.named_locomotive_availability
|
||||
.insert(name.clone(), u32::from(*value));
|
||||
}
|
||||
RuntimeEffect::SetNamedLocomotiveCost { name, value } => {
|
||||
state.named_locomotive_cost.insert(name.clone(), *value);
|
||||
}
|
||||
RuntimeEffect::SetSpecialCondition { label, value } => {
|
||||
state.special_conditions.insert(label.clone(), *value);
|
||||
}
|
||||
|
|
@ -1165,6 +1168,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
}
|
||||
|
|
@ -1416,6 +1420,43 @@ mod tests {
|
|||
assert_eq!(result.service_events[0].applied_effect_count, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn applies_named_locomotive_cost_effects() {
|
||||
let mut state = RuntimeState {
|
||||
event_runtime_records: vec![RuntimeEventRecord {
|
||||
record_id: 11,
|
||||
trigger_kind: 7,
|
||||
active: true,
|
||||
service_count: 0,
|
||||
marks_collection_dirty: false,
|
||||
one_shot: false,
|
||||
has_fired: false,
|
||||
conditions: Vec::new(),
|
||||
effects: vec![
|
||||
RuntimeEffect::SetNamedLocomotiveCost {
|
||||
name: "Big Boy".to_string(),
|
||||
value: 250000,
|
||||
},
|
||||
RuntimeEffect::SetNamedLocomotiveCost {
|
||||
name: "GP7".to_string(),
|
||||
value: 175000,
|
||||
},
|
||||
],
|
||||
}],
|
||||
..state()
|
||||
};
|
||||
|
||||
let result = execute_step_command(
|
||||
&mut state,
|
||||
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||
)
|
||||
.expect("named locomotive cost effects should succeed");
|
||||
|
||||
assert_eq!(state.named_locomotive_cost.get("Big Boy"), Some(&250000));
|
||||
assert_eq!(state.named_locomotive_cost.get("GP7"), Some(&175000));
|
||||
assert_eq!(result.service_events[0].applied_effect_count, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolves_symbolic_company_targets() {
|
||||
let mut state = RuntimeState {
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ pub struct RuntimeSummary {
|
|||
pub zero_candidate_availability_count: usize,
|
||||
pub named_locomotive_availability_count: usize,
|
||||
pub zero_named_locomotive_availability_count: usize,
|
||||
pub named_locomotive_cost_count: usize,
|
||||
pub special_condition_count: usize,
|
||||
pub enabled_special_condition_count: usize,
|
||||
pub save_profile_kind: Option<String>,
|
||||
|
|
@ -595,6 +596,7 @@ impl RuntimeSummary {
|
|||
.values()
|
||||
.filter(|value| **value == 0)
|
||||
.count(),
|
||||
named_locomotive_cost_count: state.named_locomotive_cost.len(),
|
||||
special_condition_count: state.special_conditions.len(),
|
||||
enabled_special_condition_count: state
|
||||
.special_conditions
|
||||
|
|
@ -817,6 +819,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -916,6 +919,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -964,6 +968,7 @@ mod tests {
|
|||
("GP7".to_string(), 1),
|
||||
("Mikado".to_string(), 0),
|
||||
]),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -972,6 +977,46 @@ mod tests {
|
|||
assert_eq!(summary.locomotive_catalog_count, 2);
|
||||
assert_eq!(summary.named_locomotive_availability_count, 3);
|
||||
assert_eq!(summary.zero_named_locomotive_availability_count, 2);
|
||||
assert_eq!(summary.named_locomotive_cost_count, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn counts_named_locomotive_cost_entries() {
|
||||
let state = RuntimeState {
|
||||
calendar: CalendarPoint {
|
||||
year: 1830,
|
||||
month_slot: 0,
|
||||
phase_slot: 0,
|
||||
tick_slot: 0,
|
||||
},
|
||||
world_flags: BTreeMap::new(),
|
||||
save_profile: RuntimeSaveProfileState::default(),
|
||||
world_restore: RuntimeWorldRestoreState::default(),
|
||||
metadata: BTreeMap::new(),
|
||||
companies: Vec::new(),
|
||||
selected_company_id: None,
|
||||
players: Vec::new(),
|
||||
selected_player_id: None,
|
||||
trains: Vec::new(),
|
||||
locomotive_catalog: Vec::new(),
|
||||
territories: Vec::new(),
|
||||
company_territory_track_piece_counts: Vec::new(),
|
||||
company_territory_access: Vec::new(),
|
||||
packed_event_collection: None,
|
||||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::from([
|
||||
("Big Boy".to_string(), 250000),
|
||||
("GP7".to_string(), 175000),
|
||||
]),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
||||
let summary = RuntimeSummary::from_state(&state);
|
||||
|
||||
assert_eq!(summary.named_locomotive_cost_count, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1064,6 +1109,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
@ -1142,6 +1188,7 @@ mod tests {
|
|||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -132,6 +132,10 @@ The highest-value next passes are now:
|
|||
overlay-backed locomotive catalog context: checked-in save-slice documents can populate
|
||||
`RuntimeState.named_locomotive_availability`, and boolean `0/1` availability descriptors can
|
||||
lower through `RuntimeState.locomotive_catalog` into the same ordinary event-service path
|
||||
- that same overlay-backed locomotive catalog now unlocks the recovered locomotive-cost bands too:
|
||||
nonnegative scalar rows from descriptors `352..451` and `475..500` can lower into the new
|
||||
event-owned `RuntimeState.named_locomotive_cost` map through the ordinary runtime path, while
|
||||
cargo-production and territory-access-cost rows remain metadata-rich parity-only families
|
||||
- keep in mind that the current local `.gms` corpus still exports with no packed event collection,
|
||||
so real descriptor mapping needs to stay plumbing-first until better captures exist
|
||||
- use `rrt-hook` primarily as optional capture or integration tooling, not as the first execution
|
||||
|
|
|
|||
|
|
@ -88,15 +88,19 @@ Implemented today:
|
|||
through the ordinary event-service path without requiring Trainbuy or live locomotive-pool parity
|
||||
- the boolean `0/1` subset of the recovered locomotives-page availability bands can now import
|
||||
through an overlay-backed `RuntimeState.locomotive_catalog`; non-boolean availability payloads
|
||||
and the adjacent locomotive-cost/cargo-production/access-cost families now surface as recovered
|
||||
metadata-rich parity rows with checked-in slot labels and locomotive ids where grounded, but
|
||||
execution for those scalar families remains deferred
|
||||
still remain parity-only, but the adjacent locomotive-cost bands `352..451` and `475..500` now
|
||||
import too through the same overlay-backed catalog into the event-owned
|
||||
`RuntimeState.named_locomotive_cost` map when their scalar payloads are nonnegative
|
||||
- cargo-production `230..240` and territory-access-cost `453` rows now remain as the primary
|
||||
recovered scalar-band parity families: their labels, target masks, and slot identities are
|
||||
checked in, but execution for those families is still deferred until a grounded landing surface
|
||||
exists
|
||||
|
||||
That means the next implementation work is breadth, not bootstrap. The recommended next slice is
|
||||
honest landing surfaces for one or more of those recovered scalar families, plus broader real
|
||||
grouped-descriptor and ordinary condition-id coverage beyond the current access, whole-game
|
||||
toggle, train, player, numeric-threshold, named locomotive availability, and overlay-resolved
|
||||
locomotive availability batches.
|
||||
honest landing surfaces for one or more of the remaining recovered scalar families, plus broader
|
||||
real grouped-descriptor and ordinary condition-id coverage beyond the current access, whole-game
|
||||
toggle, train, player, numeric-threshold, named locomotive availability, and named locomotive cost
|
||||
batches.
|
||||
Richer runtime ownership should still be added only where a later descriptor or condition family
|
||||
needs more than the current event-owned roster.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"snapshot_id": "packed-event-locomotive-cost-overlay-base-snapshot",
|
||||
"source": {
|
||||
"description": "Base runtime snapshot supplying locomotive catalog context for descriptor-driven named locomotive cost import."
|
||||
},
|
||||
"state": {
|
||||
"calendar": {
|
||||
"year": 1835,
|
||||
"month_slot": 1,
|
||||
"phase_slot": 2,
|
||||
"tick_slot": 4
|
||||
},
|
||||
"world_flags": {
|
||||
"base.only": true
|
||||
},
|
||||
"metadata": {
|
||||
"base.note": "preserve locomotive cost catalog context"
|
||||
},
|
||||
"locomotive_catalog": [
|
||||
{
|
||||
"locomotive_id": 1,
|
||||
"name": "Locomotive 1"
|
||||
},
|
||||
{
|
||||
"locomotive_id": 101,
|
||||
"name": "Locomotive 101"
|
||||
}
|
||||
],
|
||||
"named_locomotive_cost": {
|
||||
"Locomotive 1": 100000,
|
||||
"Locomotive 101": 200000
|
||||
},
|
||||
"event_runtime_records": [],
|
||||
"candidate_availability": {},
|
||||
"named_locomotive_availability": {},
|
||||
"special_conditions": {},
|
||||
"service_state": {
|
||||
"periodic_boundary_calls": 0,
|
||||
"trigger_dispatch_counts": {},
|
||||
"total_event_record_services": 0,
|
||||
"dirty_rerun_count": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"fixture_id": "packed-event-locomotive-cost-overlay-fixture",
|
||||
"source": {
|
||||
"kind": "captured-runtime",
|
||||
"description": "Fixture backed by an overlay import document so recovered scalar locomotive cost descriptors execute against captured catalog context."
|
||||
},
|
||||
"state_import_path": "packed-event-locomotive-cost-overlay.json",
|
||||
"commands": [
|
||||
{
|
||||
"kind": "service_trigger_kind",
|
||||
"trigger_kind": 7
|
||||
}
|
||||
],
|
||||
"expected_summary": {
|
||||
"calendar": {
|
||||
"year": 1835,
|
||||
"month_slot": 1,
|
||||
"phase_slot": 2,
|
||||
"tick_slot": 4
|
||||
},
|
||||
"calendar_projection_is_placeholder": false,
|
||||
"locomotive_catalog_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,
|
||||
"packed_event_parity_only_record_count": 1,
|
||||
"event_runtime_record_count": 1,
|
||||
"named_locomotive_cost_count": 2,
|
||||
"total_event_record_service_count": 1,
|
||||
"total_trigger_dispatch_count": 1
|
||||
},
|
||||
"expected_state_fragment": {
|
||||
"metadata": {
|
||||
"base.note": "preserve locomotive cost catalog context",
|
||||
"save_slice.import_projection": "overlay-runtime-restore-v1"
|
||||
},
|
||||
"named_locomotive_cost": {
|
||||
"Locomotive 1": 250000,
|
||||
"Locomotive 101": 325000
|
||||
},
|
||||
"packed_event_collection": {
|
||||
"live_entry_ids": [41],
|
||||
"records": [
|
||||
{
|
||||
"decode_status": "parity_only",
|
||||
"payload_family": "real_packed_v1",
|
||||
"import_outcome": "imported",
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"descriptor_id": 352,
|
||||
"recovered_locomotive_id": 1
|
||||
},
|
||||
{
|
||||
"descriptor_id": 475,
|
||||
"recovered_locomotive_id": 101
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"event_runtime_records": [
|
||||
{
|
||||
"record_id": 41,
|
||||
"service_count": 1,
|
||||
"effects": [
|
||||
{
|
||||
"kind": "set_named_locomotive_cost",
|
||||
"name": "Locomotive 1",
|
||||
"value": 250000
|
||||
},
|
||||
{
|
||||
"kind": "set_named_locomotive_cost",
|
||||
"name": "Locomotive 101",
|
||||
"value": 325000
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"save_slice_id": "packed-event-locomotive-cost-overlay-save-slice",
|
||||
"source": {
|
||||
"description": "Tracked save-slice document proving recovered scalar locomotive cost descriptors import through overlay-backed catalog context.",
|
||||
"original_save_filename": "captured-locomotive-cost-overlay.gms",
|
||||
"original_save_sha256": "locomotive-cost-overlay-sample-sha256",
|
||||
"notes": [
|
||||
"tracked as JSON save-slice document rather than raw .smp",
|
||||
"recovered locomotive cost descriptors 352 and 475 import through the ordinary runtime path when overlay catalog context resolves their ids"
|
||||
]
|
||||
},
|
||||
"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,
|
||||
"named_locomotive_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": 29952,
|
||||
"records_tag_offset": 30208,
|
||||
"close_tag_offset": 30976,
|
||||
"packed_state_version": 1001,
|
||||
"packed_state_version_hex": "0x000003e9",
|
||||
"live_id_bound": 41,
|
||||
"live_record_count": 1,
|
||||
"live_entry_ids": [41],
|
||||
"decoded_record_count": 1,
|
||||
"imported_runtime_record_count": 1,
|
||||
"records": [
|
||||
{
|
||||
"record_index": 0,
|
||||
"live_entry_id": 41,
|
||||
"payload_offset": 30240,
|
||||
"payload_len": 120,
|
||||
"decode_status": "parity_only",
|
||||
"payload_family": "real_packed_v1",
|
||||
"trigger_kind": 7,
|
||||
"one_shot": false,
|
||||
"compact_control": {
|
||||
"mode_byte_0x7ef": 7,
|
||||
"primary_selector_0x7f0": 0,
|
||||
"grouped_mode_0x7f4": 2,
|
||||
"one_shot_header_0x7f5": 0,
|
||||
"modifier_flag_0x7f9": 0,
|
||||
"modifier_flag_0x7fa": 0,
|
||||
"grouped_target_scope_ordinals_0x7fb": [0, 0, 0, 0],
|
||||
"grouped_scope_checkboxes_0x7ff": [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": [2, 0, 0, 0],
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"group_index": 0,
|
||||
"row_index": 0,
|
||||
"descriptor_id": 352,
|
||||
"descriptor_label": "Locomotive 1 Cost",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "locomotive_cost_scalar",
|
||||
"opcode": 3,
|
||||
"raw_scalar_value": 250000,
|
||||
"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 Locomotive 1 Cost to 250000",
|
||||
"recovered_locomotive_id": 1,
|
||||
"locomotive_name": null,
|
||||
"notes": [
|
||||
"locomotive cost descriptor maps to live locomotive id 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group_index": 0,
|
||||
"row_index": 1,
|
||||
"descriptor_id": 475,
|
||||
"descriptor_label": "Locomotive 101 Cost",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "locomotive_cost_scalar",
|
||||
"opcode": 3,
|
||||
"raw_scalar_value": 325000,
|
||||
"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 Locomotive 101 Cost to 325000",
|
||||
"recovered_locomotive_id": 101,
|
||||
"locomotive_name": null,
|
||||
"notes": [
|
||||
"locomotive cost descriptor maps to live locomotive id 101"
|
||||
]
|
||||
}
|
||||
],
|
||||
"decoded_conditions": [],
|
||||
"decoded_actions": [
|
||||
{
|
||||
"kind": "set_named_locomotive_cost",
|
||||
"name": "Locomotive 1",
|
||||
"value": 250000
|
||||
},
|
||||
{
|
||||
"kind": "set_named_locomotive_cost",
|
||||
"name": "Locomotive 101",
|
||||
"value": 325000
|
||||
}
|
||||
],
|
||||
"executable_import_ready": false,
|
||||
"notes": [
|
||||
"scalar locomotive cost rows use overlay catalog context"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"notes": [
|
||||
"overlay-backed locomotive cost effect sample"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"import_id": "packed-event-locomotive-cost-overlay",
|
||||
"source": {
|
||||
"description": "Overlay import document combining a tracked base snapshot with a tracked recovered locomotive-cost save slice."
|
||||
},
|
||||
"base_snapshot_path": "packed-event-locomotive-cost-overlay-base-snapshot.json",
|
||||
"save_slice_path": "packed-event-locomotive-cost-overlay-save-slice.json"
|
||||
}
|
||||
|
|
@ -26,11 +26,12 @@
|
|||
"packed_event_imported_runtime_record_count": 0,
|
||||
"packed_event_parity_only_record_count": 3,
|
||||
"packed_event_unsupported_record_count": 0,
|
||||
"packed_event_blocked_missing_locomotive_catalog_context_count": 1,
|
||||
"packed_event_blocked_missing_condition_context_count": 0,
|
||||
"packed_event_blocked_territory_condition_scope_count": 0,
|
||||
"packed_event_blocked_missing_compact_control_count": 0,
|
||||
"packed_event_blocked_unmapped_real_descriptor_count": 0,
|
||||
"packed_event_blocked_unmapped_world_descriptor_count": 3,
|
||||
"packed_event_blocked_unmapped_world_descriptor_count": 2,
|
||||
"packed_event_blocked_structural_only_count": 0,
|
||||
"event_runtime_record_count": 0,
|
||||
"total_company_cash": 0
|
||||
|
|
@ -61,7 +62,7 @@
|
|||
{
|
||||
"decode_status": "parity_only",
|
||||
"payload_family": "real_packed_v1",
|
||||
"import_outcome": "blocked_unmapped_world_descriptor",
|
||||
"import_outcome": "blocked_missing_locomotive_catalog_context",
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"descriptor_id": 352,
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@
|
|||
"decoded_actions": [],
|
||||
"executable_import_ready": false,
|
||||
"notes": [
|
||||
"recovered locomotive cost metadata is now checked in, but execution is still deferred"
|
||||
"recovered locomotive cost metadata is now checked in, but scalar rows still need overlay-backed locomotive catalog context to import"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue