use serde::{Deserialize, Serialize};
use crate::types::*;
use crate::scene::types::{Condition, StateEffect};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Encounter {
pub id: EncounterId,
pub phases: Vec<CombatPhase>,
pub standoff: Option<Standoff>,
pub party_slots: u8,
pub terrain: Terrain,
pub objectives: Vec<Objective>,
pub outcome_effects: Vec<OutcomeEffect>,
#[serde(default = "default_escapable")]
pub escapable: bool,
}
fn default_escapable() -> bool { true }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CombatPhase {
pub id: String,
pub description: String,
pub enemies: Vec<EnemyTemplate>,
#[serde(default)]
pub npc_allies: Vec<NpcCombatant>,
#[serde(default)]
pub entry_conditions: Vec<Condition>,
#[serde(default)]
pub phase_effects: Vec<StateEffect>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Standoff {
pub postures: Vec<StandoffPosture>,
pub allow_focus: bool,
pub eli_influence: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum StandoffPosture {
EarlyDraw,
SteadyHand,
Bait,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StandoffResult {
pub initiative_mods: Vec<(String, i32)>,
pub nerve_damage: Vec<(String, i32)>,
pub broken_enemies: Vec<usize>,
pub first_shot_accuracy: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CombatantState {
pub character: CharacterId,
pub hp: i32,
pub max_hp: i32,
pub nerve: i32,
pub max_nerve: i32,
pub ammo: i32,
pub max_ammo: i32,
pub wounds: Vec<Wound>,
pub position: PositionState,
pub available_skills: Vec<SkillId>,
pub available_duo_techs: Vec<DuoTechId>,
#[serde(default)]
pub active_effects: Vec<(StatusEffect, u8)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Skill {
pub id: SkillId,
pub name: String,
pub description: String,
pub line: SkillLine,
pub unlock: UnlockCondition,
pub age_variants: Vec<AgeVariant>,
pub cost: SkillCost,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgeVariant {
pub phase: AgePhase,
pub accuracy: i32,
pub damage: i32,
pub speed_priority: i32,
pub nerve_damage: i32,
pub description_override: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SkillLine {
Deadeye, Trailcraft, Command,
Hustle, Deceit,
Loyalty,
Triage, Tonics, Diagnosis,
Lariat, Guard, Break,
Hymn, Witness, Intercession,
Charges, Smoke, Collapse,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum UnlockCondition {
StartOfPhase(AgePhase),
StoryEvent(FlagId),
TurningPoint(FlagId),
Bond { character_a: CharacterId, character_b: CharacterId, threshold: i32 },
Ordeal(FlagId),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DuoTech {
pub id: DuoTechId,
pub name: String,
pub description: String,
pub members: (CharacterId, CharacterId),
pub unlock: UnlockCondition,
pub cost: DuoTechCost,
pub effect: DuoTechEffect,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WoundSeverity {
Minor,
Major,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Wound {
pub id: InjuryId,
pub name: String,
pub description: String,
pub penalties: Vec<StatPenalty>,
pub treatable: bool,
#[serde(default = "default_severity")]
pub severity: WoundSeverity,
}
fn default_severity() -> WoundSeverity { WoundSeverity::Major }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PositionState {
Open,
InCover,
PartialCover,
Elevated,
FrontLine,
BackLine,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Terrain {
pub name: String,
pub cover: Vec<CoverElement>,
#[serde(default)]
pub hazards: Vec<EnvironmentalHazard>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoverElement {
pub name: String,
pub durability: i32,
pub destructible: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EnvironmentalHazard {
FuseCharge { turns_to_detonate: u8, blast_radius: u8 },
CollapseRisk { trigger_damage: i32 },
CrowdSurge { collective_nerve: i32 },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnemyTemplate {
pub id: String,
pub name: String,
pub hp: i32,
pub nerve: i32,
pub damage: i32,
pub accuracy: i32,
pub speed: i32,
pub bluff: i32,
pub nerve_threshold: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NpcCombatant {
pub character: CharacterId,
pub behavior: NpcBehavior,
pub hp: i32,
pub nerve: i32,
#[serde(default = "default_npc_speed")]
pub speed: i32,
#[serde(default = "default_npc_accuracy")]
pub accuracy: i32,
#[serde(default = "default_npc_damage")]
pub damage: i32,
}
fn default_npc_speed() -> i32 { 10 }
fn default_npc_accuracy() -> i32 { 60 }
fn default_npc_damage() -> i32 { 8 }
#[derive(Debug, Clone)]
pub struct NpcCharacterStats {
pub speed: i32,
pub accuracy: i32,
pub damage: i32,
}
pub fn npc_stats_for(character_id: &str) -> NpcCharacterStats {
match character_id {
"cal" => NpcCharacterStats { speed: 9, accuracy: 62, damage: 9 },
"deputy_harris" => NpcCharacterStats { speed: 11, accuracy: 65, damage: 9 },
"renata" => NpcCharacterStats { speed: 10, accuracy: 68, damage: 7 },
"bale" => NpcCharacterStats { speed: 7, accuracy: 55, damage: 12 },
_ => NpcCharacterStats { speed: 10, accuracy: 60, damage: 8 },
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum NpcBehavior {
Professional,
Unreliable,
Protective,
Nervous,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Objective {
pub id: String,
pub label: String,
pub objective_type: ObjectiveType,
pub fail_consequence: Vec<StateEffect>,
pub success_consequence: Vec<StateEffect>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ObjectiveBehavior {
CivilianCasualties,
ProtectAsset,
General,
}
impl Objective {
pub fn behavior(&self) -> ObjectiveBehavior {
if self.id.contains("casualties") || self.id.contains("civilian") {
ObjectiveBehavior::CivilianCasualties
} else {
ObjectiveBehavior::General
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ObjectiveType {
Primary,
Secondary,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutcomeEffect {
pub condition: OutcomeCondition,
pub effects: Vec<StateEffect>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OutcomeCondition {
Victory,
Defeat,
ObjectiveComplete(String),
ObjectiveFailed(String),
SkillUsed { character: CharacterId, skill: SkillId },
SkillNotUsed { character: CharacterId, skill: SkillId },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SkillCost {
pub ammo: i32,
pub nerve: i32,
pub cooldown_turns: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DuoTechCost {
pub ammo: i32,
pub nerve: i32,
pub both_turns: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DuoTechEffect {
pub description: String,
pub damage: i32,
pub accuracy_bonus: i32,
pub nerve_damage: i32,
pub special: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatPenalty {
pub stat: String,
pub amount: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum StatusEffect {
Bleeding,
Stunned,
Inspired,
Suppressed,
}
impl StatusEffect {
pub fn per_turn_impact(&self) -> (i32, i32, bool) {
match self {
StatusEffect::Bleeding => (3, 0, false),
StatusEffect::Stunned => (0, 0, true),
StatusEffect::Inspired => (0, 0, false),
StatusEffect::Suppressed => (0, 0, false),
}
}
}