From 43c76adbf0f3c8e64dfc481d38e8ab8811312609 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Thu, 16 Apr 2026 09:20:49 -0700 Subject: [PATCH] Execute world track build limit descriptor --- README.md | 8 +- crates/rrt-fixtures/src/schema.rs | 10 ++ crates/rrt-runtime/src/import.rs | 110 +++++++++++++++++ crates/rrt-runtime/src/runtime.rs | 9 +- crates/rrt-runtime/src/smp.rs | 114 ++++++++++++++++++ crates/rrt-runtime/src/step.rs | 29 +++++ crates/rrt-runtime/src/summary.rs | 4 + docs/README.md | 5 +- docs/runtime-rehost-plan.md | 6 +- ...-track-build-limit-save-slice-fixture.json | 44 +++++++ ...nt-world-track-build-limit-save-slice.json | 109 +++++++++++++++++ 11 files changed, 439 insertions(+), 9 deletions(-) create mode 100644 fixtures/runtime/packed-event-world-track-build-limit-save-slice-fixture.json create mode 100644 fixtures/runtime/packed-event-world-track-build-limit-save-slice.json diff --git a/README.md b/README.md index 7173b15..61ba1a9 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,11 @@ whole-game descriptor metadata now drives the first real world-side effect batch special-condition and candidate-availability setters import natively, and descriptor `110` `Disable Stock Buying and Selling` now lowers into the keyed runtime flag `world.disable_stock_buying_and_selling`. The recovered whole-game toggle batch is broader now -too: descriptors `111..138`, excluding the scalar `Limited Track Building Amount` slot, also lower -into keyed `world_flags` for finance/trading, construction, and governance restrictions. Explicit -the late recovered special-condition toggles now execute too where current evidence is equally +too: descriptors `111..138`, with descriptor `122` `Limited Track Building Amount` now landing in +the bounded `world_restore.limited_track_building_amount` scalar and the remaining boolean lanes +lowering into keyed `world_flags`, cover finance/trading, construction, and governance +restrictions. Explicit the late recovered special-condition toggles now execute too where current +evidence is equally strong: `Use Bio-Accelerator Cars`, `Disable Cargo Economy`, `Disable Train Crashes`, `Disable Train Crashes AND Breakdowns`, and `AI Ignore Territories At Startup`. Whole-game condition decode is broader now too: checked-in world-flag condition ids can lower into `world_flag_equals` gates diff --git a/crates/rrt-fixtures/src/schema.rs b/crates/rrt-fixtures/src/schema.rs index db6872d..9c98557 100644 --- a/crates/rrt-fixtures/src/schema.rs +++ b/crates/rrt-fixtures/src/schema.rs @@ -54,6 +54,8 @@ pub struct ExpectedRuntimeSummary { #[serde(default)] pub world_restore_ai_ignore_territories_at_startup_enabled: Option, #[serde(default)] + pub world_restore_limited_track_building_amount: Option, + #[serde(default)] pub world_restore_economic_status_code: Option, #[serde(default)] pub world_restore_absolute_counter_restore_kind: Option, @@ -345,6 +347,14 @@ impl ExpectedRuntimeSummary { )); } } + if let Some(value) = self.world_restore_limited_track_building_amount { + if actual.world_restore_limited_track_building_amount != Some(value) { + mismatches.push(format!( + "world_restore_limited_track_building_amount mismatch: expected {value}, got {:?}", + actual.world_restore_limited_track_building_amount + )); + } + } if let Some(code) = self.world_restore_economic_status_code { if actual.world_restore_economic_status_code != Some(code) { mismatches.push(format!( diff --git a/crates/rrt-runtime/src/import.rs b/crates/rrt-runtime/src/import.rs index 3fb0132..fb36654 100644 --- a/crates/rrt-runtime/src/import.rs +++ b/crates/rrt-runtime/src/import.rs @@ -532,6 +532,7 @@ fn project_save_slice_components( disable_train_crashes_enabled: special_condition_enabled(32), disable_train_crashes_and_breakdowns_enabled: special_condition_enabled(33), ai_ignore_territories_at_startup_enabled: special_condition_enabled(34), + limited_track_building_amount: None, economic_status_code: None, absolute_counter_restore_kind: Some( "mode-adjusted-selected-year-lane".to_string(), @@ -1034,6 +1035,9 @@ fn lower_condition_targets_in_effect( key: key.clone(), value: *value, }, + RuntimeEffect::SetLimitedTrackBuildingAmount { value } => { + RuntimeEffect::SetLimitedTrackBuildingAmount { value: *value } + } RuntimeEffect::SetEconomicStatusCode { value } => { RuntimeEffect::SetEconomicStatusCode { value: *value } } @@ -1358,6 +1362,9 @@ fn smp_runtime_effect_to_runtime_effect( key: key.clone(), value: *value, }), + RuntimeEffect::SetLimitedTrackBuildingAmount { value } => { + Ok(RuntimeEffect::SetLimitedTrackBuildingAmount { value: *value }) + } RuntimeEffect::SetEconomicStatusCode { value } => { Ok(RuntimeEffect::SetEconomicStatusCode { value: *value }) } @@ -2099,6 +2106,7 @@ fn runtime_effect_uses_condition_true_company(effect: &RuntimeEffect) -> bool { .iter() .any(runtime_effect_uses_condition_true_company), RuntimeEffect::SetWorldFlag { .. } + | RuntimeEffect::SetLimitedTrackBuildingAmount { .. } | RuntimeEffect::SetEconomicStatusCode { .. } | RuntimeEffect::SetPlayerCash { .. } | RuntimeEffect::DeactivatePlayer { .. } @@ -2177,6 +2185,7 @@ fn runtime_effect_company_target_import_blocker( runtime_effect_company_target_import_blocker(nested, company_context) }), RuntimeEffect::SetWorldFlag { .. } + | RuntimeEffect::SetLimitedTrackBuildingAmount { .. } | RuntimeEffect::SetEconomicStatusCode { .. } | RuntimeEffect::SetCandidateAvailability { .. } | RuntimeEffect::SetSpecialCondition { .. } @@ -2799,6 +2808,32 @@ mod tests { } } + fn real_limited_track_building_amount_row( + value: i32, + ) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary { + crate::SmpLoadedPackedEventGroupedEffectRowSummary { + group_index: 0, + row_index: 0, + descriptor_id: 122, + descriptor_label: Some("Limited Track Building Amount".to_string()), + target_mask_bits: Some(0x08), + parameter_family: Some("world_track_build_limit_scalar".to_string()), + opcode: 3, + raw_scalar_value: value, + 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".to_string(), + semantic_family: Some("scalar_assignment".to_string()), + semantic_preview: Some(format!("Set Limited Track Building Amount to {value}")), + locomotive_name: None, + notes: vec![], + } + } + fn real_special_condition_row( value: i32, ) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary { @@ -5462,6 +5497,81 @@ mod tests { assert_eq!(import.state.world_restore.economic_status_code, Some(2)); } + #[test] + fn imports_real_limited_track_building_amount_descriptor_into_executable_runtime_record() { + let save_slice = SmpLoadedSaveSlice { + file_extension_hint: Some("gms".to_string()), + container_profile_family: Some("rt3-classic-save-container-v1".to_string()), + mechanism_family: "classic-save-rehydrate-v1".to_string(), + mechanism_confidence: "grounded".to_string(), + trailer_family: None, + bridge_family: None, + profile: None, + candidate_availability_table: None, + special_conditions_table: None, + event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { + source_kind: "packed-event-runtime-collection".to_string(), + mechanism_family: "classic-save-rehydrate-v1".to_string(), + mechanism_confidence: "grounded".to_string(), + container_profile_family: Some("rt3-classic-save-container-v1".to_string()), + metadata_tag_offset: 0x7100, + records_tag_offset: 0x7200, + close_tag_offset: 0x7600, + packed_state_version: 0x3e9, + packed_state_version_hex: "0x000003e9".to_string(), + live_id_bound: 52, + live_record_count: 1, + live_entry_ids: vec![52], + decoded_record_count: 1, + imported_runtime_record_count: 0, + records: vec![crate::SmpLoadedPackedEventRecordSummary { + record_index: 0, + live_entry_id: 52, + payload_offset: Some(0x7202), + payload_len: Some(120), + decode_status: "parity_only".to_string(), + payload_family: "real_packed_v1".to_string(), + trigger_kind: Some(6), + active: None, + marks_collection_dirty: None, + one_shot: Some(false), + compact_control: Some(real_compact_control()), + text_bands: packed_text_bands(), + standalone_condition_row_count: 0, + standalone_condition_rows: vec![], + negative_sentinel_scope: None, + grouped_effect_row_counts: vec![1, 0, 0, 0], + grouped_effect_rows: vec![real_limited_track_building_amount_row(18)], + decoded_conditions: Vec::new(), + decoded_actions: vec![RuntimeEffect::SetLimitedTrackBuildingAmount { + value: 18, + }], + executable_import_ready: true, + notes: vec!["decoded from grounded real 0x4e9a row framing".to_string()], + }], + }), + notes: vec![], + }; + + let mut import = project_save_slice_to_runtime_state_import( + &save_slice, + "real-limited-track-building-amount-save-slice", + None, + ) + .expect("save-slice import should project"); + + execute_step_command( + &mut import.state, + &StepCommand::ServiceTriggerKind { trigger_kind: 6 }, + ) + .expect("real limited-track-building-amount descriptor should execute"); + + assert_eq!( + import.state.world_restore.limited_track_building_amount, + Some(18) + ); + } + #[test] fn overlays_real_special_condition_descriptor_into_executable_runtime_record() { let base_state = state(); diff --git a/crates/rrt-runtime/src/runtime.rs b/crates/rrt-runtime/src/runtime.rs index 69d5cc0..71cc2cc 100644 --- a/crates/rrt-runtime/src/runtime.rs +++ b/crates/rrt-runtime/src/runtime.rs @@ -255,6 +255,9 @@ pub enum RuntimeEffect { key: String, value: bool, }, + SetLimitedTrackBuildingAmount { + value: i32, + }, SetEconomicStatusCode { value: i32, }, @@ -591,6 +594,8 @@ pub struct RuntimeWorldRestoreState { #[serde(default)] pub ai_ignore_territories_at_startup_enabled: Option, #[serde(default)] + pub limited_track_building_amount: Option, + #[serde(default)] pub economic_status_code: Option, #[serde(default)] pub absolute_counter_restore_kind: Option, @@ -1136,7 +1141,8 @@ fn validate_runtime_effect( return Err("key must not be empty".to_string()); } } - RuntimeEffect::SetEconomicStatusCode { .. } => {} + RuntimeEffect::SetLimitedTrackBuildingAmount { .. } + | RuntimeEffect::SetEconomicStatusCode { .. } => {} RuntimeEffect::SetCompanyCash { target, .. } | RuntimeEffect::ConfiscateCompanyAssets { target } | RuntimeEffect::DeactivateCompany { target } @@ -1437,6 +1443,7 @@ mod tests { disable_train_crashes_enabled: Some(false), disable_train_crashes_and_breakdowns_enabled: Some(false), ai_ignore_territories_at_startup_enabled: Some(false), + limited_track_building_amount: None, economic_status_code: None, absolute_counter_restore_kind: Some( "mode-adjusted-selected-year-lane".to_string(), diff --git a/crates/rrt-runtime/src/smp.rs b/crates/rrt-runtime/src/smp.rs index 1eb84e4..af91f11 100644 --- a/crates/rrt-runtime/src/smp.rs +++ b/crates/rrt-runtime/src/smp.rs @@ -2785,9 +2785,31 @@ fn real_grouped_effect_descriptor_metadata( .iter() .copied() .find(|metadata| metadata.descriptor_id == descriptor_id) + .or_else(|| special_condition_world_scalar_descriptor_metadata(descriptor_id)) .or_else(|| special_condition_world_toggle_descriptor_metadata(descriptor_id)) } +fn special_condition_world_scalar_descriptor_metadata( + descriptor_id: u32, +) -> Option { + let slot_index = descriptor_id.checked_sub(110)? as usize; + if slot_index != 12 { + return None; + } + let definition = KNOWN_SPECIAL_CONDITION_DEFINITIONS.get(slot_index)?; + if definition.hidden { + return None; + } + Some(RealGroupedEffectDescriptorMetadata { + descriptor_id, + label: definition.label, + target_mask_bits: 0x08, + parameter_family: "world_track_build_limit_scalar", + runtime_key: None, + executable_in_runtime: true, + }) +} + fn special_condition_world_toggle_descriptor_metadata( descriptor_id: u32, ) -> Option { @@ -3012,6 +3034,15 @@ fn decode_real_grouped_effect_action( }); } + if descriptor_metadata.executable_in_runtime + && descriptor_metadata.descriptor_id == 122 + && row.row_shape == "scalar_assignment" + { + return Some(RuntimeEffect::SetLimitedTrackBuildingAmount { + value: row.raw_scalar_value, + }); + } + if descriptor_metadata.executable_in_runtime && descriptor_metadata.parameter_family == "world_flag_toggle" && row.row_shape == "bool_toggle" @@ -3256,6 +3287,7 @@ fn parse_optional_u16_len_prefixed_string( fn runtime_effect_supported_for_save_import(effect: &RuntimeEffect) -> bool { match effect { RuntimeEffect::SetWorldFlag { .. } + | RuntimeEffect::SetLimitedTrackBuildingAmount { .. } | RuntimeEffect::SetEconomicStatusCode { .. } | RuntimeEffect::SetCandidateAvailability { .. } | RuntimeEffect::SetSpecialCondition { .. } @@ -9030,6 +9062,17 @@ mod tests { assert!(metadata.executable_in_runtime); } + #[test] + fn looks_up_limited_track_building_amount_descriptor_metadata() { + let metadata = + real_grouped_effect_descriptor_metadata(122).expect("descriptor metadata should exist"); + + assert_eq!(metadata.label, "Limited Track Building Amount"); + assert_eq!(metadata.parameter_family, "world_track_build_limit_scalar"); + assert_eq!(metadata.runtime_key, None); + assert!(metadata.executable_in_runtime); + } + #[test] fn looks_up_recovered_late_world_toggle_descriptor_metadata() { let metadata = @@ -9276,6 +9319,77 @@ mod tests { assert!(summary.records[0].executable_import_ready); } + #[test] + fn decodes_limited_track_building_amount_descriptor_family() { + let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec { + descriptor_id: 122, + opcode: 3, + raw_scalar_value: 18, + value_byte_0x09: 0, + value_dword_0x0d: 0, + value_byte_0x11: 0, + value_byte_0x12: 0, + value_word_0x14: 0, + value_word_0x16: 0, + locomotive_name: None, + }); + let group0_rows = vec![grouped_row]; + let record_body = build_real_event_record( + [b"World", b"", b"", b"", b"", b""], + Some(RealCompactControlSpec { + mode_byte_0x7ef: 6, + 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], + }), + &[], + [&group0_rows, &[], &[], &[]], + ); + + let mut bytes = Vec::new(); + bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes()); + bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes()); + let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + for word in header_words { + bytes.extend_from_slice(&word.to_le_bytes()); + } + bytes.extend_from_slice(&[0x00, 0x00]); + bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]); + bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes()); + bytes.extend_from_slice(&record_body); + bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes()); + + let report = inspect_smp_bytes(&bytes); + let summary = report + .event_runtime_collection_summary + .as_ref() + .expect("event runtime collection summary should parse"); + + assert_eq!( + summary.records[0].grouped_effect_rows[0] + .descriptor_label + .as_deref(), + Some("Limited Track Building Amount") + ); + assert_eq!( + summary.records[0].grouped_effect_rows[0] + .parameter_family + .as_deref(), + Some("world_track_build_limit_scalar") + ); + assert_eq!( + summary.records[0].decoded_actions, + vec![RuntimeEffect::SetLimitedTrackBuildingAmount { value: 18 }] + ); + assert!(summary.records[0].executable_import_ready); + } + #[test] fn decodes_recovered_late_world_toggle_descriptor_family() { let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec { diff --git a/crates/rrt-runtime/src/step.rs b/crates/rrt-runtime/src/step.rs index 440c14c..419864e 100644 --- a/crates/rrt-runtime/src/step.rs +++ b/crates/rrt-runtime/src/step.rs @@ -311,6 +311,9 @@ fn apply_runtime_effects( RuntimeEffect::SetWorldFlag { key, value } => { state.world_flags.insert(key.clone(), *value); } + RuntimeEffect::SetLimitedTrackBuildingAmount { value } => { + state.world_restore.limited_track_building_amount = Some(*value); + } RuntimeEffect::SetEconomicStatusCode { value } => { state.world_restore.economic_status_code = Some(*value); } @@ -2375,6 +2378,32 @@ mod tests { assert_eq!(state.world_restore.economic_status_code, Some(3)); } + #[test] + fn applies_limited_track_building_amount_effect() { + let mut state = RuntimeState { + event_runtime_records: vec![RuntimeEventRecord { + record_id: 91, + trigger_kind: 6, + active: true, + service_count: 0, + marks_collection_dirty: false, + one_shot: false, + has_fired: false, + conditions: Vec::new(), + effects: vec![RuntimeEffect::SetLimitedTrackBuildingAmount { value: 18 }], + }], + ..state() + }; + + execute_step_command( + &mut state, + &StepCommand::ServiceTriggerKind { trigger_kind: 6 }, + ) + .expect("limited-track-building-amount effect should succeed"); + + assert_eq!(state.world_restore.limited_track_building_amount, Some(18)); + } + #[test] fn confiscate_company_assets_zeros_company_and_retires_owned_trains() { let mut state = RuntimeState { diff --git a/crates/rrt-runtime/src/summary.rs b/crates/rrt-runtime/src/summary.rs index eb56eaa..b2d79ee 100644 --- a/crates/rrt-runtime/src/summary.rs +++ b/crates/rrt-runtime/src/summary.rs @@ -24,6 +24,7 @@ pub struct RuntimeSummary { pub world_restore_disable_train_crashes_enabled: Option, pub world_restore_disable_train_crashes_and_breakdowns_enabled: Option, pub world_restore_ai_ignore_territories_at_startup_enabled: Option, + pub world_restore_limited_track_building_amount: Option, pub world_restore_economic_status_code: Option, pub world_restore_absolute_counter_restore_kind: Option, pub world_restore_absolute_counter_adjustment_context: Option, @@ -139,6 +140,9 @@ impl RuntimeSummary { world_restore_ai_ignore_territories_at_startup_enabled: state .world_restore .ai_ignore_territories_at_startup_enabled, + world_restore_limited_track_building_amount: state + .world_restore + .limited_track_building_amount, world_restore_economic_status_code: state.world_restore.economic_status_code, world_restore_absolute_counter_restore_kind: state .world_restore diff --git a/docs/README.md b/docs/README.md index fd97004..e1882ec 100644 --- a/docs/README.md +++ b/docs/README.md @@ -110,8 +110,9 @@ The highest-value next passes are now: `110` `Disable Stock Buying and Selling` now executes too through the checked-in keyed runtime flag `world.disable_stock_buying_and_selling` - that world-toggle path now covers a broader recovered boolean scenario-rule band too: - descriptors `111..138`, excluding the scalar `Limited Track Building Amount` slot, now decode - into keyed `world_flags` for finance/trading, construction, and governance restrictions + descriptors `111..138` now decode through checked-in metadata into either keyed `world_flags` + or the bounded `world_restore.limited_track_building_amount` scalar for finance/trading, + construction, and governance restrictions - the late recovered world-toggle band now executes too where current evidence is equally strong: `Use Bio-Accelerator Cars`, `Disable Cargo Economy`, `Disable Train Crashes`, `Disable Train Crashes AND Breakdowns`, and `AI Ignore Territories At Startup` diff --git a/docs/runtime-rehost-plan.md b/docs/runtime-rehost-plan.md index 948cfa3..3c20d74 100644 --- a/docs/runtime-rehost-plan.md +++ b/docs/runtime-rehost-plan.md @@ -64,9 +64,9 @@ Implemented today: descriptor `110` `Disable Stock Buying and Selling` lowers through checked-in keyed runtime metadata into `world.disable_stock_buying_and_selling` - that world-side effect batch now covers a broader recovered boolean scenario-rule family too: - descriptors `111..138`, excluding the scalar `Limited Track Building Amount` slot, now lower - through checked-in metadata into keyed `world_flags` for finance/trading, construction, and - governance/company-formation restrictions + descriptors `111..138` now lower through checked-in metadata into either keyed `world_flags` or + the bounded `world_restore.limited_track_building_amount` scalar for finance/trading, + construction, and governance/company-formation restrictions - the same keyed world-toggle path now also covers the late special-condition band where current static evidence stays equally strong: `Use Bio-Accelerator Cars`, `Disable Cargo Economy`, `Disable Train Crashes`, `Disable Train Crashes AND Breakdowns`, and diff --git a/fixtures/runtime/packed-event-world-track-build-limit-save-slice-fixture.json b/fixtures/runtime/packed-event-world-track-build-limit-save-slice-fixture.json new file mode 100644 index 0000000..88460ef --- /dev/null +++ b/fixtures/runtime/packed-event-world-track-build-limit-save-slice-fixture.json @@ -0,0 +1,44 @@ +{ + "format_version": 1, + "fixture_id": "packed-event-world-track-build-limit-save-slice-fixture", + "source": { + "kind": "captured-runtime", + "description": "Fixture proving the recovered Limited Track Building Amount descriptor imports and executes through the ordinary runtime path." + }, + "state_save_slice_path": "packed-event-world-track-build-limit-save-slice.json", + "commands": [ + { + "kind": "service_trigger_kind", + "trigger_kind": 6 + } + ], + "expected_summary": { + "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, + "packed_event_blocked_unmapped_world_descriptor_count": 0, + "world_restore_limited_track_building_amount": 18, + "total_event_record_service_count": 1, + "total_trigger_dispatch_count": 1 + }, + "expected_state_fragment": { + "world_restore": { + "limited_track_building_amount": 18 + }, + "packed_event_collection": { + "records": [ + { + "import_outcome": "imported", + "decoded_actions": [ + { + "kind": "set_limited_track_building_amount", + "value": 18 + } + ] + } + ] + } + } +} diff --git a/fixtures/runtime/packed-event-world-track-build-limit-save-slice.json b/fixtures/runtime/packed-event-world-track-build-limit-save-slice.json new file mode 100644 index 0000000..736f918 --- /dev/null +++ b/fixtures/runtime/packed-event-world-track-build-limit-save-slice.json @@ -0,0 +1,109 @@ +{ + "format_version": 1, + "save_slice_id": "packed-event-world-track-build-limit-save-slice", + "source": { + "description": "Tracked save-slice document proving the recovered Limited Track Building Amount descriptor imports and executes.", + "original_save_filename": "captured-world-track-build-limit.gms", + "original_save_sha256": "world-track-build-limit-sample-sha256", + "notes": [ + "tracked as JSON save-slice document rather than raw .smp", + "descriptor 122 now lowers into the bounded world-restore scalar lane", + "whole-game scalar descriptor sample for Limited Track Building Amount" + ] + }, + "save_slice": { + "file_extension_hint": "gms", + "container_profile_family": "rt3-classic-save-container-v1", + "mechanism_family": "classic-save-rehydrate-v1", + "mechanism_confidence": "grounded", + "trailer_family": null, + "bridge_family": null, + "profile": null, + "candidate_availability_table": null, + "special_conditions_table": null, + "event_runtime_collection": { + "source_kind": "packed-event-runtime-collection", + "mechanism_family": "classic-save-rehydrate-v1", + "mechanism_confidence": "grounded", + "container_profile_family": "rt3-classic-save-container-v1", + "metadata_tag_offset": 32000, + "records_tag_offset": 32256, + "close_tag_offset": 33024, + "packed_state_version": 1001, + "packed_state_version_hex": "0x000003e9", + "live_id_bound": 53, + "live_record_count": 1, + "live_entry_ids": [53], + "decoded_record_count": 1, + "imported_runtime_record_count": 1, + "records": [ + { + "record_index": 0, + "live_entry_id": 53, + "payload_offset": 32320, + "payload_len": 112, + "decode_status": "executable", + "payload_family": "real_packed_v1", + "trigger_kind": 6, + "one_shot": false, + "compact_control": { + "mode_byte_0x7ef": 6, + "primary_selector_0x7f0": 0, + "grouped_mode_0x7f4": 2, + "one_shot_header_0x7f5": 0, + "modifier_flag_0x7f9": 0, + "modifier_flag_0x7fa": 0, + "grouped_target_scope_ordinals_0x7fb": [0, 0, 0, 0], + "grouped_scope_checkboxes_0x7ff": [1, 0, 0, 0], + "summary_toggle_0x800": 1, + "grouped_territory_selectors_0x80f": [-1, -1, -1, -1] + }, + "text_bands": [], + "standalone_condition_row_count": 0, + "standalone_condition_rows": [], + "negative_sentinel_scope": null, + "grouped_effect_row_counts": [1, 0, 0, 0], + "grouped_effect_rows": [ + { + "group_index": 0, + "row_index": 0, + "descriptor_id": 122, + "descriptor_label": "Limited Track Building Amount", + "target_mask_bits": 8, + "parameter_family": "world_track_build_limit_scalar", + "opcode": 3, + "raw_scalar_value": 18, + "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 Limited Track Building Amount to 18", + "locomotive_name": null, + "notes": [ + "checked-in whole-game scalar descriptor family" + ] + } + ], + "decoded_conditions": [], + "decoded_actions": [ + { + "kind": "set_limited_track_building_amount", + "value": 18 + } + ], + "executable_import_ready": true, + "notes": [ + "checked-in whole-game scalar grouped-effect import sample" + ] + } + ] + }, + "notes": [ + "whole-game limited-track-building-amount effect sample" + ] + } +}