Implement runtime variable event conditions
This commit is contained in:
parent
d3790c2ae3
commit
bd9e1421a1
15 changed files with 1442 additions and 29 deletions
|
|
@ -46,7 +46,8 @@ 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 runtime-variable strip `39..54` now executes too through bounded
|
||||
event-owned scalar maps on world/company/player/territory state, without widening save-native
|
||||
event-owned scalar maps on world/company/player/territory state, and the matching ordinary
|
||||
condition strip now gates records through those same maps too, without widening save-native
|
||||
reconstruction or adding a second packed executor. The grounded aggregate cargo-economics
|
||||
descriptors now have bounded
|
||||
runtime landing surfaces too: descriptor `105` `All Cargo Prices` plus descriptors `177..179`
|
||||
|
|
@ -54,7 +55,8 @@ runtime landing surfaces too: descriptor `105` `All Cargo Prices` plus descripto
|
|||
event-owned cargo override state, and the grounded named cargo-production strip `180..229` now
|
||||
imports into named cargo production overrides too. The named cargo-price strip `106..176`
|
||||
remains explicit `blocked_evidence_blocked_descriptor` parity until descriptor ordering is pinned
|
||||
more strongly. The first grounded
|
||||
more strongly. The add-building strip `503..519` is now explicitly classified as recovered
|
||||
shell-owned descriptor parity rather than generic unresolved residue. 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
|
||||
|
|
|
|||
|
|
@ -4532,7 +4532,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 503,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 1",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4541,7 +4541,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 504,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 2",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4550,7 +4550,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 505,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 3",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4559,7 +4559,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 506,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 4",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4568,7 +4568,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 507,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 5",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4577,7 +4577,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 508,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 6",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4586,7 +4586,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 509,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 7",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4595,7 +4595,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 510,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 8",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4604,7 +4604,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 511,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 9",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4613,7 +4613,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 512,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 10",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4622,7 +4622,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 513,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 11",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4631,7 +4631,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 514,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 12",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4640,7 +4640,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 515,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 13",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4649,7 +4649,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 516,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 14",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4658,7 +4658,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 517,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 15",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4667,7 +4667,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 518,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 16",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
@ -4676,7 +4676,7 @@
|
|||
},
|
||||
{
|
||||
"descriptor_id": 519,
|
||||
"label": "Unknown Add Building",
|
||||
"label": "Add Building Slot 17",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"runtime_key": null,
|
||||
|
|
|
|||
|
|
@ -4481,11 +4481,17 @@ mod tests {
|
|||
);
|
||||
let runtime_variable_overlay_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../fixtures/runtime/packed-event-runtime-variable-overlay-fixture.json");
|
||||
let runtime_variable_condition_overlay_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join(
|
||||
"../../fixtures/runtime/packed-event-runtime-variable-condition-overlay-fixture.json",
|
||||
);
|
||||
let cargo_economics_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../fixtures/runtime/packed-event-cargo-economics-save-slice-fixture.json");
|
||||
let cargo_economics_parity_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
|
||||
"../../fixtures/runtime/packed-event-cargo-economics-parity-save-slice-fixture.json",
|
||||
);
|
||||
let add_building_shell_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../fixtures/runtime/packed-event-add-building-shell-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",
|
||||
);
|
||||
|
|
@ -4599,10 +4605,14 @@ mod tests {
|
|||
.expect("save-slice-backed world-scalar override fixture should summarize");
|
||||
run_runtime_summarize_fixture(&runtime_variable_overlay_fixture)
|
||||
.expect("overlay-backed runtime-variable fixture should summarize");
|
||||
run_runtime_summarize_fixture(&runtime_variable_condition_overlay_fixture)
|
||||
.expect("overlay-backed runtime-variable condition fixture should summarize");
|
||||
run_runtime_summarize_fixture(&cargo_economics_fixture)
|
||||
.expect("save-slice-backed cargo-economics fixture should summarize");
|
||||
run_runtime_summarize_fixture(&cargo_economics_parity_fixture)
|
||||
.expect("save-slice-backed cargo-economics parity fixture should summarize");
|
||||
run_runtime_summarize_fixture(&add_building_shell_fixture)
|
||||
.expect("save-slice-backed add-building shell 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)
|
||||
|
|
|
|||
|
|
@ -1302,6 +1302,7 @@ fn lowered_record_decoded_conditions(
|
|||
}
|
||||
|
||||
let lowered_company_target = lowered_condition_true_company_target(record)?;
|
||||
let lowered_player_target = lowered_condition_true_player_target(record)?;
|
||||
let ordinary_rows = record
|
||||
.standalone_condition_rows
|
||||
.iter()
|
||||
|
|
@ -1313,6 +1314,7 @@ fn lowered_record_decoded_conditions(
|
|||
condition,
|
||||
row,
|
||||
lowered_company_target.as_ref(),
|
||||
lowered_player_target.as_ref(),
|
||||
company_context,
|
||||
)
|
||||
})
|
||||
|
|
@ -1949,9 +1951,19 @@ fn lower_condition_targets_in_condition(
|
|||
condition: &RuntimeCondition,
|
||||
row: &SmpLoadedPackedEventConditionRowSummary,
|
||||
lowered_company_target: Option<&RuntimeCompanyTarget>,
|
||||
lowered_player_target: Option<&RuntimePlayerTarget>,
|
||||
company_context: &ImportRuntimeContext,
|
||||
) -> Result<RuntimeCondition, ImportBlocker> {
|
||||
Ok(match condition {
|
||||
RuntimeCondition::WorldVariableThreshold {
|
||||
index,
|
||||
comparator,
|
||||
value,
|
||||
} => RuntimeCondition::WorldVariableThreshold {
|
||||
index: *index,
|
||||
comparator: *comparator,
|
||||
value: *value,
|
||||
},
|
||||
RuntimeCondition::CompanyNumericThreshold {
|
||||
target,
|
||||
metric,
|
||||
|
|
@ -1966,6 +1978,20 @@ fn lower_condition_targets_in_condition(
|
|||
comparator: *comparator,
|
||||
value: *value,
|
||||
},
|
||||
RuntimeCondition::CompanyVariableThreshold {
|
||||
target,
|
||||
index,
|
||||
comparator,
|
||||
value,
|
||||
} => RuntimeCondition::CompanyVariableThreshold {
|
||||
target: lower_condition_true_company_target_in_company_target(
|
||||
target,
|
||||
lowered_company_target,
|
||||
)?,
|
||||
index: *index,
|
||||
comparator: *comparator,
|
||||
value: *value,
|
||||
},
|
||||
RuntimeCondition::ChairmanNumericThreshold {
|
||||
target,
|
||||
metric,
|
||||
|
|
@ -1977,6 +2003,20 @@ fn lower_condition_targets_in_condition(
|
|||
comparator: *comparator,
|
||||
value: *value,
|
||||
},
|
||||
RuntimeCondition::PlayerVariableThreshold {
|
||||
target,
|
||||
index,
|
||||
comparator,
|
||||
value,
|
||||
} => RuntimeCondition::PlayerVariableThreshold {
|
||||
target: lower_condition_true_player_target_in_player_target(
|
||||
target,
|
||||
lowered_player_target,
|
||||
)?,
|
||||
index: *index,
|
||||
comparator: *comparator,
|
||||
value: *value,
|
||||
},
|
||||
RuntimeCondition::TerritoryNumericThreshold {
|
||||
target,
|
||||
metric,
|
||||
|
|
@ -1988,6 +2028,17 @@ fn lower_condition_targets_in_condition(
|
|||
comparator: *comparator,
|
||||
value: *value,
|
||||
},
|
||||
RuntimeCondition::TerritoryVariableThreshold {
|
||||
target,
|
||||
index,
|
||||
comparator,
|
||||
value,
|
||||
} => RuntimeCondition::TerritoryVariableThreshold {
|
||||
target: lower_territory_target_in_condition(target, row, company_context)?,
|
||||
index: *index,
|
||||
comparator: *comparator,
|
||||
value: *value,
|
||||
},
|
||||
RuntimeCondition::CompanyTerritoryNumericThreshold {
|
||||
target,
|
||||
territory,
|
||||
|
|
@ -2187,11 +2238,17 @@ fn record_uses_condition_true_player(record: &SmpLoadedPackedEventRecordSummary)
|
|||
fn condition_uses_condition_true_company(condition: &RuntimeCondition) -> bool {
|
||||
match condition {
|
||||
RuntimeCondition::CompanyNumericThreshold { target, .. }
|
||||
| RuntimeCondition::CompanyVariableThreshold { target, .. }
|
||||
| RuntimeCondition::CompanyTerritoryNumericThreshold { target, .. } => {
|
||||
matches!(target, RuntimeCompanyTarget::ConditionTrueCompany)
|
||||
}
|
||||
RuntimeCondition::PlayerVariableThreshold { target, .. } => {
|
||||
matches!(target, RuntimePlayerTarget::ConditionTruePlayer)
|
||||
}
|
||||
RuntimeCondition::ChairmanNumericThreshold { .. } => false,
|
||||
RuntimeCondition::TerritoryNumericThreshold { .. }
|
||||
| RuntimeCondition::TerritoryVariableThreshold { .. }
|
||||
| RuntimeCondition::WorldVariableThreshold { .. }
|
||||
| RuntimeCondition::SpecialConditionThreshold { .. }
|
||||
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
|
||||
| RuntimeCondition::NamedLocomotiveAvailabilityThreshold { .. }
|
||||
|
|
@ -2719,6 +2776,7 @@ fn conditions_provide_company_context(conditions: &[RuntimeCondition]) -> bool {
|
|||
matches!(
|
||||
condition,
|
||||
RuntimeCondition::CompanyNumericThreshold { .. }
|
||||
| RuntimeCondition::CompanyVariableThreshold { .. }
|
||||
| RuntimeCondition::CompanyTerritoryNumericThreshold { .. }
|
||||
)
|
||||
})
|
||||
|
|
@ -3026,7 +3084,8 @@ fn record_has_world_state_condition_rows(record: &SmpLoadedPackedEventRecordSumm
|
|||
fn runtime_condition_is_world_state(condition: &RuntimeCondition) -> bool {
|
||||
matches!(
|
||||
condition,
|
||||
RuntimeCondition::SpecialConditionThreshold { .. }
|
||||
RuntimeCondition::WorldVariableThreshold { .. }
|
||||
| RuntimeCondition::SpecialConditionThreshold { .. }
|
||||
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
|
||||
| RuntimeCondition::NamedLocomotiveAvailabilityThreshold { .. }
|
||||
| RuntimeCondition::NamedLocomotiveCostThreshold { .. }
|
||||
|
|
@ -3180,15 +3239,25 @@ fn runtime_condition_company_target_import_blocker(
|
|||
company_context: &ImportRuntimeContext,
|
||||
) -> Option<ImportBlocker> {
|
||||
match condition {
|
||||
RuntimeCondition::WorldVariableThreshold { .. } => None,
|
||||
RuntimeCondition::CompanyNumericThreshold { target, .. } => {
|
||||
company_target_import_blocker(target, company_context)
|
||||
}
|
||||
RuntimeCondition::CompanyVariableThreshold { target, .. } => {
|
||||
company_target_import_blocker(target, company_context)
|
||||
}
|
||||
RuntimeCondition::PlayerVariableThreshold { target, .. } => {
|
||||
player_target_import_blocker(target, company_context)
|
||||
}
|
||||
RuntimeCondition::ChairmanNumericThreshold { target, .. } => {
|
||||
chairman_target_import_blocker(target, company_context)
|
||||
}
|
||||
RuntimeCondition::TerritoryNumericThreshold { target, .. } => {
|
||||
territory_target_import_blocker(target, company_context)
|
||||
}
|
||||
RuntimeCondition::TerritoryVariableThreshold { target, .. } => {
|
||||
territory_target_import_blocker(target, company_context)
|
||||
}
|
||||
RuntimeCondition::CompanyTerritoryNumericThreshold {
|
||||
target, territory, ..
|
||||
} => company_target_import_blocker(target, company_context)
|
||||
|
|
|
|||
|
|
@ -311,24 +311,47 @@ pub enum RuntimeTrackMetric {
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind", rename_all = "snake_case")]
|
||||
pub enum RuntimeCondition {
|
||||
WorldVariableThreshold {
|
||||
index: u32,
|
||||
comparator: RuntimeConditionComparator,
|
||||
value: i64,
|
||||
},
|
||||
CompanyNumericThreshold {
|
||||
target: RuntimeCompanyTarget,
|
||||
metric: RuntimeCompanyMetric,
|
||||
comparator: RuntimeConditionComparator,
|
||||
value: i64,
|
||||
},
|
||||
CompanyVariableThreshold {
|
||||
target: RuntimeCompanyTarget,
|
||||
index: u32,
|
||||
comparator: RuntimeConditionComparator,
|
||||
value: i64,
|
||||
},
|
||||
ChairmanNumericThreshold {
|
||||
target: RuntimeChairmanTarget,
|
||||
metric: RuntimeChairmanMetric,
|
||||
comparator: RuntimeConditionComparator,
|
||||
value: i64,
|
||||
},
|
||||
PlayerVariableThreshold {
|
||||
target: RuntimePlayerTarget,
|
||||
index: u32,
|
||||
comparator: RuntimeConditionComparator,
|
||||
value: i64,
|
||||
},
|
||||
TerritoryNumericThreshold {
|
||||
target: RuntimeTerritoryTarget,
|
||||
metric: RuntimeTerritoryMetric,
|
||||
comparator: RuntimeConditionComparator,
|
||||
value: i64,
|
||||
},
|
||||
TerritoryVariableThreshold {
|
||||
target: RuntimeTerritoryTarget,
|
||||
index: u32,
|
||||
comparator: RuntimeConditionComparator,
|
||||
value: i64,
|
||||
},
|
||||
CompanyTerritoryNumericThreshold {
|
||||
target: RuntimeCompanyTarget,
|
||||
territory: RuntimeTerritoryTarget,
|
||||
|
|
@ -1209,6 +1232,7 @@ impl RuntimeState {
|
|||
validate_runtime_condition(
|
||||
condition,
|
||||
&seen_company_ids,
|
||||
&seen_player_ids,
|
||||
&seen_chairman_profile_ids,
|
||||
&seen_territory_ids,
|
||||
)
|
||||
|
|
@ -1810,6 +1834,7 @@ fn validate_event_record_template(
|
|||
validate_runtime_condition(
|
||||
condition,
|
||||
valid_company_ids,
|
||||
valid_player_ids,
|
||||
valid_chairman_profile_ids,
|
||||
valid_territory_ids,
|
||||
)
|
||||
|
|
@ -1842,19 +1867,51 @@ fn validate_event_record_template(
|
|||
fn validate_runtime_condition(
|
||||
condition: &RuntimeCondition,
|
||||
valid_company_ids: &BTreeSet<u32>,
|
||||
valid_player_ids: &BTreeSet<u32>,
|
||||
valid_chairman_profile_ids: &BTreeSet<u32>,
|
||||
valid_territory_ids: &BTreeSet<u32>,
|
||||
) -> Result<(), String> {
|
||||
match condition {
|
||||
RuntimeCondition::WorldVariableThreshold { index, .. } => {
|
||||
if !(1..=4).contains(index) {
|
||||
Err("index must be in 1..=4".to_string())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
RuntimeCondition::CompanyNumericThreshold { target, .. } => {
|
||||
validate_company_target(target, valid_company_ids)
|
||||
}
|
||||
RuntimeCondition::CompanyVariableThreshold { target, index, .. } => {
|
||||
validate_company_target(target, valid_company_ids)?;
|
||||
if !(1..=4).contains(index) {
|
||||
Err("index must be in 1..=4".to_string())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
RuntimeCondition::ChairmanNumericThreshold { target, .. } => {
|
||||
validate_chairman_target(target, valid_chairman_profile_ids)
|
||||
}
|
||||
RuntimeCondition::PlayerVariableThreshold { target, index, .. } => {
|
||||
validate_player_target(target, valid_player_ids)?;
|
||||
if !(1..=4).contains(index) {
|
||||
Err("index must be in 1..=4".to_string())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
RuntimeCondition::TerritoryNumericThreshold { target, .. } => {
|
||||
validate_territory_target(target, valid_territory_ids)
|
||||
}
|
||||
RuntimeCondition::TerritoryVariableThreshold { target, index, .. } => {
|
||||
validate_territory_target(target, valid_territory_ids)?;
|
||||
if !(1..=4).contains(index) {
|
||||
Err("index must be in 1..=4".to_string())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
RuntimeCondition::CompanyTerritoryNumericThreshold {
|
||||
target, territory, ..
|
||||
} => {
|
||||
|
|
|
|||
|
|
@ -333,9 +333,13 @@ pub(crate) fn grouped_effect_descriptor_runtime_status_name(
|
|||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum RealOrdinaryConditionMetric {
|
||||
WorldVariable(u32),
|
||||
Company(RuntimeCompanyMetric),
|
||||
CompanyVariable(u32),
|
||||
PlayerVariable(u32),
|
||||
Chairman(RuntimeChairmanMetric),
|
||||
Territory(RuntimeTerritoryMetric),
|
||||
TerritoryVariable(u32),
|
||||
CompanyTerritory(RuntimeTrackMetric),
|
||||
}
|
||||
|
||||
|
|
@ -511,6 +515,22 @@ const GROUNDED_LOCOMOTIVE_PREFIX: [&str; 61] = [
|
|||
];
|
||||
|
||||
const REAL_CANDIDATE_AVAILABILITY_CONDITION_TEMPLATE_ID: i32 = 435;
|
||||
const REAL_WORLD_VARIABLE_1_CONDITION_ID: i32 = 2241;
|
||||
const REAL_WORLD_VARIABLE_2_CONDITION_ID: i32 = 2242;
|
||||
const REAL_WORLD_VARIABLE_3_CONDITION_ID: i32 = 2243;
|
||||
const REAL_WORLD_VARIABLE_4_CONDITION_ID: i32 = 2244;
|
||||
const REAL_COMPANY_VARIABLE_1_CONDITION_ID: i32 = 2245;
|
||||
const REAL_COMPANY_VARIABLE_2_CONDITION_ID: i32 = 2246;
|
||||
const REAL_COMPANY_VARIABLE_3_CONDITION_ID: i32 = 2247;
|
||||
const REAL_COMPANY_VARIABLE_4_CONDITION_ID: i32 = 2248;
|
||||
const REAL_PLAYER_VARIABLE_1_CONDITION_ID: i32 = 2249;
|
||||
const REAL_PLAYER_VARIABLE_2_CONDITION_ID: i32 = 2250;
|
||||
const REAL_PLAYER_VARIABLE_3_CONDITION_ID: i32 = 2251;
|
||||
const REAL_PLAYER_VARIABLE_4_CONDITION_ID: i32 = 2252;
|
||||
const REAL_TERRITORY_VARIABLE_1_CONDITION_ID: i32 = 2253;
|
||||
const REAL_TERRITORY_VARIABLE_2_CONDITION_ID: i32 = 2254;
|
||||
const REAL_TERRITORY_VARIABLE_3_CONDITION_ID: i32 = 2255;
|
||||
const REAL_TERRITORY_VARIABLE_4_CONDITION_ID: i32 = 2256;
|
||||
const REAL_CHAIRMAN_CASH_CONDITION_ID: i32 = 2218;
|
||||
const REAL_CHAIRMAN_HOLDINGS_TOTAL_CONDITION_ID: i32 = 2239;
|
||||
const REAL_CHAIRMAN_NET_WORTH_CONDITION_ID: i32 = 2240;
|
||||
|
|
@ -530,7 +550,87 @@ const REAL_OTHER_CARGO_PRODUCTION_TOTAL_CONDITION_ID: i32 = 2421;
|
|||
const REAL_LIMITED_TRACK_BUILDING_AMOUNT_CONDITION_ID: i32 = 2547;
|
||||
const REAL_TERRITORY_ACCESS_COST_CONDITION_ID: i32 = 1516;
|
||||
|
||||
const REAL_ORDINARY_CONDITION_METADATA: [RealOrdinaryConditionMetadata; 40] = [
|
||||
const REAL_ORDINARY_CONDITION_METADATA: [RealOrdinaryConditionMetadata; 56] = [
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_WORLD_VARIABLE_1_CONDITION_ID,
|
||||
label: "Game Variable 1",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::WorldVariable(1)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_WORLD_VARIABLE_2_CONDITION_ID,
|
||||
label: "Game Variable 2",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::WorldVariable(2)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_WORLD_VARIABLE_3_CONDITION_ID,
|
||||
label: "Game Variable 3",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::WorldVariable(3)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_WORLD_VARIABLE_4_CONDITION_ID,
|
||||
label: "Game Variable 4",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::WorldVariable(4)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_COMPANY_VARIABLE_1_CONDITION_ID,
|
||||
label: "Company Variable 1",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyVariable(1)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_COMPANY_VARIABLE_2_CONDITION_ID,
|
||||
label: "Company Variable 2",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyVariable(2)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_COMPANY_VARIABLE_3_CONDITION_ID,
|
||||
label: "Company Variable 3",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyVariable(3)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_COMPANY_VARIABLE_4_CONDITION_ID,
|
||||
label: "Company Variable 4",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyVariable(4)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_PLAYER_VARIABLE_1_CONDITION_ID,
|
||||
label: "Player Variable 1",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::PlayerVariable(1)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_PLAYER_VARIABLE_2_CONDITION_ID,
|
||||
label: "Player Variable 2",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::PlayerVariable(2)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_PLAYER_VARIABLE_3_CONDITION_ID,
|
||||
label: "Player Variable 3",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::PlayerVariable(3)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_PLAYER_VARIABLE_4_CONDITION_ID,
|
||||
label: "Player Variable 4",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::PlayerVariable(4)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_TERRITORY_VARIABLE_1_CONDITION_ID,
|
||||
label: "Territory Variable 1",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::TerritoryVariable(1)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_TERRITORY_VARIABLE_2_CONDITION_ID,
|
||||
label: "Territory Variable 2",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::TerritoryVariable(2)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_TERRITORY_VARIABLE_3_CONDITION_ID,
|
||||
label: "Territory Variable 3",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::TerritoryVariable(3)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: REAL_TERRITORY_VARIABLE_4_CONDITION_ID,
|
||||
label: "Territory Variable 4",
|
||||
kind: RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::TerritoryVariable(4)),
|
||||
},
|
||||
RealOrdinaryConditionMetadata {
|
||||
raw_condition_id: 1802,
|
||||
label: "Current Cash",
|
||||
|
|
@ -3580,6 +3680,13 @@ fn decode_real_condition_row(
|
|||
let comparator = decode_real_condition_comparator(row.subtype)?;
|
||||
let value = decode_real_condition_threshold(&row.flag_bytes)?;
|
||||
match metadata.kind {
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::WorldVariable(index)) => {
|
||||
Some(RuntimeCondition::WorldVariableThreshold {
|
||||
index,
|
||||
comparator,
|
||||
value,
|
||||
})
|
||||
}
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(metric)) => {
|
||||
Some(RuntimeCondition::CompanyNumericThreshold {
|
||||
target: RuntimeCompanyTarget::ConditionTrueCompany,
|
||||
|
|
@ -3588,6 +3695,26 @@ fn decode_real_condition_row(
|
|||
value,
|
||||
})
|
||||
}
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyVariable(index)) => {
|
||||
Some(RuntimeCondition::CompanyVariableThreshold {
|
||||
target: RuntimeCompanyTarget::ConditionTrueCompany,
|
||||
index,
|
||||
comparator,
|
||||
value,
|
||||
})
|
||||
}
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::PlayerVariable(index)) => {
|
||||
negative_sentinel_scope.and_then(|scope| {
|
||||
real_condition_player_target(scope).map(|target| {
|
||||
RuntimeCondition::PlayerVariableThreshold {
|
||||
target,
|
||||
index,
|
||||
comparator,
|
||||
value,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Chairman(metric)) => {
|
||||
negative_sentinel_scope.and_then(|scope| {
|
||||
real_condition_chairman_target(scope).map(|target| {
|
||||
|
|
@ -3600,6 +3727,16 @@ fn decode_real_condition_row(
|
|||
})
|
||||
})
|
||||
}
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::TerritoryVariable(
|
||||
index,
|
||||
)) => negative_sentinel_scope
|
||||
.filter(|scope| scope.territory_scope_selector_is_0x63)
|
||||
.map(|_| RuntimeCondition::TerritoryVariableThreshold {
|
||||
target: RuntimeTerritoryTarget::AllTerritories,
|
||||
index,
|
||||
comparator,
|
||||
value,
|
||||
}),
|
||||
RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Territory(metric)) => {
|
||||
negative_sentinel_scope
|
||||
.filter(|scope| scope.territory_scope_selector_is_0x63)
|
||||
|
|
@ -3727,6 +3864,22 @@ fn real_condition_chairman_target(
|
|||
}
|
||||
}
|
||||
|
||||
fn real_condition_player_target(
|
||||
scope: &SmpLoadedPackedEventNegativeSentinelScopeSummary,
|
||||
) -> Option<RuntimePlayerTarget> {
|
||||
match scope.player_test_scope {
|
||||
RuntimePlayerConditionTestScope::AllPlayers => Some(RuntimePlayerTarget::AllActive),
|
||||
RuntimePlayerConditionTestScope::SelectedPlayerOnly => {
|
||||
Some(RuntimePlayerTarget::SelectedPlayer)
|
||||
}
|
||||
RuntimePlayerConditionTestScope::AiPlayersOnly => Some(RuntimePlayerTarget::AiPlayers),
|
||||
RuntimePlayerConditionTestScope::HumanPlayersOnly => {
|
||||
Some(RuntimePlayerTarget::HumanPlayers)
|
||||
}
|
||||
RuntimePlayerConditionTestScope::Disabled => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn real_grouped_effect_descriptor_metadata(
|
||||
descriptor_id: u32,
|
||||
) -> Option<RealGroupedEffectDescriptorMetadata> {
|
||||
|
|
@ -4981,9 +5134,13 @@ fn runtime_effect_supported_for_save_import(effect: &RuntimeEffect) -> bool {
|
|||
|
||||
fn runtime_condition_supported_for_save_import(condition: &RuntimeCondition) -> bool {
|
||||
match condition {
|
||||
RuntimeCondition::CompanyNumericThreshold { .. }
|
||||
RuntimeCondition::WorldVariableThreshold { .. }
|
||||
| RuntimeCondition::CompanyNumericThreshold { .. }
|
||||
| RuntimeCondition::CompanyVariableThreshold { .. }
|
||||
| RuntimeCondition::PlayerVariableThreshold { .. }
|
||||
| RuntimeCondition::ChairmanNumericThreshold { .. }
|
||||
| RuntimeCondition::TerritoryNumericThreshold { .. }
|
||||
| RuntimeCondition::TerritoryVariableThreshold { .. }
|
||||
| RuntimeCondition::CompanyTerritoryNumericThreshold { .. }
|
||||
| RuntimeCondition::SpecialConditionThreshold { .. }
|
||||
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
|
||||
|
|
@ -11188,6 +11345,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn looks_up_checked_in_chairman_and_governance_condition_metadata() {
|
||||
let world_variable = real_ordinary_condition_metadata(REAL_WORLD_VARIABLE_1_CONDITION_ID)
|
||||
.expect("world-variable condition metadata should exist");
|
||||
assert_eq!(world_variable.label, "Game Variable 1");
|
||||
|
||||
let player_variable = real_ordinary_condition_metadata(REAL_PLAYER_VARIABLE_3_CONDITION_ID)
|
||||
.expect("player-variable condition metadata should exist");
|
||||
assert_eq!(player_variable.label, "Player Variable 3");
|
||||
|
||||
let chairman_cash = real_ordinary_condition_metadata(REAL_CHAIRMAN_CASH_CONDITION_ID)
|
||||
.expect("chairman cash condition metadata should exist");
|
||||
assert_eq!(chairman_cash.label, "Player Cash");
|
||||
|
|
@ -11228,6 +11393,116 @@ mod tests {
|
|||
assert_eq!(book_value.label, "Book Value Per Share");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_world_variable_condition() {
|
||||
let row = SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 0,
|
||||
raw_condition_id: REAL_WORLD_VARIABLE_1_CONDITION_ID,
|
||||
subtype: 4,
|
||||
flag_bytes: {
|
||||
let mut bytes = vec![0; 25];
|
||||
bytes[0..4].copy_from_slice(&111_i32.to_le_bytes());
|
||||
bytes
|
||||
},
|
||||
candidate_name: None,
|
||||
comparator: Some("eq".to_string()),
|
||||
metric: Some("Game Variable 1".to_string()),
|
||||
semantic_family: Some("numeric_threshold".to_string()),
|
||||
semantic_preview: Some("Test Game Variable 1 == 111".to_string()),
|
||||
recovered_cargo_slot: None,
|
||||
recovered_cargo_class: None,
|
||||
requires_candidate_name_binding: false,
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
decode_real_condition_row(&row, None),
|
||||
Some(RuntimeCondition::WorldVariableThreshold {
|
||||
index: 1,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 111,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_player_variable_condition_from_selected_player_scope() {
|
||||
let row = SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 0,
|
||||
raw_condition_id: REAL_PLAYER_VARIABLE_3_CONDITION_ID,
|
||||
subtype: 4,
|
||||
flag_bytes: {
|
||||
let mut bytes = vec![0; 25];
|
||||
bytes[0..4].copy_from_slice(&333_i32.to_le_bytes());
|
||||
bytes
|
||||
},
|
||||
candidate_name: None,
|
||||
comparator: Some("eq".to_string()),
|
||||
metric: Some("Player Variable 3".to_string()),
|
||||
semantic_family: Some("numeric_threshold".to_string()),
|
||||
semantic_preview: Some("Test Player Variable 3 == 333".to_string()),
|
||||
recovered_cargo_slot: None,
|
||||
recovered_cargo_class: None,
|
||||
requires_candidate_name_binding: false,
|
||||
notes: vec![],
|
||||
};
|
||||
let negative_scope = SmpLoadedPackedEventNegativeSentinelScopeSummary {
|
||||
company_test_scope: RuntimeCompanyConditionTestScope::Disabled,
|
||||
player_test_scope: RuntimePlayerConditionTestScope::SelectedPlayerOnly,
|
||||
territory_scope_selector_is_0x63: false,
|
||||
source_row_indexes: vec![0],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
decode_real_condition_row(&row, Some(&negative_scope)),
|
||||
Some(RuntimeCondition::PlayerVariableThreshold {
|
||||
target: RuntimePlayerTarget::SelectedPlayer,
|
||||
index: 3,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 333,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_territory_variable_condition_with_world_territory_scope() {
|
||||
let row = SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 0,
|
||||
raw_condition_id: REAL_TERRITORY_VARIABLE_4_CONDITION_ID,
|
||||
subtype: 4,
|
||||
flag_bytes: {
|
||||
let mut bytes = vec![0; 25];
|
||||
bytes[0..4].copy_from_slice(&444_i32.to_le_bytes());
|
||||
bytes
|
||||
},
|
||||
candidate_name: None,
|
||||
comparator: Some("eq".to_string()),
|
||||
metric: Some("Territory Variable 4".to_string()),
|
||||
semantic_family: Some("numeric_threshold".to_string()),
|
||||
semantic_preview: Some("Test Territory Variable 4 == 444".to_string()),
|
||||
recovered_cargo_slot: None,
|
||||
recovered_cargo_class: None,
|
||||
requires_candidate_name_binding: false,
|
||||
notes: vec![],
|
||||
};
|
||||
let negative_scope = SmpLoadedPackedEventNegativeSentinelScopeSummary {
|
||||
company_test_scope: RuntimeCompanyConditionTestScope::Disabled,
|
||||
player_test_scope: RuntimePlayerConditionTestScope::Disabled,
|
||||
territory_scope_selector_is_0x63: true,
|
||||
source_row_indexes: vec![0],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
decode_real_condition_row(&row, Some(&negative_scope)),
|
||||
Some(RuntimeCondition::TerritoryVariableThreshold {
|
||||
target: RuntimeTerritoryTarget::AllTerritories,
|
||||
index: 4,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 444,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_chairman_cash_condition_from_selected_player_scope() {
|
||||
let row = SmpLoadedPackedEventConditionRowSummary {
|
||||
|
|
|
|||
|
|
@ -782,10 +782,25 @@ fn evaluate_record_conditions(
|
|||
}
|
||||
|
||||
let mut company_matches: Option<BTreeSet<u32>> = None;
|
||||
let mut player_matches: Option<BTreeSet<u32>> = None;
|
||||
let mut chairman_matches: Option<BTreeSet<u32>> = None;
|
||||
|
||||
for condition in conditions {
|
||||
match condition {
|
||||
RuntimeCondition::WorldVariableThreshold {
|
||||
index,
|
||||
comparator,
|
||||
value,
|
||||
} => {
|
||||
let actual = state
|
||||
.world_runtime_variables
|
||||
.get(index)
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
if !compare_condition_value(actual, *comparator, *value) {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
RuntimeCondition::CompanyNumericThreshold {
|
||||
target,
|
||||
metric,
|
||||
|
|
@ -821,6 +836,37 @@ fn evaluate_record_conditions(
|
|||
return Ok(None);
|
||||
}
|
||||
}
|
||||
RuntimeCondition::CompanyVariableThreshold {
|
||||
target,
|
||||
index,
|
||||
comparator,
|
||||
value,
|
||||
} => {
|
||||
let resolved = resolve_company_target_ids(
|
||||
state,
|
||||
target,
|
||||
&ResolvedConditionContext::default(),
|
||||
)?;
|
||||
let matching = resolved
|
||||
.into_iter()
|
||||
.filter(|company_id| {
|
||||
let actual = state
|
||||
.company_runtime_variables
|
||||
.get(company_id)
|
||||
.and_then(|vars| vars.get(index))
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
compare_condition_value(actual, *comparator, *value)
|
||||
})
|
||||
.collect::<BTreeSet<_>>();
|
||||
if matching.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
intersect_company_matches(&mut company_matches, matching);
|
||||
if company_matches.as_ref().is_some_and(BTreeSet::is_empty) {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
RuntimeCondition::TerritoryNumericThreshold {
|
||||
target,
|
||||
metric,
|
||||
|
|
@ -833,6 +879,56 @@ fn evaluate_record_conditions(
|
|||
return Ok(None);
|
||||
}
|
||||
}
|
||||
RuntimeCondition::TerritoryVariableThreshold {
|
||||
target,
|
||||
index,
|
||||
comparator,
|
||||
value,
|
||||
} => {
|
||||
let territory_ids = resolve_territory_target_ids(state, target)?;
|
||||
let actual = territory_ids
|
||||
.iter()
|
||||
.map(|territory_id| {
|
||||
state
|
||||
.territory_runtime_variables
|
||||
.get(territory_id)
|
||||
.and_then(|vars| vars.get(index))
|
||||
.copied()
|
||||
.unwrap_or(0)
|
||||
})
|
||||
.sum::<i64>();
|
||||
if !compare_condition_value(actual, *comparator, *value) {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
RuntimeCondition::PlayerVariableThreshold {
|
||||
target,
|
||||
index,
|
||||
comparator,
|
||||
value,
|
||||
} => {
|
||||
let resolved =
|
||||
resolve_player_target_ids(state, target, &ResolvedConditionContext::default())?;
|
||||
let matching = resolved
|
||||
.into_iter()
|
||||
.filter(|player_id| {
|
||||
let actual = state
|
||||
.player_runtime_variables
|
||||
.get(player_id)
|
||||
.and_then(|vars| vars.get(index))
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
compare_condition_value(actual, *comparator, *value)
|
||||
})
|
||||
.collect::<BTreeSet<_>>();
|
||||
if matching.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
intersect_player_matches(&mut player_matches, matching);
|
||||
if player_matches.as_ref().is_some_and(BTreeSet::is_empty) {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
RuntimeCondition::ChairmanNumericThreshold {
|
||||
target,
|
||||
metric,
|
||||
|
|
@ -1050,7 +1146,7 @@ fn evaluate_record_conditions(
|
|||
|
||||
Ok(Some(ResolvedConditionContext {
|
||||
matching_company_ids: company_matches.unwrap_or_default(),
|
||||
matching_player_ids: BTreeSet::new(),
|
||||
matching_player_ids: player_matches.unwrap_or_default(),
|
||||
matching_chairman_profile_ids: chairman_matches.unwrap_or_default(),
|
||||
}))
|
||||
}
|
||||
|
|
@ -1066,6 +1162,17 @@ fn intersect_company_matches(company_matches: &mut Option<BTreeSet<u32>>, next:
|
|||
}
|
||||
}
|
||||
|
||||
fn intersect_player_matches(player_matches: &mut Option<BTreeSet<u32>>, next: BTreeSet<u32>) {
|
||||
match player_matches {
|
||||
Some(existing) => {
|
||||
existing.retain(|player_id| next.contains(player_id));
|
||||
}
|
||||
None => {
|
||||
*player_matches = Some(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn intersect_chairman_matches(chairman_matches: &mut Option<BTreeSet<u32>>, next: BTreeSet<u32>) {
|
||||
match chairman_matches {
|
||||
Some(existing) => {
|
||||
|
|
@ -1586,8 +1693,9 @@ mod tests {
|
|||
RuntimeChairmanMetric, RuntimeChairmanProfile, RuntimeChairmanTarget, RuntimeCompany,
|
||||
RuntimeCompanyControllerKind, RuntimeCompanyTarget, RuntimeCondition,
|
||||
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
|
||||
RuntimePlayer, RuntimeSaveProfileState, RuntimeServiceState, RuntimeTerritory,
|
||||
RuntimeTerritoryTarget, RuntimeTrackPieceCounts, RuntimeTrain, RuntimeWorldRestoreState,
|
||||
RuntimePlayer, RuntimePlayerTarget, RuntimeSaveProfileState, RuntimeServiceState,
|
||||
RuntimeTerritory, RuntimeTerritoryTarget, RuntimeTrackPieceCounts, RuntimeTrain,
|
||||
RuntimeWorldRestoreState,
|
||||
};
|
||||
|
||||
fn state() -> RuntimeState {
|
||||
|
|
@ -2965,6 +3073,122 @@ mod tests {
|
|||
assert_eq!(state.world_flags.get("world_scalar_condition_failed"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn evaluates_runtime_variable_conditions_before_effects_run() {
|
||||
let mut state = RuntimeState {
|
||||
companies: vec![RuntimeCompany {
|
||||
company_id: 1,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
current_cash: 10,
|
||||
debt: 0,
|
||||
credit_rating_score: None,
|
||||
prime_rate: None,
|
||||
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||
active: true,
|
||||
available_track_laying_capacity: None,
|
||||
linked_chairman_profile_id: None,
|
||||
book_value_per_share: 0,
|
||||
investor_confidence: 0,
|
||||
management_attitude: 0,
|
||||
takeover_cooldown_year: None,
|
||||
merger_cooldown_year: None,
|
||||
}],
|
||||
selected_company_id: Some(1),
|
||||
players: vec![RuntimePlayer {
|
||||
player_id: 1,
|
||||
current_cash: 50,
|
||||
active: true,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
}],
|
||||
selected_player_id: Some(1),
|
||||
territories: vec![RuntimeTerritory {
|
||||
territory_id: 7,
|
||||
name: Some("North".to_string()),
|
||||
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||
}],
|
||||
world_runtime_variables: BTreeMap::from([(1, 111)]),
|
||||
company_runtime_variables: BTreeMap::from([(1, BTreeMap::from([(2, 222)]))]),
|
||||
player_runtime_variables: BTreeMap::from([(1, BTreeMap::from([(3, 333)]))]),
|
||||
territory_runtime_variables: BTreeMap::from([(7, BTreeMap::from([(4, 444)]))]),
|
||||
event_runtime_records: vec![
|
||||
RuntimeEventRecord {
|
||||
record_id: 27,
|
||||
trigger_kind: 7,
|
||||
active: true,
|
||||
service_count: 0,
|
||||
marks_collection_dirty: false,
|
||||
one_shot: false,
|
||||
has_fired: false,
|
||||
conditions: vec![
|
||||
RuntimeCondition::WorldVariableThreshold {
|
||||
index: 1,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 111,
|
||||
},
|
||||
RuntimeCondition::CompanyVariableThreshold {
|
||||
target: RuntimeCompanyTarget::SelectedCompany,
|
||||
index: 2,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 222,
|
||||
},
|
||||
RuntimeCondition::PlayerVariableThreshold {
|
||||
target: RuntimePlayerTarget::SelectedPlayer,
|
||||
index: 3,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 333,
|
||||
},
|
||||
RuntimeCondition::TerritoryVariableThreshold {
|
||||
target: RuntimeTerritoryTarget::Ids { ids: vec![7] },
|
||||
index: 4,
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 444,
|
||||
},
|
||||
],
|
||||
effects: vec![RuntimeEffect::SetWorldFlag {
|
||||
key: "runtime_variable_condition_passed".to_string(),
|
||||
value: true,
|
||||
}],
|
||||
},
|
||||
RuntimeEventRecord {
|
||||
record_id: 28,
|
||||
trigger_kind: 7,
|
||||
active: true,
|
||||
service_count: 0,
|
||||
marks_collection_dirty: false,
|
||||
one_shot: false,
|
||||
has_fired: false,
|
||||
conditions: vec![RuntimeCondition::PlayerVariableThreshold {
|
||||
target: RuntimePlayerTarget::SelectedPlayer,
|
||||
index: 4,
|
||||
comparator: RuntimeConditionComparator::Gt,
|
||||
value: 0,
|
||||
}],
|
||||
effects: vec![RuntimeEffect::SetWorldFlag {
|
||||
key: "runtime_variable_condition_failed".to_string(),
|
||||
value: true,
|
||||
}],
|
||||
},
|
||||
],
|
||||
..state()
|
||||
};
|
||||
|
||||
let result = execute_step_command(
|
||||
&mut state,
|
||||
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||
)
|
||||
.expect("runtime-variable conditions should evaluate successfully");
|
||||
|
||||
assert_eq!(result.service_events[0].serviced_record_ids, vec![27]);
|
||||
assert_eq!(
|
||||
state.world_flags.get("runtime_variable_condition_passed"),
|
||||
Some(&true)
|
||||
);
|
||||
assert_eq!(
|
||||
state.world_flags.get("runtime_variable_condition_failed"),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_shot_record_only_fires_once() {
|
||||
let mut state = RuntimeState {
|
||||
|
|
|
|||
|
|
@ -114,14 +114,17 @@ The highest-value next passes are now:
|
|||
through stable normalized keys such as `world.build_stations_cost` and
|
||||
`world.track_maintenance_cost`
|
||||
- the runtime-variable strip `39..54` now executes too through bounded event-owned scalar maps on
|
||||
world/company/player/territory state; these variables are runtime-owned only in the current
|
||||
model and are not yet reconstructed from raw saves
|
||||
world/company/player/territory state, and the matching ordinary-condition strip now gates
|
||||
imported records through those same runtime-owned variable maps; these variables are still
|
||||
runtime-owned only in the current model and are not yet reconstructed from raw saves
|
||||
- the grounded aggregate cargo-economics descriptors now execute too: descriptor `105`
|
||||
`All Cargo Prices` and descriptors `177..179` `All Cargo Production` / `All Factory Production`
|
||||
/ `All Farm/Mine Production` land on bounded event-owned cargo override state, and the grounded
|
||||
named cargo-production strip `180..229` now lands on named cargo production overrides too
|
||||
- the named cargo-price strip `106..176` remains explicit
|
||||
`blocked_evidence_blocked_descriptor` parity until descriptor ordering is pinned more strongly
|
||||
- the add-building strip `503..519` is now explicitly classified as recovered shell-owned parity,
|
||||
with tracked fixture coverage, instead of generic unresolved descriptor residue
|
||||
- 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`
|
||||
|
|
|
|||
|
|
@ -76,14 +76,17 @@ Implemented today:
|
|||
landing surface too: representative descriptors import as `SetWorldScalarOverride` and land in
|
||||
`RuntimeState.world_scalar_overrides`
|
||||
- the runtime-variable strip `39..54` now imports and executes too through bounded event-owned
|
||||
world/company/player/territory variable maps, so those descriptors no longer depend on generic
|
||||
evidence parity or ad hoc fixture-only state
|
||||
world/company/player/territory variable maps, and the matching ordinary-condition strip now
|
||||
gates runtime records through those same maps too, so those descriptors and conditions no longer
|
||||
depend on generic evidence parity or ad hoc fixture-only state
|
||||
- the grounded aggregate cargo-economics descriptors now execute too: descriptor `105`
|
||||
`All Cargo Prices` and descriptors `177..179` `All Cargo Production` / `All Factory Production`
|
||||
/ `All Farm/Mine Production` import through bounded cargo override surfaces, and the grounded
|
||||
named cargo-production strip `180..229` now imports through named cargo production overrides too
|
||||
- the named cargo-price strip `106..176` now sits on explicit
|
||||
`blocked_evidence_blocked_descriptor` parity instead of generic unmapped-descriptor frontier
|
||||
- the add-building strip `503..519` is now explicitly classified as recovered shell-owned parity
|
||||
with tracked fixture coverage, not generic unresolved descriptor residue
|
||||
- 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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"fixture_id": "packed-event-add-building-shell-save-slice-fixture",
|
||||
"source": {
|
||||
"kind": "captured-runtime",
|
||||
"description": "Fixture pinning the explicit shell-owned descriptor frontier for recovered add-building rows."
|
||||
},
|
||||
"state_save_slice_path": "packed-event-add-building-shell-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_parity_only_record_count": 1,
|
||||
"packed_event_blocked_shell_owned_descriptor_count": 1,
|
||||
"event_runtime_record_count": 0,
|
||||
"total_event_record_service_count": 0,
|
||||
"total_trigger_dispatch_count": 1
|
||||
},
|
||||
"expected_state_fragment": {
|
||||
"packed_event_collection": {
|
||||
"records": [
|
||||
{
|
||||
"import_outcome": "blocked_shell_owned_descriptor"
|
||||
}
|
||||
]
|
||||
},
|
||||
"event_runtime_records": []
|
||||
}
|
||||
}
|
||||
110
fixtures/runtime/packed-event-add-building-shell-save-slice.json
Normal file
110
fixtures/runtime/packed-event-add-building-shell-save-slice.json
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"save_slice_id": "packed-event-add-building-shell-save-slice",
|
||||
"source": {
|
||||
"description": "Tracked save-slice document pinning the recovered add-building strip on the explicit shell-owned descriptor frontier.",
|
||||
"original_save_filename": "captured-add-building-shell.gms",
|
||||
"original_save_sha256": "add-building-shell-sample-sha256",
|
||||
"notes": [
|
||||
"tracked as JSON save-slice document rather than raw .smp",
|
||||
"pins descriptor 503 as recovered shell-owned parity instead of unresolved residue"
|
||||
]
|
||||
},
|
||||
"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,
|
||||
"company_roster": null,
|
||||
"chairman_profile_table": null,
|
||||
"special_conditions_table": null,
|
||||
"event_runtime_collection": {
|
||||
"source_kind": "packed-event-runtime-collection",
|
||||
"mechanism_family": "classic-save-rehydrate-v1",
|
||||
"mechanism_confidence": "grounded",
|
||||
"container_profile_family": "rt3-classic-save-container-v1",
|
||||
"metadata_tag_offset": 28928,
|
||||
"records_tag_offset": 29184,
|
||||
"close_tag_offset": 29696,
|
||||
"packed_state_version": 1001,
|
||||
"packed_state_version_hex": "0x000003e9",
|
||||
"live_id_bound": 75,
|
||||
"live_record_count": 1,
|
||||
"live_entry_ids": [75],
|
||||
"decoded_record_count": 1,
|
||||
"imported_runtime_record_count": 0,
|
||||
"records": [
|
||||
{
|
||||
"record_index": 0,
|
||||
"live_entry_id": 75,
|
||||
"payload_offset": 29186,
|
||||
"payload_len": 120,
|
||||
"decode_status": "parity_only",
|
||||
"payload_family": "real_packed_v1",
|
||||
"trigger_kind": 7,
|
||||
"active": null,
|
||||
"marks_collection_dirty": null,
|
||||
"one_shot": false,
|
||||
"compact_control": {
|
||||
"mode_byte_0x7ef": 6,
|
||||
"primary_selector_0x7f0": 0,
|
||||
"grouped_mode_0x7f4": 2,
|
||||
"one_shot_header_0x7f5": 1,
|
||||
"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": 503,
|
||||
"descriptor_label": "Add Building Slot 1",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "world_building_spawn",
|
||||
"opcode": 3,
|
||||
"raw_scalar_value": 1,
|
||||
"value_byte_0x09": 0,
|
||||
"value_dword_0x0d": 0,
|
||||
"value_byte_0x11": 0,
|
||||
"value_byte_0x12": 0,
|
||||
"value_word_0x14": 0,
|
||||
"value_word_0x16": 0,
|
||||
"row_shape": "scalar_assignment",
|
||||
"semantic_family": "scalar_assignment",
|
||||
"semantic_preview": "Set Add Building Slot 1 to 1",
|
||||
"locomotive_name": null,
|
||||
"notes": [
|
||||
"descriptor recovered in the checked-in effect table as shell_owned parity"
|
||||
]
|
||||
}
|
||||
],
|
||||
"decoded_conditions": [],
|
||||
"decoded_actions": [],
|
||||
"executable_import_ready": false,
|
||||
"notes": [
|
||||
"add-building descriptor is recovered but remains shell-owned parity"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"notes": [
|
||||
"recovered add-building descriptor sample"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"fixture_id": "packed-event-runtime-variable-condition-overlay-fixture",
|
||||
"source": {
|
||||
"kind": "captured-runtime",
|
||||
"description": "Fixture proving runtime-variable ordinary conditions gate imported records through bounded world/company/player/territory variable surfaces."
|
||||
},
|
||||
"state_import_path": "packed-event-runtime-variable-condition-overlay.json",
|
||||
"commands": [
|
||||
{
|
||||
"kind": "service_trigger_kind",
|
||||
"trigger_kind": 7
|
||||
},
|
||||
{
|
||||
"kind": "service_trigger_kind",
|
||||
"trigger_kind": 8
|
||||
}
|
||||
],
|
||||
"expected_summary": {
|
||||
"calendar_projection_source": "base-snapshot-preserved",
|
||||
"calendar_projection_is_placeholder": false,
|
||||
"company_count": 3,
|
||||
"player_count": 2,
|
||||
"territory_count": 2,
|
||||
"packed_event_collection_present": true,
|
||||
"packed_event_record_count": 5,
|
||||
"packed_event_decoded_record_count": 5,
|
||||
"packed_event_imported_runtime_record_count": 5,
|
||||
"event_runtime_record_count": 5,
|
||||
"world_runtime_variable_count": 2,
|
||||
"company_runtime_variable_owner_count": 1,
|
||||
"player_runtime_variable_owner_count": 1,
|
||||
"territory_runtime_variable_owner_count": 1,
|
||||
"total_event_record_service_count": 5,
|
||||
"total_trigger_dispatch_count": 2
|
||||
},
|
||||
"expected_state_fragment": {
|
||||
"world_runtime_variables": {
|
||||
"1": 111,
|
||||
"2": 211
|
||||
},
|
||||
"company_runtime_variables": {
|
||||
"1": {
|
||||
"2": 222
|
||||
}
|
||||
},
|
||||
"player_runtime_variables": {
|
||||
"1": {
|
||||
"3": 333
|
||||
}
|
||||
},
|
||||
"territory_runtime_variables": {
|
||||
"7": {
|
||||
"4": 444
|
||||
}
|
||||
},
|
||||
"packed_event_collection": {
|
||||
"records": [
|
||||
{
|
||||
"import_outcome": "imported"
|
||||
},
|
||||
{
|
||||
"import_outcome": "imported"
|
||||
},
|
||||
{
|
||||
"import_outcome": "imported"
|
||||
},
|
||||
{
|
||||
"import_outcome": "imported"
|
||||
},
|
||||
{
|
||||
"import_outcome": "imported",
|
||||
"decoded_conditions": [
|
||||
{
|
||||
"kind": "world_variable_threshold",
|
||||
"index": 1,
|
||||
"comparator": "eq",
|
||||
"value": 111
|
||||
},
|
||||
{
|
||||
"kind": "company_variable_threshold",
|
||||
"target": {
|
||||
"kind": "selected_company"
|
||||
},
|
||||
"index": 2,
|
||||
"comparator": "eq",
|
||||
"value": 222
|
||||
},
|
||||
{
|
||||
"kind": "player_variable_threshold",
|
||||
"target": {
|
||||
"kind": "selected_player"
|
||||
},
|
||||
"index": 3,
|
||||
"comparator": "eq",
|
||||
"value": 333
|
||||
},
|
||||
{
|
||||
"kind": "territory_variable_threshold",
|
||||
"target": {
|
||||
"kind": "all_territories"
|
||||
},
|
||||
"index": 4,
|
||||
"comparator": "eq",
|
||||
"value": 444
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"event_runtime_records": [
|
||||
{
|
||||
"record_id": 71,
|
||||
"service_count": 1
|
||||
},
|
||||
{
|
||||
"record_id": 72,
|
||||
"service_count": 1
|
||||
},
|
||||
{
|
||||
"record_id": 73,
|
||||
"service_count": 1
|
||||
},
|
||||
{
|
||||
"record_id": 74,
|
||||
"service_count": 1
|
||||
},
|
||||
{
|
||||
"record_id": 75,
|
||||
"service_count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"import_id": "packed-event-runtime-variable-condition-overlay",
|
||||
"source": {
|
||||
"description": "Overlay import combining company/player/territory runtime context with the runtime-variable condition sample."
|
||||
},
|
||||
"base_snapshot_path": "packed-event-territory-player-overlay-base-snapshot.json",
|
||||
"save_slice_path": "packed-event-runtime-variable-condition-save-slice.json"
|
||||
}
|
||||
|
|
@ -0,0 +1,479 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"save_slice_id": "packed-event-runtime-variable-condition-save-slice",
|
||||
"source": {
|
||||
"description": "Tracked save-slice document proving runtime-variable ordinary conditions gate a packed world-variable effect.",
|
||||
"original_save_filename": "captured-runtime-variable-condition.gms",
|
||||
"original_save_sha256": "runtime-variable-condition-sample-sha256",
|
||||
"notes": [
|
||||
"tracked as JSON save-slice document rather than raw .smp",
|
||||
"uses overlay-backed company/player/territory context while proving world/company/player/territory runtime-variable conditions together"
|
||||
]
|
||||
},
|
||||
"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,
|
||||
"company_roster": null,
|
||||
"chairman_profile_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": 33280,
|
||||
"records_tag_offset": 33536,
|
||||
"close_tag_offset": 34944,
|
||||
"packed_state_version": 1001,
|
||||
"packed_state_version_hex": "0x000003e9",
|
||||
"live_id_bound": 75,
|
||||
"live_record_count": 5,
|
||||
"live_entry_ids": [71, 72, 73, 74, 75],
|
||||
"decoded_record_count": 5,
|
||||
"imported_runtime_record_count": 5,
|
||||
"records": [
|
||||
{
|
||||
"record_index": 0,
|
||||
"live_entry_id": 71,
|
||||
"payload_offset": 33568,
|
||||
"payload_len": 160,
|
||||
"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": [1, 0, 0, 0],
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"group_index": 0,
|
||||
"row_index": 0,
|
||||
"descriptor_id": 39,
|
||||
"descriptor_label": "Game Variable 1",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "runtime_variable_scalar",
|
||||
"opcode": 3,
|
||||
"raw_scalar_value": 111,
|
||||
"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 Game Variable 1 to 111",
|
||||
"grouped_target_subject": "whole_game",
|
||||
"grouped_target_scope": "whole_game",
|
||||
"recovered_locomotive_id": null,
|
||||
"locomotive_name": null,
|
||||
"notes": [
|
||||
"descriptor recovered from checked-in EventEffects semantic catalog"
|
||||
]
|
||||
}
|
||||
],
|
||||
"decoded_actions": [
|
||||
{
|
||||
"kind": "set_world_variable",
|
||||
"index": 1,
|
||||
"value": 111
|
||||
}
|
||||
],
|
||||
"executable_import_ready": true,
|
||||
"notes": [
|
||||
"runtime variable world sample"
|
||||
]
|
||||
},
|
||||
{
|
||||
"record_index": 1,
|
||||
"live_entry_id": 72,
|
||||
"payload_offset": 33728,
|
||||
"payload_len": 160,
|
||||
"decode_status": "executable",
|
||||
"payload_family": "real_packed_v1",
|
||||
"trigger_kind": 7,
|
||||
"one_shot": false,
|
||||
"compact_control": {
|
||||
"mode_byte_0x7ef": 7,
|
||||
"primary_selector_0x7f0": 99,
|
||||
"grouped_mode_0x7f4": 2,
|
||||
"one_shot_header_0x7f5": 0,
|
||||
"modifier_flag_0x7f9": 1,
|
||||
"modifier_flag_0x7fa": 0,
|
||||
"grouped_target_scope_ordinals_0x7fb": [1, 1, 1, 1],
|
||||
"grouped_scope_checkboxes_0x7ff": [1, 0, 0, 0],
|
||||
"summary_toggle_0x800": 1,
|
||||
"grouped_territory_selectors_0x80f": [-1, -1, -1, -1]
|
||||
},
|
||||
"text_bands": [],
|
||||
"standalone_condition_row_count": 0,
|
||||
"standalone_condition_rows": [],
|
||||
"grouped_effect_row_counts": [1, 0, 0, 0],
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"group_index": 0,
|
||||
"row_index": 0,
|
||||
"descriptor_id": 44,
|
||||
"descriptor_label": "Company Variable 2",
|
||||
"target_mask_bits": 1,
|
||||
"parameter_family": "runtime_variable_scalar",
|
||||
"opcode": 3,
|
||||
"raw_scalar_value": 222,
|
||||
"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 Company Variable 2 to 222",
|
||||
"grouped_target_subject": "company",
|
||||
"grouped_target_scope": "selected_company",
|
||||
"recovered_locomotive_id": null,
|
||||
"locomotive_name": null,
|
||||
"notes": [
|
||||
"descriptor recovered from checked-in EventEffects semantic catalog"
|
||||
]
|
||||
}
|
||||
],
|
||||
"decoded_actions": [
|
||||
{
|
||||
"kind": "set_company_variable",
|
||||
"target": {
|
||||
"kind": "selected_company"
|
||||
},
|
||||
"index": 2,
|
||||
"value": 222
|
||||
}
|
||||
],
|
||||
"executable_import_ready": true,
|
||||
"notes": [
|
||||
"runtime variable company sample"
|
||||
]
|
||||
},
|
||||
{
|
||||
"record_index": 2,
|
||||
"live_entry_id": 73,
|
||||
"payload_offset": 33888,
|
||||
"payload_len": 160,
|
||||
"decode_status": "executable",
|
||||
"payload_family": "real_packed_v1",
|
||||
"trigger_kind": 7,
|
||||
"one_shot": false,
|
||||
"compact_control": {
|
||||
"mode_byte_0x7ef": 7,
|
||||
"primary_selector_0x7f0": 12,
|
||||
"grouped_mode_0x7f4": 2,
|
||||
"one_shot_header_0x7f5": 0,
|
||||
"modifier_flag_0x7f9": 0,
|
||||
"modifier_flag_0x7fa": 0,
|
||||
"grouped_target_scope_ordinals_0x7fb": [1, 1, 1, 1],
|
||||
"grouped_scope_checkboxes_0x7ff": [1, 0, 0, 0],
|
||||
"summary_toggle_0x800": 1,
|
||||
"grouped_territory_selectors_0x80f": [-1, -1, -1, -1]
|
||||
},
|
||||
"text_bands": [],
|
||||
"standalone_condition_row_count": 0,
|
||||
"standalone_condition_rows": [],
|
||||
"grouped_effect_row_counts": [1, 0, 0, 0],
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"group_index": 0,
|
||||
"row_index": 0,
|
||||
"descriptor_id": 49,
|
||||
"descriptor_label": "Player Variable 3",
|
||||
"target_mask_bits": 2,
|
||||
"parameter_family": "runtime_variable_scalar",
|
||||
"opcode": 3,
|
||||
"raw_scalar_value": 333,
|
||||
"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 Player Variable 3 to 333",
|
||||
"grouped_target_subject": "player",
|
||||
"grouped_target_scope": "selected_player",
|
||||
"recovered_locomotive_id": null,
|
||||
"locomotive_name": null,
|
||||
"notes": [
|
||||
"descriptor recovered from checked-in EventEffects semantic catalog"
|
||||
]
|
||||
}
|
||||
],
|
||||
"decoded_actions": [
|
||||
{
|
||||
"kind": "set_player_variable",
|
||||
"target": {
|
||||
"kind": "selected_player"
|
||||
},
|
||||
"index": 3,
|
||||
"value": 333
|
||||
}
|
||||
],
|
||||
"executable_import_ready": true,
|
||||
"notes": [
|
||||
"runtime variable player sample"
|
||||
]
|
||||
},
|
||||
{
|
||||
"record_index": 3,
|
||||
"live_entry_id": 74,
|
||||
"payload_offset": 34048,
|
||||
"payload_len": 160,
|
||||
"decode_status": "executable",
|
||||
"payload_family": "real_packed_v1",
|
||||
"trigger_kind": 7,
|
||||
"one_shot": false,
|
||||
"compact_control": {
|
||||
"mode_byte_0x7ef": 7,
|
||||
"primary_selector_0x7f0": 12,
|
||||
"grouped_mode_0x7f4": 2,
|
||||
"one_shot_header_0x7f5": 0,
|
||||
"modifier_flag_0x7f9": 0,
|
||||
"modifier_flag_0x7fa": 0,
|
||||
"grouped_target_scope_ordinals_0x7fb": [1, 1, 1, 1],
|
||||
"grouped_scope_checkboxes_0x7ff": [1, 0, 0, 0],
|
||||
"summary_toggle_0x800": 1,
|
||||
"grouped_territory_selectors_0x80f": [7, -1, -1, -1]
|
||||
},
|
||||
"text_bands": [],
|
||||
"standalone_condition_row_count": 0,
|
||||
"standalone_condition_rows": [],
|
||||
"grouped_effect_row_counts": [1, 0, 0, 0],
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"group_index": 0,
|
||||
"row_index": 0,
|
||||
"descriptor_id": 54,
|
||||
"descriptor_label": "Territory Variable 4",
|
||||
"target_mask_bits": 4,
|
||||
"parameter_family": "runtime_variable_scalar",
|
||||
"opcode": 3,
|
||||
"raw_scalar_value": 444,
|
||||
"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 Territory Variable 4 to 444",
|
||||
"grouped_target_subject": "territory",
|
||||
"grouped_target_scope": "named_territory",
|
||||
"recovered_locomotive_id": null,
|
||||
"locomotive_name": null,
|
||||
"notes": [
|
||||
"descriptor recovered from checked-in EventEffects semantic catalog"
|
||||
]
|
||||
}
|
||||
],
|
||||
"decoded_actions": [
|
||||
{
|
||||
"kind": "set_territory_variable",
|
||||
"target": {
|
||||
"kind": "ids",
|
||||
"ids": [7]
|
||||
},
|
||||
"index": 4,
|
||||
"value": 444
|
||||
}
|
||||
],
|
||||
"executable_import_ready": true,
|
||||
"notes": [
|
||||
"runtime variable territory sample"
|
||||
]
|
||||
},
|
||||
{
|
||||
"record_index": 4,
|
||||
"live_entry_id": 75,
|
||||
"payload_offset": 34208,
|
||||
"payload_len": 224,
|
||||
"decode_status": "executable",
|
||||
"payload_family": "real_packed_v1",
|
||||
"trigger_kind": 8,
|
||||
"one_shot": false,
|
||||
"compact_control": {
|
||||
"mode_byte_0x7ef": 6,
|
||||
"primary_selector_0x7f0": 99,
|
||||
"grouped_mode_0x7f4": 2,
|
||||
"one_shot_header_0x7f5": 0,
|
||||
"modifier_flag_0x7f9": 2,
|
||||
"modifier_flag_0x7fa": 2,
|
||||
"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": 4,
|
||||
"standalone_condition_rows": [
|
||||
{
|
||||
"row_index": 0,
|
||||
"raw_condition_id": 2241,
|
||||
"subtype": 4,
|
||||
"flag_bytes": [111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"candidate_name": null,
|
||||
"comparator": "eq",
|
||||
"metric": "Game Variable 1",
|
||||
"semantic_family": "numeric_threshold",
|
||||
"semantic_preview": "Test Game Variable 1 == 111",
|
||||
"requires_candidate_name_binding": false,
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
"row_index": 1,
|
||||
"raw_condition_id": 2246,
|
||||
"subtype": 4,
|
||||
"flag_bytes": [222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"candidate_name": null,
|
||||
"comparator": "eq",
|
||||
"metric": "Company Variable 2",
|
||||
"semantic_family": "numeric_threshold",
|
||||
"semantic_preview": "Test Company Variable 2 == 222",
|
||||
"requires_candidate_name_binding": false,
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
"row_index": 2,
|
||||
"raw_condition_id": 2251,
|
||||
"subtype": 4,
|
||||
"flag_bytes": [77, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"candidate_name": null,
|
||||
"comparator": "eq",
|
||||
"metric": "Player Variable 3",
|
||||
"semantic_family": "numeric_threshold",
|
||||
"semantic_preview": "Test Player Variable 3 == 333",
|
||||
"requires_candidate_name_binding": false,
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
"row_index": 3,
|
||||
"raw_condition_id": 2256,
|
||||
"subtype": 4,
|
||||
"flag_bytes": [188, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"candidate_name": null,
|
||||
"comparator": "eq",
|
||||
"metric": "Territory Variable 4",
|
||||
"semantic_family": "numeric_threshold",
|
||||
"semantic_preview": "Test Territory Variable 4 == 444",
|
||||
"requires_candidate_name_binding": false,
|
||||
"notes": []
|
||||
}
|
||||
],
|
||||
"negative_sentinel_scope": {
|
||||
"company_test_scope": "selected_company_only",
|
||||
"player_test_scope": "selected_player_only",
|
||||
"territory_scope_selector_is_0x63": true,
|
||||
"source_row_indexes": [0]
|
||||
},
|
||||
"grouped_effect_row_counts": [1, 0, 0, 0],
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"group_index": 0,
|
||||
"row_index": 0,
|
||||
"descriptor_id": 40,
|
||||
"descriptor_label": "Game Variable 2",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "runtime_variable_scalar",
|
||||
"opcode": 3,
|
||||
"raw_scalar_value": 211,
|
||||
"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 Game Variable 2 to 211",
|
||||
"grouped_target_subject": "whole_game",
|
||||
"grouped_target_scope": "whole_game",
|
||||
"recovered_locomotive_id": null,
|
||||
"locomotive_name": null,
|
||||
"notes": [
|
||||
"descriptor recovered from checked-in EventEffects semantic catalog"
|
||||
]
|
||||
}
|
||||
],
|
||||
"decoded_conditions": [
|
||||
{
|
||||
"kind": "world_variable_threshold",
|
||||
"index": 1,
|
||||
"comparator": "eq",
|
||||
"value": 111
|
||||
},
|
||||
{
|
||||
"kind": "company_variable_threshold",
|
||||
"target": {
|
||||
"kind": "condition_true_company"
|
||||
},
|
||||
"index": 2,
|
||||
"comparator": "eq",
|
||||
"value": 222
|
||||
},
|
||||
{
|
||||
"kind": "player_variable_threshold",
|
||||
"target": {
|
||||
"kind": "selected_player"
|
||||
},
|
||||
"index": 3,
|
||||
"comparator": "eq",
|
||||
"value": 333
|
||||
},
|
||||
{
|
||||
"kind": "territory_variable_threshold",
|
||||
"target": {
|
||||
"kind": "all_territories"
|
||||
},
|
||||
"index": 4,
|
||||
"comparator": "eq",
|
||||
"value": 444
|
||||
}
|
||||
],
|
||||
"decoded_actions": [
|
||||
{
|
||||
"kind": "set_world_variable",
|
||||
"index": 2,
|
||||
"value": 211
|
||||
}
|
||||
],
|
||||
"executable_import_ready": true,
|
||||
"notes": [
|
||||
"runtime variable conditions gate a world-variable effect"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"notes": [
|
||||
"runtime variable condition executable sample"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -208,6 +208,7 @@ def classify(
|
|||
executable_in_runtime = True
|
||||
elif 503 <= descriptor_id <= 519:
|
||||
parameter_family = "world_building_spawn"
|
||||
label = f"Add Building Slot {descriptor_id - 502}"
|
||||
runtime_status = "shell_owned"
|
||||
elif signature_byte_0x63 == 0 and signature_byte_0x64 == 0x8F:
|
||||
parameter_family = "runtime_variable_scalar"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue