Ground locomotives-page descriptor semantics

This commit is contained in:
Jan Petykiewicz 2026-04-16 22:26:23 -07:00
commit 358d4cdec3
26 changed files with 1157 additions and 710 deletions

View file

@ -80,17 +80,19 @@ is broader now too: checked-in world-flag condition ids can lower into `world_fl
for boolean equality/inequality forms, so real packed records can gate whole-game effects on for boolean equality/inequality forms, so real packed records can gate whole-game effects on
existing `world_flags` without fixture-authored placeholder ids. The tracked parity save-slice no existing `world_flags` without fixture-authored placeholder ids. The tracked parity save-slice no
longer depends on a raw `unsupported_framing` placeholder either: its remaining residue is now one longer depends on a raw `unsupported_framing` placeholder either: its remaining residue is now one
recovered locomotives-page `real_packed_v1` record that lands in the explicit recovered locomotives-page `real_packed_v1` record that now lands on explicit descriptor parity
`blocked_unmapped_world_descriptor` bucket. The next recovered descriptor band is now partially instead of a generic unmapped bucket. The next recovered descriptor band is now partially
executable too: descriptors `454..456` (`All Steam/Diesel/Electric Locos Avail.`) now lower executable too: descriptors `454..456` (`All Steam/Diesel/Electric Locos Avail.`) now lower
through checked-in metadata into keyed `world_flags`, while the wider locomotive availability/cost through checked-in metadata into keyed `world_flags`, while the wider locomotive availability/cost
scalar bands are now save-native too. Raw `.smp` inspection/export reconstructs the persisted scalar bands are now save-native too. Raw `.smp` inspection/export reconstructs the persisted
`[world+0x66b6]` locomotive name table and derives a minimal `RuntimeState.locomotive_catalog`, so `[world+0x66b6]` locomotive name table and derives a minimal `RuntimeState.locomotive_catalog`, so
standalone save-slice imports can now lower recovered locomotive availability and locomotive-cost standalone save-slice imports can now lower the grounded lower locomotive availability and
rows directly into `RuntimeState.named_locomotive_availability` and locomotive-cost rows directly into `RuntimeState.named_locomotive_availability` and
`RuntimeState.named_locomotive_cost` without needing overlay snapshots when the save carries enough `RuntimeState.named_locomotive_cost` without needing overlay snapshots when the save carries enough
catalog context. The remaining recovered scalar world families execute too: cargo-production slots catalog context; the unresolved lower tail and upper locomotive bands now stay on explicit parity
`230..240` lower into `cargo_production_overrides`, and descriptor `453` lowers into instead of synthetic execution. The remaining recovered scalar world families execute too:
cargo-production slots `230..240` lower into `cargo_production_overrides`, and descriptor `453`
lowers into
`world_restore.territory_access_cost`. Whole-game ordinary-condition breadth now aligns with those `world_restore.territory_access_cost`. Whole-game ordinary-condition breadth now aligns with those
same world-scalar runtime surfaces too: named locomotive availability thresholds, named same world-scalar runtime surfaces too: named locomotive availability thresholds, named
locomotive cost thresholds, named cargo-production slot thresholds, aggregate cargo-production locomotive cost thresholds, named cargo-production slot thresholds, aggregate cargo-production

View file

@ -2984,6 +2984,12 @@ fn real_grouped_row_is_unsupported_executable_descriptor_variant(
56 | 57 => row.row_shape != "scalar_assignment", 56 | 57 => row.row_shape != "scalar_assignment",
_ => match row.parameter_family.as_deref() { _ => match row.parameter_family.as_deref() {
Some("world_scalar_override") => row.row_shape != "scalar_assignment", Some("world_scalar_override") => row.row_shape != "scalar_assignment",
Some("locomotive_availability_scalar") => {
!(row.row_shape == "scalar_assignment" && row.raw_scalar_value >= 0)
}
Some("locomotive_cost_scalar") => {
!(row.row_shape == "scalar_assignment" && row.raw_scalar_value >= 0)
}
Some("cargo_price_scalar") => { Some("cargo_price_scalar") => {
row.descriptor_id == 105 row.descriptor_id == 105
&& !(row.row_shape == "scalar_assignment" && row.raw_scalar_value >= 0) && !(row.row_shape == "scalar_assignment" && row.raw_scalar_value >= 0)
@ -4153,11 +4159,94 @@ mod tests {
descriptor_id: u32, descriptor_id: u32,
value: i32, value: i32,
) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary { ) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary {
fn grounded_locomotive_name(locomotive_id: u32) -> Option<&'static str> {
match locomotive_id {
1 => Some("2-D-2"),
2 => Some("E-88"),
3 => Some("Adler 2-2-2"),
4 => Some("USA 103"),
5 => Some("American 4-4-0"),
6 => Some("Atlantic 4-4-2"),
7 => Some("Baldwin 0-6-0"),
8 => Some("Be 5/7"),
9 => Some("Beuth 2-2-2"),
10 => Some("Big Boy 4-8-8-4"),
11 => Some("C55 Deltic"),
12 => Some("Camelback 0-6-0"),
13 => Some("Challenger 4-6-6-4"),
14 => Some("Class 01 4-6-2"),
15 => Some("Class 103"),
16 => Some("Class 132"),
17 => Some("Class 500 4-6-0"),
18 => Some("Class 9100"),
19 => Some("Class EF 66"),
20 => Some("Class 6E"),
21 => Some("Consolidation 2-8-0"),
22 => Some("Crampton 4-2-0"),
23 => Some("DD 080-X"),
24 => Some("DD40AX"),
25 => Some("Duke Class 4-4-0"),
26 => Some("E18"),
27 => Some("E428"),
28 => Some("Brenner E412"),
29 => Some("E60CP"),
30 => Some("Eight Wheeler 4-4-0"),
31 => Some("EP-2 Bipolar"),
32 => Some("ET22"),
33 => Some("F3"),
34 => Some("Fairlie 0-6-6-0"),
35 => Some("Firefly 2-2-2"),
36 => Some("FP45"),
37 => Some("Ge 6/6 Crocodile"),
38 => Some("GG1"),
39 => Some("GP7"),
40 => Some("H10 2-8-2"),
41 => Some("HST 125"),
42 => Some("Kriegslok 2-10-0"),
43 => Some("Mallard 4-6-2"),
44 => Some("Norris 4-2-0"),
45 => Some("Northern 4-8-4"),
46 => Some("Orca NX462"),
47 => Some("Pacific 4-6-2"),
48 => Some("Planet 2-2-0"),
49 => Some("Re 6/6"),
50 => Some("Red Devil 4-8-4"),
51 => Some("S3 4-4-0"),
52 => Some("NA-90D"),
53 => Some("Shay (2-Truck)"),
54 => Some("Shinkansen Series 0"),
55 => Some("Stirling 4-2-2"),
56 => Some("Trans-Euro"),
57 => Some("V200"),
58 => Some("VL80T"),
_ => None,
}
}
let recovered_locomotive_id = match descriptor_id {
241..=351 => Some(descriptor_id - 240),
_ => None,
};
let descriptor_label = match descriptor_id {
457..=474 => {
format!(
"Upper-Band Locomotive Availability Slot {}",
descriptor_id - 456
)
}
_ => recovered_locomotive_id
.map(|loco_id| {
grounded_locomotive_name(loco_id)
.map(|name| format!("{name} Availability"))
.unwrap_or_else(|| format!("Locomotive {loco_id} Availability"))
})
.unwrap_or_else(|| "Locomotive Availability".to_string()),
};
crate::SmpLoadedPackedEventGroupedEffectRowSummary { crate::SmpLoadedPackedEventGroupedEffectRowSummary {
group_index: 0, group_index: 0,
row_index: 0, row_index: 0,
descriptor_id, descriptor_id,
descriptor_label: Some("Unknown Loco Available".to_string()), descriptor_label: Some(descriptor_label.clone()),
target_mask_bits: Some(0x08), target_mask_bits: Some(0x08),
parameter_family: Some("locomotive_availability_scalar".to_string()), parameter_family: Some("locomotive_availability_scalar".to_string()),
grouped_target_subject: None, grouped_target_subject: None,
@ -4172,14 +4261,10 @@ mod tests {
value_word_0x16: 0, value_word_0x16: 0,
row_shape: "scalar_assignment".to_string(), row_shape: "scalar_assignment".to_string(),
semantic_family: Some("scalar_assignment".to_string()), semantic_family: Some("scalar_assignment".to_string()),
semantic_preview: Some(format!("Set Unknown Loco Available to {value}")), semantic_preview: Some(format!("Set {descriptor_label} to {value}")),
recovered_cargo_slot: None, recovered_cargo_slot: None,
recovered_cargo_class: None, recovered_cargo_class: None,
recovered_locomotive_id: match descriptor_id { recovered_locomotive_id,
241..=351 => Some(descriptor_id - 240),
457..=474 => Some(descriptor_id - 345),
_ => None,
},
locomotive_name: None, locomotive_name: None,
notes: vec![], notes: vec![],
} }
@ -4189,14 +4274,84 @@ mod tests {
descriptor_id: u32, descriptor_id: u32,
value: i32, value: i32,
) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary { ) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary {
fn grounded_locomotive_name(locomotive_id: u32) -> Option<&'static str> {
match locomotive_id {
1 => Some("2-D-2"),
2 => Some("E-88"),
3 => Some("Adler 2-2-2"),
4 => Some("USA 103"),
5 => Some("American 4-4-0"),
6 => Some("Atlantic 4-4-2"),
7 => Some("Baldwin 0-6-0"),
8 => Some("Be 5/7"),
9 => Some("Beuth 2-2-2"),
10 => Some("Big Boy 4-8-8-4"),
11 => Some("C55 Deltic"),
12 => Some("Camelback 0-6-0"),
13 => Some("Challenger 4-6-6-4"),
14 => Some("Class 01 4-6-2"),
15 => Some("Class 103"),
16 => Some("Class 132"),
17 => Some("Class 500 4-6-0"),
18 => Some("Class 9100"),
19 => Some("Class EF 66"),
20 => Some("Class 6E"),
21 => Some("Consolidation 2-8-0"),
22 => Some("Crampton 4-2-0"),
23 => Some("DD 080-X"),
24 => Some("DD40AX"),
25 => Some("Duke Class 4-4-0"),
26 => Some("E18"),
27 => Some("E428"),
28 => Some("Brenner E412"),
29 => Some("E60CP"),
30 => Some("Eight Wheeler 4-4-0"),
31 => Some("EP-2 Bipolar"),
32 => Some("ET22"),
33 => Some("F3"),
34 => Some("Fairlie 0-6-6-0"),
35 => Some("Firefly 2-2-2"),
36 => Some("FP45"),
37 => Some("Ge 6/6 Crocodile"),
38 => Some("GG1"),
39 => Some("GP7"),
40 => Some("H10 2-8-2"),
41 => Some("HST 125"),
42 => Some("Kriegslok 2-10-0"),
43 => Some("Mallard 4-6-2"),
44 => Some("Norris 4-2-0"),
45 => Some("Northern 4-8-4"),
46 => Some("Orca NX462"),
47 => Some("Pacific 4-6-2"),
48 => Some("Planet 2-2-0"),
49 => Some("Re 6/6"),
50 => Some("Red Devil 4-8-4"),
51 => Some("S3 4-4-0"),
52 => Some("NA-90D"),
53 => Some("Shay (2-Truck)"),
54 => Some("Shinkansen Series 0"),
55 => Some("Stirling 4-2-2"),
56 => Some("Trans-Euro"),
57 => Some("V200"),
58 => Some("VL80T"),
_ => None,
}
}
let recovered_locomotive_id = match descriptor_id { let recovered_locomotive_id = match descriptor_id {
352..=451 => Some(descriptor_id - 351), 352..=451 => Some(descriptor_id - 351),
475..=500 => Some(descriptor_id - 374),
_ => None, _ => None,
}; };
let descriptor_label = recovered_locomotive_id let descriptor_label = match descriptor_id {
.map(|loco_id| format!("Locomotive {loco_id} Cost")) 475..=502 => format!("Upper-Band Locomotive Cost Slot {}", descriptor_id - 474),
.unwrap_or_else(|| "Locomotive Cost".to_string()); _ => recovered_locomotive_id
.map(|loco_id| {
grounded_locomotive_name(loco_id)
.map(|name| format!("{name} Cost"))
.unwrap_or_else(|| format!("Locomotive {loco_id} Cost"))
})
.unwrap_or_else(|| "Locomotive Cost".to_string()),
};
crate::SmpLoadedPackedEventGroupedEffectRowSummary { crate::SmpLoadedPackedEventGroupedEffectRowSummary {
group_index: 0, group_index: 0,
row_index: 0, row_index: 0,
@ -4228,6 +4383,71 @@ mod tests {
fn save_named_locomotive_table( fn save_named_locomotive_table(
count: usize, count: usize,
) -> crate::SmpLoadedNamedLocomotiveAvailabilityTable { ) -> crate::SmpLoadedNamedLocomotiveAvailabilityTable {
fn grounded_locomotive_name(index: usize) -> String {
match index {
0 => "2-D-2",
1 => "E-88",
2 => "Adler 2-2-2",
3 => "USA 103",
4 => "American 4-4-0",
5 => "Atlantic 4-4-2",
6 => "Baldwin 0-6-0",
7 => "Be 5/7",
8 => "Beuth 2-2-2",
9 => "Big Boy 4-8-8-4",
10 => "C55 Deltic",
11 => "Camelback 0-6-0",
12 => "Challenger 4-6-6-4",
13 => "Class 01 4-6-2",
14 => "Class 103",
15 => "Class 132",
16 => "Class 500 4-6-0",
17 => "Class 9100",
18 => "Class EF 66",
19 => "Class 6E",
20 => "Consolidation 2-8-0",
21 => "Crampton 4-2-0",
22 => "DD 080-X",
23 => "DD40AX",
24 => "Duke Class 4-4-0",
25 => "E18",
26 => "E428",
27 => "Brenner E412",
28 => "E60CP",
29 => "Eight Wheeler 4-4-0",
30 => "EP-2 Bipolar",
31 => "ET22",
32 => "F3",
33 => "Fairlie 0-6-6-0",
34 => "Firefly 2-2-2",
35 => "FP45",
36 => "Ge 6/6 Crocodile",
37 => "GG1",
38 => "GP7",
39 => "H10 2-8-2",
40 => "HST 125",
41 => "Kriegslok 2-10-0",
42 => "Mallard 4-6-2",
43 => "Norris 4-2-0",
44 => "Northern 4-8-4",
45 => "Orca NX462",
46 => "Pacific 4-6-2",
47 => "Planet 2-2-0",
48 => "Re 6/6",
49 => "Red Devil 4-8-4",
50 => "S3 4-4-0",
51 => "NA-90D",
52 => "Shay (2-Truck)",
53 => "Shinkansen Series 0",
54 => "Stirling 4-2-2",
55 => "Trans-Euro",
56 => "V200",
57 => "VL80T",
_ => return format!("Locomotive {}", index + 1),
}
.to_string()
}
crate::SmpLoadedNamedLocomotiveAvailabilityTable { crate::SmpLoadedNamedLocomotiveAvailabilityTable {
source_kind: "runtime-save-direct-serializer".to_string(), source_kind: "runtime-save-direct-serializer".to_string(),
semantic_family: "scenario-named-locomotive-availability-table".to_string(), semantic_family: "scenario-named-locomotive-availability-table".to_string(),
@ -4241,7 +4461,7 @@ mod tests {
.map(|index| crate::SmpRt3105SaveNameTableEntry { .map(|index| crate::SmpRt3105SaveNameTableEntry {
index, index,
offset: 0x7c78 + index * 0x41, offset: 0x7c78 + index * 0x41,
text: format!("Locomotive {}", index + 1), text: grounded_locomotive_name(index),
availability_dword: 1, availability_dword: 1,
availability_dword_hex: "0x00000001".to_string(), availability_dword_hex: "0x00000001".to_string(),
trailer_word: 1, trailer_word: 1,
@ -6712,7 +6932,7 @@ mod tests {
group_index: 0, group_index: 0,
row_index: 0, row_index: 0,
descriptor_id: 250, descriptor_id: 250,
descriptor_label: Some("Unknown Loco Available".to_string()), descriptor_label: Some("Big Boy 4-8-8-4 Availability".to_string()),
target_mask_bits: Some(0x08), target_mask_bits: Some(0x08),
parameter_family: Some("locomotive_availability_scalar".to_string()), parameter_family: Some("locomotive_availability_scalar".to_string()),
grouped_target_subject: None, grouped_target_subject: None,
@ -6727,7 +6947,9 @@ mod tests {
value_word_0x16: 0, value_word_0x16: 0,
row_shape: "scalar_assignment".to_string(), row_shape: "scalar_assignment".to_string(),
semantic_family: Some("scalar_assignment".to_string()), semantic_family: Some("scalar_assignment".to_string()),
semantic_preview: Some("Set Unknown Loco Available to 42".to_string()), semantic_preview: Some(
"Set Big Boy 4-8-8-4 Availability to 42".to_string(),
),
recovered_cargo_slot: None, recovered_cargo_slot: None,
recovered_cargo_class: None, recovered_cargo_class: None,
recovered_locomotive_id: Some(10), recovered_locomotive_id: Some(10),
@ -6856,7 +7078,7 @@ mod tests {
bridge_family: None, bridge_family: None,
profile: None, profile: None,
candidate_availability_table: None, candidate_availability_table: None,
named_locomotive_availability_table: Some(save_named_locomotive_table(112)), named_locomotive_availability_table: Some(save_named_locomotive_table(58)),
locomotive_catalog: None, locomotive_catalog: None,
cargo_catalog: None, cargo_catalog: None,
company_roster: None, company_roster: None,
@ -6896,7 +7118,7 @@ mod tests {
grouped_effect_row_counts: vec![2, 0, 0, 0], grouped_effect_row_counts: vec![2, 0, 0, 0],
grouped_effect_rows: vec![ grouped_effect_rows: vec![
real_locomotive_availability_row(250, 42), real_locomotive_availability_row(250, 42),
real_locomotive_availability_row(457, 7), real_locomotive_availability_row(298, 7),
], ],
decoded_conditions: Vec::new(), decoded_conditions: Vec::new(),
decoded_actions: vec![], decoded_actions: vec![],
@ -6917,7 +7139,7 @@ mod tests {
) )
.expect("save slice should project"); .expect("save slice should project");
assert_eq!(import.state.locomotive_catalog.len(), 112); assert_eq!(import.state.locomotive_catalog.len(), 58);
assert_eq!(import.state.event_runtime_records.len(), 1); assert_eq!(import.state.event_runtime_records.len(), 1);
assert_eq!( assert_eq!(
import import
@ -6938,14 +7160,11 @@ mod tests {
import import
.state .state
.named_locomotive_availability .named_locomotive_availability
.get("Locomotive 10"), .get("Big Boy 4-8-8-4"),
Some(&42) Some(&42)
); );
assert_eq!( assert_eq!(
import import.state.named_locomotive_availability.get("VL80T"),
.state
.named_locomotive_availability
.get("Locomotive 112"),
Some(&7) Some(&7)
); );
} }
@ -6973,11 +7192,11 @@ mod tests {
locomotive_catalog: vec![ locomotive_catalog: vec![
crate::RuntimeLocomotiveCatalogEntry { crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 10, locomotive_id: 10,
name: "Locomotive 10".to_string(), name: "Big Boy 4-8-8-4".to_string(),
}, },
crate::RuntimeLocomotiveCatalogEntry { crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 112, locomotive_id: 58,
name: "Locomotive 112".to_string(), name: "VL80T".to_string(),
}, },
], ],
cargo_catalog: Vec::new(), cargo_catalog: Vec::new(),
@ -6988,8 +7207,8 @@ mod tests {
event_runtime_records: Vec::new(), event_runtime_records: Vec::new(),
candidate_availability: BTreeMap::new(), candidate_availability: BTreeMap::new(),
named_locomotive_availability: BTreeMap::from([ named_locomotive_availability: BTreeMap::from([
("Locomotive 10".to_string(), 0), ("Big Boy 4-8-8-4".to_string(), 0),
("Locomotive 112".to_string(), 1), ("VL80T".to_string(), 1),
]), ]),
named_locomotive_cost: BTreeMap::new(), named_locomotive_cost: BTreeMap::new(),
all_cargo_price_override: None, all_cargo_price_override: None,
@ -7052,7 +7271,7 @@ mod tests {
grouped_effect_row_counts: vec![2, 0, 0, 0], grouped_effect_row_counts: vec![2, 0, 0, 0],
grouped_effect_rows: vec![ grouped_effect_rows: vec![
real_locomotive_availability_row(250, 42), real_locomotive_availability_row(250, 42),
real_locomotive_availability_row(457, 7), real_locomotive_availability_row(298, 7),
], ],
decoded_conditions: Vec::new(), decoded_conditions: Vec::new(),
decoded_actions: vec![], decoded_actions: vec![],
@ -7094,14 +7313,11 @@ mod tests {
import import
.state .state
.named_locomotive_availability .named_locomotive_availability
.get("Locomotive 10"), .get("Big Boy 4-8-8-4"),
Some(&42) Some(&42)
); );
assert_eq!( assert_eq!(
import import.state.named_locomotive_availability.get("VL80T"),
.state
.named_locomotive_availability
.get("Locomotive 112"),
Some(&7) Some(&7)
); );
} }
@ -7275,7 +7491,7 @@ mod tests {
bridge_family: None, bridge_family: None,
profile: None, profile: None,
candidate_availability_table: None, candidate_availability_table: None,
named_locomotive_availability_table: Some(save_named_locomotive_table(112)), named_locomotive_availability_table: Some(save_named_locomotive_table(58)),
locomotive_catalog: None, locomotive_catalog: None,
cargo_catalog: None, cargo_catalog: None,
company_roster: None, company_roster: None,
@ -7315,7 +7531,7 @@ mod tests {
grouped_effect_row_counts: vec![2, 0, 0, 0], grouped_effect_row_counts: vec![2, 0, 0, 0],
grouped_effect_rows: vec![ grouped_effect_rows: vec![
real_locomotive_cost_row(352, 250000), real_locomotive_cost_row(352, 250000),
real_locomotive_cost_row(475, 325000), real_locomotive_cost_row(409, 325000),
], ],
decoded_conditions: Vec::new(), decoded_conditions: Vec::new(),
decoded_actions: vec![], decoded_actions: vec![],
@ -7335,7 +7551,7 @@ mod tests {
) )
.expect("save slice should project"); .expect("save slice should project");
assert_eq!(import.state.locomotive_catalog.len(), 112); assert_eq!(import.state.locomotive_catalog.len(), 58);
assert_eq!(import.state.event_runtime_records.len(), 1); assert_eq!(import.state.event_runtime_records.len(), 1);
assert_eq!( assert_eq!(
import import
@ -7353,11 +7569,11 @@ mod tests {
.expect("save-derived locomotive cost record should run"); .expect("save-derived locomotive cost record should run");
assert_eq!( assert_eq!(
import.state.named_locomotive_cost.get("Locomotive 1"), import.state.named_locomotive_cost.get("2-D-2"),
Some(&250000) Some(&250000)
); );
assert_eq!( assert_eq!(
import.state.named_locomotive_cost.get("Locomotive 101"), import.state.named_locomotive_cost.get("VL80T"),
Some(&325000) Some(&325000)
); );
} }
@ -7385,11 +7601,11 @@ mod tests {
locomotive_catalog: vec![ locomotive_catalog: vec![
crate::RuntimeLocomotiveCatalogEntry { crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 1, locomotive_id: 1,
name: "Locomotive 1".to_string(), name: "2-D-2".to_string(),
}, },
crate::RuntimeLocomotiveCatalogEntry { crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 101, locomotive_id: 58,
name: "Locomotive 101".to_string(), name: "VL80T".to_string(),
}, },
], ],
cargo_catalog: Vec::new(), cargo_catalog: Vec::new(),
@ -7401,8 +7617,8 @@ mod tests {
candidate_availability: BTreeMap::new(), candidate_availability: BTreeMap::new(),
named_locomotive_availability: BTreeMap::new(), named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::from([ named_locomotive_cost: BTreeMap::from([
("Locomotive 1".to_string(), 100000), ("2-D-2".to_string(), 100000),
("Locomotive 101".to_string(), 200000), ("VL80T".to_string(), 200000),
]), ]),
all_cargo_price_override: None, all_cargo_price_override: None,
named_cargo_price_overrides: BTreeMap::new(), named_cargo_price_overrides: BTreeMap::new(),
@ -7464,7 +7680,7 @@ mod tests {
grouped_effect_row_counts: vec![2, 0, 0, 0], grouped_effect_row_counts: vec![2, 0, 0, 0],
grouped_effect_rows: vec![ grouped_effect_rows: vec![
real_locomotive_cost_row(352, 250000), real_locomotive_cost_row(352, 250000),
real_locomotive_cost_row(475, 325000), real_locomotive_cost_row(409, 325000),
], ],
decoded_conditions: Vec::new(), decoded_conditions: Vec::new(),
decoded_actions: vec![], decoded_actions: vec![],
@ -7502,11 +7718,11 @@ mod tests {
.expect("overlay-imported locomotive cost record should run"); .expect("overlay-imported locomotive cost record should run");
assert_eq!( assert_eq!(
import.state.named_locomotive_cost.get("Locomotive 1"), import.state.named_locomotive_cost.get("2-D-2"),
Some(&250000) Some(&250000)
); );
assert_eq!( assert_eq!(
import.state.named_locomotive_cost.get("Locomotive 101"), import.state.named_locomotive_cost.get("VL80T"),
Some(&325000) Some(&325000)
); );
} }
@ -7584,7 +7800,7 @@ mod tests {
.packed_event_collection .packed_event_collection
.as_ref() .as_ref()
.and_then(|summary| summary.records[0].import_outcome.as_deref()), .and_then(|summary| summary.records[0].import_outcome.as_deref()),
Some("blocked_evidence_blocked_descriptor") Some("blocked_variant_or_scope_blocked_descriptor")
); );
} }

View file

@ -446,6 +446,67 @@ const KNOWN_CARGO_SLOT_DEFINITIONS: [KnownCargoSlotDefinition; 11] = [
}, },
]; ];
const GROUNDED_LOCOMOTIVE_PREFIX: [&str; 58] = [
"2-D-2",
"E-88",
"Adler 2-2-2",
"USA 103",
"American 4-4-0",
"Atlantic 4-4-2",
"Baldwin 0-6-0",
"Be 5/7",
"Beuth 2-2-2",
"Big Boy 4-8-8-4",
"C55 Deltic",
"Camelback 0-6-0",
"Challenger 4-6-6-4",
"Class 01 4-6-2",
"Class 103",
"Class 132",
"Class 500 4-6-0",
"Class 9100",
"Class EF 66",
"Class 6E",
"Consolidation 2-8-0",
"Crampton 4-2-0",
"DD 080-X",
"DD40AX",
"Duke Class 4-4-0",
"E18",
"E428",
"Brenner E412",
"E60CP",
"Eight Wheeler 4-4-0",
"EP-2 Bipolar",
"ET22",
"F3",
"Fairlie 0-6-6-0",
"Firefly 2-2-2",
"FP45",
"Ge 6/6 Crocodile",
"GG1",
"GP7",
"H10 2-8-2",
"HST 125",
"Kriegslok 2-10-0",
"Mallard 4-6-2",
"Norris 4-2-0",
"Northern 4-8-4",
"Orca NX462",
"Pacific 4-6-2",
"Planet 2-2-0",
"Re 6/6",
"Red Devil 4-8-4",
"S3 4-4-0",
"NA-90D",
"Shay (2-Truck)",
"Shinkansen Series 0",
"Stirling 4-2-2",
"Trans-Euro",
"V200",
"VL80T",
];
const REAL_CANDIDATE_AVAILABILITY_CONDITION_TEMPLATE_ID: i32 = 435; const REAL_CANDIDATE_AVAILABILITY_CONDITION_TEMPLATE_ID: i32 = 435;
const REAL_CHAIRMAN_CASH_CONDITION_ID: i32 = 2218; const REAL_CHAIRMAN_CASH_CONDITION_ID: i32 = 2218;
const REAL_CHAIRMAN_HOLDINGS_TOTAL_CONDITION_ID: i32 = 2239; const REAL_CHAIRMAN_HOLDINGS_TOTAL_CONDITION_ID: i32 = 2239;
@ -3752,42 +3813,90 @@ fn recovered_cargo_production_slot(descriptor_id: u32) -> Option<u32> {
fn recovered_locomotive_availability_descriptor_metadata( fn recovered_locomotive_availability_descriptor_metadata(
descriptor_id: u32, descriptor_id: u32,
) -> Option<RealGroupedEffectDescriptorMetadata> { ) -> Option<RealGroupedEffectDescriptorMetadata> {
(241..=351) if let Some(loco_id) = recovered_locomotive_availability_loco_id(descriptor_id) {
.contains(&descriptor_id) let label = recovered_locomotive_availability_label(loco_id);
.then_some(RealGroupedEffectDescriptorMetadata { let executable_in_runtime = (loco_id as usize) <= GROUNDED_LOCOMOTIVE_PREFIX.len();
return Some(RealGroupedEffectDescriptorMetadata {
descriptor_id, descriptor_id,
label: "Unknown Loco Available", label,
target_mask_bits: 0x08,
parameter_family: "locomotive_availability_scalar",
runtime_key: None,
runtime_status: if executable_in_runtime {
RealGroupedEffectRuntimeStatus::Executable
} else {
RealGroupedEffectRuntimeStatus::EvidenceBlocked
},
executable_in_runtime,
});
}
(457..=474)
.contains(&descriptor_id)
.then(|| RealGroupedEffectDescriptorMetadata {
descriptor_id,
label: upper_band_locomotive_availability_label(descriptor_id),
target_mask_bits: 0x08, target_mask_bits: 0x08,
parameter_family: "locomotive_availability_scalar", parameter_family: "locomotive_availability_scalar",
runtime_key: None, runtime_key: None,
runtime_status: RealGroupedEffectRuntimeStatus::EvidenceBlocked, runtime_status: RealGroupedEffectRuntimeStatus::EvidenceBlocked,
executable_in_runtime: false, executable_in_runtime: false,
}) })
.or_else(|| {
(457..=474)
.contains(&descriptor_id)
.then_some(RealGroupedEffectDescriptorMetadata {
descriptor_id,
label: "Unknown Loco Available",
target_mask_bits: 0x08,
parameter_family: "locomotive_availability_scalar",
runtime_key: None,
runtime_status: RealGroupedEffectRuntimeStatus::EvidenceBlocked,
executable_in_runtime: false,
})
})
} }
fn recovered_locomotive_availability_loco_id(descriptor_id: u32) -> Option<u32> { fn recovered_locomotive_availability_loco_id(descriptor_id: u32) -> Option<u32> {
if (241..=351).contains(&descriptor_id) { if (241..=351).contains(&descriptor_id) {
return Some(descriptor_id - 240); return Some(descriptor_id - 240);
} }
if (457..=474).contains(&descriptor_id) {
return Some(descriptor_id - 345);
}
None None
} }
fn grounded_locomotive_name(loco_id: u32) -> Option<&'static str> {
let index = loco_id.checked_sub(1)? as usize;
GROUNDED_LOCOMOTIVE_PREFIX.get(index).copied()
}
fn recovered_locomotive_availability_label(loco_id: u32) -> &'static str {
static LABELS: OnceLock<BTreeMap<u32, &'static str>> = OnceLock::new();
LABELS
.get_or_init(|| {
(1..=111)
.map(|loco_id| {
let label = grounded_locomotive_name(loco_id)
.map(|name| format!("{name} Availability"))
.unwrap_or_else(|| {
format!("Lower-Band Locomotive Availability Slot {loco_id}")
});
(loco_id, Box::leak(label.into_boxed_str()) as &'static str)
})
.collect()
})
.get(&loco_id)
.copied()
.expect("lower-band locomotive availability label should exist")
}
fn upper_band_locomotive_availability_label(descriptor_id: u32) -> &'static str {
static LABELS: OnceLock<BTreeMap<u32, &'static str>> = OnceLock::new();
LABELS
.get_or_init(|| {
(457..=474)
.map(|descriptor_id| {
let label = format!(
"Upper-Band Locomotive Availability Slot {}",
descriptor_id - 456
);
(
descriptor_id,
Box::leak(label.into_boxed_str()) as &'static str,
)
})
.collect()
})
.get(&descriptor_id)
.copied()
.expect("upper-band locomotive availability label should exist")
}
fn recovered_cargo_production_label(descriptor_id: u32) -> Option<&'static str> { fn recovered_cargo_production_label(descriptor_id: u32) -> Option<&'static str> {
known_cargo_slot_definition_for_descriptor_id(descriptor_id).map(|definition| definition.label) known_cargo_slot_definition_for_descriptor_id(descriptor_id).map(|definition| definition.label)
} }
@ -3803,9 +3912,6 @@ fn recovered_locomotive_cost_loco_id(descriptor_id: u32) -> Option<u32> {
if (352..=451).contains(&descriptor_id) { if (352..=451).contains(&descriptor_id) {
return Some(descriptor_id - 351); return Some(descriptor_id - 351);
} }
if (475..=500).contains(&descriptor_id) {
return Some(descriptor_id - 374);
}
None None
} }
@ -3814,11 +3920,14 @@ fn recovered_locomotive_cost_label(descriptor_id: u32) -> Option<&'static str> {
LABELS LABELS
.get_or_init(|| { .get_or_init(|| {
(352..=451) (352..=451)
.chain(475..=500)
.filter_map(|descriptor_id| { .filter_map(|descriptor_id| {
recovered_locomotive_cost_loco_id(descriptor_id).map(|loco_id| { recovered_locomotive_cost_loco_id(descriptor_id).map(|loco_id| {
let label = Box::leak(format!("Locomotive {loco_id} Cost").into_boxed_str()) let label = grounded_locomotive_name(loco_id)
as &'static str; .map(|name| format!("{name} Cost"))
.unwrap_or_else(|| {
format!("Lower-Band Locomotive Cost Slot {loco_id}")
});
let label = Box::leak(label.into_boxed_str()) as &'static str;
(descriptor_id, label) (descriptor_id, label)
}) })
}) })
@ -3826,20 +3935,45 @@ fn recovered_locomotive_cost_label(descriptor_id: u32) -> Option<&'static str> {
}) })
.get(&descriptor_id) .get(&descriptor_id)
.copied() .copied()
.or_else(|| upper_band_locomotive_cost_label(descriptor_id))
}
fn upper_band_locomotive_cost_label(descriptor_id: u32) -> Option<&'static str> {
static LABELS: OnceLock<BTreeMap<u32, &'static str>> = OnceLock::new();
LABELS
.get_or_init(|| {
(475..=502)
.map(|descriptor_id| {
let label = format!("Upper-Band Locomotive Cost Slot {}", descriptor_id - 474);
(
descriptor_id,
Box::leak(label.into_boxed_str()) as &'static str,
)
})
.collect()
})
.get(&descriptor_id)
.copied()
} }
fn recovered_locomotive_cost_descriptor_metadata( fn recovered_locomotive_cost_descriptor_metadata(
descriptor_id: u32, descriptor_id: u32,
) -> Option<RealGroupedEffectDescriptorMetadata> { ) -> Option<RealGroupedEffectDescriptorMetadata> {
recovered_locomotive_cost_label(descriptor_id).map(|label| { recovered_locomotive_cost_label(descriptor_id).map(|label| {
let executable_in_runtime = recovered_locomotive_cost_loco_id(descriptor_id)
.is_some_and(|loco_id| (loco_id as usize) <= GROUNDED_LOCOMOTIVE_PREFIX.len());
RealGroupedEffectDescriptorMetadata { RealGroupedEffectDescriptorMetadata {
descriptor_id, descriptor_id,
label, label,
target_mask_bits: 0x08, target_mask_bits: 0x08,
parameter_family: "locomotive_cost_scalar", parameter_family: "locomotive_cost_scalar",
runtime_key: None, runtime_key: None,
runtime_status: RealGroupedEffectRuntimeStatus::EvidenceBlocked, runtime_status: if executable_in_runtime {
executable_in_runtime: false, RealGroupedEffectRuntimeStatus::Executable
} else {
RealGroupedEffectRuntimeStatus::EvidenceBlocked
},
executable_in_runtime,
} }
}) })
} }
@ -11275,11 +11409,11 @@ mod tests {
let metadata = let metadata =
real_grouped_effect_descriptor_metadata(250).expect("descriptor metadata should exist"); real_grouped_effect_descriptor_metadata(250).expect("descriptor metadata should exist");
assert_eq!(metadata.label, "Unknown Loco Available"); assert_eq!(metadata.label, "Big Boy 4-8-8-4 Availability");
assert_eq!(metadata.target_mask_bits, 0x08); assert_eq!(metadata.target_mask_bits, 0x08);
assert_eq!(metadata.parameter_family, "locomotive_availability_scalar"); assert_eq!(metadata.parameter_family, "locomotive_availability_scalar");
assert_eq!(metadata.runtime_key, None); assert_eq!(metadata.runtime_key, None);
assert!(!metadata.executable_in_runtime); assert!(metadata.executable_in_runtime);
} }
#[test] #[test]
@ -11287,10 +11421,10 @@ mod tests {
let metadata = let metadata =
real_grouped_effect_descriptor_metadata(457).expect("descriptor metadata should exist"); real_grouped_effect_descriptor_metadata(457).expect("descriptor metadata should exist");
assert_eq!(metadata.label, "Unknown Loco Available"); assert_eq!(metadata.label, "Upper-Band Locomotive Availability Slot 1");
assert_eq!(metadata.target_mask_bits, 0x08); assert_eq!(metadata.target_mask_bits, 0x08);
assert_eq!(metadata.parameter_family, "locomotive_availability_scalar"); assert_eq!(metadata.parameter_family, "locomotive_availability_scalar");
assert_eq!(recovered_locomotive_availability_loco_id(457), Some(112)); assert_eq!(recovered_locomotive_availability_loco_id(457), None);
} }
#[test] #[test]
@ -11312,6 +11446,10 @@ mod tests {
.expect("row should parse"); .expect("row should parse");
assert_eq!(row.descriptor_id, 250); assert_eq!(row.descriptor_id, 250);
assert_eq!(
row.descriptor_label.as_deref(),
Some("Big Boy 4-8-8-4 Availability")
);
assert_eq!(row.recovered_locomotive_id, Some(10)); assert_eq!(row.recovered_locomotive_id, Some(10));
assert_eq!( assert_eq!(
row.parameter_family.as_deref(), row.parameter_family.as_deref(),
@ -11360,12 +11498,12 @@ mod tests {
let metadata = let metadata =
real_grouped_effect_descriptor_metadata(352).expect("descriptor metadata should exist"); real_grouped_effect_descriptor_metadata(352).expect("descriptor metadata should exist");
assert_eq!(metadata.label, "Locomotive 1 Cost"); assert_eq!(metadata.label, "2-D-2 Cost");
assert_eq!(metadata.target_mask_bits, 0x08); assert_eq!(metadata.target_mask_bits, 0x08);
assert_eq!(metadata.parameter_family, "locomotive_cost_scalar"); assert_eq!(metadata.parameter_family, "locomotive_cost_scalar");
assert_eq!(metadata.runtime_key, None); assert_eq!(metadata.runtime_key, None);
assert_eq!(recovered_locomotive_cost_loco_id(352), Some(1)); assert_eq!(recovered_locomotive_cost_loco_id(352), Some(1));
assert!(!metadata.executable_in_runtime); assert!(metadata.executable_in_runtime);
} }
#[test] #[test]
@ -11373,9 +11511,9 @@ mod tests {
let metadata = let metadata =
real_grouped_effect_descriptor_metadata(475).expect("descriptor metadata should exist"); real_grouped_effect_descriptor_metadata(475).expect("descriptor metadata should exist");
assert_eq!(metadata.label, "Locomotive 101 Cost"); assert_eq!(metadata.label, "Upper-Band Locomotive Cost Slot 1");
assert_eq!(metadata.parameter_family, "locomotive_cost_scalar"); assert_eq!(metadata.parameter_family, "locomotive_cost_scalar");
assert_eq!(recovered_locomotive_cost_loco_id(475), Some(101)); assert_eq!(recovered_locomotive_cost_loco_id(475), None);
assert!(!metadata.executable_in_runtime); assert!(!metadata.executable_in_runtime);
} }
@ -11410,7 +11548,7 @@ mod tests {
.expect("row should parse"); .expect("row should parse");
assert_eq!(row.descriptor_id, 352); assert_eq!(row.descriptor_id, 352);
assert_eq!(row.descriptor_label.as_deref(), Some("Locomotive 1 Cost")); assert_eq!(row.descriptor_label.as_deref(), Some("2-D-2 Cost"));
assert_eq!(row.recovered_locomotive_id, Some(1)); assert_eq!(row.recovered_locomotive_id, Some(1));
assert_eq!( assert_eq!(
row.parameter_family.as_deref(), row.parameter_family.as_deref(),

View file

@ -777,11 +777,11 @@ mod tests {
locomotive_catalog: vec![ locomotive_catalog: vec![
crate::RuntimeLocomotiveCatalogEntry { crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 10, locomotive_id: 10,
name: "Locomotive 10".to_string(), name: "Big Boy 4-8-8-4".to_string(),
}, },
crate::RuntimeLocomotiveCatalogEntry { crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 112, locomotive_id: 58,
name: "Locomotive 112".to_string(), name: "VL80T".to_string(),
}, },
], ],
cargo_catalog: Vec::new(), cargo_catalog: Vec::new(),
@ -1041,11 +1041,11 @@ mod tests {
locomotive_catalog: vec![ locomotive_catalog: vec![
crate::RuntimeLocomotiveCatalogEntry { crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 10, locomotive_id: 10,
name: "Locomotive 10".to_string(), name: "Big Boy 4-8-8-4".to_string(),
}, },
crate::RuntimeLocomotiveCatalogEntry { crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 112, locomotive_id: 58,
name: "Locomotive 112".to_string(), name: "VL80T".to_string(),
}, },
], ],
cargo_catalog: Vec::new(), cargo_catalog: Vec::new(),
@ -1097,11 +1097,11 @@ mod tests {
locomotive_catalog: vec![ locomotive_catalog: vec![
crate::RuntimeLocomotiveCatalogEntry { crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 10, locomotive_id: 10,
name: "Locomotive 10".to_string(), name: "Big Boy 4-8-8-4".to_string(),
}, },
crate::RuntimeLocomotiveCatalogEntry { crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 112, locomotive_id: 58,
name: "Locomotive 112".to_string(), name: "VL80T".to_string(),
}, },
], ],
cargo_catalog: Vec::new(), cargo_catalog: Vec::new(),

View file

@ -154,14 +154,12 @@ The highest-value next passes are now:
can lower into `world_flag_equals` gates for boolean equality/inequality forms, so real packed can lower into `world_flag_equals` gates for boolean equality/inequality forms, so real packed
rows can gate whole-game effects on existing `world_flags` rows can gate whole-game effects on existing `world_flags`
- the tracked parity save-slice now keeps its remaining non-imported residue as structured - the tracked parity save-slice now keeps its remaining non-imported residue as structured
`real_packed_v1` parity records, with the first captured leftover now identified as the `real_packed_v1` parity records, with the first captured leftover now pinned to the unresolved
locomotives-page `Unknown Loco Available` band and moved onto the explicit upper locomotives-page availability tail and moved onto explicit descriptor parity
`blocked_unmapped_world_descriptor` frontier
- the next recovered locomotives-page descriptor batch is partially executable too: - the next recovered locomotives-page descriptor batch is partially executable too:
descriptors `454..456` (`All Steam/Diesel/Electric Locos Avail.`) now lower through checked-in descriptors `454..456` (`All Steam/Diesel/Electric Locos Avail.`) now lower through checked-in
metadata into keyed `world_flags`, while the wider locomotive availability/cost scalar bands now metadata into keyed `world_flags`, while the wider locomotive availability/cost scalar bands now
split cleanly between executable scalar availability/cost rows and the remaining world-side split cleanly between the grounded lower executable prefix and the explicit unresolved tails
scalar families
- raw `.smp` inspection/export now reconstructs the persisted save-side named locomotive table and - raw `.smp` inspection/export now reconstructs the persisted save-side named locomotive table and
derives a minimal locomotive catalog from its row order, so save-slice documents can carry both derives a minimal locomotive catalog from its row order, so save-slice documents can carry both
`RuntimeState.named_locomotive_availability` and the catalog context needed for descriptor `RuntimeState.named_locomotive_availability` and the catalog context needed for descriptor

View file

@ -115,12 +115,12 @@ Implemented today:
`world_flags` through `world_flag_equals` without fixture-authored placeholder ids `world_flags` through `world_flag_equals` without fixture-authored placeholder ids
- the tracked parity save-slice no longer preserves an opaque `unsupported_framing` record; its - the tracked parity save-slice no longer preserves an opaque `unsupported_framing` record; its
remaining captured residue is now structurally decoded `real_packed_v1` parity state that lands remaining captured residue is now structurally decoded `real_packed_v1` parity state that lands
in existing explicit blocker buckets, with the first leftover now identified as the in existing explicit blocker buckets, with the first leftover now pinned to the unresolved
locomotives-page `Unknown Loco Available` band instead of anonymous descriptor residue upper locomotives-page availability tail instead of anonymous descriptor residue
- the next recovered locomotives-page descriptor band is now partially executable too: - the next recovered locomotives-page descriptor band is now partially executable too:
descriptors `230..240`, `241..351`, `352..451`, `453`, and `457..500` now carry recovered descriptors `230..240`, `241..351`, `352..451`, `453`, `457..474`, and `475..502` now carry
world-side scalar metadata, while descriptors `454..456` (`All Steam/Diesel/Electric Locos recovered world-side scalar metadata, while descriptors `454..456`
Avail.`) now execute as keyed `world_flags` (`All Steam/Diesel/Electric Locos Avail.`) now execute as keyed `world_flags`
- a first-class named locomotive availability runtime surface now exists too: - a first-class named locomotive availability runtime surface now exists too:
save-slice documents can carry the persisted `[world+0x66b6]` name table into save-slice documents can carry the persisted `[world+0x66b6]` name table into
`RuntimeState.named_locomotive_availability`, and imported runtime effects can mutate that map `RuntimeState.named_locomotive_availability`, and imported runtime effects can mutate that map
@ -130,9 +130,10 @@ Implemented today:
raw `.smp` inspection/export now reconstructs the save-side locomotive row family and derives the raw `.smp` inspection/export now reconstructs the save-side locomotive row family and derives the
catalog directly into save-slice documents, so standalone save-slice imports can execute those catalog directly into save-slice documents, so standalone save-slice imports can execute those
rows whenever the save carries enough catalog entries rows whenever the save carries enough catalog entries
- the adjacent locomotive-cost bands `352..451` and `475..500` now import too through the same - the grounded lower locomotive-cost band `352..409` now imports too through the same save-native
save-native or embedded catalog into the event-owned `RuntimeState.named_locomotive_cost` map or embedded catalog into the event-owned `RuntimeState.named_locomotive_cost` map when its
when their scalar payloads are nonnegative scalar payloads are nonnegative; the unresolved lower tail and upper cost tail now stay on
explicit parity instead of synthetic execution
- the remaining recovered scalar world families now execute as well: cargo-production `230..240` - the remaining recovered scalar world families now execute as well: cargo-production `230..240`
rows lower into slot-indexed `cargo_production_overrides`, and territory-access-cost descriptor rows lower into slot-indexed `cargo_production_overrides`, and territory-access-cost descriptor
`453` lowers into `world_restore.territory_access_cost` `453` lowers into `world_restore.territory_access_cost`

View file

@ -42,7 +42,7 @@
{ {
"descriptor_id": 250, "descriptor_id": 250,
"recovered_locomotive_id": 10, "recovered_locomotive_id": 10,
"semantic_preview": "Set Unknown Loco Available to 42" "semantic_preview": "Set Big Boy 4-8-8-4 Availability to 42"
} }
] ]
} }

View file

@ -67,7 +67,7 @@
"group_index": 0, "group_index": 0,
"row_index": 0, "row_index": 0,
"descriptor_id": 250, "descriptor_id": 250,
"descriptor_label": "Unknown Loco Available", "descriptor_label": "Big Boy 4-8-8-4 Availability",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"opcode": 3, "opcode": 3,
@ -80,7 +80,7 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Unknown Loco Available to 42", "semantic_preview": "Set Big Boy 4-8-8-4 Availability to 42",
"recovered_locomotive_id": 10, "recovered_locomotive_id": 10,
"locomotive_name": null, "locomotive_name": null,
"notes": [] "notes": []

View file

@ -20,16 +20,16 @@
"locomotive_catalog": [ "locomotive_catalog": [
{ {
"locomotive_id": 10, "locomotive_id": 10,
"name": "Locomotive 10" "name": "Big Boy 4-8-8-4"
}, },
{ {
"locomotive_id": 112, "locomotive_id": 58,
"name": "Locomotive 112" "name": "VL80T"
} }
], ],
"named_locomotive_availability": { "named_locomotive_availability": {
"Locomotive 10": 0, "Big Boy 4-8-8-4": 0,
"Locomotive 112": 1 "VL80T": 1
}, },
"event_runtime_records": [], "event_runtime_records": [],
"candidate_availability": {}, "candidate_availability": {},

View file

@ -38,8 +38,8 @@
"save_slice.import_projection": "overlay-runtime-restore-v1" "save_slice.import_projection": "overlay-runtime-restore-v1"
}, },
"named_locomotive_availability": { "named_locomotive_availability": {
"Locomotive 10": 42, "Big Boy 4-8-8-4": 42,
"Locomotive 112": 7 "VL80T": 7
}, },
"packed_event_collection": { "packed_event_collection": {
"live_entry_ids": [33], "live_entry_ids": [33],
@ -54,8 +54,8 @@
"recovered_locomotive_id": 10 "recovered_locomotive_id": 10
}, },
{ {
"descriptor_id": 457, "descriptor_id": 298,
"recovered_locomotive_id": 112 "recovered_locomotive_id": 58
} }
] ]
} }
@ -68,12 +68,12 @@
"effects": [ "effects": [
{ {
"kind": "set_named_locomotive_availability_value", "kind": "set_named_locomotive_availability_value",
"name": "Locomotive 10", "name": "Big Boy 4-8-8-4",
"value": 42 "value": 42
}, },
{ {
"kind": "set_named_locomotive_availability_value", "kind": "set_named_locomotive_availability_value",
"name": "Locomotive 112", "name": "VL80T",
"value": 7 "value": 7
} }
] ]

View file

@ -7,7 +7,7 @@
"original_save_sha256": "locomotive-availability-overlay-sample-sha256", "original_save_sha256": "locomotive-availability-overlay-sample-sha256",
"notes": [ "notes": [
"tracked as JSON save-slice document rather than raw .smp", "tracked as JSON save-slice document rather than raw .smp",
"uses synthetic catalog names to prove descriptor-to-id lowering plus overlay-backed scalar resolution" "uses grounded lower-band locomotive ids to prove overlay-backed scalar resolution"
] ]
}, },
"save_slice": { "save_slice": {
@ -67,7 +67,7 @@
"group_index": 0, "group_index": 0,
"row_index": 0, "row_index": 0,
"descriptor_id": 250, "descriptor_id": 250,
"descriptor_label": "Unknown Loco Available", "descriptor_label": "Big Boy 4-8-8-4 Availability",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"opcode": 3, "opcode": 3,
@ -80,7 +80,7 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Unknown Loco Available to 42", "semantic_preview": "Set Big Boy 4-8-8-4 Availability to 42",
"recovered_locomotive_id": 10, "recovered_locomotive_id": 10,
"locomotive_name": null, "locomotive_name": null,
"notes": [] "notes": []
@ -88,8 +88,8 @@
{ {
"group_index": 0, "group_index": 0,
"row_index": 1, "row_index": 1,
"descriptor_id": 457, "descriptor_id": 298,
"descriptor_label": "Unknown Loco Available", "descriptor_label": "VL80T Availability",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"opcode": 3, "opcode": 3,
@ -102,8 +102,8 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Unknown Loco Available to 7", "semantic_preview": "Set VL80T Availability to 7",
"recovered_locomotive_id": 112, "recovered_locomotive_id": 58,
"locomotive_name": null, "locomotive_name": null,
"notes": [] "notes": []
} }

View file

@ -31,8 +31,8 @@
"save_slice.locomotive_catalog_source_kind": "save-direct-locomotive-row-run-ordinal-catalog" "save_slice.locomotive_catalog_source_kind": "save-direct-locomotive-row-run-ordinal-catalog"
}, },
"named_locomotive_availability": { "named_locomotive_availability": {
"Locomotive 10": 42, "Big Boy 4-8-8-4": 42,
"Locomotive 112": 7 "VL80T": 7
}, },
"packed_event_collection": { "packed_event_collection": {
"live_entry_ids": [33], "live_entry_ids": [33],

View file

@ -7,7 +7,7 @@
"original_save_sha256": "locomotive-availability-save-slice-sample-sha256", "original_save_sha256": "locomotive-availability-save-slice-sample-sha256",
"notes": [ "notes": [
"tracked as JSON save-slice document rather than raw .smp", "tracked as JSON save-slice document rather than raw .smp",
"uses embedded save-native locomotive catalog entries to prove standalone save-slice execution for lower-band and upper-band locomotive availability descriptors" "uses embedded save-native locomotive catalog entries to prove standalone save-slice execution for grounded lower-band locomotive availability descriptors"
] ]
}, },
"save_slice": { "save_slice": {
@ -26,8 +26,8 @@
"entries_offset": 31864, "entries_offset": 31864,
"observed_entry_count": 2, "observed_entry_count": 2,
"entries": [ "entries": [
{ "locomotive_id": 10, "name": "Locomotive 10" }, { "locomotive_id": 10, "name": "Big Boy 4-8-8-4" },
{ "locomotive_id": 112, "name": "Locomotive 112" } { "locomotive_id": 58, "name": "VL80T" }
] ]
}, },
"special_conditions_table": null, "special_conditions_table": null,
@ -77,7 +77,7 @@
"group_index": 0, "group_index": 0,
"row_index": 0, "row_index": 0,
"descriptor_id": 250, "descriptor_id": 250,
"descriptor_label": "Unknown Loco Available", "descriptor_label": "Big Boy 4-8-8-4 Availability",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"opcode": 3, "opcode": 3,
@ -90,7 +90,7 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Unknown Loco Available to 42", "semantic_preview": "Set Big Boy 4-8-8-4 Availability to 42",
"recovered_locomotive_id": 10, "recovered_locomotive_id": 10,
"locomotive_name": null, "locomotive_name": null,
"notes": [] "notes": []
@ -98,8 +98,8 @@
{ {
"group_index": 0, "group_index": 0,
"row_index": 1, "row_index": 1,
"descriptor_id": 457, "descriptor_id": 298,
"descriptor_label": "Unknown Loco Available", "descriptor_label": "VL80T Availability",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"opcode": 3, "opcode": 3,
@ -112,8 +112,8 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Unknown Loco Available to 7", "semantic_preview": "Set VL80T Availability to 7",
"recovered_locomotive_id": 112, "recovered_locomotive_id": 58,
"locomotive_name": null, "locomotive_name": null,
"notes": [] "notes": []
} }

View file

@ -20,16 +20,16 @@
"locomotive_catalog": [ "locomotive_catalog": [
{ {
"locomotive_id": 1, "locomotive_id": 1,
"name": "Locomotive 1" "name": "2-D-2"
}, },
{ {
"locomotive_id": 101, "locomotive_id": 58,
"name": "Locomotive 101" "name": "VL80T"
} }
], ],
"named_locomotive_cost": { "named_locomotive_cost": {
"Locomotive 1": 100000, "2-D-2": 100000,
"Locomotive 101": 200000 "VL80T": 200000
}, },
"event_runtime_records": [], "event_runtime_records": [],
"candidate_availability": {}, "candidate_availability": {},

View file

@ -37,8 +37,8 @@
"save_slice.import_projection": "overlay-runtime-restore-v1" "save_slice.import_projection": "overlay-runtime-restore-v1"
}, },
"named_locomotive_cost": { "named_locomotive_cost": {
"Locomotive 1": 250000, "2-D-2": 250000,
"Locomotive 101": 325000 "VL80T": 325000
}, },
"packed_event_collection": { "packed_event_collection": {
"live_entry_ids": [41], "live_entry_ids": [41],
@ -53,8 +53,8 @@
"recovered_locomotive_id": 1 "recovered_locomotive_id": 1
}, },
{ {
"descriptor_id": 475, "descriptor_id": 409,
"recovered_locomotive_id": 101 "recovered_locomotive_id": 58
} }
] ]
} }
@ -67,12 +67,12 @@
"effects": [ "effects": [
{ {
"kind": "set_named_locomotive_cost", "kind": "set_named_locomotive_cost",
"name": "Locomotive 1", "name": "2-D-2",
"value": 250000 "value": 250000
}, },
{ {
"kind": "set_named_locomotive_cost", "kind": "set_named_locomotive_cost",
"name": "Locomotive 101", "name": "VL80T",
"value": 325000 "value": 325000
} }
] ]

View file

@ -7,7 +7,7 @@
"original_save_sha256": "locomotive-cost-overlay-sample-sha256", "original_save_sha256": "locomotive-cost-overlay-sample-sha256",
"notes": [ "notes": [
"tracked as JSON save-slice document rather than raw .smp", "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" "recovered locomotive cost descriptors 352 and 409 import through the ordinary runtime path when overlay catalog context resolves their ids"
] ]
}, },
"save_slice": { "save_slice": {
@ -68,7 +68,7 @@
"group_index": 0, "group_index": 0,
"row_index": 0, "row_index": 0,
"descriptor_id": 352, "descriptor_id": 352,
"descriptor_label": "Locomotive 1 Cost", "descriptor_label": "2-D-2 Cost",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"opcode": 3, "opcode": 3,
@ -81,7 +81,7 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Locomotive 1 Cost to 250000", "semantic_preview": "Set 2-D-2 Cost to 250000",
"recovered_locomotive_id": 1, "recovered_locomotive_id": 1,
"locomotive_name": null, "locomotive_name": null,
"notes": [ "notes": [
@ -91,8 +91,8 @@
{ {
"group_index": 0, "group_index": 0,
"row_index": 1, "row_index": 1,
"descriptor_id": 475, "descriptor_id": 409,
"descriptor_label": "Locomotive 101 Cost", "descriptor_label": "VL80T Cost",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"opcode": 3, "opcode": 3,
@ -105,11 +105,11 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Locomotive 101 Cost to 325000", "semantic_preview": "Set VL80T Cost to 325000",
"recovered_locomotive_id": 101, "recovered_locomotive_id": 58,
"locomotive_name": null, "locomotive_name": null,
"notes": [ "notes": [
"locomotive cost descriptor maps to live locomotive id 101" "locomotive cost descriptor maps to live locomotive id 58"
] ]
} }
], ],
@ -117,12 +117,12 @@
"decoded_actions": [ "decoded_actions": [
{ {
"kind": "set_named_locomotive_cost", "kind": "set_named_locomotive_cost",
"name": "Locomotive 1", "name": "2-D-2",
"value": 250000 "value": 250000
}, },
{ {
"kind": "set_named_locomotive_cost", "kind": "set_named_locomotive_cost",
"name": "Locomotive 101", "name": "VL80T",
"value": 325000 "value": 325000
} }
], ],

View file

@ -30,8 +30,8 @@
"save_slice.locomotive_catalog_source_kind": "save-direct-locomotive-row-run-ordinal-catalog" "save_slice.locomotive_catalog_source_kind": "save-direct-locomotive-row-run-ordinal-catalog"
}, },
"named_locomotive_cost": { "named_locomotive_cost": {
"Locomotive 1": 250000, "2-D-2": 250000,
"Locomotive 101": 325000 "VL80T": 325000
}, },
"packed_event_collection": { "packed_event_collection": {
"live_entry_ids": [41], "live_entry_ids": [41],

View file

@ -7,7 +7,7 @@
"original_save_sha256": "locomotive-cost-save-slice-sample-sha256", "original_save_sha256": "locomotive-cost-save-slice-sample-sha256",
"notes": [ "notes": [
"tracked as JSON save-slice document rather than raw .smp", "tracked as JSON save-slice document rather than raw .smp",
"uses embedded save-native locomotive catalog entries to prove standalone save-slice execution for lower-band and upper-band locomotive cost descriptors" "uses embedded save-native locomotive catalog entries to prove standalone save-slice execution for grounded lower-band locomotive cost descriptors"
] ]
}, },
"save_slice": { "save_slice": {
@ -26,8 +26,8 @@
"entries_offset": 31864, "entries_offset": 31864,
"observed_entry_count": 2, "observed_entry_count": 2,
"entries": [ "entries": [
{ "locomotive_id": 1, "name": "Locomotive 1" }, { "locomotive_id": 1, "name": "2-D-2" },
{ "locomotive_id": 101, "name": "Locomotive 101" } { "locomotive_id": 58, "name": "VL80T" }
] ]
}, },
"special_conditions_table": null, "special_conditions_table": null,
@ -78,7 +78,7 @@
"group_index": 0, "group_index": 0,
"row_index": 0, "row_index": 0,
"descriptor_id": 352, "descriptor_id": 352,
"descriptor_label": "Locomotive 1 Cost", "descriptor_label": "2-D-2 Cost",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"opcode": 3, "opcode": 3,
@ -91,7 +91,7 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Locomotive 1 Cost to 250000", "semantic_preview": "Set 2-D-2 Cost to 250000",
"recovered_locomotive_id": 1, "recovered_locomotive_id": 1,
"locomotive_name": null, "locomotive_name": null,
"notes": [ "notes": [
@ -101,8 +101,8 @@
{ {
"group_index": 0, "group_index": 0,
"row_index": 1, "row_index": 1,
"descriptor_id": 475, "descriptor_id": 409,
"descriptor_label": "Locomotive 101 Cost", "descriptor_label": "VL80T Cost",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"opcode": 3, "opcode": 3,
@ -115,11 +115,11 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Locomotive 101 Cost to 325000", "semantic_preview": "Set VL80T Cost to 325000",
"recovered_locomotive_id": 101, "recovered_locomotive_id": 58,
"locomotive_name": null, "locomotive_name": null,
"notes": [ "notes": [
"locomotive cost descriptor maps to live locomotive id 101" "locomotive cost descriptor maps to live locomotive id 58"
] ]
} }
], ],
@ -127,12 +127,12 @@
"decoded_actions": [ "decoded_actions": [
{ {
"kind": "set_named_locomotive_cost", "kind": "set_named_locomotive_cost",
"name": "Locomotive 1", "name": "2-D-2",
"value": 250000 "value": 250000
}, },
{ {
"kind": "set_named_locomotive_cost", "kind": "set_named_locomotive_cost",
"name": "Locomotive 101", "name": "VL80T",
"value": 325000 "value": 325000
} }
], ],

View file

@ -24,17 +24,18 @@
"packed_event_collection_present": true, "packed_event_collection_present": true,
"packed_event_record_count": 2, "packed_event_record_count": 2,
"packed_event_decoded_record_count": 2, "packed_event_decoded_record_count": 2,
"packed_event_imported_runtime_record_count": 2, "packed_event_imported_runtime_record_count": 1,
"packed_event_parity_only_record_count": 2, "packed_event_parity_only_record_count": 2,
"packed_event_unsupported_record_count": 0, "packed_event_unsupported_record_count": 0,
"packed_event_blocked_missing_locomotive_catalog_context_count": 0, "packed_event_blocked_missing_locomotive_catalog_context_count": 0,
"packed_event_blocked_evidence_blocked_descriptor_count": 1,
"packed_event_blocked_missing_condition_context_count": 0, "packed_event_blocked_missing_condition_context_count": 0,
"packed_event_blocked_territory_condition_scope_count": 0, "packed_event_blocked_territory_condition_scope_count": 0,
"packed_event_blocked_missing_compact_control_count": 0, "packed_event_blocked_missing_compact_control_count": 0,
"packed_event_blocked_unmapped_real_descriptor_count": 0, "packed_event_blocked_unmapped_real_descriptor_count": 0,
"packed_event_blocked_unmapped_world_descriptor_count": 0, "packed_event_blocked_unmapped_world_descriptor_count": 0,
"packed_event_blocked_structural_only_count": 0, "packed_event_blocked_structural_only_count": 0,
"event_runtime_record_count": 2, "event_runtime_record_count": 1,
"total_company_cash": 0 "total_company_cash": 0
}, },
"expected_state_fragment": { "expected_state_fragment": {
@ -50,16 +51,15 @@
{ {
"decode_status": "parity_only", "decode_status": "parity_only",
"payload_family": "real_packed_v1", "payload_family": "real_packed_v1",
"import_outcome": "imported", "import_outcome": "blocked_evidence_blocked_descriptor",
"grouped_effect_rows": [ "grouped_effect_rows": [
{ {
"descriptor_id": 250, "descriptor_id": 457,
"descriptor_label": "Unknown Loco Available", "descriptor_label": "Upper-Band Locomotive Availability Slot 1",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Unknown Loco Available to 42", "semantic_preview": "Set Upper-Band Locomotive Availability Slot 1 to 42",
"recovered_locomotive_id": 10,
"row_shape": "scalar_assignment" "row_shape": "scalar_assignment"
} }
] ]

View file

@ -7,7 +7,7 @@
"original_save_sha256": "parity-sample-sha256", "original_save_sha256": "parity-sample-sha256",
"notes": [ "notes": [
"tracked as JSON save-slice document rather than raw .smp", "tracked as JSON save-slice document rather than raw .smp",
"preserves one recovered scalar locomotive-availability row that now imports through save-native locomotive catalog context and one semantically decoded imported row" "preserves one upper-band locomotive availability tail row as explicit parity and one semantically decoded imported row"
] ]
}, },
"save_slice": { "save_slice": {
@ -25,16 +25,16 @@
"entries_offset": 31864, "entries_offset": 31864,
"observed_entry_count": 10, "observed_entry_count": 10,
"entries": [ "entries": [
{ "locomotive_id": 1, "name": "Locomotive 1" }, { "locomotive_id": 1, "name": "2-D-2" },
{ "locomotive_id": 2, "name": "Locomotive 2" }, { "locomotive_id": 2, "name": "E-88" },
{ "locomotive_id": 3, "name": "Locomotive 3" }, { "locomotive_id": 3, "name": "Adler 2-2-2" },
{ "locomotive_id": 4, "name": "Locomotive 4" }, { "locomotive_id": 4, "name": "USA 103" },
{ "locomotive_id": 5, "name": "Locomotive 5" }, { "locomotive_id": 5, "name": "American 4-4-0" },
{ "locomotive_id": 6, "name": "Locomotive 6" }, { "locomotive_id": 6, "name": "Atlantic 4-4-2" },
{ "locomotive_id": 7, "name": "Locomotive 7" }, { "locomotive_id": 7, "name": "Baldwin 0-6-0" },
{ "locomotive_id": 8, "name": "Locomotive 8" }, { "locomotive_id": 8, "name": "Be 5/7" },
{ "locomotive_id": 9, "name": "Locomotive 9" }, { "locomotive_id": 9, "name": "Beuth 2-2-2" },
{ "locomotive_id": 10, "name": "Locomotive 10" } { "locomotive_id": 10, "name": "Big Boy 4-8-8-4" }
] ]
}, },
"special_conditions_table": null, "special_conditions_table": null,
@ -83,8 +83,8 @@
{ {
"group_index": 0, "group_index": 0,
"row_index": 0, "row_index": 0,
"descriptor_id": 250, "descriptor_id": 457,
"descriptor_label": "Unknown Loco Available", "descriptor_label": "Upper-Band Locomotive Availability Slot 1",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"opcode": 3, "opcode": 3,
@ -97,11 +97,11 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Unknown Loco Available to 42", "semantic_preview": "Set Upper-Band Locomotive Availability Slot 1 to 42",
"recovered_locomotive_id": 10, "recovered_locomotive_id": null,
"locomotive_name": null, "locomotive_name": null,
"notes": [ "notes": [
"recovered locomotive availability descriptor family now imports through save-native locomotive catalog context" "upper-band locomotive availability descriptor tail remains explicit evidence-blocked parity"
] ]
} }
], ],
@ -109,7 +109,7 @@
"executable_import_ready": false, "executable_import_ready": false,
"notes": [ "notes": [
"decoded from grounded real 0x4e9a row framing", "decoded from grounded real 0x4e9a row framing",
"recovered locomotives-page descriptor band is now checked in, and this scalar family now imports through named locomotive availability overrides when the save slice carries locomotive catalog context" "recovered locomotives-page upper-band tail remains explicit evidence-blocked parity while lower grounded ids import through named locomotive availability overrides"
] ]
}, },
{ {

View file

@ -31,7 +31,8 @@
"packed_event_blocked_missing_condition_context_count": 0, "packed_event_blocked_missing_condition_context_count": 0,
"packed_event_blocked_territory_condition_scope_count": 0, "packed_event_blocked_territory_condition_scope_count": 0,
"packed_event_blocked_missing_compact_control_count": 0, "packed_event_blocked_missing_compact_control_count": 0,
"packed_event_blocked_evidence_blocked_descriptor_count": 1, "packed_event_blocked_evidence_blocked_descriptor_count": 0,
"packed_event_blocked_variant_or_scope_blocked_descriptor_count": 1,
"packed_event_blocked_unmapped_real_descriptor_count": 0, "packed_event_blocked_unmapped_real_descriptor_count": 0,
"packed_event_blocked_unmapped_world_descriptor_count": 0, "packed_event_blocked_unmapped_world_descriptor_count": 0,
"packed_event_blocked_structural_only_count": 0, "packed_event_blocked_structural_only_count": 0,
@ -68,15 +69,15 @@
{ {
"decode_status": "parity_only", "decode_status": "parity_only",
"payload_family": "real_packed_v1", "payload_family": "real_packed_v1",
"import_outcome": "blocked_evidence_blocked_descriptor", "import_outcome": "blocked_variant_or_scope_blocked_descriptor",
"grouped_effect_rows": [ "grouped_effect_rows": [
{ {
"descriptor_id": 352, "descriptor_id": 352,
"descriptor_label": "Locomotive 1 Cost", "descriptor_label": "2-D-2 Cost",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Locomotive 1 Cost to -250000", "semantic_preview": "Set 2-D-2 Cost to -250000",
"recovered_locomotive_id": 1, "recovered_locomotive_id": 1,
"row_shape": "scalar_assignment" "row_shape": "scalar_assignment"
} }

View file

@ -143,7 +143,7 @@
"group_index": 0, "group_index": 0,
"row_index": 0, "row_index": 0,
"descriptor_id": 352, "descriptor_id": 352,
"descriptor_label": "Locomotive 1 Cost", "descriptor_label": "2-D-2 Cost",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"opcode": 3, "opcode": 3,
@ -156,7 +156,7 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Locomotive 1 Cost to -250000", "semantic_preview": "Set 2-D-2 Cost to -250000",
"recovered_locomotive_id": 1, "recovered_locomotive_id": 1,
"locomotive_name": null, "locomotive_name": null,
"notes": [ "notes": [

View file

@ -45,7 +45,7 @@
"Big Boy": 42 "Big Boy": 42
}, },
"named_locomotive_cost": { "named_locomotive_cost": {
"Locomotive 1": 250000 "2-D-2": 250000
}, },
"cargo_production_overrides": { "cargo_production_overrides": {
"1": 125, "1": 125,
@ -103,7 +103,7 @@
}, },
{ {
"kind": "named_locomotive_cost_threshold", "kind": "named_locomotive_cost_threshold",
"name": "Locomotive 1", "name": "2-D-2",
"comparator": "eq", "comparator": "eq",
"value": 250000 "value": 250000
}, },

View file

@ -87,7 +87,7 @@
"group_index": 0, "group_index": 0,
"row_index": 0, "row_index": 0,
"descriptor_id": 250, "descriptor_id": 250,
"descriptor_label": "Unknown Loco Available", "descriptor_label": "Big Boy 4-8-8-4 Availability",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"opcode": 3, "opcode": 3,
@ -100,7 +100,7 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Unknown Loco Available to 42", "semantic_preview": "Set Big Boy 4-8-8-4 Availability to 42",
"recovered_locomotive_id": 10, "recovered_locomotive_id": 10,
"locomotive_name": null, "locomotive_name": null,
"notes": [ "notes": [
@ -111,7 +111,7 @@
"group_index": 0, "group_index": 0,
"row_index": 1, "row_index": 1,
"descriptor_id": 352, "descriptor_id": 352,
"descriptor_label": "Locomotive 1 Cost", "descriptor_label": "2-D-2 Cost",
"target_mask_bits": 8, "target_mask_bits": 8,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"opcode": 3, "opcode": 3,
@ -124,7 +124,7 @@
"value_word_0x16": 0, "value_word_0x16": 0,
"row_shape": "scalar_assignment", "row_shape": "scalar_assignment",
"semantic_family": "scalar_assignment", "semantic_family": "scalar_assignment",
"semantic_preview": "Set Locomotive 1 Cost to 250000", "semantic_preview": "Set 2-D-2 Cost to 250000",
"recovered_locomotive_id": 1, "recovered_locomotive_id": 1,
"locomotive_name": null, "locomotive_name": null,
"notes": [ "notes": [
@ -257,7 +257,7 @@
}, },
{ {
"kind": "set_named_locomotive_cost", "kind": "set_named_locomotive_cost",
"name": "Locomotive 1", "name": "2-D-2",
"value": 250000 "value": 250000
}, },
{ {
@ -333,11 +333,11 @@
"raw_condition_id": 2423, "raw_condition_id": 2423,
"subtype": 4, "subtype": 4,
"flag_bytes": [144, 208, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "flag_bytes": [144, 208, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"candidate_name": "Locomotive 1", "candidate_name": "2-D-2",
"comparator": "eq", "comparator": "eq",
"metric": "Named Locomotive Cost: Locomotive 1", "metric": "Named Locomotive Cost: 2-D-2",
"semantic_family": "world_scalar_threshold", "semantic_family": "world_scalar_threshold",
"semantic_preview": "Test Named Locomotive Cost: Locomotive 1 == 250000", "semantic_preview": "Test Named Locomotive Cost: 2-D-2 == 250000",
"requires_candidate_name_binding": false, "requires_candidate_name_binding": false,
"notes": [ "notes": [
"checked-in world-scalar condition metadata sample" "checked-in world-scalar condition metadata sample"
@ -489,7 +489,7 @@
}, },
{ {
"kind": "named_locomotive_cost_threshold", "kind": "named_locomotive_cost_threshold",
"name": "Locomotive 1", "name": "2-D-2",
"comparator": "eq", "comparator": "eq",
"value": 250000 "value": 250000
}, },

View file

@ -5,6 +5,67 @@ import argparse
import json import json
from pathlib import Path from pathlib import Path
GROUNDED_LOCOMOTIVE_PREFIX = {
1: "2-D-2",
2: "E-88",
3: "Adler 2-2-2",
4: "USA 103",
5: "American 4-4-0",
6: "Atlantic 4-4-2",
7: "Baldwin 0-6-0",
8: "Be 5/7",
9: "Beuth 2-2-2",
10: "Big Boy 4-8-8-4",
11: "C55 Deltic",
12: "Camelback 0-6-0",
13: "Challenger 4-6-6-4",
14: "Class 01 4-6-2",
15: "Class 103",
16: "Class 132",
17: "Class 500 4-6-0",
18: "Class 9100",
19: "Class EF 66",
20: "Class 6E",
21: "Consolidation 2-8-0",
22: "Crampton 4-2-0",
23: "DD 080-X",
24: "DD40AX",
25: "Duke Class 4-4-0",
26: "E18",
27: "E428",
28: "Brenner E412",
29: "E60CP",
30: "Eight Wheeler 4-4-0",
31: "EP-2 Bipolar",
32: "ET22",
33: "F3",
34: "Fairlie 0-6-6-0",
35: "Firefly 2-2-2",
36: "FP45",
37: "Ge 6/6 Crocodile",
38: "GG1",
39: "GP7",
40: "H10 2-8-2",
41: "HST 125",
42: "Kriegslok 2-10-0",
43: "Mallard 4-6-2",
44: "Norris 4-2-0",
45: "Northern 4-8-4",
46: "Orca NX462",
47: "Pacific 4-6-2",
48: "Planet 2-2-0",
49: "Re 6/6",
50: "Red Devil 4-8-4",
51: "S3 4-4-0",
52: "NA-90D",
53: "Shay (2-Truck)",
54: "Shinkansen Series 0",
55: "Stirling 4-2-2",
56: "Trans-Euro",
57: "V200",
58: "VL80T",
}
def normalize_world_scalar_key(label: str) -> str: def normalize_world_scalar_key(label: str) -> str:
parts: list[str] = [] parts: list[str] = []
@ -20,6 +81,20 @@ def normalize_world_scalar_key(label: str) -> str:
return "world." + "_".join(parts) return "world." + "_".join(parts)
def locomotive_availability_label(locomotive_id: int) -> str:
name = GROUNDED_LOCOMOTIVE_PREFIX.get(locomotive_id)
if name is not None:
return f"{name} Availability"
return f"Lower-Band Locomotive Availability Slot {locomotive_id}"
def locomotive_cost_label(locomotive_id: int) -> str:
name = GROUNDED_LOCOMOTIVE_PREFIX.get(locomotive_id)
if name is not None:
return f"{name} Cost"
return f"Lower-Band Locomotive Cost Slot {locomotive_id}"
def classify(row: dict[str, object]) -> dict[str, object]: def classify(row: dict[str, object]) -> dict[str, object]:
descriptor_id = int(row["descriptor_id"]) descriptor_id = int(row["descriptor_id"])
label = str(row["label"]) label = str(row["label"])
@ -71,10 +146,26 @@ def classify(row: dict[str, object]) -> dict[str, object]:
parameter_family = "cargo_production_scalar" parameter_family = "cargo_production_scalar"
runtime_status = "executable" runtime_status = "executable"
executable_in_runtime = True executable_in_runtime = True
elif 241 <= descriptor_id <= 351 or 457 <= descriptor_id <= 474: elif 241 <= descriptor_id <= 351:
parameter_family = "locomotive_availability_scalar" parameter_family = "locomotive_availability_scalar"
elif 352 <= descriptor_id <= 451 or 475 <= descriptor_id <= 502: locomotive_id = descriptor_id - 240
label = locomotive_availability_label(locomotive_id)
if locomotive_id <= len(GROUNDED_LOCOMOTIVE_PREFIX):
runtime_status = "executable"
executable_in_runtime = True
elif 457 <= descriptor_id <= 474:
parameter_family = "locomotive_availability_scalar"
label = f"Upper-Band Locomotive Availability Slot {descriptor_id - 456}"
elif 352 <= descriptor_id <= 451:
parameter_family = "locomotive_cost_scalar" parameter_family = "locomotive_cost_scalar"
locomotive_id = descriptor_id - 351
label = locomotive_cost_label(locomotive_id)
if locomotive_id <= len(GROUNDED_LOCOMOTIVE_PREFIX):
runtime_status = "executable"
executable_in_runtime = True
elif 475 <= descriptor_id <= 502:
parameter_family = "locomotive_cost_scalar"
label = f"Upper-Band Locomotive Cost Slot {descriptor_id - 474}"
elif descriptor_id == 453: elif descriptor_id == 453:
parameter_family = "territory_access_cost_scalar" parameter_family = "territory_access_cost_scalar"
runtime_status = "executable" runtime_status = "executable"