Broaden chairman target scope event support
This commit is contained in:
parent
5c2156bcbf
commit
a63de904fa
15 changed files with 1257 additions and 30 deletions
|
|
@ -1161,6 +1161,7 @@ fn runtime_packed_event_grouped_effect_row_summary_from_smp(
|
|||
target_mask_bits: row.target_mask_bits,
|
||||
parameter_family: row.parameter_family.clone(),
|
||||
grouped_target_subject: row.grouped_target_subject.clone(),
|
||||
grouped_target_scope: row.grouped_target_scope.clone(),
|
||||
opcode: row.opcode,
|
||||
raw_scalar_value: row.raw_scalar_value,
|
||||
value_byte_0x09: row.value_byte_0x09,
|
||||
|
|
@ -1279,6 +1280,7 @@ fn lowered_record_decoded_actions(
|
|||
if let Some(blocker) = packed_record_condition_scope_import_blocker(record, company_context) {
|
||||
return Err(blocker);
|
||||
}
|
||||
ensure_condition_true_chairman_context(record)?;
|
||||
|
||||
let lowered_company_target = lowered_condition_true_company_target(record)?;
|
||||
let lowered_player_target = lowered_condition_true_player_target(record)?;
|
||||
|
|
@ -1582,6 +1584,18 @@ fn lower_condition_targets_in_effect(
|
|||
)?,
|
||||
value: *value,
|
||||
},
|
||||
RuntimeEffect::SetCompanyGovernanceScalar {
|
||||
target,
|
||||
metric,
|
||||
value,
|
||||
} => RuntimeEffect::SetCompanyGovernanceScalar {
|
||||
target: lower_condition_true_company_target_in_company_target(
|
||||
target,
|
||||
lowered_company_target,
|
||||
)?,
|
||||
metric: *metric,
|
||||
value: *value,
|
||||
},
|
||||
RuntimeEffect::SetChairmanCash { target, value } => RuntimeEffect::SetChairmanCash {
|
||||
target: target.clone(),
|
||||
value: *value,
|
||||
|
|
@ -1912,6 +1926,23 @@ fn lower_condition_true_player_target_in_player_target(
|
|||
}
|
||||
}
|
||||
|
||||
fn ensure_condition_true_chairman_context(
|
||||
record: &SmpLoadedPackedEventRecordSummary,
|
||||
) -> Result<(), ImportBlocker> {
|
||||
if !record_uses_condition_true_chairman(record) {
|
||||
return Ok(());
|
||||
}
|
||||
if record
|
||||
.decoded_conditions
|
||||
.iter()
|
||||
.any(|condition| matches!(condition, RuntimeCondition::ChairmanNumericThreshold { .. }))
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ImportBlocker::MissingConditionContext)
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_territory_target_in_condition(
|
||||
target: &RuntimeTerritoryTarget,
|
||||
row: &SmpLoadedPackedEventConditionRowSummary,
|
||||
|
|
@ -1991,6 +2022,15 @@ fn chairman_target_import_blocker(
|
|||
None
|
||||
}
|
||||
}
|
||||
RuntimeChairmanTarget::HumanChairmen | RuntimeChairmanTarget::AiChairmen => {
|
||||
if company_context.known_chairman_profile_ids.is_empty() {
|
||||
Some(ImportBlocker::MissingChairmanContext)
|
||||
} else if !company_context.has_complete_company_controller_context {
|
||||
Some(ImportBlocker::MissingCompanyRoleContext)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
RuntimeChairmanTarget::SelectedChairman => {
|
||||
if company_context.selected_chairman_profile_id.is_some() {
|
||||
None
|
||||
|
|
@ -1998,6 +2038,13 @@ fn chairman_target_import_blocker(
|
|||
Some(ImportBlocker::MissingChairmanContext)
|
||||
}
|
||||
}
|
||||
RuntimeChairmanTarget::ConditionTrueChairman => {
|
||||
if company_context.known_chairman_profile_ids.is_empty() {
|
||||
Some(ImportBlocker::MissingChairmanContext)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
RuntimeChairmanTarget::Ids { ids } => {
|
||||
if company_context.known_chairman_profile_ids.is_empty() {
|
||||
Some(ImportBlocker::MissingChairmanContext)
|
||||
|
|
@ -2173,6 +2220,25 @@ fn smp_runtime_effect_to_runtime_effect(
|
|||
Err(company_target_import_error_message(target, company_context))
|
||||
}
|
||||
}
|
||||
RuntimeEffect::SetCompanyGovernanceScalar {
|
||||
target,
|
||||
metric,
|
||||
value,
|
||||
} => {
|
||||
if company_target_allowed_for_import(
|
||||
target,
|
||||
company_context,
|
||||
allow_condition_true_company,
|
||||
) {
|
||||
Ok(RuntimeEffect::SetCompanyGovernanceScalar {
|
||||
target: target.clone(),
|
||||
metric: *metric,
|
||||
value: *value,
|
||||
})
|
||||
} else {
|
||||
Err(company_target_import_error_message(target, company_context))
|
||||
}
|
||||
}
|
||||
RuntimeEffect::RetireTrains {
|
||||
company_target,
|
||||
territory_target,
|
||||
|
|
@ -2838,10 +2904,9 @@ fn real_grouped_row_is_unsupported_chairman_target_scope(
|
|||
) -> bool {
|
||||
matches!(row.grouped_target_subject.as_deref(), Some("chairman"))
|
||||
&& matches!(row.descriptor_id, 1 | 14)
|
||||
&& row
|
||||
.notes
|
||||
.iter()
|
||||
.any(|note| note == "chairman row requires selected-chairman scope")
|
||||
&& row.notes.iter().any(|note| {
|
||||
note.starts_with("chairman row uses unsupported grouped target scope ordinal ")
|
||||
})
|
||||
}
|
||||
|
||||
fn real_grouped_row_is_unsupported_territory_access_scope(
|
||||
|
|
@ -2883,6 +2948,7 @@ fn real_grouped_row_is_unsupported_retire_train_scope(
|
|||
fn runtime_effect_uses_condition_true_company(effect: &RuntimeEffect) -> bool {
|
||||
match effect {
|
||||
RuntimeEffect::SetCompanyCash { target, .. }
|
||||
| RuntimeEffect::SetCompanyGovernanceScalar { target, .. }
|
||||
| RuntimeEffect::SetCompanyTerritoryAccess { target, .. }
|
||||
| RuntimeEffect::ConfiscateCompanyAssets { target }
|
||||
| RuntimeEffect::DeactivateCompany { target }
|
||||
|
|
@ -2936,12 +3002,34 @@ fn runtime_effect_uses_condition_true_player(effect: &RuntimeEffect) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn record_uses_condition_true_chairman(record: &SmpLoadedPackedEventRecordSummary) -> bool {
|
||||
record
|
||||
.decoded_actions
|
||||
.iter()
|
||||
.any(runtime_effect_uses_condition_true_chairman)
|
||||
}
|
||||
|
||||
fn runtime_effect_uses_condition_true_chairman(effect: &RuntimeEffect) -> bool {
|
||||
match effect {
|
||||
RuntimeEffect::SetChairmanCash { target, .. }
|
||||
| RuntimeEffect::DeactivateChairman { target } => {
|
||||
matches!(target, RuntimeChairmanTarget::ConditionTrueChairman)
|
||||
}
|
||||
RuntimeEffect::AppendEventRecord { record } => record
|
||||
.effects
|
||||
.iter()
|
||||
.any(runtime_effect_uses_condition_true_chairman),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn runtime_effect_company_target_import_blocker(
|
||||
effect: &RuntimeEffect,
|
||||
company_context: &ImportRuntimeContext,
|
||||
) -> Option<ImportBlocker> {
|
||||
match effect {
|
||||
RuntimeEffect::SetCompanyCash { target, .. }
|
||||
| RuntimeEffect::SetCompanyGovernanceScalar { target, .. }
|
||||
| RuntimeEffect::SetCompanyTerritoryAccess { target, .. }
|
||||
| RuntimeEffect::ConfiscateCompanyAssets { target }
|
||||
| RuntimeEffect::DeactivateCompany { target }
|
||||
|
|
@ -3490,6 +3578,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x01),
|
||||
parameter_family: Some("company_finance_scalar".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 8,
|
||||
raw_scalar_value: 7,
|
||||
value_byte_0x09: 1,
|
||||
|
|
@ -3520,6 +3609,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x01),
|
||||
parameter_family: Some("company_lifecycle_toggle".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 1,
|
||||
raw_scalar_value: if enabled { 1 } else { 0 },
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -3551,6 +3641,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x01),
|
||||
parameter_family: Some("company_build_limit_scalar".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 3,
|
||||
raw_scalar_value: value,
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -3581,6 +3672,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x02),
|
||||
parameter_family: Some("player_lifecycle_toggle".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 1,
|
||||
raw_scalar_value: if enabled { 1 } else { 0 },
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -3615,6 +3707,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x05),
|
||||
parameter_family: Some("territory_access_toggle".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 1,
|
||||
raw_scalar_value: if enabled { 1 } else { 0 },
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -3646,6 +3739,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("whole_game_state_enum".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 3,
|
||||
raw_scalar_value: value,
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -3676,6 +3770,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("world_track_build_limit_scalar".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 3,
|
||||
raw_scalar_value: value,
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -3706,6 +3801,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("special_condition_scalar".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 3,
|
||||
raw_scalar_value: value,
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -3736,6 +3832,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("candidate_availability_scalar".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 3,
|
||||
raw_scalar_value: value,
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -3767,6 +3864,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("locomotive_availability_scalar".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 3,
|
||||
raw_scalar_value: value,
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -3810,6 +3908,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("locomotive_cost_scalar".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 3,
|
||||
raw_scalar_value: value,
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -3998,6 +4097,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("cargo_production_scalar".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 3,
|
||||
raw_scalar_value: value,
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -4028,6 +4128,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("territory_access_cost_scalar".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 3,
|
||||
raw_scalar_value: value,
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -4060,6 +4161,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("world_flag_toggle".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 0,
|
||||
raw_scalar_value: if enabled { 1 } else { 0 },
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -4093,6 +4195,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x01),
|
||||
parameter_family: Some("company_confiscation_variant".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 1,
|
||||
raw_scalar_value: if enabled { 1 } else { 0 },
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -4128,6 +4231,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x0d),
|
||||
parameter_family: Some("company_or_territory_asset_toggle".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 1,
|
||||
raw_scalar_value: if enabled { 1 } else { 0 },
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -4159,6 +4263,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x01),
|
||||
parameter_family: Some("company_confiscation_variant".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 1,
|
||||
raw_scalar_value: 0,
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -6005,6 +6110,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("locomotive_availability_scalar".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 3,
|
||||
raw_scalar_value: 42,
|
||||
value_byte_0x09: 0,
|
||||
|
|
@ -7155,6 +7261,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x01),
|
||||
parameter_family: Some("company_finance_scalar".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 8,
|
||||
raw_scalar_value: 250,
|
||||
value_byte_0x09: 1,
|
||||
|
|
@ -8785,6 +8892,7 @@ mod tests {
|
|||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("candidate_availability_scalar".to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode: 3,
|
||||
raw_scalar_value: 1,
|
||||
value_byte_0x09: 0,
|
||||
|
|
|
|||
|
|
@ -196,7 +196,10 @@ pub enum RuntimePlayerTarget {
|
|||
#[serde(tag = "kind", rename_all = "snake_case")]
|
||||
pub enum RuntimeChairmanTarget {
|
||||
AllActive,
|
||||
HumanChairmen,
|
||||
AiChairmen,
|
||||
SelectedChairman,
|
||||
ConditionTrueChairman,
|
||||
Ids { ids: Vec<u32> },
|
||||
}
|
||||
|
||||
|
|
@ -402,6 +405,11 @@ pub enum RuntimeEffect {
|
|||
target: RuntimeChairmanTarget,
|
||||
value: i64,
|
||||
},
|
||||
SetCompanyGovernanceScalar {
|
||||
target: RuntimeCompanyTarget,
|
||||
metric: RuntimeCompanyMetric,
|
||||
value: i64,
|
||||
},
|
||||
DeactivatePlayer {
|
||||
target: RuntimePlayerTarget,
|
||||
},
|
||||
|
|
@ -661,6 +669,8 @@ pub struct RuntimePackedEventGroupedEffectRowSummary {
|
|||
pub parameter_family: Option<String>,
|
||||
#[serde(default)]
|
||||
pub grouped_target_subject: Option<String>,
|
||||
#[serde(default)]
|
||||
pub grouped_target_scope: Option<String>,
|
||||
pub opcode: u8,
|
||||
pub raw_scalar_value: i32,
|
||||
pub value_byte_0x09: u8,
|
||||
|
|
@ -1512,6 +1522,10 @@ fn validate_runtime_effect(
|
|||
| RuntimeEffect::AdjustCompanyDebt { target, .. } => {
|
||||
validate_company_target(target, valid_company_ids)?;
|
||||
}
|
||||
RuntimeEffect::SetCompanyGovernanceScalar { target, metric, .. } => {
|
||||
validate_company_target(target, valid_company_ids)?;
|
||||
validate_company_governance_scalar_metric(*metric)?;
|
||||
}
|
||||
RuntimeEffect::SetCompanyTerritoryAccess {
|
||||
target, territory, ..
|
||||
} => {
|
||||
|
|
@ -1761,7 +1775,11 @@ fn validate_chairman_target(
|
|||
valid_chairman_profile_ids: &BTreeSet<u32>,
|
||||
) -> Result<(), String> {
|
||||
match target {
|
||||
RuntimeChairmanTarget::AllActive | RuntimeChairmanTarget::SelectedChairman => Ok(()),
|
||||
RuntimeChairmanTarget::AllActive
|
||||
| RuntimeChairmanTarget::HumanChairmen
|
||||
| RuntimeChairmanTarget::AiChairmen
|
||||
| RuntimeChairmanTarget::SelectedChairman
|
||||
| RuntimeChairmanTarget::ConditionTrueChairman => Ok(()),
|
||||
RuntimeChairmanTarget::Ids { ids } => {
|
||||
if ids.is_empty() {
|
||||
return Err("target ids must not be empty".to_string());
|
||||
|
|
@ -1778,6 +1796,19 @@ fn validate_chairman_target(
|
|||
}
|
||||
}
|
||||
|
||||
fn validate_company_governance_scalar_metric(metric: RuntimeCompanyMetric) -> Result<(), String> {
|
||||
match metric {
|
||||
RuntimeCompanyMetric::CreditRating
|
||||
| RuntimeCompanyMetric::PrimeRate
|
||||
| RuntimeCompanyMetric::BookValuePerShare
|
||||
| RuntimeCompanyMetric::InvestorConfidence
|
||||
| RuntimeCompanyMetric::ManagementAttitude => Ok(()),
|
||||
_ => Err(
|
||||
"governance scalar effect requires a writable company governance metric".to_string(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_territory_target(
|
||||
target: &RuntimeTerritoryTarget,
|
||||
valid_territory_ids: &BTreeSet<u32>,
|
||||
|
|
|
|||
|
|
@ -2007,6 +2007,8 @@ pub struct SmpLoadedPackedEventGroupedEffectRowSummary {
|
|||
pub parameter_family: Option<String>,
|
||||
#[serde(default)]
|
||||
pub grouped_target_subject: Option<String>,
|
||||
#[serde(default)]
|
||||
pub grouped_target_scope: Option<String>,
|
||||
pub opcode: u8,
|
||||
pub raw_scalar_value: i32,
|
||||
pub value_byte_0x09: u8,
|
||||
|
|
@ -2736,9 +2738,20 @@ fn parse_real_event_runtime_record_summary(
|
|||
}
|
||||
if let Some(control) = compact_control.as_ref() {
|
||||
for row in &mut grouped_effect_rows {
|
||||
row.grouped_target_subject = derive_real_grouped_target_subject(row, control)
|
||||
let target_subject = derive_real_grouped_target_subject(row, control);
|
||||
let target_scope_ordinal = control
|
||||
.grouped_target_scope_ordinals_0x7fb
|
||||
.get(row.group_index)
|
||||
.copied();
|
||||
row.grouped_target_subject = target_subject
|
||||
.map(real_grouped_target_subject_name)
|
||||
.map(str::to_string);
|
||||
row.grouped_target_scope = derive_real_grouped_target_scope_name(
|
||||
row,
|
||||
control,
|
||||
target_subject,
|
||||
target_scope_ordinal,
|
||||
);
|
||||
let company_target_present = control
|
||||
.grouped_target_scope_ordinals_0x7fb
|
||||
.get(row.group_index)
|
||||
|
|
@ -2771,13 +2784,13 @@ fn parse_real_event_runtime_record_summary(
|
|||
row.notes
|
||||
.push("territory access row is missing company or territory scope".to_string());
|
||||
}
|
||||
if matches!(
|
||||
derive_real_grouped_target_subject(row, control),
|
||||
Some(RealGroupedTargetSubject::Chairman)
|
||||
) && !chairman_target_present
|
||||
if matches!(target_subject, Some(RealGroupedTargetSubject::Chairman))
|
||||
&& !chairman_target_present
|
||||
{
|
||||
row.notes
|
||||
.push("chairman row requires selected-chairman scope".to_string());
|
||||
let ordinal = target_scope_ordinal.unwrap_or(u8::MAX);
|
||||
row.notes.push(format!(
|
||||
"chairman row uses unsupported grouped target scope ordinal {ordinal}"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3337,6 +3350,7 @@ fn parse_real_grouped_effect_row_summary(
|
|||
target_mask_bits: descriptor_metadata.map(|metadata| metadata.target_mask_bits),
|
||||
parameter_family: descriptor_metadata.map(|metadata| metadata.parameter_family.to_string()),
|
||||
grouped_target_subject: None,
|
||||
grouped_target_scope: None,
|
||||
opcode,
|
||||
raw_scalar_value,
|
||||
value_byte_0x09,
|
||||
|
|
@ -3524,9 +3538,11 @@ fn real_condition_chairman_target(
|
|||
RuntimePlayerConditionTestScope::SelectedPlayerOnly => {
|
||||
Some(RuntimeChairmanTarget::SelectedChairman)
|
||||
}
|
||||
RuntimePlayerConditionTestScope::Disabled
|
||||
| RuntimePlayerConditionTestScope::AiPlayersOnly
|
||||
| RuntimePlayerConditionTestScope::HumanPlayersOnly => None,
|
||||
RuntimePlayerConditionTestScope::AiPlayersOnly => Some(RuntimeChairmanTarget::AiChairmen),
|
||||
RuntimePlayerConditionTestScope::HumanPlayersOnly => {
|
||||
Some(RuntimeChairmanTarget::HumanChairmen)
|
||||
}
|
||||
RuntimePlayerConditionTestScope::Disabled => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3892,6 +3908,63 @@ fn real_grouped_target_subject_name(subject: RealGroupedTargetSubject) -> &'stat
|
|||
}
|
||||
}
|
||||
|
||||
fn derive_real_grouped_target_scope_name(
|
||||
row: &SmpLoadedPackedEventGroupedEffectRowSummary,
|
||||
compact_control: &SmpLoadedPackedEventCompactControlSummary,
|
||||
target_subject: Option<RealGroupedTargetSubject>,
|
||||
target_scope_ordinal: Option<u8>,
|
||||
) -> Option<String> {
|
||||
match target_subject {
|
||||
Some(RealGroupedTargetSubject::Company) => target_scope_ordinal
|
||||
.map(real_grouped_company_scope_name)
|
||||
.map(str::to_string),
|
||||
Some(RealGroupedTargetSubject::Player) => target_scope_ordinal
|
||||
.map(real_grouped_player_scope_name)
|
||||
.map(str::to_string),
|
||||
Some(RealGroupedTargetSubject::Chairman) => target_scope_ordinal
|
||||
.map(real_grouped_chairman_scope_name)
|
||||
.map(str::to_string),
|
||||
Some(RealGroupedTargetSubject::Territory) => compact_control
|
||||
.grouped_territory_selectors_0x80f
|
||||
.get(row.group_index)
|
||||
.copied()
|
||||
.filter(|selector| *selector >= 0)
|
||||
.map(|_| "specified_territories".to_string()),
|
||||
Some(RealGroupedTargetSubject::WholeGame) => Some("whole_game".to_string()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn real_grouped_company_scope_name(ordinal: u8) -> &'static str {
|
||||
match ordinal {
|
||||
0 => "condition_true_company",
|
||||
1 => "selected_company",
|
||||
2 => "human_companies",
|
||||
3 => "ai_companies",
|
||||
_ => "unsupported_company_scope",
|
||||
}
|
||||
}
|
||||
|
||||
fn real_grouped_player_scope_name(ordinal: u8) -> &'static str {
|
||||
match ordinal {
|
||||
0 => "condition_true_player",
|
||||
1 => "selected_player",
|
||||
2 => "human_players",
|
||||
3 => "ai_players",
|
||||
_ => "unsupported_player_scope",
|
||||
}
|
||||
}
|
||||
|
||||
fn real_grouped_chairman_scope_name(ordinal: u8) -> &'static str {
|
||||
match ordinal {
|
||||
0 => "condition_true_chairman",
|
||||
1 => "selected_chairman",
|
||||
2 => "human_chairmen",
|
||||
3 => "ai_chairmen",
|
||||
_ => "unsupported_chairman_scope",
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_real_grouped_effect_actions(
|
||||
grouped_effect_rows: &[SmpLoadedPackedEventGroupedEffectRowSummary],
|
||||
compact_control: &SmpLoadedPackedEventCompactControlSummary,
|
||||
|
|
@ -4127,7 +4200,10 @@ fn real_grouped_player_target(ordinal: u8) -> Option<RuntimePlayerTarget> {
|
|||
|
||||
fn real_grouped_chairman_target(ordinal: u8) -> Option<RuntimeChairmanTarget> {
|
||||
match ordinal {
|
||||
0 => Some(RuntimeChairmanTarget::ConditionTrueChairman),
|
||||
1 => Some(RuntimeChairmanTarget::SelectedChairman),
|
||||
2 => Some(RuntimeChairmanTarget::HumanChairmen),
|
||||
3 => Some(RuntimeChairmanTarget::AiChairmen),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -4287,12 +4363,16 @@ fn runtime_effect_supported_for_save_import(effect: &RuntimeEffect) -> bool {
|
|||
| RuntimeEffect::DeactivateChairman { target } => matches!(
|
||||
target,
|
||||
RuntimeChairmanTarget::AllActive
|
||||
| RuntimeChairmanTarget::HumanChairmen
|
||||
| RuntimeChairmanTarget::AiChairmen
|
||||
| RuntimeChairmanTarget::SelectedChairman
|
||||
| RuntimeChairmanTarget::ConditionTrueChairman
|
||||
| RuntimeChairmanTarget::Ids { .. }
|
||||
),
|
||||
RuntimeEffect::SetWorldFlag { .. }
|
||||
| RuntimeEffect::SetLimitedTrackBuildingAmount { .. }
|
||||
| RuntimeEffect::SetEconomicStatusCode { .. }
|
||||
| RuntimeEffect::SetCompanyGovernanceScalar { .. }
|
||||
| RuntimeEffect::SetCandidateAvailability { .. }
|
||||
| RuntimeEffect::SetNamedLocomotiveAvailability { .. }
|
||||
| RuntimeEffect::SetNamedLocomotiveAvailabilityValue { .. }
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ struct AppliedEffectsSummary {
|
|||
struct ResolvedConditionContext {
|
||||
matching_company_ids: BTreeSet<u32>,
|
||||
matching_player_ids: BTreeSet<u32>,
|
||||
#[allow(dead_code)]
|
||||
matching_chairman_profile_ids: BTreeSet<u32>,
|
||||
}
|
||||
|
||||
|
|
@ -363,6 +362,48 @@ fn apply_runtime_effects(
|
|||
chairman.current_cash = *value;
|
||||
}
|
||||
}
|
||||
RuntimeEffect::SetCompanyGovernanceScalar {
|
||||
target,
|
||||
metric,
|
||||
value,
|
||||
} => {
|
||||
let company_ids = resolve_company_target_ids(state, target, condition_context)?;
|
||||
for company_id in company_ids {
|
||||
let company = state
|
||||
.companies
|
||||
.iter_mut()
|
||||
.find(|company| company.company_id == company_id)
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"missing company_id {company_id} while applying governance effect"
|
||||
)
|
||||
})?;
|
||||
match metric {
|
||||
RuntimeCompanyMetric::CreditRating => {
|
||||
company.credit_rating_score = Some(*value);
|
||||
}
|
||||
RuntimeCompanyMetric::PrimeRate => {
|
||||
company.prime_rate = Some(*value);
|
||||
}
|
||||
RuntimeCompanyMetric::BookValuePerShare => {
|
||||
company.book_value_per_share = *value;
|
||||
}
|
||||
RuntimeCompanyMetric::InvestorConfidence => {
|
||||
company.investor_confidence = *value;
|
||||
}
|
||||
RuntimeCompanyMetric::ManagementAttitude => {
|
||||
company.management_attitude = *value;
|
||||
}
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"unsupported governance metric {:?} in company governance effect",
|
||||
metric
|
||||
));
|
||||
}
|
||||
}
|
||||
mutated_company_ids.insert(company_id);
|
||||
}
|
||||
}
|
||||
RuntimeEffect::DeactivatePlayer { target } => {
|
||||
let player_ids = resolve_player_target_ids(state, target, condition_context)?;
|
||||
for player_id in player_ids {
|
||||
|
|
@ -1152,7 +1193,7 @@ fn resolve_player_target_ids(
|
|||
fn resolve_chairman_target_ids(
|
||||
state: &RuntimeState,
|
||||
target: &RuntimeChairmanTarget,
|
||||
_condition_context: &ResolvedConditionContext,
|
||||
condition_context: &ResolvedConditionContext,
|
||||
) -> Result<Vec<u32>, String> {
|
||||
match target {
|
||||
RuntimeChairmanTarget::AllActive => Ok(state
|
||||
|
|
@ -1161,6 +1202,30 @@ fn resolve_chairman_target_ids(
|
|||
.filter(|profile| profile.active)
|
||||
.map(|profile| profile.profile_id)
|
||||
.collect()),
|
||||
RuntimeChairmanTarget::HumanChairmen => Ok(state
|
||||
.chairman_profiles
|
||||
.iter()
|
||||
.filter(|profile| {
|
||||
chairman_profile_matches_company_controller_kind(
|
||||
state,
|
||||
profile,
|
||||
RuntimeCompanyControllerKind::Human,
|
||||
)
|
||||
})
|
||||
.map(|profile| profile.profile_id)
|
||||
.collect()),
|
||||
RuntimeChairmanTarget::AiChairmen => Ok(state
|
||||
.chairman_profiles
|
||||
.iter()
|
||||
.filter(|profile| {
|
||||
chairman_profile_matches_company_controller_kind(
|
||||
state,
|
||||
profile,
|
||||
RuntimeCompanyControllerKind::Ai,
|
||||
)
|
||||
})
|
||||
.map(|profile| profile.profile_id)
|
||||
.collect()),
|
||||
RuntimeChairmanTarget::Ids { ids } => {
|
||||
let known_ids = state
|
||||
.chairman_profiles
|
||||
|
|
@ -1193,9 +1258,37 @@ fn resolve_chairman_target_ids(
|
|||
)
|
||||
}
|
||||
}
|
||||
RuntimeChairmanTarget::ConditionTrueChairman => {
|
||||
if condition_context.matching_chairman_profile_ids.is_empty() {
|
||||
Err("target requires chairman condition-evaluation context".to_string())
|
||||
} else {
|
||||
Ok(condition_context
|
||||
.matching_chairman_profile_ids
|
||||
.iter()
|
||||
.copied()
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn chairman_profile_matches_company_controller_kind(
|
||||
state: &RuntimeState,
|
||||
profile: &crate::RuntimeChairmanProfile,
|
||||
controller_kind: RuntimeCompanyControllerKind,
|
||||
) -> bool {
|
||||
profile.active
|
||||
&& profile
|
||||
.linked_company_id
|
||||
.and_then(|company_id| {
|
||||
state
|
||||
.companies
|
||||
.iter()
|
||||
.find(|company| company.company_id == company_id)
|
||||
})
|
||||
.is_some_and(|company| company.controller_kind == controller_kind)
|
||||
}
|
||||
|
||||
fn resolve_territory_target_ids(
|
||||
state: &RuntimeState,
|
||||
target: &RuntimeTerritoryTarget,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue