Implement whole-game packed event conditions
This commit is contained in:
parent
e9c8bfbb9c
commit
cc54a00e25
16 changed files with 1184 additions and 42 deletions
10
README.md
10
README.md
|
|
@ -25,9 +25,13 @@ named-territory binding now executes, and the runtime now also carries the minim
|
||||||
train roster and opaque economic-status lane needed for real descriptors `8` `Economic Status`, `9`
|
train roster and opaque economic-status lane needed for real descriptors `8` `Economic Status`, `9`
|
||||||
`Confiscate All`, and `15` `Retire Train` to execute through the same path. Descriptor `3`
|
`Confiscate All`, and `15` `Retire Train` to execute through the same path. Descriptor `3`
|
||||||
`Territory - Allow All` now executes too, reinterpreted as company-to-territory access rights
|
`Territory - Allow All` now executes too, reinterpreted as company-to-territory access rights
|
||||||
rather than a territory-owned policy bit. Shell purchase-flow and selected-profile parity remain
|
rather than a territory-owned policy bit. Whole-game ordinary-condition execution now exists too:
|
||||||
out of scope. Mixed supported/unsupported real rows still stay parity-only. The PE32 hook remains
|
special-condition thresholds, candidate-availability thresholds, and economic-status-code
|
||||||
useful as capture and integration tooling, but it is no longer the main execution milestone.
|
thresholds now gate imported runtime records through the same service path, with explicit unmapped
|
||||||
|
world-condition and world-descriptor frontier buckets where current checked-in metadata still
|
||||||
|
stops. Shell purchase-flow and selected-profile parity remain out of scope. Mixed
|
||||||
|
supported/unsupported real rows still stay parity-only. The PE32 hook remains useful as capture
|
||||||
|
and integration tooling, but it is no longer the main execution milestone.
|
||||||
|
|
||||||
## Project Docs
|
## Project Docs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,10 +118,14 @@ pub struct ExpectedRuntimeSummary {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub packed_event_blocked_unmapped_ordinary_condition_count: Option<usize>,
|
pub packed_event_blocked_unmapped_ordinary_condition_count: Option<usize>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub packed_event_blocked_unmapped_world_condition_count: Option<usize>,
|
||||||
|
#[serde(default)]
|
||||||
pub packed_event_blocked_missing_compact_control_count: Option<usize>,
|
pub packed_event_blocked_missing_compact_control_count: Option<usize>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub packed_event_blocked_unmapped_real_descriptor_count: Option<usize>,
|
pub packed_event_blocked_unmapped_real_descriptor_count: Option<usize>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub packed_event_blocked_unmapped_world_descriptor_count: Option<usize>,
|
||||||
|
#[serde(default)]
|
||||||
pub packed_event_blocked_territory_access_variant_count: Option<usize>,
|
pub packed_event_blocked_territory_access_variant_count: Option<usize>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub packed_event_blocked_territory_access_scope_count: Option<usize>,
|
pub packed_event_blocked_territory_access_scope_count: Option<usize>,
|
||||||
|
|
@ -601,6 +605,14 @@ impl ExpectedRuntimeSummary {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(count) = self.packed_event_blocked_unmapped_world_condition_count {
|
||||||
|
if actual.packed_event_blocked_unmapped_world_condition_count != count {
|
||||||
|
mismatches.push(format!(
|
||||||
|
"packed_event_blocked_unmapped_world_condition_count mismatch: expected {count}, got {}",
|
||||||
|
actual.packed_event_blocked_unmapped_world_condition_count
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
if let Some(count) = self.packed_event_blocked_missing_compact_control_count {
|
if let Some(count) = self.packed_event_blocked_missing_compact_control_count {
|
||||||
if actual.packed_event_blocked_missing_compact_control_count != count {
|
if actual.packed_event_blocked_missing_compact_control_count != count {
|
||||||
mismatches.push(format!(
|
mismatches.push(format!(
|
||||||
|
|
@ -617,6 +629,14 @@ impl ExpectedRuntimeSummary {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(count) = self.packed_event_blocked_unmapped_world_descriptor_count {
|
||||||
|
if actual.packed_event_blocked_unmapped_world_descriptor_count != count {
|
||||||
|
mismatches.push(format!(
|
||||||
|
"packed_event_blocked_unmapped_world_descriptor_count mismatch: expected {count}, got {}",
|
||||||
|
actual.packed_event_blocked_unmapped_world_descriptor_count
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
if let Some(count) = self.packed_event_blocked_territory_access_variant_count {
|
if let Some(count) = self.packed_event_blocked_territory_access_variant_count {
|
||||||
if actual.packed_event_blocked_territory_access_variant_count != count {
|
if actual.packed_event_blocked_territory_access_variant_count != count {
|
||||||
mismatches.push(format!(
|
mismatches.push(format!(
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,7 @@ enum ImportBlocker {
|
||||||
MissingTerritoryContext,
|
MissingTerritoryContext,
|
||||||
NamedTerritoryBinding,
|
NamedTerritoryBinding,
|
||||||
UnmappedOrdinaryCondition,
|
UnmappedOrdinaryCondition,
|
||||||
|
UnmappedWorldCondition,
|
||||||
MissingTrainContext,
|
MissingTrainContext,
|
||||||
MissingTrainTerritoryContext,
|
MissingTrainTerritoryContext,
|
||||||
}
|
}
|
||||||
|
|
@ -923,7 +924,11 @@ fn packed_record_condition_scope_import_blocker(
|
||||||
.count();
|
.count();
|
||||||
if ordinary_condition_row_count != 0 {
|
if ordinary_condition_row_count != 0 {
|
||||||
if ordinary_condition_row_count != record.decoded_conditions.len() {
|
if ordinary_condition_row_count != record.decoded_conditions.len() {
|
||||||
return Some(ImportBlocker::UnmappedOrdinaryCondition);
|
return Some(if record_has_world_state_condition_rows(record) {
|
||||||
|
ImportBlocker::UnmappedWorldCondition
|
||||||
|
} else {
|
||||||
|
ImportBlocker::UnmappedOrdinaryCondition
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (!company_context.has_territory_context)
|
if (!company_context.has_territory_context)
|
||||||
&& (record
|
&& (record
|
||||||
|
|
@ -1205,6 +1210,30 @@ fn lower_condition_targets_in_condition(
|
||||||
comparator: *comparator,
|
comparator: *comparator,
|
||||||
value: *value,
|
value: *value,
|
||||||
},
|
},
|
||||||
|
RuntimeCondition::SpecialConditionThreshold {
|
||||||
|
label,
|
||||||
|
comparator,
|
||||||
|
value,
|
||||||
|
} => RuntimeCondition::SpecialConditionThreshold {
|
||||||
|
label: label.clone(),
|
||||||
|
comparator: *comparator,
|
||||||
|
value: *value,
|
||||||
|
},
|
||||||
|
RuntimeCondition::CandidateAvailabilityThreshold {
|
||||||
|
name,
|
||||||
|
comparator,
|
||||||
|
value,
|
||||||
|
} => RuntimeCondition::CandidateAvailabilityThreshold {
|
||||||
|
name: name.clone(),
|
||||||
|
comparator: *comparator,
|
||||||
|
value: *value,
|
||||||
|
},
|
||||||
|
RuntimeCondition::EconomicStatusCodeThreshold { comparator, value } => {
|
||||||
|
RuntimeCondition::EconomicStatusCodeThreshold {
|
||||||
|
comparator: *comparator,
|
||||||
|
value: *value,
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1281,7 +1310,10 @@ fn condition_uses_condition_true_company(condition: &RuntimeCondition) -> bool {
|
||||||
| RuntimeCondition::CompanyTerritoryNumericThreshold { target, .. } => {
|
| RuntimeCondition::CompanyTerritoryNumericThreshold { target, .. } => {
|
||||||
matches!(target, RuntimeCompanyTarget::ConditionTrueCompany)
|
matches!(target, RuntimeCompanyTarget::ConditionTrueCompany)
|
||||||
}
|
}
|
||||||
RuntimeCondition::TerritoryNumericThreshold { .. } => false,
|
RuntimeCondition::TerritoryNumericThreshold { .. }
|
||||||
|
| RuntimeCondition::SpecialConditionThreshold { .. }
|
||||||
|
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
|
||||||
|
| RuntimeCondition::EconomicStatusCodeThreshold { .. } => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1351,8 +1383,11 @@ fn smp_runtime_effect_to_runtime_effect(
|
||||||
territory,
|
territory,
|
||||||
value,
|
value,
|
||||||
} => {
|
} => {
|
||||||
if !company_target_allowed_for_import(target, company_context, allow_condition_true_company)
|
if !company_target_allowed_for_import(
|
||||||
{
|
target,
|
||||||
|
company_context,
|
||||||
|
allow_condition_true_company,
|
||||||
|
) {
|
||||||
Err(company_target_import_error_message(target, company_context))
|
Err(company_target_import_error_message(target, company_context))
|
||||||
} else if territory_target_import_blocker(territory, company_context).is_some() {
|
} else if territory_target_import_blocker(territory, company_context).is_some() {
|
||||||
Err("packed effect requires territory runtime context".to_string())
|
Err("packed effect requires territory runtime context".to_string())
|
||||||
|
|
@ -1657,6 +1692,9 @@ fn company_target_import_error_message(
|
||||||
Some(ImportBlocker::UnmappedOrdinaryCondition) => {
|
Some(ImportBlocker::UnmappedOrdinaryCondition) => {
|
||||||
"packed ordinary condition is not yet mapped".to_string()
|
"packed ordinary condition is not yet mapped".to_string()
|
||||||
}
|
}
|
||||||
|
Some(ImportBlocker::UnmappedWorldCondition) => {
|
||||||
|
"packed whole-game condition is not yet mapped".to_string()
|
||||||
|
}
|
||||||
Some(ImportBlocker::MissingTrainContext) => {
|
Some(ImportBlocker::MissingTrainContext) => {
|
||||||
"packed effect requires runtime train context".to_string()
|
"packed effect requires runtime train context".to_string()
|
||||||
}
|
}
|
||||||
|
|
@ -1788,12 +1826,23 @@ fn determine_packed_event_import_outcome(
|
||||||
{
|
{
|
||||||
return "blocked_retire_train_variant".to_string();
|
return "blocked_retire_train_variant".to_string();
|
||||||
}
|
}
|
||||||
|
if record
|
||||||
|
.grouped_effect_rows
|
||||||
|
.iter()
|
||||||
|
.any(real_grouped_row_is_world_state_family)
|
||||||
|
{
|
||||||
|
return "blocked_unmapped_world_descriptor".to_string();
|
||||||
|
}
|
||||||
return if record
|
return if record
|
||||||
.standalone_condition_rows
|
.standalone_condition_rows
|
||||||
.iter()
|
.iter()
|
||||||
.any(|row| row.raw_condition_id >= 0)
|
.any(|row| row.raw_condition_id >= 0)
|
||||||
{
|
{
|
||||||
"blocked_unmapped_ordinary_condition".to_string()
|
if record_has_world_state_condition_rows(record) {
|
||||||
|
"blocked_unmapped_world_condition".to_string()
|
||||||
|
} else {
|
||||||
|
"blocked_unmapped_ordinary_condition".to_string()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
"blocked_unmapped_real_descriptor".to_string()
|
"blocked_unmapped_real_descriptor".to_string()
|
||||||
};
|
};
|
||||||
|
|
@ -1814,6 +1863,58 @@ fn determine_packed_event_import_outcome(
|
||||||
"blocked_unsupported_decode".to_string()
|
"blocked_unsupported_decode".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn record_has_world_state_condition_rows(record: &SmpLoadedPackedEventRecordSummary) -> bool {
|
||||||
|
record
|
||||||
|
.decoded_conditions
|
||||||
|
.iter()
|
||||||
|
.any(runtime_condition_is_world_state)
|
||||||
|
|| record
|
||||||
|
.standalone_condition_rows
|
||||||
|
.iter()
|
||||||
|
.any(ordinary_condition_row_is_world_state_family)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtime_condition_is_world_state(condition: &RuntimeCondition) -> bool {
|
||||||
|
matches!(
|
||||||
|
condition,
|
||||||
|
RuntimeCondition::SpecialConditionThreshold { .. }
|
||||||
|
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
|
||||||
|
| RuntimeCondition::EconomicStatusCodeThreshold { .. }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ordinary_condition_row_is_world_state_family(
|
||||||
|
row: &SmpLoadedPackedEventConditionRowSummary,
|
||||||
|
) -> bool {
|
||||||
|
row.metric.as_deref().is_some_and(|metric| {
|
||||||
|
metric.contains("Special Condition")
|
||||||
|
|| metric.contains("Candidate Availability")
|
||||||
|
|| metric.contains("Economic Status")
|
||||||
|
|| metric.contains("World Flag")
|
||||||
|
}) || row
|
||||||
|
.semantic_family
|
||||||
|
.as_deref()
|
||||||
|
.is_some_and(|family| family == "world_state_threshold" || family == "world_flag_equals")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn real_grouped_row_is_world_state_family(
|
||||||
|
row: &SmpLoadedPackedEventGroupedEffectRowSummary,
|
||||||
|
) -> bool {
|
||||||
|
row.target_mask_bits == Some(0x08)
|
||||||
|
|| row.parameter_family.as_deref().is_some_and(|family| {
|
||||||
|
family.starts_with("whole_game_")
|
||||||
|
|| family.starts_with("special_condition")
|
||||||
|
|| family.starts_with("candidate_availability")
|
||||||
|
|| family.starts_with("world_flag")
|
||||||
|
})
|
||||||
|
|| row.descriptor_label.as_deref().is_some_and(|label| {
|
||||||
|
label.contains("Special Condition")
|
||||||
|
|| label.contains("Candidate Availability")
|
||||||
|
|| label.contains("World Flag")
|
||||||
|
|| label == "Economic Status"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn packed_record_company_target_import_blocker(
|
fn packed_record_company_target_import_blocker(
|
||||||
record: &SmpLoadedPackedEventRecordSummary,
|
record: &SmpLoadedPackedEventRecordSummary,
|
||||||
company_context: &ImportRuntimeContext,
|
company_context: &ImportRuntimeContext,
|
||||||
|
|
@ -1858,6 +1959,9 @@ fn runtime_condition_company_target_import_blocker(
|
||||||
target, territory, ..
|
target, territory, ..
|
||||||
} => company_target_import_blocker(target, company_context)
|
} => company_target_import_blocker(target, company_context)
|
||||||
.or_else(|| territory_target_import_blocker(territory, company_context)),
|
.or_else(|| territory_target_import_blocker(territory, company_context)),
|
||||||
|
RuntimeCondition::SpecialConditionThreshold { .. }
|
||||||
|
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
|
||||||
|
| RuntimeCondition::EconomicStatusCodeThreshold { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1896,6 +2000,7 @@ fn company_target_import_outcome(blocker: ImportBlocker) -> &'static str {
|
||||||
ImportBlocker::MissingTerritoryContext => "blocked_missing_territory_context",
|
ImportBlocker::MissingTerritoryContext => "blocked_missing_territory_context",
|
||||||
ImportBlocker::NamedTerritoryBinding => "blocked_named_territory_binding",
|
ImportBlocker::NamedTerritoryBinding => "blocked_named_territory_binding",
|
||||||
ImportBlocker::UnmappedOrdinaryCondition => "blocked_unmapped_ordinary_condition",
|
ImportBlocker::UnmappedOrdinaryCondition => "blocked_unmapped_ordinary_condition",
|
||||||
|
ImportBlocker::UnmappedWorldCondition => "blocked_unmapped_world_condition",
|
||||||
ImportBlocker::MissingTrainContext => "blocked_missing_train_context",
|
ImportBlocker::MissingTrainContext => "blocked_missing_train_context",
|
||||||
ImportBlocker::MissingTrainTerritoryContext => "blocked_missing_train_territory_context",
|
ImportBlocker::MissingTrainTerritoryContext => "blocked_missing_train_territory_context",
|
||||||
}
|
}
|
||||||
|
|
@ -4621,8 +4726,10 @@ mod tests {
|
||||||
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
||||||
grouped_effect_rows: vec![real_territory_access_row(
|
grouped_effect_rows: vec![real_territory_access_row(
|
||||||
true,
|
true,
|
||||||
vec!["territory access row is missing company or territory scope"
|
vec![
|
||||||
.to_string()],
|
"territory access row is missing company or territory scope"
|
||||||
|
.to_string(),
|
||||||
|
],
|
||||||
)],
|
)],
|
||||||
decoded_conditions: Vec::new(),
|
decoded_conditions: Vec::new(),
|
||||||
decoded_actions: vec![],
|
decoded_actions: vec![],
|
||||||
|
|
|
||||||
|
|
@ -39,14 +39,13 @@ pub use runtime::{
|
||||||
RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess,
|
RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess,
|
||||||
RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition, RuntimeConditionComparator,
|
RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition, RuntimeConditionComparator,
|
||||||
RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
|
RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
|
||||||
RuntimePackedEventCollectionSummary,
|
RuntimePackedEventCollectionSummary, RuntimePackedEventCompactControlSummary,
|
||||||
RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary,
|
RuntimePackedEventConditionRowSummary, RuntimePackedEventGroupedEffectRowSummary,
|
||||||
RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary,
|
RuntimePackedEventNegativeSentinelScopeSummary, RuntimePackedEventRecordSummary,
|
||||||
RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer,
|
RuntimePackedEventTextBandSummary, RuntimePlayer, RuntimePlayerConditionTestScope,
|
||||||
RuntimePlayerConditionTestScope, RuntimePlayerTarget, RuntimeSaveProfileState,
|
RuntimePlayerTarget, RuntimeSaveProfileState, RuntimeServiceState, RuntimeState,
|
||||||
RuntimeServiceState, RuntimeState, RuntimeTerritory, RuntimeTerritoryMetric,
|
RuntimeTerritory, RuntimeTerritoryMetric, RuntimeTerritoryTarget, RuntimeTrackMetric,
|
||||||
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain,
|
RuntimeTrackPieceCounts, RuntimeTrain, RuntimeWorldRestoreState,
|
||||||
RuntimeWorldRestoreState,
|
|
||||||
};
|
};
|
||||||
pub use smp::{
|
pub use smp::{
|
||||||
SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION, SmpAlignedRuntimeRuleBandLane,
|
SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION, SmpAlignedRuntimeRuleBandLane,
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,20 @@ pub enum RuntimeCondition {
|
||||||
comparator: RuntimeConditionComparator,
|
comparator: RuntimeConditionComparator,
|
||||||
value: i64,
|
value: i64,
|
||||||
},
|
},
|
||||||
|
SpecialConditionThreshold {
|
||||||
|
label: String,
|
||||||
|
comparator: RuntimeConditionComparator,
|
||||||
|
value: i64,
|
||||||
|
},
|
||||||
|
CandidateAvailabilityThreshold {
|
||||||
|
name: String,
|
||||||
|
comparator: RuntimeConditionComparator,
|
||||||
|
value: i64,
|
||||||
|
},
|
||||||
|
EconomicStatusCodeThreshold {
|
||||||
|
comparator: RuntimeConditionComparator,
|
||||||
|
value: i64,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
@ -1235,6 +1249,21 @@ fn validate_runtime_condition(
|
||||||
validate_company_target(target, valid_company_ids)?;
|
validate_company_target(target, valid_company_ids)?;
|
||||||
validate_territory_target(territory, valid_territory_ids)
|
validate_territory_target(territory, valid_territory_ids)
|
||||||
}
|
}
|
||||||
|
RuntimeCondition::SpecialConditionThreshold { label, .. } => {
|
||||||
|
if label.trim().is_empty() {
|
||||||
|
Err("label must not be empty".to_string())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuntimeCondition::CandidateAvailabilityThreshold { name, .. } => {
|
||||||
|
if name.trim().is_empty() {
|
||||||
|
Err("name must not be empty".to_string())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuntimeCondition::EconomicStatusCodeThreshold { .. } => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2126,9 +2126,8 @@ fn parse_real_event_runtime_record_summary(
|
||||||
&& row.raw_scalar_value != 0
|
&& row.raw_scalar_value != 0
|
||||||
&& (!company_target_present || !territory_target_present)
|
&& (!company_target_present || !territory_target_present)
|
||||||
{
|
{
|
||||||
row.notes.push(
|
row.notes
|
||||||
"territory access row is missing company or territory scope".to_string(),
|
.push("territory access row is missing company or territory scope".to_string());
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2927,18 +2926,20 @@ fn runtime_effect_supported_for_save_import(effect: &RuntimeEffect) -> bool {
|
||||||
),
|
),
|
||||||
RuntimeEffect::SetCompanyTerritoryAccess {
|
RuntimeEffect::SetCompanyTerritoryAccess {
|
||||||
target, territory, ..
|
target, territory, ..
|
||||||
} => matches!(
|
} => {
|
||||||
target,
|
matches!(
|
||||||
RuntimeCompanyTarget::AllActive
|
target,
|
||||||
| RuntimeCompanyTarget::Ids { .. }
|
RuntimeCompanyTarget::AllActive
|
||||||
| RuntimeCompanyTarget::HumanCompanies
|
| RuntimeCompanyTarget::Ids { .. }
|
||||||
| RuntimeCompanyTarget::AiCompanies
|
| RuntimeCompanyTarget::HumanCompanies
|
||||||
| RuntimeCompanyTarget::SelectedCompany
|
| RuntimeCompanyTarget::AiCompanies
|
||||||
| RuntimeCompanyTarget::ConditionTrueCompany
|
| RuntimeCompanyTarget::SelectedCompany
|
||||||
) && matches!(
|
| RuntimeCompanyTarget::ConditionTrueCompany
|
||||||
territory,
|
) && matches!(
|
||||||
RuntimeTerritoryTarget::AllTerritories | RuntimeTerritoryTarget::Ids { .. }
|
territory,
|
||||||
),
|
RuntimeTerritoryTarget::AllTerritories | RuntimeTerritoryTarget::Ids { .. }
|
||||||
|
)
|
||||||
|
}
|
||||||
RuntimeEffect::SetCompanyCash { target, .. }
|
RuntimeEffect::SetCompanyCash { target, .. }
|
||||||
| RuntimeEffect::AdjustCompanyCash { target, .. }
|
| RuntimeEffect::AdjustCompanyCash { target, .. }
|
||||||
| RuntimeEffect::AdjustCompanyDebt { target, .. } => matches!(
|
| RuntimeEffect::AdjustCompanyDebt { target, .. } => matches!(
|
||||||
|
|
@ -2961,7 +2962,10 @@ fn runtime_condition_supported_for_save_import(condition: &RuntimeCondition) ->
|
||||||
match condition {
|
match condition {
|
||||||
RuntimeCondition::CompanyNumericThreshold { .. }
|
RuntimeCondition::CompanyNumericThreshold { .. }
|
||||||
| RuntimeCondition::TerritoryNumericThreshold { .. }
|
| RuntimeCondition::TerritoryNumericThreshold { .. }
|
||||||
| RuntimeCondition::CompanyTerritoryNumericThreshold { .. } => true,
|
| RuntimeCondition::CompanyTerritoryNumericThreshold { .. }
|
||||||
|
| RuntimeCondition::SpecialConditionThreshold { .. }
|
||||||
|
| RuntimeCondition::CandidateAvailabilityThreshold { .. }
|
||||||
|
| RuntimeCondition::EconomicStatusCodeThreshold { .. } => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -651,6 +651,46 @@ fn evaluate_record_conditions(
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RuntimeCondition::SpecialConditionThreshold {
|
||||||
|
label,
|
||||||
|
comparator,
|
||||||
|
value,
|
||||||
|
} => {
|
||||||
|
let actual = state
|
||||||
|
.special_conditions
|
||||||
|
.get(label)
|
||||||
|
.copied()
|
||||||
|
.map(i64::from)
|
||||||
|
.unwrap_or(0);
|
||||||
|
if !compare_condition_value(actual, *comparator, *value) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuntimeCondition::CandidateAvailabilityThreshold {
|
||||||
|
name,
|
||||||
|
comparator,
|
||||||
|
value,
|
||||||
|
} => {
|
||||||
|
let actual = state
|
||||||
|
.candidate_availability
|
||||||
|
.get(name)
|
||||||
|
.copied()
|
||||||
|
.map(i64::from)
|
||||||
|
.unwrap_or(0);
|
||||||
|
if !compare_condition_value(actual, *comparator, *value) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuntimeCondition::EconomicStatusCodeThreshold { comparator, value } => {
|
||||||
|
let actual = state
|
||||||
|
.world_restore
|
||||||
|
.economic_status_code
|
||||||
|
.map(i64::from)
|
||||||
|
.unwrap_or(0);
|
||||||
|
if !compare_condition_value(actual, *comparator, *value) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1039,7 +1079,8 @@ fn set_company_territory_access_pairs(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
access_entries.retain(|entry| {
|
access_entries.retain(|entry| {
|
||||||
!(company_ids.contains(&entry.company_id) && territory_ids.contains(&entry.territory_id))
|
!(company_ids.contains(&entry.company_id)
|
||||||
|
&& territory_ids.contains(&entry.territory_id))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1785,6 +1826,78 @@ mod tests {
|
||||||
assert!(error.contains("condition-evaluation context"));
|
assert!(error.contains("condition-evaluation context"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn evaluates_world_state_conditions_before_effects_run() {
|
||||||
|
let mut state = RuntimeState {
|
||||||
|
world_restore: RuntimeWorldRestoreState {
|
||||||
|
economic_status_code: Some(3),
|
||||||
|
..RuntimeWorldRestoreState::default()
|
||||||
|
},
|
||||||
|
candidate_availability: BTreeMap::from([(String::from("Mogul"), 2)]),
|
||||||
|
special_conditions: BTreeMap::from([(String::from("Use Wartime Cargos"), 1)]),
|
||||||
|
event_runtime_records: vec![
|
||||||
|
RuntimeEventRecord {
|
||||||
|
record_id: 23,
|
||||||
|
trigger_kind: 7,
|
||||||
|
active: true,
|
||||||
|
service_count: 0,
|
||||||
|
marks_collection_dirty: false,
|
||||||
|
one_shot: false,
|
||||||
|
has_fired: false,
|
||||||
|
conditions: vec![
|
||||||
|
RuntimeCondition::SpecialConditionThreshold {
|
||||||
|
label: "Use Wartime Cargos".to_string(),
|
||||||
|
comparator: RuntimeConditionComparator::Ge,
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
RuntimeCondition::CandidateAvailabilityThreshold {
|
||||||
|
name: "Mogul".to_string(),
|
||||||
|
comparator: RuntimeConditionComparator::Eq,
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
RuntimeCondition::EconomicStatusCodeThreshold {
|
||||||
|
comparator: RuntimeConditionComparator::Eq,
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
effects: vec![RuntimeEffect::SetWorldFlag {
|
||||||
|
key: "world_condition_passed".to_string(),
|
||||||
|
value: true,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
RuntimeEventRecord {
|
||||||
|
record_id: 24,
|
||||||
|
trigger_kind: 7,
|
||||||
|
active: true,
|
||||||
|
service_count: 0,
|
||||||
|
marks_collection_dirty: false,
|
||||||
|
one_shot: false,
|
||||||
|
has_fired: false,
|
||||||
|
conditions: vec![RuntimeCondition::SpecialConditionThreshold {
|
||||||
|
label: "Disable Cargo Economy".to_string(),
|
||||||
|
comparator: RuntimeConditionComparator::Gt,
|
||||||
|
value: 0,
|
||||||
|
}],
|
||||||
|
effects: vec![RuntimeEffect::SetWorldFlag {
|
||||||
|
key: "world_condition_failed".to_string(),
|
||||||
|
value: true,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
..state()
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = execute_step_command(
|
||||||
|
&mut state,
|
||||||
|
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||||
|
)
|
||||||
|
.expect("world-state conditions should evaluate successfully");
|
||||||
|
|
||||||
|
assert_eq!(result.service_events[0].serviced_record_ids, vec![23]);
|
||||||
|
assert_eq!(state.world_flags.get("world_condition_passed"), Some(&true));
|
||||||
|
assert_eq!(state.world_flags.get("world_condition_failed"), None);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_shot_record_only_fires_once() {
|
fn one_shot_record_only_fires_once() {
|
||||||
let mut state = RuntimeState {
|
let mut state = RuntimeState {
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,10 @@ pub struct RuntimeSummary {
|
||||||
pub packed_event_blocked_missing_territory_context_count: usize,
|
pub packed_event_blocked_missing_territory_context_count: usize,
|
||||||
pub packed_event_blocked_named_territory_binding_count: usize,
|
pub packed_event_blocked_named_territory_binding_count: usize,
|
||||||
pub packed_event_blocked_unmapped_ordinary_condition_count: usize,
|
pub packed_event_blocked_unmapped_ordinary_condition_count: usize,
|
||||||
|
pub packed_event_blocked_unmapped_world_condition_count: usize,
|
||||||
pub packed_event_blocked_missing_compact_control_count: usize,
|
pub packed_event_blocked_missing_compact_control_count: usize,
|
||||||
pub packed_event_blocked_unmapped_real_descriptor_count: usize,
|
pub packed_event_blocked_unmapped_real_descriptor_count: usize,
|
||||||
|
pub packed_event_blocked_unmapped_world_descriptor_count: usize,
|
||||||
pub packed_event_blocked_territory_access_variant_count: usize,
|
pub packed_event_blocked_territory_access_variant_count: usize,
|
||||||
pub packed_event_blocked_territory_access_scope_count: usize,
|
pub packed_event_blocked_territory_access_scope_count: usize,
|
||||||
pub packed_event_blocked_missing_train_context_count: usize,
|
pub packed_event_blocked_missing_train_context_count: usize,
|
||||||
|
|
@ -393,6 +395,20 @@ impl RuntimeSummary {
|
||||||
.count()
|
.count()
|
||||||
})
|
})
|
||||||
.unwrap_or(0),
|
.unwrap_or(0),
|
||||||
|
packed_event_blocked_unmapped_world_condition_count: state
|
||||||
|
.packed_event_collection
|
||||||
|
.as_ref()
|
||||||
|
.map(|summary| {
|
||||||
|
summary
|
||||||
|
.records
|
||||||
|
.iter()
|
||||||
|
.filter(|record| {
|
||||||
|
record.import_outcome.as_deref()
|
||||||
|
== Some("blocked_unmapped_world_condition")
|
||||||
|
})
|
||||||
|
.count()
|
||||||
|
})
|
||||||
|
.unwrap_or(0),
|
||||||
packed_event_blocked_missing_compact_control_count: state
|
packed_event_blocked_missing_compact_control_count: state
|
||||||
.packed_event_collection
|
.packed_event_collection
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -421,6 +437,20 @@ impl RuntimeSummary {
|
||||||
.count()
|
.count()
|
||||||
})
|
})
|
||||||
.unwrap_or(0),
|
.unwrap_or(0),
|
||||||
|
packed_event_blocked_unmapped_world_descriptor_count: state
|
||||||
|
.packed_event_collection
|
||||||
|
.as_ref()
|
||||||
|
.map(|summary| {
|
||||||
|
summary
|
||||||
|
.records
|
||||||
|
.iter()
|
||||||
|
.filter(|record| {
|
||||||
|
record.import_outcome.as_deref()
|
||||||
|
== Some("blocked_unmapped_world_descriptor")
|
||||||
|
})
|
||||||
|
.count()
|
||||||
|
})
|
||||||
|
.unwrap_or(0),
|
||||||
packed_event_blocked_territory_access_variant_count: state
|
packed_event_blocked_territory_access_variant_count: state
|
||||||
.packed_event_collection
|
.packed_event_collection
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -843,4 +873,107 @@ mod tests {
|
||||||
assert_eq!(summary.company_count, 2);
|
assert_eq!(summary.company_count, 2);
|
||||||
assert_eq!(summary.active_company_count, 1);
|
assert_eq!(summary.active_company_count, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn counts_world_frontier_buckets_separately() {
|
||||||
|
let state = RuntimeState {
|
||||||
|
calendar: CalendarPoint {
|
||||||
|
year: 1830,
|
||||||
|
month_slot: 0,
|
||||||
|
phase_slot: 0,
|
||||||
|
tick_slot: 0,
|
||||||
|
},
|
||||||
|
world_flags: BTreeMap::new(),
|
||||||
|
save_profile: RuntimeSaveProfileState::default(),
|
||||||
|
world_restore: RuntimeWorldRestoreState::default(),
|
||||||
|
metadata: BTreeMap::new(),
|
||||||
|
companies: Vec::new(),
|
||||||
|
selected_company_id: None,
|
||||||
|
players: Vec::new(),
|
||||||
|
selected_player_id: None,
|
||||||
|
trains: Vec::new(),
|
||||||
|
territories: Vec::new(),
|
||||||
|
company_territory_track_piece_counts: Vec::new(),
|
||||||
|
company_territory_access: Vec::new(),
|
||||||
|
packed_event_collection: Some(RuntimePackedEventCollectionSummary {
|
||||||
|
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()),
|
||||||
|
packed_state_version: 0x3e9,
|
||||||
|
packed_state_version_hex: "0x000003e9".to_string(),
|
||||||
|
live_id_bound: 2,
|
||||||
|
live_record_count: 2,
|
||||||
|
live_entry_ids: vec![21, 22],
|
||||||
|
decoded_record_count: 2,
|
||||||
|
imported_runtime_record_count: 0,
|
||||||
|
records: vec![
|
||||||
|
RuntimePackedEventRecordSummary {
|
||||||
|
record_index: 0,
|
||||||
|
live_entry_id: 21,
|
||||||
|
payload_offset: Some(0x7202),
|
||||||
|
payload_len: Some(96),
|
||||||
|
decode_status: "parity_only".to_string(),
|
||||||
|
payload_family: "real_packed_v1".to_string(),
|
||||||
|
trigger_kind: Some(7),
|
||||||
|
active: None,
|
||||||
|
marks_collection_dirty: None,
|
||||||
|
one_shot: None,
|
||||||
|
compact_control: None,
|
||||||
|
text_bands: Vec::new(),
|
||||||
|
standalone_condition_row_count: 0,
|
||||||
|
standalone_condition_rows: Vec::new(),
|
||||||
|
negative_sentinel_scope: None,
|
||||||
|
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
||||||
|
grouped_effect_rows: Vec::new(),
|
||||||
|
grouped_company_targets: Vec::new(),
|
||||||
|
decoded_conditions: Vec::new(),
|
||||||
|
decoded_actions: Vec::new(),
|
||||||
|
executable_import_ready: false,
|
||||||
|
import_outcome: Some("blocked_unmapped_world_descriptor".to_string()),
|
||||||
|
notes: Vec::new(),
|
||||||
|
},
|
||||||
|
RuntimePackedEventRecordSummary {
|
||||||
|
record_index: 1,
|
||||||
|
live_entry_id: 22,
|
||||||
|
payload_offset: Some(0x7242),
|
||||||
|
payload_len: Some(96),
|
||||||
|
decode_status: "parity_only".to_string(),
|
||||||
|
payload_family: "real_packed_v1".to_string(),
|
||||||
|
trigger_kind: Some(7),
|
||||||
|
active: None,
|
||||||
|
marks_collection_dirty: None,
|
||||||
|
one_shot: None,
|
||||||
|
compact_control: None,
|
||||||
|
text_bands: Vec::new(),
|
||||||
|
standalone_condition_row_count: 1,
|
||||||
|
standalone_condition_rows: Vec::new(),
|
||||||
|
negative_sentinel_scope: None,
|
||||||
|
grouped_effect_row_counts: vec![0, 0, 0, 0],
|
||||||
|
grouped_effect_rows: Vec::new(),
|
||||||
|
grouped_company_targets: Vec::new(),
|
||||||
|
decoded_conditions: Vec::new(),
|
||||||
|
decoded_actions: Vec::new(),
|
||||||
|
executable_import_ready: false,
|
||||||
|
import_outcome: Some("blocked_unmapped_world_condition".to_string()),
|
||||||
|
notes: Vec::new(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
event_runtime_records: Vec::new(),
|
||||||
|
candidate_availability: BTreeMap::new(),
|
||||||
|
special_conditions: BTreeMap::new(),
|
||||||
|
service_state: RuntimeServiceState::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let summary = RuntimeSummary::from_state(&state);
|
||||||
|
assert_eq!(
|
||||||
|
summary.packed_event_blocked_unmapped_world_descriptor_count,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.packed_event_blocked_unmapped_world_condition_count,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,10 @@ The highest-value next passes are now:
|
||||||
- descriptor `3` `Territory - Allow All` now executes as company-to-territory access rights through
|
- descriptor `3` `Territory - Allow All` now executes as company-to-territory access rights through
|
||||||
the same ordinary runtime path; shell purchase-flow parity remains out of scope, and mixed
|
the same ordinary runtime path; shell purchase-flow parity remains out of scope, and mixed
|
||||||
supported/unsupported real rows still stay parity-only
|
supported/unsupported real rows still stay parity-only
|
||||||
|
- whole-game ordinary-condition execution now exists too: special-condition thresholds,
|
||||||
|
candidate-availability thresholds, and economic-status-code thresholds now gate imported runtime
|
||||||
|
records, and the packed-event frontier now reports explicit unmapped world-condition and
|
||||||
|
world-descriptor buckets
|
||||||
- keep in mind that the current local `.gms` corpus still exports with no packed event collection,
|
- keep in mind that the current local `.gms` corpus still exports with no packed event collection,
|
||||||
so real descriptor mapping needs to stay plumbing-first until better captures exist
|
so real descriptor mapping needs to stay plumbing-first until better captures exist
|
||||||
- use `rrt-hook` primarily as optional capture or integration tooling, not as the first execution
|
- use `rrt-hook` primarily as optional capture or integration tooling, not as the first execution
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,16 @@ Implemented today:
|
||||||
- descriptor `3` = `Territory - Allow All` now imports and executes too, reinterpreted as
|
- descriptor `3` = `Territory - Allow All` now imports and executes too, reinterpreted as
|
||||||
company-to-territory access rights instead of a territory-owned policy bit; shell purchase-flow
|
company-to-territory access rights instead of a territory-owned policy bit; shell purchase-flow
|
||||||
and selected-profile parity still remain outside the runtime surface
|
and selected-profile parity still remain outside the runtime surface
|
||||||
|
- the first whole-game ordinary-condition batch now executes too: special-condition thresholds,
|
||||||
|
candidate-availability thresholds, and economic-status-code thresholds now gate runtime records
|
||||||
|
through the same service path, and whole-game parity frontiers now report explicit unmapped
|
||||||
|
world-condition and world-descriptor buckets rather than falling back to the generic ordinary
|
||||||
|
or descriptor counts
|
||||||
|
|
||||||
That means the next implementation work is breadth, not bootstrap. The recommended next slice is
|
That means the next implementation work is breadth, not bootstrap. The recommended next slice is
|
||||||
broader real grouped-descriptor and ordinary condition-id coverage beyond the current access,
|
broader real grouped-descriptor and ordinary condition-id coverage beyond the current access,
|
||||||
world, train, player, and numeric-threshold batches, plus richer runtime ownership only where a
|
whole-game, train, player, and numeric-threshold batches, plus richer runtime ownership only where
|
||||||
later descriptor family needs more than the current event-owned roster.
|
a later descriptor or condition family needs more than the current event-owned roster.
|
||||||
|
|
||||||
## Why This Boundary
|
## Why This Boundary
|
||||||
|
|
||||||
|
|
@ -396,8 +401,8 @@ Checked-in fixture families already include:
|
||||||
## Next Slice
|
## Next Slice
|
||||||
|
|
||||||
The recommended next implementation slice is broader ordinary-condition and grouped-descriptor
|
The recommended next implementation slice is broader ordinary-condition and grouped-descriptor
|
||||||
breadth on top of the now-stable numeric-threshold, overlay-context, named-territory, player,
|
breadth on top of the now-stable numeric-threshold, whole-game-state, overlay-context,
|
||||||
world/train, and company-territory-access batches.
|
named-territory, player, world/train, and company-territory-access batches.
|
||||||
|
|
||||||
Target behavior:
|
Target behavior:
|
||||||
|
|
||||||
|
|
@ -405,9 +410,9 @@ Target behavior:
|
||||||
company finance, company track, aggregate territory track, and company-territory track numeric
|
company finance, company track, aggregate territory track, and company-territory track numeric
|
||||||
thresholds all pass through parse, semantic summary, overlay-backed import, and ordinary trigger
|
thresholds all pass through parse, semantic summary, overlay-backed import, and ordinary trigger
|
||||||
execution
|
execution
|
||||||
- extend ordinary condition coverage beyond numeric thresholds only when comparator semantics,
|
- extend ordinary condition coverage beyond the current numeric-threshold and whole-game-state
|
||||||
runtime ownership, and binding rules are grounded enough to lower honestly into the normalized
|
families only when comparator semantics, runtime ownership, and binding rules are grounded
|
||||||
runtime path
|
enough to lower honestly into the normalized runtime path
|
||||||
- keep named-territory ordinary rows on exact case-sensitive binding until captured evidence
|
- keep named-territory ordinary rows on exact case-sensitive binding until captured evidence
|
||||||
justifies alias tables or fuzzier matching
|
justifies alias tables or fuzzier matching
|
||||||
- keep player-owned condition scope within the minimal event runtime model until later slices need
|
- keep player-owned condition scope within the minimal event runtime model until later slices need
|
||||||
|
|
@ -430,6 +435,9 @@ Fixture work for that slice:
|
||||||
|
|
||||||
- preserve the new ordinary-condition tracked overlays for executable company finance, company
|
- preserve the new ordinary-condition tracked overlays for executable company finance, company
|
||||||
track, aggregate territory track, and company-territory track thresholds
|
track, aggregate territory track, and company-territory track thresholds
|
||||||
|
- preserve the new whole-game tracked save-slice fixtures for executable special-condition and
|
||||||
|
candidate-availability/economic-status batches, plus the parity-only world-condition and
|
||||||
|
world-descriptor frontier sample
|
||||||
- preserve the named-territory no-match tracked overlay as the explicit binding blocker frontier
|
- preserve the named-territory no-match tracked overlay as the explicit binding blocker frontier
|
||||||
- preserve the territory-access tracked overlays and parity samples so descriptor `3` access-rights
|
- preserve the territory-access tracked overlays and parity samples so descriptor `3` access-rights
|
||||||
execution does not regress while other grouped families widen
|
execution does not regress while other grouped families widen
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
{
|
||||||
|
"format_version": 1,
|
||||||
|
"fixture_id": "packed-event-world-condition-gated-save-slice-fixture",
|
||||||
|
"source": {
|
||||||
|
"kind": "captured-runtime",
|
||||||
|
"description": "Fixture proving whole-game ordinary conditions gate imported world-state effects."
|
||||||
|
},
|
||||||
|
"state_save_slice_path": "packed-event-world-condition-gated-save-slice.json",
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"kind": "service_trigger_kind",
|
||||||
|
"trigger_kind": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "service_trigger_kind",
|
||||||
|
"trigger_kind": 7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expected_summary": {
|
||||||
|
"packed_event_collection_present": true,
|
||||||
|
"packed_event_record_count": 2,
|
||||||
|
"packed_event_decoded_record_count": 2,
|
||||||
|
"packed_event_imported_runtime_record_count": 2,
|
||||||
|
"event_runtime_record_count": 2,
|
||||||
|
"candidate_availability_count": 2,
|
||||||
|
"zero_candidate_availability_count": 0,
|
||||||
|
"special_condition_count": 1,
|
||||||
|
"enabled_special_condition_count": 1,
|
||||||
|
"world_restore_economic_status_code": 4,
|
||||||
|
"total_event_record_service_count": 2,
|
||||||
|
"total_trigger_dispatch_count": 2
|
||||||
|
},
|
||||||
|
"expected_state_fragment": {
|
||||||
|
"world_restore": {
|
||||||
|
"economic_status_code": 4
|
||||||
|
},
|
||||||
|
"candidate_availability": {
|
||||||
|
"Mogul": 2,
|
||||||
|
"Turbo Diesel": 1
|
||||||
|
},
|
||||||
|
"packed_event_collection": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"import_outcome": "imported"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"import_outcome": "imported",
|
||||||
|
"decoded_conditions": [
|
||||||
|
{
|
||||||
|
"kind": "special_condition_threshold",
|
||||||
|
"label": "Use Wartime Cargos",
|
||||||
|
"comparator": "ge",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "candidate_availability_threshold",
|
||||||
|
"name": "Mogul",
|
||||||
|
"comparator": "ge",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "economic_status_code_threshold",
|
||||||
|
"comparator": "ge",
|
||||||
|
"value": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"event_runtime_records": [
|
||||||
|
{
|
||||||
|
"record_id": 41,
|
||||||
|
"service_count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"record_id": 44,
|
||||||
|
"service_count": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,297 @@
|
||||||
|
{
|
||||||
|
"format_version": 1,
|
||||||
|
"save_slice_id": "packed-event-world-condition-gated-save-slice",
|
||||||
|
"source": {
|
||||||
|
"description": "Tracked save-slice document with whole-game conditions gating a whole-game effect.",
|
||||||
|
"original_save_filename": "captured-world-condition-gated.gms",
|
||||||
|
"original_save_sha256": "world-condition-gated-sample-sha256",
|
||||||
|
"notes": [
|
||||||
|
"tracked as JSON save-slice document rather than raw .smp",
|
||||||
|
"proves whole-game ordinary conditions gate imported runtime effects"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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": {
|
||||||
|
"source_kind": "save-bridge-secondary-block",
|
||||||
|
"semantic_family": "scenario-named-candidate-availability-table",
|
||||||
|
"header_offset": 27248,
|
||||||
|
"entries_offset": 27345,
|
||||||
|
"entries_end_offset": 27432,
|
||||||
|
"observed_entry_count": 2,
|
||||||
|
"zero_availability_count": 1,
|
||||||
|
"zero_availability_names": [
|
||||||
|
"Turbo Diesel"
|
||||||
|
],
|
||||||
|
"footer_progress_hex_words": [
|
||||||
|
"0x000032dc",
|
||||||
|
"0x00003714"
|
||||||
|
],
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"offset": 27345,
|
||||||
|
"text": "Mogul",
|
||||||
|
"availability_dword": 2,
|
||||||
|
"availability_dword_hex": "0x00000002",
|
||||||
|
"trailer_word": 2,
|
||||||
|
"trailer_word_hex": "0x00000002"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"offset": 27373,
|
||||||
|
"text": "Turbo Diesel",
|
||||||
|
"availability_dword": 0,
|
||||||
|
"availability_dword_hex": "0x00000000",
|
||||||
|
"trailer_word": 0,
|
||||||
|
"trailer_word_hex": "0x00000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"special_conditions_table": {
|
||||||
|
"source_kind": "save-fixed-special-conditions-range",
|
||||||
|
"table_offset": 3428,
|
||||||
|
"table_len": 144,
|
||||||
|
"enabled_visible_count": 1,
|
||||||
|
"enabled_visible_labels": [
|
||||||
|
"Use Wartime Cargos"
|
||||||
|
],
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"slot_index": 0,
|
||||||
|
"hidden": false,
|
||||||
|
"label_id": 2874,
|
||||||
|
"help_id": 2875,
|
||||||
|
"label": "Use Wartime Cargos",
|
||||||
|
"value": 1,
|
||||||
|
"value_hex": "0x00000001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slot_index": 35,
|
||||||
|
"hidden": true,
|
||||||
|
"label_id": 3,
|
||||||
|
"help_id": 3,
|
||||||
|
"label": "Hidden sentinel",
|
||||||
|
"value": 1,
|
||||||
|
"value_hex": "0x00000001"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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": 30528,
|
||||||
|
"records_tag_offset": 30784,
|
||||||
|
"close_tag_offset": 31616,
|
||||||
|
"packed_state_version": 1001,
|
||||||
|
"packed_state_version_hex": "0x000003e9",
|
||||||
|
"live_id_bound": 44,
|
||||||
|
"live_record_count": 2,
|
||||||
|
"live_entry_ids": [41, 44],
|
||||||
|
"decoded_record_count": 2,
|
||||||
|
"imported_runtime_record_count": 2,
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"record_index": 0,
|
||||||
|
"live_entry_id": 41,
|
||||||
|
"payload_offset": 30848,
|
||||||
|
"payload_len": 96,
|
||||||
|
"decode_status": "parity_only",
|
||||||
|
"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": 8,
|
||||||
|
"descriptor_label": "Economic Status",
|
||||||
|
"target_mask_bits": 8,
|
||||||
|
"parameter_family": "whole_game_state_enum",
|
||||||
|
"opcode": 3,
|
||||||
|
"raw_scalar_value": 4,
|
||||||
|
"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 Economic Status to 4",
|
||||||
|
"locomotive_name": null,
|
||||||
|
"notes": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decoded_conditions": [],
|
||||||
|
"decoded_actions": [
|
||||||
|
{
|
||||||
|
"kind": "set_economic_status_code",
|
||||||
|
"value": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"executable_import_ready": true,
|
||||||
|
"notes": [
|
||||||
|
"world-side setup record"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"record_index": 1,
|
||||||
|
"live_entry_id": 44,
|
||||||
|
"payload_offset": 30976,
|
||||||
|
"payload_len": 184,
|
||||||
|
"decode_status": "parity_only",
|
||||||
|
"payload_family": "real_packed_v1",
|
||||||
|
"trigger_kind": 7,
|
||||||
|
"one_shot": false,
|
||||||
|
"compact_control": {
|
||||||
|
"mode_byte_0x7ef": 7,
|
||||||
|
"primary_selector_0x7f0": 0,
|
||||||
|
"grouped_mode_0x7f4": 2,
|
||||||
|
"one_shot_header_0x7f5": 0,
|
||||||
|
"modifier_flag_0x7f9": 0,
|
||||||
|
"modifier_flag_0x7fa": 0,
|
||||||
|
"grouped_target_scope_ordinals_0x7fb": [0, 0, 0, 0],
|
||||||
|
"grouped_scope_checkboxes_0x7ff": [1, 0, 0, 0],
|
||||||
|
"summary_toggle_0x800": 1,
|
||||||
|
"grouped_territory_selectors_0x80f": [-1, -1, -1, -1]
|
||||||
|
},
|
||||||
|
"text_bands": [],
|
||||||
|
"standalone_condition_row_count": 3,
|
||||||
|
"standalone_condition_rows": [
|
||||||
|
{
|
||||||
|
"row_index": 0,
|
||||||
|
"raw_condition_id": 3901,
|
||||||
|
"subtype": 0,
|
||||||
|
"flag_bytes": [1, 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": "ge",
|
||||||
|
"metric": "Special Condition: Use Wartime Cargos",
|
||||||
|
"semantic_family": "world_state_threshold",
|
||||||
|
"semantic_preview": "Test Use Wartime Cargos >= 1",
|
||||||
|
"requires_candidate_name_binding": false,
|
||||||
|
"notes": [
|
||||||
|
"tracked whole-game condition sample"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"row_index": 1,
|
||||||
|
"raw_condition_id": 3902,
|
||||||
|
"subtype": 0,
|
||||||
|
"flag_bytes": [2, 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": "ge",
|
||||||
|
"metric": "Candidate Availability: Mogul",
|
||||||
|
"semantic_family": "world_state_threshold",
|
||||||
|
"semantic_preview": "Test Mogul >= 2",
|
||||||
|
"requires_candidate_name_binding": false,
|
||||||
|
"notes": [
|
||||||
|
"tracked whole-game condition sample"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"row_index": 2,
|
||||||
|
"raw_condition_id": 3903,
|
||||||
|
"subtype": 0,
|
||||||
|
"flag_bytes": [4, 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": "ge",
|
||||||
|
"metric": "Economic Status",
|
||||||
|
"semantic_family": "world_state_threshold",
|
||||||
|
"semantic_preview": "Test Economic Status >= 4",
|
||||||
|
"requires_candidate_name_binding": false,
|
||||||
|
"notes": [
|
||||||
|
"tracked whole-game condition sample"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"negative_sentinel_scope": null,
|
||||||
|
"grouped_effect_row_counts": [1, 0, 0, 0],
|
||||||
|
"grouped_effect_rows": [
|
||||||
|
{
|
||||||
|
"group_index": 0,
|
||||||
|
"row_index": 0,
|
||||||
|
"descriptor_id": 109,
|
||||||
|
"descriptor_label": "Turbo Diesel Availability",
|
||||||
|
"target_mask_bits": 8,
|
||||||
|
"parameter_family": "candidate_availability_scalar",
|
||||||
|
"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 Turbo Diesel Availability to 1",
|
||||||
|
"locomotive_name": null,
|
||||||
|
"notes": [
|
||||||
|
"tracked whole-game grouped-effect sample"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decoded_conditions": [
|
||||||
|
{
|
||||||
|
"kind": "special_condition_threshold",
|
||||||
|
"label": "Use Wartime Cargos",
|
||||||
|
"comparator": "ge",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "candidate_availability_threshold",
|
||||||
|
"name": "Mogul",
|
||||||
|
"comparator": "ge",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "economic_status_code_threshold",
|
||||||
|
"comparator": "ge",
|
||||||
|
"value": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decoded_actions": [
|
||||||
|
{
|
||||||
|
"kind": "set_candidate_availability",
|
||||||
|
"name": "Turbo Diesel",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"executable_import_ready": true,
|
||||||
|
"notes": [
|
||||||
|
"whole-game ordinary conditions gate a whole-game effect"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"whole-game condition-gated effect sample"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"format_version": 1,
|
||||||
|
"fixture_id": "packed-event-world-parity-save-slice-fixture",
|
||||||
|
"source": {
|
||||||
|
"kind": "captured-runtime",
|
||||||
|
"description": "Fixture keeping the current whole-game descriptor and condition frontier explicit."
|
||||||
|
},
|
||||||
|
"state_save_slice_path": "packed-event-world-parity-save-slice.json",
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"kind": "service_trigger_kind",
|
||||||
|
"trigger_kind": 7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expected_summary": {
|
||||||
|
"packed_event_collection_present": true,
|
||||||
|
"packed_event_record_count": 2,
|
||||||
|
"packed_event_decoded_record_count": 2,
|
||||||
|
"packed_event_imported_runtime_record_count": 0,
|
||||||
|
"event_runtime_record_count": 0,
|
||||||
|
"packed_event_blocked_unmapped_world_descriptor_count": 1,
|
||||||
|
"packed_event_blocked_unmapped_world_condition_count": 1
|
||||||
|
},
|
||||||
|
"expected_state_fragment": {
|
||||||
|
"packed_event_collection": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"import_outcome": "blocked_unmapped_world_descriptor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"import_outcome": "blocked_unmapped_world_condition"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
153
fixtures/runtime/packed-event-world-parity-save-slice.json
Normal file
153
fixtures/runtime/packed-event-world-parity-save-slice.json
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
{
|
||||||
|
"format_version": 1,
|
||||||
|
"save_slice_id": "packed-event-world-parity-save-slice",
|
||||||
|
"source": {
|
||||||
|
"description": "Tracked save-slice document preserving the current whole-game descriptor and condition frontier.",
|
||||||
|
"original_save_filename": "captured-world-parity.gms",
|
||||||
|
"original_save_sha256": "world-parity-sample-sha256",
|
||||||
|
"notes": [
|
||||||
|
"tracked as JSON save-slice document rather than raw .smp",
|
||||||
|
"keeps one unmapped world descriptor and one unmapped world condition explicit"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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": 52,
|
||||||
|
"live_record_count": 2,
|
||||||
|
"live_entry_ids": [49, 52],
|
||||||
|
"decoded_record_count": 2,
|
||||||
|
"imported_runtime_record_count": 0,
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"record_index": 0,
|
||||||
|
"live_entry_id": 49,
|
||||||
|
"payload_offset": 32320,
|
||||||
|
"payload_len": 112,
|
||||||
|
"decode_status": "parity_only",
|
||||||
|
"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": 110,
|
||||||
|
"descriptor_label": "Disable Stock Buying and Selling",
|
||||||
|
"target_mask_bits": 8,
|
||||||
|
"parameter_family": "world_flag_toggle",
|
||||||
|
"opcode": 0,
|
||||||
|
"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": "bool_toggle",
|
||||||
|
"semantic_family": "bool_toggle",
|
||||||
|
"semantic_preview": "Set Disable Stock Buying and Selling to TRUE",
|
||||||
|
"locomotive_name": null,
|
||||||
|
"notes": [
|
||||||
|
"recovered whole-game descriptor family without a checked-in executable mapping yet"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decoded_conditions": [],
|
||||||
|
"decoded_actions": [],
|
||||||
|
"executable_import_ready": false,
|
||||||
|
"notes": [
|
||||||
|
"world-side descriptor remains parity-only"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"record_index": 1,
|
||||||
|
"live_entry_id": 52,
|
||||||
|
"payload_offset": 32464,
|
||||||
|
"payload_len": 120,
|
||||||
|
"decode_status": "parity_only",
|
||||||
|
"payload_family": "real_packed_v1",
|
||||||
|
"trigger_kind": 7,
|
||||||
|
"one_shot": false,
|
||||||
|
"compact_control": {
|
||||||
|
"mode_byte_0x7ef": 7,
|
||||||
|
"primary_selector_0x7f0": 0,
|
||||||
|
"grouped_mode_0x7f4": 2,
|
||||||
|
"one_shot_header_0x7f5": 0,
|
||||||
|
"modifier_flag_0x7f9": 0,
|
||||||
|
"modifier_flag_0x7fa": 0,
|
||||||
|
"grouped_target_scope_ordinals_0x7fb": [0, 0, 0, 0],
|
||||||
|
"grouped_scope_checkboxes_0x7ff": [0, 0, 0, 0],
|
||||||
|
"summary_toggle_0x800": 1,
|
||||||
|
"grouped_territory_selectors_0x80f": [-1, -1, -1, -1]
|
||||||
|
},
|
||||||
|
"text_bands": [],
|
||||||
|
"standalone_condition_row_count": 1,
|
||||||
|
"standalone_condition_rows": [
|
||||||
|
{
|
||||||
|
"row_index": 0,
|
||||||
|
"raw_condition_id": 3904,
|
||||||
|
"subtype": 0,
|
||||||
|
"flag_bytes": [1, 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": "ge",
|
||||||
|
"metric": "Special Condition: Disable Stock Buying and Selling",
|
||||||
|
"semantic_family": "world_state_threshold",
|
||||||
|
"semantic_preview": "Test Disable Stock Buying and Selling >= 1",
|
||||||
|
"requires_candidate_name_binding": false,
|
||||||
|
"notes": [
|
||||||
|
"recovered world-side ordinary condition family without a checked-in executable mapping yet"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"negative_sentinel_scope": null,
|
||||||
|
"grouped_effect_row_counts": [0, 0, 0, 0],
|
||||||
|
"grouped_effect_rows": [],
|
||||||
|
"decoded_conditions": [],
|
||||||
|
"decoded_actions": [],
|
||||||
|
"executable_import_ready": false,
|
||||||
|
"notes": [
|
||||||
|
"world-side ordinary condition remains parity-only"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"whole-game parity frontier sample"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"format_version": 1,
|
||||||
|
"fixture_id": "packed-event-world-special-condition-save-slice-fixture",
|
||||||
|
"source": {
|
||||||
|
"kind": "captured-runtime",
|
||||||
|
"description": "Fixture proving a whole-game special-condition effect imports and executes through the ordinary runtime path."
|
||||||
|
},
|
||||||
|
"state_save_slice_path": "packed-event-world-special-condition-save-slice.json",
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"kind": "service_trigger_kind",
|
||||||
|
"trigger_kind": 7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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,
|
||||||
|
"special_condition_count": 1,
|
||||||
|
"enabled_special_condition_count": 1,
|
||||||
|
"total_event_record_service_count": 1,
|
||||||
|
"total_trigger_dispatch_count": 1
|
||||||
|
},
|
||||||
|
"expected_state_fragment": {
|
||||||
|
"special_conditions": {
|
||||||
|
"Use Wartime Cargos": 1
|
||||||
|
},
|
||||||
|
"packed_event_collection": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"import_outcome": "imported",
|
||||||
|
"decoded_actions": [
|
||||||
|
{
|
||||||
|
"kind": "set_special_condition",
|
||||||
|
"label": "Use Wartime Cargos",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
{
|
||||||
|
"format_version": 1,
|
||||||
|
"save_slice_id": "packed-event-world-special-condition-save-slice",
|
||||||
|
"source": {
|
||||||
|
"description": "Tracked save-slice document with a whole-game special-condition effect row.",
|
||||||
|
"original_save_filename": "captured-world-special-condition.gms",
|
||||||
|
"original_save_sha256": "world-special-condition-sample-sha256",
|
||||||
|
"notes": [
|
||||||
|
"tracked as JSON save-slice document rather than raw .smp",
|
||||||
|
"proves whole-game special-condition effects import through the ordinary runtime path"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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": 30000,
|
||||||
|
"records_tag_offset": 30256,
|
||||||
|
"close_tag_offset": 30720,
|
||||||
|
"packed_state_version": 1001,
|
||||||
|
"packed_state_version_hex": "0x000003e9",
|
||||||
|
"live_id_bound": 12,
|
||||||
|
"live_record_count": 1,
|
||||||
|
"live_entry_ids": [12],
|
||||||
|
"decoded_record_count": 1,
|
||||||
|
"imported_runtime_record_count": 1,
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"record_index": 0,
|
||||||
|
"live_entry_id": 12,
|
||||||
|
"payload_offset": 30336,
|
||||||
|
"payload_len": 112,
|
||||||
|
"decode_status": "parity_only",
|
||||||
|
"payload_family": "real_packed_v1",
|
||||||
|
"trigger_kind": 7,
|
||||||
|
"one_shot": false,
|
||||||
|
"compact_control": {
|
||||||
|
"mode_byte_0x7ef": 7,
|
||||||
|
"primary_selector_0x7f0": 0,
|
||||||
|
"grouped_mode_0x7f4": 2,
|
||||||
|
"one_shot_header_0x7f5": 0,
|
||||||
|
"modifier_flag_0x7f9": 0,
|
||||||
|
"modifier_flag_0x7fa": 0,
|
||||||
|
"grouped_target_scope_ordinals_0x7fb": [0, 0, 0, 0],
|
||||||
|
"grouped_scope_checkboxes_0x7ff": [1, 0, 0, 0],
|
||||||
|
"summary_toggle_0x800": 1,
|
||||||
|
"grouped_territory_selectors_0x80f": [-1, -1, -1, -1]
|
||||||
|
},
|
||||||
|
"text_bands": [],
|
||||||
|
"standalone_condition_row_count": 0,
|
||||||
|
"standalone_condition_rows": [],
|
||||||
|
"negative_sentinel_scope": null,
|
||||||
|
"grouped_effect_row_counts": [1, 0, 0, 0],
|
||||||
|
"grouped_effect_rows": [
|
||||||
|
{
|
||||||
|
"group_index": 0,
|
||||||
|
"row_index": 0,
|
||||||
|
"descriptor_id": 108,
|
||||||
|
"descriptor_label": "Use Wartime Cargos",
|
||||||
|
"target_mask_bits": 8,
|
||||||
|
"parameter_family": "special_condition_scalar",
|
||||||
|
"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 Use Wartime Cargos to 1",
|
||||||
|
"locomotive_name": null,
|
||||||
|
"notes": [
|
||||||
|
"tracked world-side descriptor sample"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"decoded_conditions": [],
|
||||||
|
"decoded_actions": [
|
||||||
|
{
|
||||||
|
"kind": "set_special_condition",
|
||||||
|
"label": "Use Wartime Cargos",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"executable_import_ready": true,
|
||||||
|
"notes": [
|
||||||
|
"tracked whole-game grouped-effect import sample"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"whole-game special-condition effect sample"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue