Execute aggregate cargo packed event conditions
This commit is contained in:
parent
c17b9f55f7
commit
377de45631
18 changed files with 1036 additions and 176 deletions
|
|
@ -725,6 +725,7 @@ fn project_save_slice_components(
|
|||
.map(|entry| RuntimeCargoCatalogEntry {
|
||||
slot_id: entry.slot_id,
|
||||
label: entry.label.clone(),
|
||||
cargo_class: entry.cargo_class,
|
||||
supplied_token_stem: entry
|
||||
.supplied_cargo_token_probable_high16_ascii_stem
|
||||
.clone(),
|
||||
|
|
@ -977,7 +978,10 @@ fn runtime_packed_event_condition_row_summary_from_smp(
|
|||
semantic_preview: row.semantic_preview.clone(),
|
||||
requires_candidate_name_binding: row.requires_candidate_name_binding,
|
||||
recovered_cargo_slot: row.recovered_cargo_slot,
|
||||
recovered_cargo_class: row.recovered_cargo_class.clone(),
|
||||
recovered_cargo_class: cargo_entry
|
||||
.map(|entry| format!("{:?}", entry.cargo_class).to_ascii_lowercase())
|
||||
.map(|value| value.replace("farmmine", "farm_mine"))
|
||||
.or_else(|| row.recovered_cargo_class.clone()),
|
||||
recovered_cargo_label: cargo_entry
|
||||
.map(|entry| entry.label.clone())
|
||||
.or_else(|| row.recovered_cargo_slot.map(default_cargo_slot_label)),
|
||||
|
|
@ -1015,7 +1019,10 @@ fn runtime_packed_event_grouped_effect_row_summary_from_smp(
|
|||
semantic_family: row.semantic_family.clone(),
|
||||
semantic_preview: row.semantic_preview.clone(),
|
||||
recovered_cargo_slot: row.recovered_cargo_slot,
|
||||
recovered_cargo_class: row.recovered_cargo_class.clone(),
|
||||
recovered_cargo_class: cargo_entry
|
||||
.map(|entry| format!("{:?}", entry.cargo_class).to_ascii_lowercase())
|
||||
.map(|value| value.replace("farmmine", "farm_mine"))
|
||||
.or_else(|| row.recovered_cargo_class.clone()),
|
||||
recovered_cargo_label: cargo_entry
|
||||
.map(|entry| entry.label.clone())
|
||||
.or_else(|| row.recovered_cargo_slot.map(default_cargo_slot_label)),
|
||||
|
|
@ -1663,6 +1670,24 @@ fn lower_condition_targets_in_condition(
|
|||
value: *value,
|
||||
}
|
||||
}
|
||||
RuntimeCondition::FactoryProductionTotalThreshold { comparator, value } => {
|
||||
RuntimeCondition::FactoryProductionTotalThreshold {
|
||||
comparator: *comparator,
|
||||
value: *value,
|
||||
}
|
||||
}
|
||||
RuntimeCondition::FarmMineProductionTotalThreshold { comparator, value } => {
|
||||
RuntimeCondition::FarmMineProductionTotalThreshold {
|
||||
comparator: *comparator,
|
||||
value: *value,
|
||||
}
|
||||
}
|
||||
RuntimeCondition::OtherCargoProductionTotalThreshold { comparator, value } => {
|
||||
RuntimeCondition::OtherCargoProductionTotalThreshold {
|
||||
comparator: *comparator,
|
||||
value: *value,
|
||||
}
|
||||
}
|
||||
RuntimeCondition::LimitedTrackBuildingAmountThreshold { comparator, value } => {
|
||||
RuntimeCondition::LimitedTrackBuildingAmountThreshold {
|
||||
comparator: *comparator,
|
||||
|
|
@ -1768,6 +1793,9 @@ fn condition_uses_condition_true_company(condition: &RuntimeCondition) -> bool {
|
|||
| RuntimeCondition::NamedLocomotiveCostThreshold { .. }
|
||||
| RuntimeCondition::CargoProductionSlotThreshold { .. }
|
||||
| RuntimeCondition::CargoProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::FactoryProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::FarmMineProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::OtherCargoProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::LimitedTrackBuildingAmountThreshold { .. }
|
||||
| RuntimeCondition::TerritoryAccessCostThreshold { .. }
|
||||
| RuntimeCondition::EconomicStatusCodeThreshold { .. }
|
||||
|
|
@ -2391,6 +2419,9 @@ fn runtime_condition_is_world_state(condition: &RuntimeCondition) -> bool {
|
|||
| RuntimeCondition::NamedLocomotiveCostThreshold { .. }
|
||||
| RuntimeCondition::CargoProductionSlotThreshold { .. }
|
||||
| RuntimeCondition::CargoProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::FactoryProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::FarmMineProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::OtherCargoProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::LimitedTrackBuildingAmountThreshold { .. }
|
||||
| RuntimeCondition::TerritoryAccessCostThreshold { .. }
|
||||
| RuntimeCondition::EconomicStatusCodeThreshold { .. }
|
||||
|
|
@ -2489,6 +2520,9 @@ fn runtime_condition_company_target_import_blocker(
|
|||
| RuntimeCondition::NamedLocomotiveCostThreshold { .. }
|
||||
| RuntimeCondition::CargoProductionSlotThreshold { .. }
|
||||
| RuntimeCondition::CargoProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::FactoryProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::FarmMineProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::OtherCargoProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::LimitedTrackBuildingAmountThreshold { .. }
|
||||
| RuntimeCondition::TerritoryAccessCostThreshold { .. }
|
||||
| RuntimeCondition::EconomicStatusCodeThreshold { .. }
|
||||
|
|
@ -3532,12 +3566,49 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn save_cargo_catalog(
|
||||
entries: &[(u32, crate::RuntimeCargoClass)],
|
||||
) -> crate::SmpLoadedCargoCatalog {
|
||||
crate::SmpLoadedCargoCatalog {
|
||||
source_kind: "recipe-book-summary-slot-catalog".to_string(),
|
||||
semantic_family: "scenario-save-derived-cargo-catalog".to_string(),
|
||||
root_offset: Some(0x0fe7),
|
||||
observed_entry_count: entries.len(),
|
||||
entries: entries
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(
|
||||
|(index, (slot_id, cargo_class))| crate::SmpLoadedCargoCatalogEntry {
|
||||
slot_id: *slot_id,
|
||||
label: format!("Cargo Production Slot {slot_id}"),
|
||||
cargo_class: *cargo_class,
|
||||
book_index: index,
|
||||
max_annual_production_word: 0,
|
||||
mode_word: 0,
|
||||
runtime_import_branch_kind: "zero-mode-skipped".to_string(),
|
||||
annual_amount_word: 0,
|
||||
supplied_cargo_token_word: 0,
|
||||
supplied_cargo_token_probable_high16_ascii_stem: None,
|
||||
demanded_cargo_token_word: 0,
|
||||
demanded_cargo_token_probable_high16_ascii_stem: None,
|
||||
},
|
||||
)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn real_cargo_production_row(
|
||||
descriptor_id: u32,
|
||||
value: i32,
|
||||
) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
let slot = descriptor_id.saturating_sub(229);
|
||||
let descriptor_label = format!("Cargo Production Slot {slot}");
|
||||
let recovered_cargo_class = match slot {
|
||||
1..=4 => Some("factory".to_string()),
|
||||
5..=8 => Some("farm_mine".to_string()),
|
||||
9..=11 => Some("other".to_string()),
|
||||
_ => None,
|
||||
};
|
||||
crate::SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
group_index: 0,
|
||||
row_index: 0,
|
||||
|
|
@ -3557,7 +3628,7 @@ mod tests {
|
|||
semantic_family: Some("scalar_assignment".to_string()),
|
||||
semantic_preview: Some(format!("Set {descriptor_label} to {value}")),
|
||||
recovered_cargo_slot: Some(slot),
|
||||
recovered_cargo_class: None,
|
||||
recovered_cargo_class,
|
||||
recovered_locomotive_id: None,
|
||||
locomotive_name: None,
|
||||
notes: vec![],
|
||||
|
|
@ -3957,7 +4028,11 @@ mod tests {
|
|||
},
|
||||
),
|
||||
locomotive_catalog: None,
|
||||
cargo_catalog: None,
|
||||
cargo_catalog: Some(save_cargo_catalog(&[
|
||||
(1, crate::RuntimeCargoClass::Factory),
|
||||
(5, crate::RuntimeCargoClass::FarmMine),
|
||||
(9, crate::RuntimeCargoClass::Other),
|
||||
])),
|
||||
special_conditions_table: Some(crate::SmpLoadedSpecialConditionsTable {
|
||||
source_kind: "save-fixed-special-conditions-range".to_string(),
|
||||
table_offset: 0x0d64,
|
||||
|
|
@ -4283,7 +4358,11 @@ mod tests {
|
|||
candidate_availability_table: None,
|
||||
named_locomotive_availability_table: None,
|
||||
locomotive_catalog: None,
|
||||
cargo_catalog: None,
|
||||
cargo_catalog: Some(save_cargo_catalog(&[
|
||||
(1, crate::RuntimeCargoClass::Factory),
|
||||
(5, crate::RuntimeCargoClass::FarmMine),
|
||||
(9, crate::RuntimeCargoClass::Other),
|
||||
])),
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
|
||||
source_kind: "packed-event-runtime-collection".to_string(),
|
||||
|
|
@ -4399,7 +4478,11 @@ mod tests {
|
|||
candidate_availability_table: None,
|
||||
named_locomotive_availability_table: None,
|
||||
locomotive_catalog: None,
|
||||
cargo_catalog: None,
|
||||
cargo_catalog: Some(save_cargo_catalog(&[
|
||||
(1, crate::RuntimeCargoClass::Factory),
|
||||
(5, crate::RuntimeCargoClass::FarmMine),
|
||||
(9, crate::RuntimeCargoClass::Other),
|
||||
])),
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
|
||||
source_kind: "packed-event-runtime-collection".to_string(),
|
||||
|
|
@ -4493,7 +4576,11 @@ mod tests {
|
|||
candidate_availability_table: None,
|
||||
named_locomotive_availability_table: None,
|
||||
locomotive_catalog: None,
|
||||
cargo_catalog: None,
|
||||
cargo_catalog: Some(save_cargo_catalog(&[
|
||||
(1, crate::RuntimeCargoClass::Factory),
|
||||
(5, crate::RuntimeCargoClass::FarmMine),
|
||||
(9, crate::RuntimeCargoClass::Other),
|
||||
])),
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
|
||||
source_kind: "packed-event-runtime-collection".to_string(),
|
||||
|
|
@ -8122,7 +8209,11 @@ mod tests {
|
|||
candidate_availability_table: None,
|
||||
named_locomotive_availability_table: None,
|
||||
locomotive_catalog: None,
|
||||
cargo_catalog: None,
|
||||
cargo_catalog: Some(save_cargo_catalog(&[
|
||||
(1, crate::RuntimeCargoClass::Factory),
|
||||
(5, crate::RuntimeCargoClass::FarmMine),
|
||||
(9, crate::RuntimeCargoClass::Other),
|
||||
])),
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
|
||||
source_kind: "packed-event-runtime-collection".to_string(),
|
||||
|
|
@ -8156,11 +8247,13 @@ mod tests {
|
|||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: vec![],
|
||||
negative_sentinel_scope: None,
|
||||
grouped_effect_row_counts: vec![5, 0, 0, 0],
|
||||
grouped_effect_row_counts: vec![7, 0, 0, 0],
|
||||
grouped_effect_rows: vec![
|
||||
real_locomotive_availability_row(250, 42),
|
||||
real_locomotive_cost_row(352, 250000),
|
||||
real_cargo_production_row(230, 125),
|
||||
real_cargo_production_row(234, 75),
|
||||
real_cargo_production_row(238, 30),
|
||||
real_limited_track_building_amount_row(18),
|
||||
real_territory_access_cost_row(750000),
|
||||
],
|
||||
|
|
@ -8178,6 +8271,8 @@ mod tests {
|
|||
slot: 1,
|
||||
value: 125,
|
||||
},
|
||||
RuntimeEffect::SetCargoProductionSlot { slot: 5, value: 75 },
|
||||
RuntimeEffect::SetCargoProductionSlot { slot: 9, value: 30 },
|
||||
RuntimeEffect::SetLimitedTrackBuildingAmount { value: 18 },
|
||||
RuntimeEffect::SetTerritoryAccessCost { value: 750000 },
|
||||
],
|
||||
|
|
@ -8197,7 +8292,7 @@ mod tests {
|
|||
one_shot: Some(false),
|
||||
compact_control: Some(real_compact_control()),
|
||||
text_bands: packed_text_bands(),
|
||||
standalone_condition_row_count: 6,
|
||||
standalone_condition_row_count: 9,
|
||||
standalone_condition_rows: vec![
|
||||
crate::SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 0,
|
||||
|
|
@ -8262,7 +8357,7 @@ mod tests {
|
|||
.to_string(),
|
||||
),
|
||||
recovered_cargo_slot: Some(1),
|
||||
recovered_cargo_class: None,
|
||||
recovered_cargo_class: Some("factory".to_string()),
|
||||
requires_candidate_name_binding: false,
|
||||
notes: vec![],
|
||||
},
|
||||
|
|
@ -8280,7 +8375,7 @@ mod tests {
|
|||
metric: Some("Cargo Production Total".to_string()),
|
||||
semantic_family: Some("world_scalar_threshold".to_string()),
|
||||
semantic_preview: Some(
|
||||
"Test Cargo Production Total == 125".to_string(),
|
||||
"Test Cargo Production Total == 230".to_string(),
|
||||
),
|
||||
recovered_cargo_slot: None,
|
||||
recovered_cargo_class: None,
|
||||
|
|
@ -8289,6 +8384,69 @@ mod tests {
|
|||
},
|
||||
crate::SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 4,
|
||||
raw_condition_id: 2419,
|
||||
subtype: 4,
|
||||
flag_bytes: {
|
||||
let mut bytes = vec![0; 25];
|
||||
bytes[0..4].copy_from_slice(&125_i32.to_le_bytes());
|
||||
bytes
|
||||
},
|
||||
candidate_name: None,
|
||||
comparator: Some("eq".to_string()),
|
||||
metric: Some("Factory Production Total".to_string()),
|
||||
semantic_family: Some("world_scalar_threshold".to_string()),
|
||||
semantic_preview: Some(
|
||||
"Test Factory Production Total == 125".to_string(),
|
||||
),
|
||||
recovered_cargo_slot: None,
|
||||
recovered_cargo_class: Some("factory".to_string()),
|
||||
requires_candidate_name_binding: false,
|
||||
notes: vec![],
|
||||
},
|
||||
crate::SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 5,
|
||||
raw_condition_id: 2420,
|
||||
subtype: 4,
|
||||
flag_bytes: {
|
||||
let mut bytes = vec![0; 25];
|
||||
bytes[0..4].copy_from_slice(&75_i32.to_le_bytes());
|
||||
bytes
|
||||
},
|
||||
candidate_name: None,
|
||||
comparator: Some("eq".to_string()),
|
||||
metric: Some("Farm/Mine Production Total".to_string()),
|
||||
semantic_family: Some("world_scalar_threshold".to_string()),
|
||||
semantic_preview: Some(
|
||||
"Test Farm/Mine Production Total == 75".to_string(),
|
||||
),
|
||||
recovered_cargo_slot: None,
|
||||
recovered_cargo_class: Some("farm_mine".to_string()),
|
||||
requires_candidate_name_binding: false,
|
||||
notes: vec![],
|
||||
},
|
||||
crate::SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 6,
|
||||
raw_condition_id: 2421,
|
||||
subtype: 4,
|
||||
flag_bytes: {
|
||||
let mut bytes = vec![0; 25];
|
||||
bytes[0..4].copy_from_slice(&30_i32.to_le_bytes());
|
||||
bytes
|
||||
},
|
||||
candidate_name: None,
|
||||
comparator: Some("eq".to_string()),
|
||||
metric: Some("Other Cargo Production Total".to_string()),
|
||||
semantic_family: Some("world_scalar_threshold".to_string()),
|
||||
semantic_preview: Some(
|
||||
"Test Other Cargo Production Total == 30".to_string(),
|
||||
),
|
||||
recovered_cargo_slot: None,
|
||||
recovered_cargo_class: Some("other".to_string()),
|
||||
requires_candidate_name_binding: false,
|
||||
notes: vec![],
|
||||
},
|
||||
crate::SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 7,
|
||||
raw_condition_id: 2547,
|
||||
subtype: 4,
|
||||
flag_bytes: {
|
||||
|
|
@ -8309,7 +8467,7 @@ mod tests {
|
|||
notes: vec![],
|
||||
},
|
||||
crate::SmpLoadedPackedEventConditionRowSummary {
|
||||
row_index: 5,
|
||||
row_index: 8,
|
||||
raw_condition_id: 1516,
|
||||
subtype: 4,
|
||||
flag_bytes: {
|
||||
|
|
@ -8355,9 +8513,21 @@ mod tests {
|
|||
value: 125,
|
||||
},
|
||||
RuntimeCondition::CargoProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 230,
|
||||
},
|
||||
RuntimeCondition::FactoryProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 125,
|
||||
},
|
||||
RuntimeCondition::FarmMineProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 75,
|
||||
},
|
||||
RuntimeCondition::OtherCargoProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 30,
|
||||
},
|
||||
RuntimeCondition::LimitedTrackBuildingAmountThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 18,
|
||||
|
|
@ -8391,6 +8561,76 @@ mod tests {
|
|||
&crate::StepCommand::ServiceTriggerKind { trigger_kind: 6 },
|
||||
)
|
||||
.expect("setup trigger should execute");
|
||||
assert_eq!(
|
||||
import.state.named_locomotive_availability.get("Big Boy"),
|
||||
Some(&42)
|
||||
);
|
||||
assert_eq!(
|
||||
import.state.named_locomotive_cost.get("Locomotive 1"),
|
||||
Some(&250000)
|
||||
);
|
||||
assert_eq!(import.state.cargo_production_overrides.get(&1), Some(&125));
|
||||
assert_eq!(import.state.cargo_production_overrides.get(&5), Some(&75));
|
||||
assert_eq!(import.state.cargo_production_overrides.get(&9), Some(&30));
|
||||
assert_eq!(import.state.cargo_catalog.len(), 3);
|
||||
assert_eq!(
|
||||
import.state.cargo_catalog[0].cargo_class,
|
||||
crate::RuntimeCargoClass::Factory
|
||||
);
|
||||
assert_eq!(
|
||||
import.state.cargo_catalog[1].cargo_class,
|
||||
crate::RuntimeCargoClass::FarmMine
|
||||
);
|
||||
assert_eq!(
|
||||
import.state.cargo_catalog[2].cargo_class,
|
||||
crate::RuntimeCargoClass::Other
|
||||
);
|
||||
assert_eq!(import.state.event_runtime_records.len(), 2);
|
||||
assert_eq!(
|
||||
import.state.event_runtime_records[1].conditions,
|
||||
vec![
|
||||
RuntimeCondition::NamedLocomotiveAvailabilityThreshold {
|
||||
name: "Big Boy".to_string(),
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 42,
|
||||
},
|
||||
RuntimeCondition::NamedLocomotiveCostThreshold {
|
||||
name: "Locomotive 1".to_string(),
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 250000,
|
||||
},
|
||||
RuntimeCondition::CargoProductionSlotThreshold {
|
||||
slot: 1,
|
||||
label: "Cargo Production Slot 1".to_string(),
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 125,
|
||||
},
|
||||
RuntimeCondition::CargoProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 230,
|
||||
},
|
||||
RuntimeCondition::FactoryProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 125,
|
||||
},
|
||||
RuntimeCondition::FarmMineProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 75,
|
||||
},
|
||||
RuntimeCondition::OtherCargoProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 30,
|
||||
},
|
||||
RuntimeCondition::LimitedTrackBuildingAmountThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 18,
|
||||
},
|
||||
RuntimeCondition::TerritoryAccessCostThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 750000,
|
||||
},
|
||||
]
|
||||
);
|
||||
crate::execute_step_command(
|
||||
&mut import.state,
|
||||
&crate::StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ pub use pk4::{
|
|||
extract_pk4_entry_bytes, extract_pk4_entry_file, inspect_pk4_bytes, inspect_pk4_file,
|
||||
};
|
||||
pub use runtime::{
|
||||
RuntimeCargoCatalogEntry, RuntimeCompany, RuntimeCompanyConditionTestScope,
|
||||
RuntimeCargoCatalogEntry, RuntimeCargoClass, RuntimeCompany, RuntimeCompanyConditionTestScope,
|
||||
RuntimeCompanyControllerKind, RuntimeCompanyMetric, RuntimeCompanyTarget,
|
||||
RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition,
|
||||
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
|
||||
|
|
|
|||
|
|
@ -118,11 +118,22 @@ pub struct RuntimeCargoCatalogEntry {
|
|||
pub slot_id: u32,
|
||||
pub label: String,
|
||||
#[serde(default)]
|
||||
pub cargo_class: RuntimeCargoClass,
|
||||
#[serde(default)]
|
||||
pub supplied_token_stem: Option<String>,
|
||||
#[serde(default)]
|
||||
pub demanded_token_stem: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum RuntimeCargoClass {
|
||||
#[default]
|
||||
Other,
|
||||
Factory,
|
||||
FarmMine,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind", rename_all = "snake_case")]
|
||||
pub enum RuntimeCompanyTarget {
|
||||
|
|
@ -274,6 +285,18 @@ pub enum RuntimeCondition {
|
|||
comparator: RuntimeConditionComparator,
|
||||
value: i64,
|
||||
},
|
||||
FactoryProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator,
|
||||
value: i64,
|
||||
},
|
||||
FarmMineProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator,
|
||||
value: i64,
|
||||
},
|
||||
OtherCargoProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator,
|
||||
value: i64,
|
||||
},
|
||||
LimitedTrackBuildingAmountThreshold {
|
||||
comparator: RuntimeConditionComparator,
|
||||
value: i64,
|
||||
|
|
@ -1475,6 +1498,9 @@ fn validate_runtime_condition(
|
|||
}
|
||||
}
|
||||
RuntimeCondition::CargoProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::FactoryProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::FarmMineProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::OtherCargoProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::LimitedTrackBuildingAmountThreshold { .. }
|
||||
| RuntimeCondition::TerritoryAccessCostThreshold { .. } => Ok(()),
|
||||
RuntimeCondition::EconomicStatusCodeThreshold { .. } => Ok(()),
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ use serde::{Deserialize, Serialize};
|
|||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::{
|
||||
RuntimeCompanyConditionTestScope, RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCondition,
|
||||
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecordTemplate,
|
||||
RuntimePlayerConditionTestScope, RuntimePlayerTarget, RuntimeTerritoryMetric,
|
||||
RuntimeTerritoryTarget, RuntimeTrackMetric,
|
||||
RuntimeCargoClass, RuntimeCompanyConditionTestScope, RuntimeCompanyMetric,
|
||||
RuntimeCompanyTarget, RuntimeCondition, RuntimeConditionComparator, RuntimeEffect,
|
||||
RuntimeEventRecordTemplate, RuntimePlayerConditionTestScope, RuntimePlayerTarget,
|
||||
RuntimeTerritoryMetric, RuntimeTerritoryTarget, RuntimeTrackMetric,
|
||||
};
|
||||
|
||||
pub const SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION: u32 = 0x03ec;
|
||||
|
|
@ -266,6 +266,83 @@ struct RealOrdinaryConditionMetadata {
|
|||
kind: RealOrdinaryConditionKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct KnownCargoSlotDefinition {
|
||||
slot_id: u32,
|
||||
label: &'static str,
|
||||
cargo_class: RuntimeCargoClass,
|
||||
descriptor_id: u32,
|
||||
}
|
||||
|
||||
const KNOWN_CARGO_SLOT_DEFINITIONS: [KnownCargoSlotDefinition; 11] = [
|
||||
KnownCargoSlotDefinition {
|
||||
slot_id: 1,
|
||||
label: "Cargo Production Slot 1",
|
||||
cargo_class: RuntimeCargoClass::Factory,
|
||||
descriptor_id: 230,
|
||||
},
|
||||
KnownCargoSlotDefinition {
|
||||
slot_id: 2,
|
||||
label: "Cargo Production Slot 2",
|
||||
cargo_class: RuntimeCargoClass::Factory,
|
||||
descriptor_id: 231,
|
||||
},
|
||||
KnownCargoSlotDefinition {
|
||||
slot_id: 3,
|
||||
label: "Cargo Production Slot 3",
|
||||
cargo_class: RuntimeCargoClass::Factory,
|
||||
descriptor_id: 232,
|
||||
},
|
||||
KnownCargoSlotDefinition {
|
||||
slot_id: 4,
|
||||
label: "Cargo Production Slot 4",
|
||||
cargo_class: RuntimeCargoClass::Factory,
|
||||
descriptor_id: 233,
|
||||
},
|
||||
KnownCargoSlotDefinition {
|
||||
slot_id: 5,
|
||||
label: "Cargo Production Slot 5",
|
||||
cargo_class: RuntimeCargoClass::FarmMine,
|
||||
descriptor_id: 234,
|
||||
},
|
||||
KnownCargoSlotDefinition {
|
||||
slot_id: 6,
|
||||
label: "Cargo Production Slot 6",
|
||||
cargo_class: RuntimeCargoClass::FarmMine,
|
||||
descriptor_id: 235,
|
||||
},
|
||||
KnownCargoSlotDefinition {
|
||||
slot_id: 7,
|
||||
label: "Cargo Production Slot 7",
|
||||
cargo_class: RuntimeCargoClass::FarmMine,
|
||||
descriptor_id: 236,
|
||||
},
|
||||
KnownCargoSlotDefinition {
|
||||
slot_id: 8,
|
||||
label: "Cargo Production Slot 8",
|
||||
cargo_class: RuntimeCargoClass::FarmMine,
|
||||
descriptor_id: 237,
|
||||
},
|
||||
KnownCargoSlotDefinition {
|
||||
slot_id: 9,
|
||||
label: "Cargo Production Slot 9",
|
||||
cargo_class: RuntimeCargoClass::Other,
|
||||
descriptor_id: 238,
|
||||
},
|
||||
KnownCargoSlotDefinition {
|
||||
slot_id: 10,
|
||||
label: "Cargo Production Slot 10",
|
||||
cargo_class: RuntimeCargoClass::Other,
|
||||
descriptor_id: 239,
|
||||
},
|
||||
KnownCargoSlotDefinition {
|
||||
slot_id: 11,
|
||||
label: "Cargo Production Slot 11",
|
||||
cargo_class: RuntimeCargoClass::Other,
|
||||
descriptor_id: 240,
|
||||
},
|
||||
];
|
||||
|
||||
const REAL_CANDIDATE_AVAILABILITY_CONDITION_TEMPLATE_ID: i32 = 435;
|
||||
const REAL_CARGO_PRODUCTION_CONDITION_TEMPLATE_ID: i32 = 200;
|
||||
const REAL_NAMED_LOCOMOTIVE_AVAILABILITY_CONDITION_ID: i32 = 2422;
|
||||
|
|
@ -1629,6 +1706,8 @@ pub struct SmpLoadedLocomotiveCatalog {
|
|||
pub struct SmpLoadedCargoCatalogEntry {
|
||||
pub slot_id: u32,
|
||||
pub label: String,
|
||||
#[serde(default)]
|
||||
pub cargo_class: RuntimeCargoClass,
|
||||
pub book_index: usize,
|
||||
pub max_annual_production_word: u32,
|
||||
pub mode_word: u32,
|
||||
|
|
@ -2080,9 +2159,11 @@ fn derive_cargo_catalog_from_recipe_book_probe(
|
|||
.find(|line| line.imports_to_runtime_descriptor)
|
||||
.or_else(|| book.lines.first())?;
|
||||
let slot_id = (book.book_index + 1) as u32;
|
||||
let definition = known_cargo_slot_definition(slot_id)?;
|
||||
Some(SmpLoadedCargoCatalogEntry {
|
||||
slot_id,
|
||||
label: format!("Cargo Production Slot {slot_id}"),
|
||||
label: definition.label.to_string(),
|
||||
cargo_class: definition.cargo_class,
|
||||
book_index: book.book_index,
|
||||
max_annual_production_word: book.max_annual_production_word,
|
||||
mode_word: line.mode_word,
|
||||
|
|
@ -2113,6 +2194,30 @@ fn derive_cargo_catalog_from_recipe_book_probe(
|
|||
})
|
||||
}
|
||||
|
||||
fn known_cargo_slot_definition(slot_id: u32) -> Option<KnownCargoSlotDefinition> {
|
||||
KNOWN_CARGO_SLOT_DEFINITIONS
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|definition| definition.slot_id == slot_id)
|
||||
}
|
||||
|
||||
fn known_cargo_slot_definition_for_descriptor_id(
|
||||
descriptor_id: u32,
|
||||
) -> Option<KnownCargoSlotDefinition> {
|
||||
KNOWN_CARGO_SLOT_DEFINITIONS
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|definition| definition.descriptor_id == descriptor_id)
|
||||
}
|
||||
|
||||
fn runtime_cargo_class_name(cargo_class: RuntimeCargoClass) -> &'static str {
|
||||
match cargo_class {
|
||||
RuntimeCargoClass::Factory => "factory",
|
||||
RuntimeCargoClass::FarmMine => "farm_mine",
|
||||
RuntimeCargoClass::Other => "other",
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_event_runtime_collection_summary(
|
||||
bytes: &[u8],
|
||||
container_profile: Option<&SmpContainerProfile>,
|
||||
|
|
@ -2766,6 +2871,12 @@ fn parse_real_condition_row_summary(
|
|||
_ => None,
|
||||
}),
|
||||
recovered_cargo_class: ordinary_metadata.and_then(|metadata| match metadata.kind {
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CargoProductionSlot) => {
|
||||
candidate_name_ref
|
||||
.and_then(recovered_cargo_production_slot_from_condition_name)
|
||||
.and_then(known_cargo_slot_definition)
|
||||
.map(|definition| runtime_cargo_class_name(definition.cargo_class).to_string())
|
||||
}
|
||||
RealOrdinaryConditionKind::WorldState(
|
||||
RealWorldConditionKind::FactoryProductionTotal,
|
||||
) => Some("factory".to_string()),
|
||||
|
|
@ -3083,7 +3194,9 @@ fn parse_real_grouped_effect_row_summary(
|
|||
value_word_0x16,
|
||||
)),
|
||||
recovered_cargo_slot: recovered_cargo_production_slot(descriptor_id),
|
||||
recovered_cargo_class: None,
|
||||
recovered_cargo_class: recovered_cargo_production_slot(descriptor_id)
|
||||
.and_then(known_cargo_slot_definition)
|
||||
.map(|definition| runtime_cargo_class_name(definition.cargo_class).to_string()),
|
||||
recovered_locomotive_id: recovered_locomotive_availability_loco_id(descriptor_id)
|
||||
.or_else(|| recovered_locomotive_cost_loco_id(descriptor_id)),
|
||||
locomotive_name,
|
||||
|
|
@ -3156,9 +3269,12 @@ fn decode_real_condition_row(
|
|||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CargoProductionSlot) => {
|
||||
row.candidate_name.as_ref().and_then(|name| {
|
||||
recovered_cargo_production_slot_from_condition_name(name).map(|slot| {
|
||||
let label = known_cargo_slot_definition(slot)
|
||||
.map(|definition| definition.label.to_string())
|
||||
.unwrap_or_else(|| name.clone());
|
||||
RuntimeCondition::CargoProductionSlotThreshold {
|
||||
slot,
|
||||
label: name.clone(),
|
||||
label,
|
||||
comparator,
|
||||
value,
|
||||
}
|
||||
|
|
@ -3185,11 +3301,15 @@ fn decode_real_condition_row(
|
|||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CargoProductionTotal) => {
|
||||
Some(RuntimeCondition::CargoProductionTotalThreshold { comparator, value })
|
||||
}
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::FactoryProductionTotal) => {
|
||||
Some(RuntimeCondition::FactoryProductionTotalThreshold { comparator, value })
|
||||
}
|
||||
RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::FarmMineProductionTotal) => {
|
||||
Some(RuntimeCondition::FarmMineProductionTotalThreshold { comparator, value })
|
||||
}
|
||||
RealOrdinaryConditionKind::WorldState(
|
||||
RealWorldConditionKind::FactoryProductionTotal
|
||||
| RealWorldConditionKind::FarmMineProductionTotal
|
||||
| RealWorldConditionKind::OtherCargoProductionTotal,
|
||||
) => None,
|
||||
RealWorldConditionKind::OtherCargoProductionTotal,
|
||||
) => Some(RuntimeCondition::OtherCargoProductionTotalThreshold { comparator, value }),
|
||||
RealOrdinaryConditionKind::WorldState(
|
||||
RealWorldConditionKind::LimitedTrackBuildingAmount,
|
||||
) => Some(RuntimeCondition::LimitedTrackBuildingAmountThreshold { comparator, value }),
|
||||
|
|
@ -3295,27 +3415,14 @@ fn recovered_locomotive_availability_loco_id(descriptor_id: u32) -> Option<u32>
|
|||
}
|
||||
|
||||
fn recovered_cargo_production_label(descriptor_id: u32) -> Option<&'static str> {
|
||||
static LABELS: OnceLock<BTreeMap<u32, &'static str>> = OnceLock::new();
|
||||
LABELS
|
||||
.get_or_init(|| {
|
||||
(230..=240)
|
||||
.enumerate()
|
||||
.map(|(slot_index, descriptor_id)| {
|
||||
let label = Box::leak(
|
||||
format!("Cargo Production Slot {}", slot_index + 1).into_boxed_str(),
|
||||
) as &'static str;
|
||||
(descriptor_id, label)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.get(&descriptor_id)
|
||||
.copied()
|
||||
known_cargo_slot_definition_for_descriptor_id(descriptor_id).map(|definition| definition.label)
|
||||
}
|
||||
|
||||
fn recovered_cargo_production_slot_from_condition_name(name: &str) -> Option<u32> {
|
||||
let suffix = name.strip_prefix("Cargo Production Slot ")?;
|
||||
let slot = suffix.parse::<u32>().ok()?;
|
||||
(1..=11).contains(&slot).then_some(slot)
|
||||
KNOWN_CARGO_SLOT_DEFINITIONS
|
||||
.iter()
|
||||
.find(|definition| definition.label == name)
|
||||
.map(|definition| definition.slot_id)
|
||||
}
|
||||
|
||||
fn recovered_locomotive_cost_loco_id(descriptor_id: u32) -> Option<u32> {
|
||||
|
|
@ -3998,6 +4105,9 @@ fn runtime_condition_supported_for_save_import(condition: &RuntimeCondition) ->
|
|||
| RuntimeCondition::NamedLocomotiveCostThreshold { .. }
|
||||
| RuntimeCondition::CargoProductionSlotThreshold { .. }
|
||||
| RuntimeCondition::CargoProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::FactoryProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::FarmMineProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::OtherCargoProductionTotalThreshold { .. }
|
||||
| RuntimeCondition::LimitedTrackBuildingAmountThreshold { .. }
|
||||
| RuntimeCondition::TerritoryAccessCostThreshold { .. }
|
||||
| RuntimeCondition::EconomicStatusCodeThreshold { .. }
|
||||
|
|
@ -9979,6 +10089,24 @@ mod tests {
|
|||
200,
|
||||
None,
|
||||
),
|
||||
build_real_condition_row_with_threshold(
|
||||
REAL_FACTORY_PRODUCTION_TOTAL_CONDITION_ID,
|
||||
4,
|
||||
125,
|
||||
None,
|
||||
),
|
||||
build_real_condition_row_with_threshold(
|
||||
REAL_FARM_MINE_PRODUCTION_TOTAL_CONDITION_ID,
|
||||
4,
|
||||
75,
|
||||
None,
|
||||
),
|
||||
build_real_condition_row_with_threshold(
|
||||
REAL_OTHER_CARGO_PRODUCTION_TOTAL_CONDITION_ID,
|
||||
4,
|
||||
30,
|
||||
None,
|
||||
),
|
||||
build_real_condition_row_with_threshold(
|
||||
REAL_LIMITED_TRACK_BUILDING_AMOUNT_CONDITION_ID,
|
||||
4,
|
||||
|
|
@ -10048,6 +10176,18 @@ mod tests {
|
|||
comparator: RuntimeConditionComparator::Ge,
|
||||
value: 200,
|
||||
},
|
||||
RuntimeCondition::FactoryProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 125,
|
||||
},
|
||||
RuntimeCondition::FarmMineProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 75,
|
||||
},
|
||||
RuntimeCondition::OtherCargoProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 30,
|
||||
},
|
||||
RuntimeCondition::LimitedTrackBuildingAmountThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 18,
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ use std::collections::BTreeSet;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
RuntimeCompanyControllerKind, RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCondition,
|
||||
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecordTemplate, RuntimePlayerTarget,
|
||||
RuntimeState, RuntimeSummary, RuntimeTerritoryMetric, RuntimeTerritoryTarget,
|
||||
RuntimeTrackMetric, RuntimeTrackPieceCounts, calendar::BoundaryEventKind,
|
||||
RuntimeCargoClass, RuntimeCompanyControllerKind, RuntimeCompanyMetric, RuntimeCompanyTarget,
|
||||
RuntimeCondition, RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecordTemplate,
|
||||
RuntimePlayerTarget, RuntimeState, RuntimeSummary, RuntimeTerritoryMetric,
|
||||
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts,
|
||||
calendar::BoundaryEventKind,
|
||||
};
|
||||
|
||||
const PERIODIC_TRIGGER_KIND_ORDER: [u8; 6] = [1, 0, 3, 2, 5, 4];
|
||||
|
|
@ -779,6 +780,24 @@ fn evaluate_record_conditions(
|
|||
return Ok(None);
|
||||
}
|
||||
}
|
||||
RuntimeCondition::FactoryProductionTotalThreshold { comparator, value } => {
|
||||
let actual = cargo_production_total_for_class(state, RuntimeCargoClass::Factory);
|
||||
if !compare_condition_value(actual, *comparator, *value) {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
RuntimeCondition::FarmMineProductionTotalThreshold { comparator, value } => {
|
||||
let actual = cargo_production_total_for_class(state, RuntimeCargoClass::FarmMine);
|
||||
if !compare_condition_value(actual, *comparator, *value) {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
RuntimeCondition::OtherCargoProductionTotalThreshold { comparator, value } => {
|
||||
let actual = cargo_production_total_for_class(state, RuntimeCargoClass::Other);
|
||||
if !compare_condition_value(actual, *comparator, *value) {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
RuntimeCondition::LimitedTrackBuildingAmountThreshold { comparator, value } => {
|
||||
let actual = state
|
||||
.world_restore
|
||||
|
|
@ -1142,6 +1161,17 @@ fn compare_condition_value(
|
|||
}
|
||||
}
|
||||
|
||||
fn cargo_production_total_for_class(state: &RuntimeState, cargo_class: RuntimeCargoClass) -> i64 {
|
||||
state
|
||||
.cargo_catalog
|
||||
.iter()
|
||||
.filter(|entry| entry.cargo_class == cargo_class)
|
||||
.filter_map(|entry| state.cargo_production_overrides.get(&entry.slot_id))
|
||||
.copied()
|
||||
.map(i64::from)
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn apply_u64_delta(current: u64, delta: i64, company_id: u32) -> Result<u64, String> {
|
||||
if delta >= 0 {
|
||||
current
|
||||
|
|
@ -2228,9 +2258,32 @@ mod tests {
|
|||
territory_access_cost: Some(750000),
|
||||
..RuntimeWorldRestoreState::default()
|
||||
},
|
||||
cargo_catalog: vec![
|
||||
crate::RuntimeCargoCatalogEntry {
|
||||
slot_id: 1,
|
||||
label: "Cargo Production Slot 1".to_string(),
|
||||
cargo_class: RuntimeCargoClass::Factory,
|
||||
supplied_token_stem: None,
|
||||
demanded_token_stem: None,
|
||||
},
|
||||
crate::RuntimeCargoCatalogEntry {
|
||||
slot_id: 5,
|
||||
label: "Cargo Production Slot 5".to_string(),
|
||||
cargo_class: RuntimeCargoClass::FarmMine,
|
||||
supplied_token_stem: None,
|
||||
demanded_token_stem: None,
|
||||
},
|
||||
crate::RuntimeCargoCatalogEntry {
|
||||
slot_id: 9,
|
||||
label: "Cargo Production Slot 9".to_string(),
|
||||
cargo_class: RuntimeCargoClass::Other,
|
||||
supplied_token_stem: None,
|
||||
demanded_token_stem: None,
|
||||
},
|
||||
],
|
||||
named_locomotive_availability: BTreeMap::from([(String::from("Big Boy"), 42)]),
|
||||
named_locomotive_cost: BTreeMap::from([(String::from("GP7"), 175000)]),
|
||||
cargo_production_overrides: BTreeMap::from([(1, 125), (2, 75)]),
|
||||
cargo_production_overrides: BTreeMap::from([(1, 125), (5, 75), (9, 30)]),
|
||||
event_runtime_records: vec![
|
||||
RuntimeEventRecord {
|
||||
record_id: 25,
|
||||
|
|
@ -2259,7 +2312,19 @@ mod tests {
|
|||
},
|
||||
RuntimeCondition::CargoProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 200,
|
||||
value: 230,
|
||||
},
|
||||
RuntimeCondition::FactoryProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 125,
|
||||
},
|
||||
RuntimeCondition::FarmMineProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 75,
|
||||
},
|
||||
RuntimeCondition::OtherCargoProductionTotalThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
value: 30,
|
||||
},
|
||||
RuntimeCondition::LimitedTrackBuildingAmountThreshold {
|
||||
comparator: RuntimeConditionComparator::Eq,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue