Add territory and player packed event import
This commit is contained in:
parent
f73234cb99
commit
ca208f74e0
26 changed files with 1912 additions and 272 deletions
|
|
@ -4,8 +4,9 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::{
|
||||
RuntimeCompanyControllerKind, RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCondition,
|
||||
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecordTemplate, RuntimeState,
|
||||
RuntimeSummary, RuntimeTerritoryMetric, RuntimeTrackMetric, RuntimeTrackPieceCounts,
|
||||
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecordTemplate, RuntimePlayerTarget,
|
||||
RuntimeState, RuntimeSummary, RuntimeTerritoryMetric, RuntimeTerritoryTarget,
|
||||
RuntimeTrackMetric, RuntimeTrackPieceCounts,
|
||||
calendar::BoundaryEventKind,
|
||||
};
|
||||
|
||||
|
|
@ -48,6 +49,7 @@ pub struct ServiceEvent {
|
|||
pub serviced_record_ids: Vec<u32>,
|
||||
pub applied_effect_count: u32,
|
||||
pub mutated_company_ids: Vec<u32>,
|
||||
pub mutated_player_ids: Vec<u32>,
|
||||
pub appended_record_ids: Vec<u32>,
|
||||
pub activated_record_ids: Vec<u32>,
|
||||
pub deactivated_record_ids: Vec<u32>,
|
||||
|
|
@ -84,6 +86,7 @@ struct AppliedEffectsSummary {
|
|||
#[derive(Debug, Default)]
|
||||
struct ResolvedConditionContext {
|
||||
matching_company_ids: BTreeSet<u32>,
|
||||
matching_player_ids: BTreeSet<u32>,
|
||||
}
|
||||
|
||||
pub fn execute_step_command(
|
||||
|
|
@ -205,6 +208,7 @@ fn service_trigger_kind(
|
|||
let mut serviced_record_ids = Vec::new();
|
||||
let mut applied_effect_count = 0_u32;
|
||||
let mut mutated_company_ids = BTreeSet::new();
|
||||
let mut mutated_player_ids = BTreeSet::new();
|
||||
let mut appended_record_ids = Vec::new();
|
||||
let mut activated_record_ids = Vec::new();
|
||||
let mut deactivated_record_ids = Vec::new();
|
||||
|
|
@ -245,6 +249,7 @@ fn service_trigger_kind(
|
|||
&record_effects,
|
||||
&condition_context,
|
||||
&mut mutated_company_ids,
|
||||
&mut mutated_player_ids,
|
||||
&mut staged_event_graph_mutations,
|
||||
)?;
|
||||
applied_effect_count += effect_summary.applied_effect_count;
|
||||
|
|
@ -276,6 +281,7 @@ fn service_trigger_kind(
|
|||
serviced_record_ids,
|
||||
applied_effect_count,
|
||||
mutated_company_ids: mutated_company_ids.into_iter().collect(),
|
||||
mutated_player_ids: mutated_player_ids.into_iter().collect(),
|
||||
appended_record_ids,
|
||||
activated_record_ids,
|
||||
deactivated_record_ids,
|
||||
|
|
@ -296,6 +302,7 @@ fn apply_runtime_effects(
|
|||
effects: &[RuntimeEffect],
|
||||
condition_context: &ResolvedConditionContext,
|
||||
mutated_company_ids: &mut BTreeSet<u32>,
|
||||
mutated_player_ids: &mut BTreeSet<u32>,
|
||||
staged_event_graph_mutations: &mut Vec<EventGraphMutation>,
|
||||
) -> Result<AppliedEffectsSummary, String> {
|
||||
let mut summary = AppliedEffectsSummary::default();
|
||||
|
|
@ -319,6 +326,20 @@ fn apply_runtime_effects(
|
|||
mutated_company_ids.insert(company_id);
|
||||
}
|
||||
}
|
||||
RuntimeEffect::SetPlayerCash { target, value } => {
|
||||
let player_ids = resolve_player_target_ids(state, target, condition_context)?;
|
||||
for player_id in player_ids {
|
||||
let player = state
|
||||
.players
|
||||
.iter_mut()
|
||||
.find(|player| player.player_id == player_id)
|
||||
.ok_or_else(|| {
|
||||
format!("missing player_id {player_id} while applying cash effect")
|
||||
})?;
|
||||
player.current_cash = *value;
|
||||
mutated_player_ids.insert(player_id);
|
||||
}
|
||||
}
|
||||
RuntimeEffect::DeactivateCompany { target } => {
|
||||
let company_ids = resolve_company_target_ids(state, target, condition_context)?;
|
||||
for company_id in company_ids {
|
||||
|
|
@ -520,21 +541,25 @@ fn evaluate_record_conditions(
|
|||
}
|
||||
}
|
||||
RuntimeCondition::TerritoryNumericThreshold {
|
||||
target,
|
||||
metric,
|
||||
comparator,
|
||||
value,
|
||||
} => {
|
||||
let actual = territory_metric_value(state, *metric);
|
||||
let territory_ids = resolve_territory_target_ids(state, target)?;
|
||||
let actual = territory_metric_value(state, &territory_ids, *metric);
|
||||
if !compare_condition_value(actual, *comparator, *value) {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
RuntimeCondition::CompanyTerritoryNumericThreshold {
|
||||
target,
|
||||
territory,
|
||||
metric,
|
||||
comparator,
|
||||
value,
|
||||
} => {
|
||||
let territory_ids = resolve_territory_target_ids(state, territory)?;
|
||||
let resolved = resolve_company_target_ids(
|
||||
state,
|
||||
target,
|
||||
|
|
@ -544,7 +569,12 @@ fn evaluate_record_conditions(
|
|||
.into_iter()
|
||||
.filter(|company_id| {
|
||||
compare_condition_value(
|
||||
company_territory_metric_value(state, *company_id, *metric),
|
||||
company_territory_metric_value(
|
||||
state,
|
||||
*company_id,
|
||||
&territory_ids,
|
||||
*metric,
|
||||
),
|
||||
*comparator,
|
||||
*value,
|
||||
)
|
||||
|
|
@ -563,6 +593,7 @@ fn evaluate_record_conditions(
|
|||
|
||||
Ok(Some(ResolvedConditionContext {
|
||||
matching_company_ids: company_matches.unwrap_or_default(),
|
||||
matching_player_ids: BTreeSet::new(),
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -676,6 +707,119 @@ fn resolve_company_target_ids(
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_player_target_ids(
|
||||
state: &RuntimeState,
|
||||
target: &RuntimePlayerTarget,
|
||||
condition_context: &ResolvedConditionContext,
|
||||
) -> Result<Vec<u32>, String> {
|
||||
match target {
|
||||
RuntimePlayerTarget::AllActive => Ok(state
|
||||
.players
|
||||
.iter()
|
||||
.filter(|player| player.active)
|
||||
.map(|player| player.player_id)
|
||||
.collect()),
|
||||
RuntimePlayerTarget::Ids { ids } => {
|
||||
let known_ids = state
|
||||
.players
|
||||
.iter()
|
||||
.map(|player| player.player_id)
|
||||
.collect::<BTreeSet<_>>();
|
||||
for player_id in ids {
|
||||
if !known_ids.contains(player_id) {
|
||||
return Err(format!("target references unknown player_id {player_id}"));
|
||||
}
|
||||
}
|
||||
Ok(ids.clone())
|
||||
}
|
||||
RuntimePlayerTarget::HumanPlayers => {
|
||||
if state
|
||||
.players
|
||||
.iter()
|
||||
.any(|player| player.controller_kind == RuntimeCompanyControllerKind::Unknown)
|
||||
{
|
||||
return Err(
|
||||
"target requires player role context but at least one player has unknown controller_kind"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Ok(state
|
||||
.players
|
||||
.iter()
|
||||
.filter(|player| {
|
||||
player.active && player.controller_kind == RuntimeCompanyControllerKind::Human
|
||||
})
|
||||
.map(|player| player.player_id)
|
||||
.collect())
|
||||
}
|
||||
RuntimePlayerTarget::AiPlayers => {
|
||||
if state
|
||||
.players
|
||||
.iter()
|
||||
.any(|player| player.controller_kind == RuntimeCompanyControllerKind::Unknown)
|
||||
{
|
||||
return Err(
|
||||
"target requires player role context but at least one player has unknown controller_kind"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Ok(state
|
||||
.players
|
||||
.iter()
|
||||
.filter(|player| {
|
||||
player.active && player.controller_kind == RuntimeCompanyControllerKind::Ai
|
||||
})
|
||||
.map(|player| player.player_id)
|
||||
.collect())
|
||||
}
|
||||
RuntimePlayerTarget::SelectedPlayer => {
|
||||
let selected_player_id = state
|
||||
.selected_player_id
|
||||
.ok_or_else(|| "target requires selected_player_id context".to_string())?;
|
||||
if state
|
||||
.players
|
||||
.iter()
|
||||
.any(|player| player.player_id == selected_player_id && player.active)
|
||||
{
|
||||
Ok(vec![selected_player_id])
|
||||
} else {
|
||||
Err("target requires selected_player_id to reference an active player".to_string())
|
||||
}
|
||||
}
|
||||
RuntimePlayerTarget::ConditionTruePlayer => {
|
||||
if condition_context.matching_player_ids.is_empty() {
|
||||
Err("target requires player condition-evaluation context".to_string())
|
||||
} else {
|
||||
Ok(condition_context.matching_player_ids.iter().copied().collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_territory_target_ids(
|
||||
state: &RuntimeState,
|
||||
target: &RuntimeTerritoryTarget,
|
||||
) -> Result<Vec<u32>, String> {
|
||||
match target {
|
||||
RuntimeTerritoryTarget::AllTerritories => {
|
||||
Ok(state.territories.iter().map(|territory| territory.territory_id).collect())
|
||||
}
|
||||
RuntimeTerritoryTarget::Ids { ids } => {
|
||||
let known_ids = state
|
||||
.territories
|
||||
.iter()
|
||||
.map(|territory| territory.territory_id)
|
||||
.collect::<BTreeSet<_>>();
|
||||
for territory_id in ids {
|
||||
if !known_ids.contains(territory_id) {
|
||||
return Err(format!("territory target references unknown territory_id {territory_id}"));
|
||||
}
|
||||
}
|
||||
Ok(ids.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn company_metric_value(company: &crate::RuntimeCompany, metric: RuntimeCompanyMetric) -> i64 {
|
||||
match metric {
|
||||
RuntimeCompanyMetric::CurrentCash => company.current_cash,
|
||||
|
|
@ -697,9 +841,14 @@ fn company_metric_value(company: &crate::RuntimeCompany, metric: RuntimeCompanyM
|
|||
}
|
||||
}
|
||||
|
||||
fn territory_metric_value(state: &RuntimeState, metric: RuntimeTerritoryMetric) -> i64 {
|
||||
fn territory_metric_value(
|
||||
state: &RuntimeState,
|
||||
territory_ids: &[u32],
|
||||
metric: RuntimeTerritoryMetric,
|
||||
) -> i64 {
|
||||
state.territories
|
||||
.iter()
|
||||
.filter(|territory| territory_ids.contains(&territory.territory_id))
|
||||
.map(|territory| {
|
||||
track_piece_metric_value(
|
||||
territory.track_piece_counts,
|
||||
|
|
@ -712,11 +861,12 @@ fn territory_metric_value(state: &RuntimeState, metric: RuntimeTerritoryMetric)
|
|||
fn company_territory_metric_value(
|
||||
state: &RuntimeState,
|
||||
company_id: u32,
|
||||
territory_ids: &[u32],
|
||||
metric: RuntimeTrackMetric,
|
||||
) -> i64 {
|
||||
state.company_territory_track_piece_counts
|
||||
.iter()
|
||||
.filter(|entry| entry.company_id == company_id)
|
||||
.filter(|entry| entry.company_id == company_id && territory_ids.contains(&entry.territory_id))
|
||||
.map(|entry| track_piece_metric_value(entry.track_piece_counts, metric))
|
||||
.sum()
|
||||
}
|
||||
|
|
@ -805,6 +955,8 @@ mod tests {
|
|||
available_track_laying_capacity: None,
|
||||
}],
|
||||
selected_company_id: None,
|
||||
players: Vec::new(),
|
||||
selected_player_id: None,
|
||||
territories: Vec::new(),
|
||||
company_territory_track_piece_counts: Vec::new(),
|
||||
packed_event_collection: None,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue