Close EventEffects semantic descriptor frontier

This commit is contained in:
Jan Petykiewicz 2026-04-16 20:20:41 -07:00
commit 51a7bbd756
18 changed files with 5432 additions and 175 deletions

View file

@ -31,12 +31,21 @@ company-targeted and chairman-targeted descriptor and condition batches can exec
save-slice fixtures without overlay snapshots when that context is present; raw `.gms` inspection
still does not reconstruct those company/chairman collections automatically. A checked-in
`EventEffects` export now exists too in
`artifacts/exports/rt3-1.06/event-effects-table.json`, and the first recovered governance
descriptor tranche now imports through the generic company-governance scalar effect surface:
`artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer now
exists beside it in `artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json`. Recovered
descriptor rows now land on explicit semantic frontier buckets such as
`blocked_shell_owned_descriptor`, `blocked_evidence_blocked_descriptor`, and
`blocked_variant_or_scope_blocked_descriptor` instead of generic anonymous descriptor residue. The
first recovered governance descriptor tranche now imports through the generic
company-governance scalar effect surface:
descriptor `56` `Credit Rating` and descriptor `57` `Prime Rate` execute from ordinary real packed
rows, while adjacent recovered finance/control-transfer descriptors such as `55` `Stock Prices`
and `58` `Merger Premium` now land on explicit shell-owned parity instead of anonymous unmapped
descriptor residue. The first grounded
descriptor residue. The recovered whole-game scalar economy/performance strip `59..104` now has a
bounded runtime landing surface too: representative descriptors import into
`RuntimeState.world_scalar_overrides` through stable normalized keys such as
`world.build_stations_cost`, `world.track_maintenance_cost`, `world.all_engine_speeds`, and
`world.hotel_revenue`. The first grounded
condition-side unlock now exists for negative-sentinel `raw_condition_id = -1` company scopes, and
the first ordinary nonnegative condition batch now executes too: numeric-threshold company
finance, company track, aggregate territory track, and company-territory track rows can import
@ -82,9 +91,8 @@ same world-scalar runtime surfaces too: named locomotive availability thresholds
locomotive cost thresholds, named cargo-production slot thresholds, aggregate cargo-production
thresholds, factory/farm-mine/other cargo-production thresholds, limited-track-building-amount
thresholds, and territory-access-cost thresholds all gate imported runtime records through the
same service path. Explicit
unmapped world-condition and world-descriptor
frontier buckets still remain where current checked-in metadata stops, and
same service path. Explicit unmapped world-condition frontier buckets still remain where current
checked-in metadata stops, and
`blocked_missing_locomotive_catalog_context` is now reserved for intentionally incomplete save-side
catalog context instead of the normal save-slice path. Cargo slot identity and class metadata are
now save-native too: the recipe-book probe lowers into `RuntimeState.cargo_catalog`, so save-slice

File diff suppressed because it is too large Load diff

View file

@ -4476,6 +4476,9 @@ mod tests {
let world_scalar_executable_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
"../../fixtures/runtime/packed-event-world-scalar-executable-save-slice-fixture.json",
);
let world_scalar_override_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
"../../fixtures/runtime/packed-event-world-scalar-override-save-slice-fixture.json",
);
let world_scalar_condition_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
"../../fixtures/runtime/packed-event-world-scalar-condition-save-slice-fixture.json",
);
@ -4585,6 +4588,8 @@ mod tests {
.expect("save-slice-backed recovered scalar-band parity fixture should summarize");
run_runtime_summarize_fixture(&world_scalar_executable_fixture)
.expect("save-slice-backed executable world-scalar fixture should summarize");
run_runtime_summarize_fixture(&world_scalar_override_fixture)
.expect("save-slice-backed world-scalar override fixture should summarize");
run_runtime_summarize_fixture(&world_scalar_condition_fixture)
.expect("save-slice-backed executable world-scalar condition fixture should summarize");
run_runtime_summarize_fixture(&world_scalar_condition_parity_fixture)

View file

@ -190,6 +190,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
},
@ -377,6 +378,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
},

View file

@ -148,6 +148,10 @@ pub struct ExpectedRuntimeSummary {
#[serde(default)]
pub packed_event_blocked_shell_owned_descriptor_count: Option<usize>,
#[serde(default)]
pub packed_event_blocked_evidence_blocked_descriptor_count: Option<usize>,
#[serde(default)]
pub packed_event_blocked_variant_or_scope_blocked_descriptor_count: Option<usize>,
#[serde(default)]
pub packed_event_blocked_unmapped_real_descriptor_count: Option<usize>,
#[serde(default)]
pub packed_event_blocked_unmapped_world_descriptor_count: Option<usize>,
@ -184,6 +188,8 @@ pub struct ExpectedRuntimeSummary {
#[serde(default)]
pub cargo_production_override_count: Option<usize>,
#[serde(default)]
pub world_scalar_override_count: Option<usize>,
#[serde(default)]
pub special_condition_count: Option<usize>,
#[serde(default)]
pub enabled_special_condition_count: Option<usize>,
@ -761,6 +767,22 @@ impl ExpectedRuntimeSummary {
));
}
}
if let Some(count) = self.packed_event_blocked_evidence_blocked_descriptor_count {
if actual.packed_event_blocked_evidence_blocked_descriptor_count != count {
mismatches.push(format!(
"packed_event_blocked_evidence_blocked_descriptor_count mismatch: expected {count}, got {}",
actual.packed_event_blocked_evidence_blocked_descriptor_count
));
}
}
if let Some(count) = self.packed_event_blocked_variant_or_scope_blocked_descriptor_count {
if actual.packed_event_blocked_variant_or_scope_blocked_descriptor_count != count {
mismatches.push(format!(
"packed_event_blocked_variant_or_scope_blocked_descriptor_count mismatch: expected {count}, got {}",
actual.packed_event_blocked_variant_or_scope_blocked_descriptor_count
));
}
}
if let Some(count) = self.packed_event_blocked_unmapped_real_descriptor_count {
if actual.packed_event_blocked_unmapped_real_descriptor_count != count {
mismatches.push(format!(
@ -905,6 +927,14 @@ impl ExpectedRuntimeSummary {
));
}
}
if let Some(count) = self.world_scalar_override_count {
if actual.world_scalar_override_count != count {
mismatches.push(format!(
"world_scalar_override_count mismatch: expected {count}, got {}",
actual.world_scalar_override_count
));
}
}
if let Some(count) = self.special_condition_count {
if actual.special_condition_count != count {
mismatches.push(format!(

View file

@ -31,6 +31,7 @@ pub const REQUIRED_EXPORTS: &[&str] = &[
"artifacts/exports/rt3-1.06/pending-template-store-record-kinds.csv",
"artifacts/exports/rt3-1.06/pending-template-store-management.md",
"artifacts/exports/rt3-1.06/event-effects-table.json",
"artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json",
];
pub const REQUIRED_ATLAS_HEADINGS: &[&str] = &[

View file

@ -106,6 +106,7 @@ struct SaveSliceProjection {
cargo_catalog: Option<Vec<RuntimeCargoCatalogEntry>>,
named_locomotive_cost: BTreeMap<String, u32>,
cargo_production_overrides: BTreeMap<u32, u32>,
world_scalar_overrides: BTreeMap<String, i64>,
special_conditions: BTreeMap<String, u32>,
}
@ -277,6 +278,7 @@ pub fn project_save_slice_to_runtime_state_import(
named_locomotive_availability: projection.named_locomotive_availability,
named_locomotive_cost: projection.named_locomotive_cost,
cargo_production_overrides: projection.cargo_production_overrides,
world_scalar_overrides: projection.world_scalar_overrides,
special_conditions: projection.special_conditions,
service_state: RuntimeServiceState::default(),
};
@ -364,6 +366,7 @@ pub fn project_save_slice_overlay_to_runtime_state_import(
named_locomotive_availability: projection.named_locomotive_availability,
named_locomotive_cost: base_state.named_locomotive_cost.clone(),
cargo_production_overrides: base_state.cargo_production_overrides.clone(),
world_scalar_overrides: base_state.world_scalar_overrides.clone(),
special_conditions: projection.special_conditions,
service_state: base_state.service_state.clone(),
};
@ -872,6 +875,7 @@ fn project_save_slice_components(
let named_locomotive_cost = BTreeMap::new();
let cargo_production_overrides = BTreeMap::new();
let world_scalar_overrides = BTreeMap::new();
let mut packed_event_context = company_context.clone();
if has_company_projection {
@ -948,6 +952,7 @@ fn project_save_slice_components(
cargo_catalog,
named_locomotive_cost,
cargo_production_overrides,
world_scalar_overrides,
special_conditions,
})
}
@ -1316,6 +1321,10 @@ fn lower_contextual_real_grouped_effects(
if real_grouped_row_is_unsupported_chairman_target_scope(row) {
return Err(ImportBlocker::ChairmanTargetScope);
}
if let Some(effect) = lower_contextual_world_scalar_override_effect(row)? {
effects.push(effect);
continue;
}
if let Some(effect) = lower_contextual_cargo_production_effect(row)? {
effects.push(effect);
continue;
@ -1347,6 +1356,28 @@ fn lower_contextual_real_grouped_effects(
Ok(effects)
}
fn lower_contextual_world_scalar_override_effect(
row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> Result<Option<RuntimeEffect>, ImportBlocker> {
if row.parameter_family.as_deref() != Some("world_scalar_override") {
return Ok(None);
}
if row.row_shape != "scalar_assignment" {
return Ok(None);
}
let Some(key) = row
.descriptor_label
.as_deref()
.map(crate::smp::runtime_world_scalar_key_from_label)
else {
return Ok(None);
};
Ok(Some(RuntimeEffect::SetWorldScalarOverride {
key,
value: i64::from(row.raw_scalar_value),
}))
}
fn lower_contextual_locomotive_availability_effect(
row: &SmpLoadedPackedEventGroupedEffectRowSummary,
company_context: &ImportRuntimeContext,
@ -1564,6 +1595,12 @@ fn lower_condition_targets_in_effect(
key: key.clone(),
value: *value,
},
RuntimeEffect::SetWorldScalarOverride { key, value } => {
RuntimeEffect::SetWorldScalarOverride {
key: key.clone(),
value: *value,
}
}
RuntimeEffect::SetLimitedTrackBuildingAmount { value } => {
RuntimeEffect::SetLimitedTrackBuildingAmount { value: *value }
}
@ -2346,6 +2383,12 @@ fn smp_runtime_effect_to_runtime_effect(
value: *value,
})
}
RuntimeEffect::SetWorldScalarOverride { key, value } => {
Ok(RuntimeEffect::SetWorldScalarOverride {
key: key.clone(),
value: *value,
})
}
RuntimeEffect::SetCargoProductionSlot { slot, value } => {
Ok(RuntimeEffect::SetCargoProductionSlot {
slot: *slot,
@ -2690,6 +2733,27 @@ fn determine_packed_event_import_outcome(
{
return "blocked_shell_owned_descriptor".to_string();
}
if record
.grouped_effect_rows
.iter()
.any(real_grouped_row_is_evidence_blocked_descriptor_family)
{
return "blocked_evidence_blocked_descriptor".to_string();
}
if record
.grouped_effect_rows
.iter()
.any(real_grouped_row_is_variant_or_scope_blocked_descriptor_family)
{
return "blocked_variant_or_scope_blocked_descriptor".to_string();
}
if record
.grouped_effect_rows
.iter()
.any(real_grouped_row_is_unsupported_executable_descriptor_variant)
{
return "blocked_variant_or_scope_blocked_descriptor".to_string();
}
if record
.grouped_effect_rows
.iter()
@ -2788,9 +2852,47 @@ fn real_grouped_row_is_world_state_family(
fn real_grouped_row_is_shell_owned_descriptor_family(
row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> bool {
real_grouped_row_has_runtime_status(row, "shell_owned")
}
fn real_grouped_row_is_evidence_blocked_descriptor_family(
row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> bool {
real_grouped_row_has_runtime_status(row, "evidence_blocked")
}
fn real_grouped_row_is_variant_or_scope_blocked_descriptor_family(
row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> bool {
real_grouped_row_has_runtime_status(row, "variant_or_scope_blocked")
}
fn real_grouped_row_has_runtime_status(
row: &SmpLoadedPackedEventGroupedEffectRowSummary,
status: &str,
) -> bool {
crate::smp::grouped_effect_descriptor_runtime_status_name(row.descriptor_id)
.is_some_and(|status| status == "shell_owned")
.is_some_and(|runtime_status| runtime_status == status)
}
fn real_grouped_row_is_unsupported_executable_descriptor_variant(
row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> bool {
if !real_grouped_row_has_runtime_status(row, "executable") {
return false;
}
match row.descriptor_id {
1 => !(row.opcode == 8 && row.row_shape == "multivalue_scalar"),
2 => !(row.opcode == 8 && row.row_shape == "multivalue_scalar"),
8 | 108 | 109 | 122 => row.row_shape != "scalar_assignment",
13 | 14 => !(row.row_shape == "bool_toggle" && row.raw_scalar_value != 0),
56 | 57 => row.row_shape != "scalar_assignment",
_ => {
row.parameter_family.as_deref() == Some("world_scalar_override")
&& row.row_shape != "scalar_assignment"
}
}
}
fn packed_record_company_target_import_blocker(
@ -2980,6 +3082,7 @@ fn runtime_effect_uses_condition_true_company(effect: &RuntimeEffect) -> bool {
.iter()
.any(runtime_effect_uses_condition_true_company),
RuntimeEffect::SetWorldFlag { .. }
| RuntimeEffect::SetWorldScalarOverride { .. }
| RuntimeEffect::SetLimitedTrackBuildingAmount { .. }
| RuntimeEffect::SetEconomicStatusCode { .. }
| RuntimeEffect::SetPlayerCash { .. }
@ -3093,6 +3196,7 @@ fn runtime_effect_company_target_import_blocker(
runtime_effect_company_target_import_blocker(nested, company_context)
}),
RuntimeEffect::SetWorldFlag { .. }
| RuntimeEffect::SetWorldScalarOverride { .. }
| RuntimeEffect::SetLimitedTrackBuildingAmount { .. }
| RuntimeEffect::SetEconomicStatusCode { .. }
| RuntimeEffect::SetCandidateAvailability { .. }
@ -3449,6 +3553,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
}
@ -6634,6 +6739,7 @@ mod tests {
]),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -7039,6 +7145,7 @@ mod tests {
("Locomotive 101".to_string(), 200000),
]),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -7211,7 +7318,7 @@ mod tests {
.packed_event_collection
.as_ref()
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
Some("blocked_unmapped_world_descriptor")
Some("blocked_evidence_blocked_descriptor")
);
}
@ -7439,6 +7546,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -8207,7 +8315,7 @@ mod tests {
.packed_event_collection
.as_ref()
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
Some("blocked_unmapped_real_descriptor")
Some("blocked_variant_or_scope_blocked_descriptor")
);
}
@ -8295,7 +8403,7 @@ mod tests {
.packed_event_collection
.as_ref()
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
Some("blocked_unmapped_real_descriptor")
Some("blocked_variant_or_scope_blocked_descriptor")
);
}
@ -9713,6 +9821,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -9886,6 +9995,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -11574,6 +11684,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState {
periodic_boundary_calls: 9,
@ -11758,6 +11869,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
},

View file

@ -110,6 +110,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
},

View file

@ -467,6 +467,10 @@ pub enum RuntimeEffect {
slot: u32,
value: u32,
},
SetWorldScalarOverride {
key: String,
value: i64,
},
SetTerritoryAccessCost {
value: u32,
},
@ -842,6 +846,8 @@ pub struct RuntimeState {
#[serde(default)]
pub cargo_production_overrides: BTreeMap<u32, u32>,
#[serde(default)]
pub world_scalar_overrides: BTreeMap<String, i64>,
#[serde(default)]
pub special_conditions: BTreeMap<String, u32>,
#[serde(default)]
pub service_state: RuntimeServiceState,
@ -1489,6 +1495,11 @@ impl RuntimeState {
));
}
}
for key in self.world_scalar_overrides.keys() {
if key.trim().is_empty() {
return Err("world_scalar_overrides 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());
@ -1512,6 +1523,11 @@ fn validate_runtime_effect(
return Err("key must not be empty".to_string());
}
}
RuntimeEffect::SetWorldScalarOverride { key, .. } => {
if key.trim().is_empty() {
return Err("key must not be empty".to_string());
}
}
RuntimeEffect::SetLimitedTrackBuildingAmount { .. }
| RuntimeEffect::SetEconomicStatusCode { .. } => {}
RuntimeEffect::SetCompanyCash { target, .. }
@ -1901,6 +1917,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -1964,6 +1981,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2031,6 +2049,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2108,6 +2127,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2210,6 +2230,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2264,6 +2285,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2318,6 +2340,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2389,6 +2412,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2450,6 +2474,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2515,6 +2540,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2576,6 +2602,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2643,6 +2670,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2704,6 +2732,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2765,6 +2794,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2819,6 +2849,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -2883,6 +2914,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};

View file

@ -141,20 +141,19 @@ enum RealGroupedEffectRuntimeStatus {
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
struct CheckedInEventEffectsTableArtifact {
descriptors: Vec<CheckedInEventEffectDescriptorRow>,
struct CheckedInEventEffectsSemanticCatalogArtifact {
descriptors: Vec<CheckedInEventEffectSemanticRow>,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
struct CheckedInEventEffectDescriptorRow {
struct CheckedInEventEffectSemanticRow {
descriptor_id: u32,
selector_order: f32,
target_mask_bits: u8,
label_id: u32,
label: String,
signature_byte_0x63: u8,
signature_byte_0x64: u8,
signature_hex_0x63_0x6d: String,
target_mask_bits: u8,
parameter_family: String,
runtime_key: Option<String>,
runtime_status: String,
executable_in_runtime: bool,
}
const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetadata; 12] = [
@ -281,10 +280,10 @@ fn checked_in_event_effect_descriptor_rows()
-> &'static BTreeMap<u32, RealGroupedEffectDescriptorMetadata> {
static ROWS: OnceLock<BTreeMap<u32, RealGroupedEffectDescriptorMetadata>> = OnceLock::new();
ROWS.get_or_init(|| {
let artifact: CheckedInEventEffectsTableArtifact = serde_json::from_str(include_str!(
"../../../artifacts/exports/rt3-1.06/event-effects-table.json"
))
.expect("checked-in event-effects artifact should parse");
let artifact: CheckedInEventEffectsSemanticCatalogArtifact = serde_json::from_str(
include_str!("../../../artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json"),
)
.expect("checked-in event-effects semantic catalog should parse");
artifact
.descriptors
.into_iter()
@ -299,14 +298,20 @@ fn checked_in_event_effect_descriptor_rows()
}
fn checked_in_event_effect_descriptor_metadata(
row: CheckedInEventEffectDescriptorRow,
row: CheckedInEventEffectSemanticRow,
) -> RealGroupedEffectDescriptorMetadata {
let label = Box::leak(row.label.clone().into_boxed_str()) as &'static str;
let (parameter_family, runtime_key, runtime_status, executable_in_runtime) =
classify_checked_in_event_effect_descriptor(&row, label);
debug_assert!(!row.signature_hex_0x63_0x6d.is_empty());
debug_assert!(row.label_id <= u16::MAX as u32);
let _ = row.selector_order;
let parameter_family = Box::leak(row.parameter_family.into_boxed_str()) as &'static str;
let runtime_key = row
.runtime_key
.map(|key| Box::leak(key.into_boxed_str()) as &'static str);
let runtime_status = match row.runtime_status.as_str() {
"executable" => RealGroupedEffectRuntimeStatus::Executable,
"shell_owned" => RealGroupedEffectRuntimeStatus::ShellOwned,
"evidence_blocked" => RealGroupedEffectRuntimeStatus::EvidenceBlocked,
"variant_or_scope_blocked" => RealGroupedEffectRuntimeStatus::VariantOrScopeBlocked,
other => panic!("unknown checked-in event-effect runtime status {other}"),
};
RealGroupedEffectDescriptorMetadata {
descriptor_id: row.descriptor_id,
label,
@ -314,141 +319,10 @@ fn checked_in_event_effect_descriptor_metadata(
parameter_family,
runtime_key,
runtime_status,
executable_in_runtime,
executable_in_runtime: row.executable_in_runtime,
}
}
fn classify_checked_in_event_effect_descriptor(
row: &CheckedInEventEffectDescriptorRow,
label: &'static str,
) -> (
&'static str,
Option<&'static str>,
RealGroupedEffectRuntimeStatus,
bool,
) {
let descriptor_id = row.descriptor_id;
match descriptor_id {
4..=7 => {
return (
"scenario_outcome_shell_action",
None,
RealGroupedEffectRuntimeStatus::ShellOwned,
false,
);
}
10 | 11 | 12 | 23 => {
return (
"company_confiscation_variant",
None,
RealGroupedEffectRuntimeStatus::VariantOrScopeBlocked,
false,
);
}
17..=21 => {
return (
"company_or_territory_destruction_variant",
None,
RealGroupedEffectRuntimeStatus::ShellOwned,
false,
);
}
24 => {
return (
"control_transfer_shell_action",
None,
RealGroupedEffectRuntimeStatus::ShellOwned,
false,
);
}
56 => {
return (
"company_governance_scalar",
None,
RealGroupedEffectRuntimeStatus::Executable,
true,
);
}
57 => {
return (
"company_governance_scalar",
None,
RealGroupedEffectRuntimeStatus::Executable,
true,
);
}
58 => {
return (
"company_finance_shell_scalar",
None,
RealGroupedEffectRuntimeStatus::ShellOwned,
false,
);
}
_ => {}
}
if row.signature_byte_0x63 == 0 && row.signature_byte_0x64 == 0x8f {
return (
"runtime_variable_scalar",
None,
RealGroupedEffectRuntimeStatus::EvidenceBlocked,
false,
);
}
if row.target_mask_bits == 0x0b && label == "Stock Prices" {
return (
"company_finance_shell_scalar",
None,
RealGroupedEffectRuntimeStatus::ShellOwned,
false,
);
}
if label.contains("Cost")
|| label.contains("Revenue")
|| label.contains("Speed")
|| label.contains("Reliability")
|| label.contains("Acceleration")
|| label.contains("Pulling Power")
|| label.contains("Usage Rate")
|| label.contains("Maintenance")
{
return (
"world_scalar_economy_or_locomotive",
None,
RealGroupedEffectRuntimeStatus::EvidenceBlocked,
false,
);
}
if label.contains("Earthquake") || label.contains("Storm") {
return (
"world_disaster_scalar",
None,
RealGroupedEffectRuntimeStatus::EvidenceBlocked,
false,
);
}
if label.contains("Add Building") {
return (
"world_building_spawn",
None,
RealGroupedEffectRuntimeStatus::ShellOwned,
false,
);
}
(
"recovered_effect_table_descriptor",
None,
RealGroupedEffectRuntimeStatus::EvidenceBlocked,
false,
)
}
pub(crate) fn grouped_effect_descriptor_runtime_status_name(
descriptor_id: u32,
) -> Option<&'static str> {
@ -4101,7 +3975,27 @@ fn runtime_world_flag_key(
})
}
fn runtime_world_flag_key_from_label(label: &str) -> String {
pub(crate) fn runtime_world_flag_key_from_label(label: &str) -> String {
normalize_runtime_world_key(label)
}
fn runtime_world_scalar_key(
descriptor_metadata: RealGroupedEffectDescriptorMetadata,
) -> Option<String> {
descriptor_metadata
.runtime_key
.map(str::to_string)
.or_else(|| {
(descriptor_metadata.parameter_family == "world_scalar_override")
.then(|| normalize_runtime_world_key(descriptor_metadata.label))
})
}
pub(crate) fn runtime_world_scalar_key_from_label(label: &str) -> String {
normalize_runtime_world_key(label)
}
fn normalize_runtime_world_key(label: &str) -> String {
let mut key = String::with_capacity(label.len() + 6);
key.push_str("world.");
let mut last_was_underscore = false;
@ -4140,6 +4034,9 @@ fn derive_real_grouped_target_subject(
if row.parameter_family.as_deref() == Some("company_governance_scalar") {
return Some(RealGroupedTargetSubject::Company);
}
if row.parameter_family.as_deref() == Some("world_scalar_override") {
return Some(RealGroupedTargetSubject::WholeGame);
}
match row.target_mask_bits {
Some(0x08) => Some(RealGroupedTargetSubject::WholeGame),
Some(0x01) => Some(RealGroupedTargetSubject::Company),
@ -4353,6 +4250,16 @@ fn decode_real_grouped_effect_action(
});
}
if descriptor_metadata.executable_in_runtime
&& descriptor_metadata.parameter_family == "world_scalar_override"
&& row.row_shape == "scalar_assignment"
{
return Some(RuntimeEffect::SetWorldScalarOverride {
key: runtime_world_scalar_key(descriptor_metadata)?,
value: i64::from(row.raw_scalar_value),
});
}
if descriptor_metadata.executable_in_runtime
&& descriptor_metadata.parameter_family == "cargo_production_scalar"
&& row.row_shape == "scalar_assignment"
@ -4657,6 +4564,7 @@ fn runtime_effect_supported_for_save_import(effect: &RuntimeEffect) -> bool {
| RuntimeEffect::SetNamedLocomotiveAvailabilityValue { .. }
| RuntimeEffect::SetNamedLocomotiveCost { .. }
| RuntimeEffect::SetCargoProductionSlot { .. }
| RuntimeEffect::SetWorldScalarOverride { .. }
| RuntimeEffect::SetTerritoryAccessCost { .. }
| RuntimeEffect::SetSpecialCondition { .. }
| RuntimeEffect::ConfiscateCompanyAssets { .. }

View file

@ -313,6 +313,9 @@ fn apply_runtime_effects(
RuntimeEffect::SetWorldFlag { key, value } => {
state.world_flags.insert(key.clone(), *value);
}
RuntimeEffect::SetWorldScalarOverride { key, value } => {
state.world_scalar_overrides.insert(key.clone(), *value);
}
RuntimeEffect::SetLimitedTrackBuildingAmount { value } => {
state.world_restore.limited_track_building_amount = Some(*value);
}
@ -1559,6 +1562,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
}
@ -1912,6 +1916,10 @@ mod tests {
has_fired: false,
conditions: Vec::new(),
effects: vec![
RuntimeEffect::SetWorldScalarOverride {
key: "world.build_stations_cost".to_string(),
value: 350000,
},
RuntimeEffect::SetCargoProductionSlot {
slot: 1,
value: 125,
@ -1928,9 +1936,15 @@ mod tests {
)
.expect("world scalar override effects should succeed");
assert_eq!(
state
.world_scalar_overrides
.get("world.build_stations_cost"),
Some(&350000)
);
assert_eq!(state.cargo_production_overrides.get(&1), Some(&125));
assert_eq!(state.world_restore.territory_access_cost, Some(750000));
assert_eq!(result.service_events[0].applied_effect_count, 2);
assert_eq!(result.service_events[0].applied_effect_count, 3);
}
#[test]

View file

@ -71,6 +71,8 @@ pub struct RuntimeSummary {
pub packed_event_blocked_unmapped_world_condition_count: usize,
pub packed_event_blocked_missing_compact_control_count: usize,
pub packed_event_blocked_shell_owned_descriptor_count: usize,
pub packed_event_blocked_evidence_blocked_descriptor_count: usize,
pub packed_event_blocked_variant_or_scope_blocked_descriptor_count: usize,
pub packed_event_blocked_unmapped_real_descriptor_count: usize,
pub packed_event_blocked_unmapped_world_descriptor_count: usize,
pub packed_event_blocked_territory_access_variant_count: usize,
@ -89,6 +91,7 @@ pub struct RuntimeSummary {
pub zero_named_locomotive_availability_count: usize,
pub named_locomotive_cost_count: usize,
pub cargo_production_override_count: usize,
pub world_scalar_override_count: usize,
pub special_condition_count: usize,
pub enabled_special_condition_count: usize,
pub save_profile_kind: Option<String>,
@ -511,6 +514,34 @@ impl RuntimeSummary {
.count()
})
.unwrap_or(0),
packed_event_blocked_evidence_blocked_descriptor_count: state
.packed_event_collection
.as_ref()
.map(|summary| {
summary
.records
.iter()
.filter(|record| {
record.import_outcome.as_deref()
== Some("blocked_evidence_blocked_descriptor")
})
.count()
})
.unwrap_or(0),
packed_event_blocked_variant_or_scope_blocked_descriptor_count: state
.packed_event_collection
.as_ref()
.map(|summary| {
summary
.records
.iter()
.filter(|record| {
record.import_outcome.as_deref()
== Some("blocked_variant_or_scope_blocked_descriptor")
})
.count()
})
.unwrap_or(0),
packed_event_blocked_unmapped_real_descriptor_count: state
.packed_event_collection
.as_ref()
@ -676,6 +707,7 @@ impl RuntimeSummary {
.count(),
named_locomotive_cost_count: state.named_locomotive_cost.len(),
cargo_production_override_count: state.cargo_production_overrides.len(),
world_scalar_override_count: state.world_scalar_overrides.len(),
special_condition_count: state.special_conditions.len(),
enabled_special_condition_count: state
.special_conditions
@ -903,6 +935,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -1019,6 +1052,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -1072,6 +1106,7 @@ mod tests {
]),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -1117,6 +1152,7 @@ mod tests {
("GP7".to_string(), 175000),
]),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -1160,6 +1196,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::from([(1, 125), (2, 250)]),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -1265,6 +1302,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -1348,6 +1386,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
@ -1427,6 +1466,7 @@ mod tests {
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};

View file

@ -98,12 +98,21 @@ The highest-value next passes are now:
can execute from standalone save-slice fixtures without overlay snapshots when that context is
present; raw `.gms` inspection/export still does not reconstruct those company/chairman surfaces
- a checked-in `EventEffects` export now exists at
`artifacts/exports/rt3-1.06/event-effects-table.json`, and the first recovered governance
descriptor tranche now executes through the generic company-governance scalar effect surface:
descriptor `56` `Credit Rating` and descriptor `57` `Prime Rate`
`artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer
now exists at `artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json`
- recovered descriptor rows now land on explicit semantic frontier buckets such as
`blocked_shell_owned_descriptor`, `blocked_evidence_blocked_descriptor`, and
`blocked_variant_or_scope_blocked_descriptor` instead of generic unmapped-descriptor residue
- the first recovered governance descriptor tranche now executes through the generic
company-governance scalar effect surface: descriptor `56` `Credit Rating` and descriptor `57`
`Prime Rate`
- adjacent recovered finance/control-transfer descriptors such as `55` `Stock Prices` and `58`
`Merger Premium` now land on explicit shell-owned descriptor parity instead of generic unmapped
descriptor residue
- the recovered whole-game scalar economy/performance strip `59..104` now has a bounded runtime
landing surface too: representative rows execute into `RuntimeState.world_scalar_overrides`
through stable normalized keys such as `world.build_stations_cost` and
`world.track_maintenance_cost`
- widen real packed-event executable coverage descriptor by descriptor after identity, target mask,
and normalized effect semantics are all grounded, not just after row framing is parsed
- the first grounded condition-side unlock now exists for negative-sentinel `raw_condition_id = -1`
@ -203,6 +212,14 @@ python3 tools/py/extract_event_effects.py \
artifacts/exports/rt3-1.06/event-effects-table.json
```
Regenerate the checked-in `EventEffects` semantic catalog with:
```bash
python3 tools/py/build_event_effect_semantic_catalog.py \
artifacts/exports/rt3-1.06/event-effects-table.json \
artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json
```
That default export now walks two roots:
- `entry:0x005a313b`

View file

@ -61,13 +61,20 @@ Implemented today:
without overlay snapshots when the checked-in documents include that context, while raw `.gms`
inspection/export still leaves those company/chairman surfaces absent
- a checked-in `EventEffects` export now exists too at
`artifacts/exports/rt3-1.06/event-effects-table.json`, and the first recovered
company-governance descriptor tranche now executes through the generic
`artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer
now exists at `artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json`
- recovered descriptor rows now classify into explicit semantic frontier buckets such as
`blocked_shell_owned_descriptor`, `blocked_evidence_blocked_descriptor`, and
`blocked_variant_or_scope_blocked_descriptor` instead of generic unmapped-descriptor buckets
- the first recovered company-governance descriptor tranche now executes through the generic
`SetCompanyGovernanceScalar` surface: descriptor `56` `Credit Rating` and descriptor `57`
`Prime Rate` now import through ordinary company target lowering
- adjacent recovered finance/control-transfer descriptors such as `55` `Stock Prices` and `58`
`Merger Premium` now land on explicit shell-owned descriptor parity instead of generic unmapped
descriptor buckets
- the recovered whole-game scalar economy/performance strip `59..104` now has a bounded runtime
landing surface too: representative descriptors import as `SetWorldScalarOverride` and land in
`RuntimeState.world_scalar_overrides`
- a minimal event-owned train surface and an opaque economic-status lane now exist in runtime
state, and real descriptors `8` = `Economic Status`, `9` = `Confiscate All`, and `15` =
`Retire Train` now import and execute through the ordinary runtime path when overlay context
@ -138,11 +145,11 @@ Implemented today:
That means the next implementation work is still breadth, not bootstrap. The current descriptor
frontier is no longer anonymous id recovery; it is the remaining recovered-but-nonexecutable
families from the checked-in effect table, especially broader company/world scalar bands and the
shell-owned finance/control-transfer rows that still need final classification or bounded runtime
landing surfaces. Raw save reconstruction for company/chairman context is still a later tranche
once stronger evidence exists. Richer runtime ownership should still be added only where a later
descriptor or condition family needs more than the current event-owned roster.
families from the checked-in semantic catalog, especially cargo-price, add-building, and other
descriptor clusters that now have explicit shell-owned or evidence-blocked status but not yet a
bounded executable landing surface. Raw save reconstruction for company/chairman context is still a
later tranche once stronger evidence exists. Richer runtime ownership should still be added only
where a later descriptor or condition family needs more than the current event-owned roster.
## Why This Boundary

View file

@ -31,8 +31,9 @@
"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_evidence_blocked_descriptor_count": 1,
"packed_event_blocked_unmapped_real_descriptor_count": 0,
"packed_event_blocked_unmapped_world_descriptor_count": 1,
"packed_event_blocked_unmapped_world_descriptor_count": 0,
"packed_event_blocked_structural_only_count": 0,
"event_runtime_record_count": 2,
"cargo_production_override_count": 0,
@ -67,7 +68,7 @@
{
"decode_status": "parity_only",
"payload_family": "real_packed_v1",
"import_outcome": "blocked_unmapped_world_descriptor",
"import_outcome": "blocked_evidence_blocked_descriptor",
"grouped_effect_rows": [
{
"descriptor_id": 352,

View file

@ -0,0 +1,70 @@
{
"format_version": 1,
"fixture_id": "packed-event-world-scalar-override-save-slice-fixture",
"source": {
"kind": "captured-runtime",
"description": "Fixture proving representative 59..104 world scalar descriptors execute through the bounded world_scalar_overrides runtime surface."
},
"state_save_slice_path": "packed-event-world-scalar-override-save-slice.json",
"commands": [
{
"kind": "service_trigger_kind",
"trigger_kind": 7
}
],
"expected_summary": {
"calendar_projection_source": "default-1830-placeholder",
"calendar_projection_is_placeholder": true,
"packed_event_collection_present": true,
"packed_event_record_count": 1,
"packed_event_decoded_record_count": 1,
"packed_event_imported_runtime_record_count": 1,
"event_runtime_record_count": 1,
"world_scalar_override_count": 4,
"total_event_record_service_count": 1,
"total_trigger_dispatch_count": 1
},
"expected_state_fragment": {
"world_scalar_overrides": {
"world.build_stations_cost": 350000,
"world.track_maintenance_cost": 22000,
"world.all_engine_speeds": 115,
"world.hotel_revenue": 175
},
"packed_event_collection": {
"records": [
{
"import_outcome": "imported"
}
]
},
"event_runtime_records": [
{
"record_id": 61,
"service_count": 1,
"effects": [
{
"kind": "set_world_scalar_override",
"key": "world.build_stations_cost",
"value": 350000
},
{
"kind": "set_world_scalar_override",
"key": "world.track_maintenance_cost",
"value": 22000
},
{
"kind": "set_world_scalar_override",
"key": "world.all_engine_speeds",
"value": 115
},
{
"kind": "set_world_scalar_override",
"key": "world.hotel_revenue",
"value": 175
}
]
}
]
}
}

View file

@ -0,0 +1,198 @@
{
"format_version": 1,
"save_slice_id": "packed-event-world-scalar-override-save-slice",
"source": {
"description": "Tracked save-slice document proving the checked-in scalar economy/performance descriptor band imports into world_scalar_overrides.",
"original_save_filename": "captured-world-scalar-override.gms",
"original_save_sha256": "world-scalar-override-sample-sha256",
"notes": [
"tracked as JSON save-slice document rather than raw .smp",
"pins representative executable descriptors from the recovered 59..104 economy/performance band"
]
},
"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,
"locomotive_catalog": null,
"cargo_catalog": 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": 31360,
"records_tag_offset": 31616,
"close_tag_offset": 32128,
"packed_state_version": 1001,
"packed_state_version_hex": "0x000003e9",
"live_id_bound": 61,
"live_record_count": 1,
"live_entry_ids": [61],
"decoded_record_count": 1,
"imported_runtime_record_count": 0,
"records": [
{
"record_index": 0,
"live_entry_id": 61,
"payload_offset": 31648,
"payload_len": 176,
"decode_status": "executable",
"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": [],
"grouped_effect_row_counts": [4, 0, 0, 0],
"grouped_effect_rows": [
{
"group_index": 0,
"row_index": 0,
"descriptor_id": 59,
"descriptor_label": "Build Stations Cost",
"target_mask_bits": 15,
"parameter_family": "world_scalar_override",
"opcode": 3,
"raw_scalar_value": 350000,
"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 Build Stations Cost to 350000",
"recovered_locomotive_id": null,
"locomotive_name": null,
"notes": [
"descriptor recovered from checked-in EventEffects semantic catalog"
]
},
{
"group_index": 0,
"row_index": 1,
"descriptor_id": 66,
"descriptor_label": "Track Maintenance Cost",
"target_mask_bits": 11,
"parameter_family": "world_scalar_override",
"opcode": 3,
"raw_scalar_value": 22000,
"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 Track Maintenance Cost to 22000",
"recovered_locomotive_id": null,
"locomotive_name": null,
"notes": [
"descriptor recovered from checked-in EventEffects semantic catalog"
]
},
{
"group_index": 0,
"row_index": 2,
"descriptor_id": 81,
"descriptor_label": "All Engine Speeds",
"target_mask_bits": 15,
"parameter_family": "world_scalar_override",
"opcode": 3,
"raw_scalar_value": 115,
"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 All Engine Speeds to 115",
"recovered_locomotive_id": null,
"locomotive_name": null,
"notes": [
"descriptor recovered from checked-in EventEffects semantic catalog"
]
},
{
"group_index": 0,
"row_index": 3,
"descriptor_id": 102,
"descriptor_label": "Hotel Revenue",
"target_mask_bits": 15,
"parameter_family": "world_scalar_override",
"opcode": 3,
"raw_scalar_value": 175,
"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 Hotel Revenue to 175",
"recovered_locomotive_id": null,
"locomotive_name": null,
"notes": [
"descriptor recovered from checked-in EventEffects semantic catalog"
]
}
],
"decoded_actions": [
{
"kind": "set_world_scalar_override",
"key": "world.build_stations_cost",
"value": 350000
},
{
"kind": "set_world_scalar_override",
"key": "world.track_maintenance_cost",
"value": 22000
},
{
"kind": "set_world_scalar_override",
"key": "world.all_engine_speeds",
"value": 115
},
{
"kind": "set_world_scalar_override",
"key": "world.hotel_revenue",
"value": 175
}
],
"executable_import_ready": true,
"notes": [
"representative whole-game scalar economy/performance descriptors execute through world_scalar_overrides"
]
}
]
},
"notes": [
"world scalar override executable sample"
]
}
}

View file

@ -0,0 +1,124 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
from pathlib import Path
def normalize_world_scalar_key(label: str) -> str:
parts: list[str] = []
current = []
for ch in label:
if ch.isalnum():
current.append(ch.lower())
elif current:
parts.append("".join(current))
current = []
if current:
parts.append("".join(current))
return "world." + "_".join(parts)
def classify(row: dict[str, object]) -> dict[str, object]:
descriptor_id = int(row["descriptor_id"])
label = str(row["label"])
signature_byte_0x63 = int(row["signature_byte_0x63"])
signature_byte_0x64 = int(row["signature_byte_0x64"])
parameter_family = "recovered_effect_table_descriptor"
runtime_key = None
runtime_status = "evidence_blocked"
executable_in_runtime = False
if 4 <= descriptor_id <= 7:
parameter_family = "scenario_outcome_shell_action"
runtime_status = "shell_owned"
elif descriptor_id in {10, 11, 12, 23}:
parameter_family = "company_confiscation_variant"
runtime_status = "variant_or_scope_blocked"
elif 17 <= descriptor_id <= 21:
parameter_family = "company_or_territory_destruction_variant"
runtime_status = "shell_owned"
elif descriptor_id == 24:
parameter_family = "control_transfer_shell_action"
runtime_status = "shell_owned"
elif descriptor_id in {55, 58}:
parameter_family = "company_finance_shell_scalar"
runtime_status = "shell_owned"
elif descriptor_id in {56, 57}:
parameter_family = "company_governance_scalar"
runtime_status = "executable"
executable_in_runtime = True
elif 59 <= descriptor_id <= 104:
parameter_family = "world_scalar_override"
runtime_key = normalize_world_scalar_key(label)
runtime_status = "executable"
executable_in_runtime = True
elif 105 <= descriptor_id <= 176:
parameter_family = "cargo_price_scalar"
elif 177 <= descriptor_id <= 240:
parameter_family = "cargo_production_scalar"
elif 241 <= descriptor_id <= 351 or 457 <= descriptor_id <= 474:
parameter_family = "locomotive_availability_scalar"
elif 352 <= descriptor_id <= 451 or 475 <= descriptor_id <= 502:
parameter_family = "locomotive_cost_scalar"
elif descriptor_id == 453:
parameter_family = "territory_access_cost_scalar"
runtime_status = "executable"
executable_in_runtime = True
elif descriptor_id == 454:
parameter_family = "world_flag_toggle"
runtime_key = "world.all_steam_locos_available"
runtime_status = "executable"
executable_in_runtime = True
elif descriptor_id == 455:
parameter_family = "world_flag_toggle"
runtime_key = "world.all_diesel_locos_available"
runtime_status = "executable"
executable_in_runtime = True
elif descriptor_id == 456:
parameter_family = "world_flag_toggle"
runtime_key = "world.all_electric_locos_available"
runtime_status = "executable"
executable_in_runtime = True
elif 503 <= descriptor_id <= 519:
parameter_family = "world_building_spawn"
runtime_status = "shell_owned"
elif signature_byte_0x63 == 0 and signature_byte_0x64 == 0x8F:
parameter_family = "runtime_variable_scalar"
elif "Earthquake" in label or "Storm" in label:
parameter_family = "world_disaster_scalar"
return {
"descriptor_id": descriptor_id,
"label": label,
"target_mask_bits": int(row["target_mask_bits"]),
"parameter_family": parameter_family,
"runtime_key": runtime_key,
"runtime_status": runtime_status,
"executable_in_runtime": executable_in_runtime,
}
def main() -> None:
parser = argparse.ArgumentParser(
description="Build a checked-in semantic catalog over the raw RT3 EventEffects table."
)
parser.add_argument("raw_table", type=Path)
parser.add_argument("out", type=Path)
args = parser.parse_args()
raw_artifact = json.loads(args.raw_table.read_text(encoding="utf-8"))
descriptors = [classify(row) for row in raw_artifact["descriptors"]]
artifact = {
"descriptor_count": len(descriptors),
"raw_table_binary_sha256": raw_artifact.get("binary_sha256"),
"semantic_catalog_version": 1,
"descriptors": descriptors,
}
args.out.write_text(json.dumps(artifact, indent=2) + "\n", encoding="utf-8")
if __name__ == "__main__":
main()