use rand::{thread_rng, Rng}; use uuid::Uuid; use util::{IntPct}; use item::{Item}; use game::{Game, Colour, Value, Action}; use construct::{Construct, ConstructEffect, EffectMeta, Stat}; use effect::{Effect, Cooldown}; // pub fn dev_resolve(a_id: Uuid, b_id: Uuid, skill: Skill) { // let mut resolutions =vec![]; // let mut a = Construct::new(); // a.id = a_id; // let mut b = Construct::new(); // b.id = b_id; // if skill.aoe() { // Send an aoe skill event for anims // game.event(Event::new(&a, &b).event(Event::AoeSkill { skill }).stages(EventStages::StartEnd)); // } // return cast_actions(skill, &mut a, &mut b, resolutions); // } fn modify_cast(game: &Game, cast: Cast) -> Vec { let target_player = game.players.iter() .find(|t| t.constructs.iter().any(|c| c.id == cast.target)) .unwrap(); if let Some(t) = target_player.intercepting() { return vec![Cast { target: t.id, ..cast }]; } // if game.construct[source].multistrike() { // return vec![ // Cast { target: t.id, ..cast }, // Cast { target: t.id, ..cast }, // ]; // } let targets = match cast.skill.aoe() { true => game.players.iter() .find(|t| t.constructs.iter().any(|c| c.id == cast.target)) .unwrap() .constructs .iter() .map(|c| Cast { target: c.id, ..cast }) .collect(), false => vec![cast], }; return targets; } pub fn resolve(game: &mut Game, cast: Cast) { let casts = modify_cast(game, cast); // let source = game.construct_by_id(cast.source).unwrap().clone(); // if skill.aoe() { // Send an aoe skill event for anims // game.event(Event::new(&source, // &game.construct_by_id(cast.target).unwrap().clone()).event(Event::AoeSkill { skill }).stages(EventStages::StartEnd)); // } for cast in casts { game.actions(cast_actions(cast)); } } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct Cast { pub id: Uuid, pub player: Uuid, pub source: Uuid, pub target: Uuid, pub skill: Skill, pub speed: usize, } impl Cast { pub fn new(source: Uuid, player: Uuid, target: Uuid, skill: Skill) -> Cast { return Cast { id: Uuid::new_v4(), source, player, target, skill, speed: 0, }; } pub fn new_tick(source: &mut Construct, target: &mut Construct, skill: Skill) -> Cast { Cast { id: Uuid::new_v4(), source: source.id, player: source.account, target: target.id, skill, speed: source.skill_speed(skill), } } pub fn used_cooldown(&self) -> bool { return self.skill.base_cd().is_some(); } } pub type Disable = Vec; pub type Immunity = Vec; #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct Event { pub target: EventConstruct, pub variant: EventVariant, pub stages: EventStages, pub delay: i64, } impl Event { pub fn new(variant: EventVariant, target: &Construct) -> Event { let stages = variant.stages(); Event { target: EventConstruct { id: target.id, red: target.red_life(), green: target.green_life(), blue: target.blue_life(), }, variant, delay: stages.delay(), stages: stages, } } } #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub enum EventVariant { Disable { skill: Skill, disable: Disable }, Immunity { skill: Skill, immunity: Immunity }, Damage { skill: Skill, amount: usize, mitigation: usize, colour: Colour }, Healing { skill: Skill, amount: usize, overhealing: usize }, Recharge { skill: Skill, red: usize, blue: usize }, Inversion { skill: Skill }, Reflection { skill: Skill }, AoeSkill { skill: Skill }, Skill { skill: Skill }, Effect { skill: Skill, effect: Effect, duration: u8, construct_effects: Vec }, Removal { skill: Skill, effect: Option, construct_effects: Vec }, TargetKo { skill: Skill }, // skill not necessary but makes it neater as all events are arrays in js Ko (), Forfeit (), Incomplete (), // not used Evasion { skill: Skill, evasion_rating: usize }, } impl EventVariant { fn stages(&self) -> EventStages { match self { EventVariant::Disable { skill, disable} => EventStages::PostOnly, EventVariant::Immunity { skill, immunity} => EventStages::PostOnly, EventVariant::Damage { skill, amount, mitigation, colour} => EventStages::PostOnly, EventVariant::Healing { skill, amount, overhealing} => EventStages::PostOnly, EventVariant::Recharge { skill, red, blue} => EventStages::PostOnly, EventVariant::Inversion { skill } => EventStages::PostOnly, EventVariant::Reflection { skill } => EventStages::PostOnly, EventVariant::AoeSkill { skill } => EventStages::PostOnly, EventVariant::Skill { skill } => EventStages::PostOnly, EventVariant::Effect { skill, effect, duration, construct_effects } => EventStages::PostOnly, EventVariant::Removal { skill, effect, construct_effects } => EventStages::PostOnly, EventVariant::TargetKo { skill } => EventStages::PostOnly, EventVariant::Ko () => EventStages::PostOnly, EventVariant::Forfeit () => EventStages::PostOnly, EventVariant::Incomplete () => EventStages::PostOnly, EventVariant::Evasion { skill, evasion_rating } => EventStages::PostOnly, } } } // used to show the progress of a construct // while the resolutions are animating #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct EventConstruct { pub id: Uuid, pub red: usize, pub green: usize, pub blue: usize, } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum EventStages { #[serde(rename = "START_SKILL END_SKILL POST_SKILL")] AllStages, // Anim Anim Anim #[serde(rename = "START_SKILL END_SKILL")] StartEnd, // Anim Anim Skip #[serde(rename = "START_SKILL POST_SKILL")] StartPost, // Anim Skip Anim #[serde(rename = "START_SKILL")] StartOnly, // Anim Skip Skip #[serde(rename = "END_SKILL POST_SKILL")] EndPost, // Skip Anim Anim #[serde(rename = "END_SKILL")] EndOnly, // Skip Anim Skip #[serde(rename = "POST_SKILL")] PostOnly, // Skip Skip Anim } impl EventStages { fn delay(self) -> i64 { let source_duration = 1000; // Time for SOURCE ONLY let target_delay = 500; // Used for Source + Target let target_duration = 1500; // Time for TARGET ONLY let post_skill = 1000; // Time for all POST let source_and_target_total = target_delay + target_duration; // SOURCE + TARGET time match self { EventStages::AllStages => source_and_target_total + post_skill, // Anim Anim Anim EventStages::StartEnd => source_and_target_total, // Anim Anim Skip EventStages::StartPost => source_duration + post_skill, // Anim Skip Anim EventStages::StartOnly => source_duration, // Anim Skip Skip EventStages::EndPost => target_duration + post_skill, // Skip Anim Anim EventStages::EndOnly => target_duration, // Skip Anim Skip EventStages::PostOnly => post_skill, // Skip Skip Anim } } } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Skill { Attack, Debuff, Buff, Block, // reduce damage Stun, // Boost -- sounds nice // Evade, // actively evade // Nightmare, // Sleep, Amplify, #[serde(rename = "Amplify+")] AmplifyPlus, #[serde(rename = "Amplify++")] AmplifyPlusPlus, Absorb, #[serde(rename = "Absorb+")] AbsorbPlus, #[serde(rename = "Absorb++")] AbsorbPlusPlus, Banish, #[serde(rename = "Banish+")] BanishPlus, #[serde(rename = "Banish++")] BanishPlusPlus, Bash, #[serde(rename = "Bash+")] BashPlus, #[serde(rename = "Bash++")] BashPlusPlus, Blast, #[serde(rename = "Blast+")] BlastPlus, #[serde(rename = "Blast++")] BlastPlusPlus, Chaos, #[serde(rename = "Chaos+")] ChaosPlus, #[serde(rename = "Chaos++")] ChaosPlusPlus, Sustain, #[serde(rename = "Sustain+")] SustainPlus, #[serde(rename = "Sustain++")] SustainPlusPlus, Electrify, #[serde(rename = "Electrify+")] ElectrifyPlus, #[serde(rename = "Electrify++")] ElectrifyPlusPlus, Curse, #[serde(rename = "Curse+")] CursePlus, #[serde(rename = "Curse++")] CursePlusPlus, Decay, #[serde(rename = "Decay+")] DecayPlus, #[serde(rename = "Decay++")] DecayPlusPlus, Haste, #[serde(rename = "Haste+")] HastePlus, #[serde(rename = "Haste++")] HastePlusPlus, Heal, #[serde(rename = "Heal+")] HealPlus, #[serde(rename = "Heal++")] HealPlusPlus, Hybrid, #[serde(rename = "Hybrid+")] HybridPlus, #[serde(rename = "Hybrid++")] HybridPlusPlus, Invert, #[serde(rename = "Invert+")] InvertPlus, #[serde(rename = "Invert++")] InvertPlusPlus, Counter, #[serde(rename = "Counter+")] CounterPlus, #[serde(rename = "Counter++")] CounterPlusPlus, Purge, #[serde(rename = "Purge+")] PurgePlus, #[serde(rename = "Purge++")] PurgePlusPlus, Purify, #[serde(rename = "Purify+")] PurifyPlus, #[serde(rename = "Purify++")] PurifyPlusPlus, Reflect, #[serde(rename = "Reflect+")] ReflectPlus, #[serde(rename = "Reflect++")] ReflectPlusPlus, Recharge, #[serde(rename = "Recharge+")] RechargePlus, #[serde(rename = "Recharge++")] RechargePlusPlus, Ruin, #[serde(rename = "Ruin+")] RuinPlus, #[serde(rename = "Ruin++")] RuinPlusPlus, Link, #[serde(rename = "Link+")] LinkPlus, #[serde(rename = "Link++")] LinkPlusPlus, Silence, #[serde(rename = "Silence+")] SilencePlus, #[serde(rename = "Silence++")] SilencePlusPlus, Slay, #[serde(rename = "Slay+")] SlayPlus, #[serde(rename = "Slay++")] SlayPlusPlus, Sleep, #[serde(rename = "Sleep+")] SleepPlus, #[serde(rename = "Sleep++")] SleepPlusPlus, Restrict, #[serde(rename = "Restrict+")] RestrictPlus, #[serde(rename = "Restrict++")] RestrictPlusPlus, Strike, #[serde(rename = "Strike+")] StrikePlus, #[serde(rename = "Strike++")] StrikePlusPlus, Siphon, #[serde(rename = "Siphon+")] SiphonPlus, #[serde(rename = "Siphon++")] SiphonPlusPlus, Intercept, #[serde(rename = "Intercept+")] InterceptPlus, #[serde(rename = "Intercept++")] InterceptPlusPlus, Break, #[serde(rename = "Break+")] BreakPlus, #[serde(rename = "Break++")] BreakPlusPlus, Triage, #[serde(rename = "Triage+")] TriagePlus, #[serde(rename = "Triage++")] TriagePlusPlus, Absorption, #[serde(rename = "Absorption+")] AbsorptionPlus, #[serde(rename = "Absorption++")] AbsorptionPlusPlus, CounterAttack, #[serde(rename = "CounterAttack+")] CounterAttackPlus, #[serde(rename = "CounterAttack++")] CounterAttackPlusPlus, Electrocute, #[serde(rename = "Electrocute+")] ElectrocutePlus, #[serde(rename = "Electrocute++")] ElectrocutePlusPlus, ElectrocuteTick, #[serde(rename = "ElectrocuteTick+")] ElectrocuteTickPlus, #[serde(rename = "ElectrocuteTick++")] ElectrocuteTickPlusPlus, DecayTick, // dot #[serde(rename = "DecayTick+")] DecayTickPlus, #[serde(rename = "DecayTick++")] DecayTickPlusPlus, HasteStrike, HybridBlast, SiphonTick, #[serde(rename = "SiphonTick+")] SiphonTickPlus, #[serde(rename = "SiphonTick++")] SiphonTickPlusPlus, TriageTick, #[serde(rename = "TriageTick+")] TriageTickPlus, #[serde(rename = "TriageTick++")] TriageTickPlusPlus, } impl Skill { pub fn multiplier(&self) -> usize { match self { // Attack Base Skill::Attack => 80, // Base Skill::Blast => 105, // BB Skill::BlastPlus => 125, // BB Skill::BlastPlusPlus => 145, // BB Skill::Chaos => 40, // BR Skill::ChaosPlus => 50, // BR Skill::ChaosPlusPlus => 65, // BR Skill::Heal => 115, //GG Skill::HealPlus => 135, //GG Skill::HealPlusPlus => 160, //GG Skill::SiphonTick => 25, // GB Skill::SiphonTickPlus => 27, Skill::SiphonTickPlusPlus => 30, Skill::Slay => 40, // RG Skill::SlayPlus => 50, Skill::SlayPlusPlus => 65, Skill::Strike => 90, //RR Skill::StrikePlus => 110, Skill::StrikePlusPlus => 140, // Block Base Skill::ElectrocuteTick => 80, Skill::ElectrocuteTickPlus => 90, Skill::ElectrocuteTickPlusPlus => 100, Skill::CounterAttack => 115, Skill::CounterAttackPlus => 130, Skill::CounterAttackPlusPlus => 160, Skill::Purify => 45, //Green dmg (heal) Skill::PurifyPlus => 60, Skill::PurifyPlusPlus => 85, Skill::Reflect => 45, //Recharge blue life (heal) Skill::ReflectPlus => 70, Skill::ReflectPlusPlus => 100, Skill::Recharge => 70, //Recharge red and blue life (heal) Skill::RechargePlus => 90, Skill::RechargePlusPlus => 110, Skill::Sustain => 110, // Recharge red life (heal) Skill::SustainPlus => 130, Skill::SustainPlusPlus => 150, // Stun Base Skill::Sleep => 160, //Green dmg (heal) Skill::SleepPlus => 200, Skill::SleepPlusPlus => 240, Skill::Banish => 50, //Green dmg (heal) Skill::BanishPlus => 65, Skill::BanishPlusPlus => 80, Skill::Bash => 45, Skill::BashPlus => 55, Skill::BashPlusPlus => 70, Skill::Link => 25, Skill::LinkPlus => 35, Skill::LinkPlusPlus => 45, Skill::Ruin => 40, Skill::RuinPlus => 55, Skill::RuinPlusPlus => 70, // Debuff Base Skill::DecayTick => 33, Skill::DecayTickPlus => 37, Skill::DecayTickPlusPlus => 45, Skill::Silence => 55, // Deals more per blue skill on target Skill::SilencePlus => 65, Skill::SilencePlusPlus => 80, Skill::Restrict => 40, // Deals more per red skill on target Skill::RestrictPlus => 55, Skill::RestrictPlusPlus => 70, // Buff base Skill::HybridBlast => 50, Skill::HasteStrike => 60, Skill::Absorb=> 95, Skill::AbsorbPlus => 110, Skill::AbsorbPlusPlus => 120, Skill::Intercept => 85, Skill::InterceptPlus => 100, Skill::InterceptPlusPlus => 125, Skill::TriageTick => 75, Skill::TriageTickPlus => 90, Skill::TriageTickPlusPlus => 110, _ => 100, } } pub fn effect(&self) -> Vec { match self { // Modifiers Skill::Amplify => vec![ConstructEffect {effect: Effect::Amplify, duration: 2, meta: Some(EffectMeta::Multiplier(150)), tick: None}], Skill::AmplifyPlus => vec![ConstructEffect {effect: Effect::Amplify, duration: 3, meta: Some(EffectMeta::Multiplier(175)), tick: None}], Skill::AmplifyPlusPlus => vec![ConstructEffect {effect: Effect::Amplify, duration: 4, meta: Some(EffectMeta::Multiplier(200)), tick: None}], Skill::Banish => vec![ConstructEffect {effect: Effect::Banish, duration: 2, meta: None, tick: None}], Skill::BanishPlus => vec![ConstructEffect {effect: Effect::Banish, duration: 2, meta: None, tick: None}], Skill::BanishPlusPlus => vec![ConstructEffect {effect: Effect::Banish, duration: 2, meta: None, tick: None}], Skill::Block => vec![ConstructEffect {effect: Effect::Block, duration: 1, meta: Some(EffectMeta::Multiplier(35)), tick: None}], Skill::Buff => vec![ConstructEffect {effect: Effect::Buff, duration: 3, meta: Some(EffectMeta::Multiplier(130)), tick: None }], Skill::Electrify => vec![ConstructEffect {effect: Effect::Electric, duration: 1, meta: Some(EffectMeta::Skill(Skill::Electrocute)), tick: None}], Skill::ElectrifyPlus => vec![ConstructEffect {effect: Effect::Electric, duration: 1, meta: Some(EffectMeta::Skill(Skill::ElectrocutePlus)), tick: None}], Skill::ElectrifyPlusPlus => vec![ConstructEffect {effect: Effect::Electric, duration: 1, meta: Some(EffectMeta::Skill(Skill::ElectrocutePlusPlus)), tick: None}], Skill::Electrocute => vec![ConstructEffect {effect: Effect::Electrocute, duration: 2, meta: Some(EffectMeta::Skill(Skill::ElectrocuteTick)), tick: None}], Skill::ElectrocutePlus => vec![ConstructEffect {effect: Effect::Electrocute, duration: 3, meta: Some(EffectMeta::Skill(Skill::ElectrocuteTickPlus)), tick: None}], Skill::ElectrocutePlusPlus => vec![ConstructEffect {effect: Effect::Electrocute, duration: 4, meta: Some(EffectMeta::Skill(Skill::ElectrocuteTickPlusPlus)), tick: None}], Skill::Sustain => vec![ConstructEffect {effect: Effect::Sustain, duration: 1, meta: None, tick: None }], Skill::SustainPlus => vec![ConstructEffect {effect: Effect::Sustain, duration: 1, meta: None, tick: None }], Skill::SustainPlusPlus => vec![ConstructEffect {effect: Effect::Sustain, duration: 1, meta: None, tick: None }], Skill::Curse => vec![ConstructEffect {effect: Effect::Curse, duration: 2, meta: Some(EffectMeta::Multiplier(150)), tick: None}], Skill::CursePlus => vec![ConstructEffect {effect: Effect::Curse, duration: 2, meta: Some(EffectMeta::Multiplier(175)), tick: None}], Skill::CursePlusPlus => vec![ConstructEffect {effect: Effect::Curse, duration: 3, meta: Some(EffectMeta::Multiplier(200)), tick: None}], Skill::Debuff => vec![ConstructEffect {effect: Effect::Slow, duration: 3, meta: Some(EffectMeta::Multiplier(50)), tick: None }], Skill::Decay => vec![ConstructEffect {effect: Effect::Wither, duration: 3, meta: Some(EffectMeta::Multiplier(50)), tick: None }, ConstructEffect {effect: Effect::Decay, duration: 3, meta: Some(EffectMeta::Skill(Skill::DecayTick)), tick: None}], Skill::DecayPlus => vec![ConstructEffect {effect: Effect::Wither, duration: 3, meta: Some(EffectMeta::Multiplier(35)), tick: None }, ConstructEffect {effect: Effect::Decay, duration: 3, meta: Some(EffectMeta::Skill(Skill::DecayTickPlus)), tick: None}], Skill::DecayPlusPlus => vec![ConstructEffect {effect: Effect::Wither, duration: 4, meta: Some(EffectMeta::Multiplier(20)), tick: None }, ConstructEffect {effect: Effect::Decay, duration: 4, meta: Some(EffectMeta::Skill(Skill::DecayTickPlusPlus)), tick: None}], Skill::Haste => vec![ConstructEffect {effect: Effect::Haste, duration: 3, meta: Some(EffectMeta::Multiplier(150)), tick: None }], Skill::HastePlus => vec![ConstructEffect {effect: Effect::Haste, duration: 4, meta: Some(EffectMeta::Multiplier(175)), tick: None }], Skill::HastePlusPlus => vec![ConstructEffect {effect: Effect::Haste, duration: 5, meta: Some(EffectMeta::Multiplier(225)), tick: None }], Skill::Absorb => vec![ConstructEffect {effect: Effect::Absorb, duration: 1, meta: Some(EffectMeta::Skill(Skill::Absorption)), tick: None}], Skill::AbsorbPlus => vec![ConstructEffect {effect: Effect::Absorb, duration: 1, meta: Some(EffectMeta::Skill(Skill::AbsorptionPlus)), tick: None}], Skill::AbsorbPlusPlus => vec![ConstructEffect {effect: Effect::Absorb, duration: 1, meta: Some(EffectMeta::Skill(Skill::AbsorptionPlusPlus)), tick: None}], Skill::Absorption => vec![ConstructEffect {effect: Effect::Absorption, duration: 3, meta: None, tick: None}], Skill::AbsorptionPlus => vec![ConstructEffect {effect: Effect::Absorption, duration: 4, meta: None, tick: None}], Skill::AbsorptionPlusPlus => vec![ConstructEffect {effect: Effect::Absorption, duration: 5, meta: None, tick: None}], Skill::Hybrid => vec![ConstructEffect {effect: Effect::Hybrid, duration: 3, meta: Some(EffectMeta::Multiplier(150)), tick: None }], Skill::HybridPlus => vec![ConstructEffect {effect: Effect::Hybrid, duration: 4, meta: Some(EffectMeta::Multiplier(175)), tick: None }], Skill::HybridPlusPlus => vec![ConstructEffect {effect: Effect::Hybrid, duration: 5, meta: Some(EffectMeta::Multiplier(200)), tick: None }], Skill::Invert => vec![ConstructEffect {effect: Effect::Invert, duration: 2, meta: None, tick: None}], Skill::InvertPlus => vec![ConstructEffect {effect: Effect::Invert, duration: 3, meta: None, tick: None}], Skill::InvertPlusPlus => vec![ConstructEffect {effect: Effect::Invert, duration: 4, meta: None, tick: None}], Skill::Counter => vec![ConstructEffect {effect: Effect::Counter, duration: 1, meta: Some(EffectMeta::Skill(Skill::CounterAttack)), tick: None}], Skill::CounterPlus => vec![ConstructEffect {effect: Effect::Counter, duration: 1, meta: Some(EffectMeta::Skill(Skill::CounterAttackPlus)), tick: None}], Skill::CounterPlusPlus => vec![ConstructEffect {effect: Effect::Counter, duration: 1, meta: Some(EffectMeta::Skill(Skill::CounterAttackPlusPlus)), tick: None}], Skill::Reflect => vec![ConstructEffect {effect: Effect::Reflect, duration: 1, meta: None, tick: None }], Skill::ReflectPlus => vec![ConstructEffect {effect: Effect::Reflect, duration: 1, meta: None, tick: None }], Skill::ReflectPlusPlus => vec![ConstructEffect {effect: Effect::Reflect, duration: 1, meta: None, tick: None }], Skill::Break => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}, ConstructEffect {effect: Effect::Vulnerable, duration: 3, meta: Some(EffectMeta::Multiplier(150)), tick: None}], Skill::BreakPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}, ConstructEffect {effect: Effect::Vulnerable, duration: 4, meta: Some(EffectMeta::Multiplier(175)), tick: None}], Skill::BreakPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}, ConstructEffect {effect: Effect::Vulnerable, duration: 4, meta: Some(EffectMeta::Multiplier(200)), tick: None}], Skill::Ruin => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}], Skill::RuinPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}], Skill::RuinPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}], Skill::Purge => vec![ConstructEffect {effect: Effect::Purge, duration: 2, meta: None, tick: None}], Skill::PurgePlus => vec![ConstructEffect {effect: Effect::Purge, duration: 3, meta: None, tick: None}], Skill::PurgePlusPlus => vec![ConstructEffect {effect: Effect::Purge, duration: 4, meta: None, tick: None}], Skill::Link => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}], Skill::LinkPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}], Skill::LinkPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}], Skill::Silence => vec![ConstructEffect {effect: Effect::Silence, duration: 2, meta: None, tick: None}], Skill::SilencePlus => vec![ConstructEffect {effect: Effect::Silence, duration: 2, meta: None, tick: None}], Skill::SilencePlusPlus => vec![ConstructEffect {effect: Effect::Silence, duration: 2, meta: None, tick: None}], Skill::Siphon => vec![ConstructEffect {effect: Effect::Siphon, duration: 2, meta: Some(EffectMeta::Skill(Skill::SiphonTick)), tick: None}], Skill::SiphonPlus => vec![ConstructEffect {effect: Effect::Siphon, duration: 3, meta: Some(EffectMeta::Skill(Skill::SiphonTickPlus)), tick: None}], Skill::SiphonPlusPlus => vec![ConstructEffect {effect: Effect::Siphon, duration: 4, meta: Some(EffectMeta::Skill(Skill::SiphonTickPlusPlus)), tick: None}], Skill::Sleep => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}], Skill::SleepPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 3, meta: None, tick: None}], Skill::SleepPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 4, meta: None, tick: None}], Skill::Restrict => vec![ConstructEffect {effect: Effect::Restrict, duration: 2, meta: None, tick: None}], Skill::RestrictPlus => vec![ConstructEffect {effect: Effect::Restrict, duration: 2, meta: None, tick: None}], Skill::RestrictPlusPlus => vec![ConstructEffect {effect: Effect::Restrict, duration: 2, meta: None, tick: None}], Skill::Bash => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: Some(EffectMeta::Skill(Skill::Bash)), tick: None}], Skill::BashPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: Some(EffectMeta::Skill(Skill::BashPlus)), tick: None}], Skill::BashPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: Some(EffectMeta::Skill(Skill::BashPlusPlus)), tick: None}], Skill::Stun => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}], Skill::Intercept => vec![ConstructEffect {effect: Effect::Intercept, duration: 1, meta: None, tick: None}], Skill::InterceptPlus => vec![ConstructEffect {effect: Effect::Intercept, duration: 1, meta: None, tick: None}], Skill::InterceptPlusPlus => vec![ConstructEffect {effect: Effect::Intercept, duration: 1, meta: None, tick: None}], Skill::Triage => vec![ConstructEffect {effect: Effect::Triage, duration: 2, meta: Some(EffectMeta::Skill(Skill::TriageTick)), tick: None}], Skill::TriagePlus => vec![ConstructEffect {effect: Effect::Triage, duration: 3, meta: Some(EffectMeta::Skill(Skill::TriageTickPlus)), tick: None}], Skill::TriagePlusPlus => vec![ConstructEffect {effect: Effect::Triage, duration: 4, meta: Some(EffectMeta::Skill(Skill::TriageTickPlusPlus)), tick: None}], Skill::Purify => vec![ConstructEffect { effect: Effect::Pure, duration: 2, meta: Some(EffectMeta::Multiplier(150)), tick: None}], Skill::PurifyPlus => vec![ConstructEffect { effect: Effect::Pure, duration: 2, meta: Some(EffectMeta::Multiplier(175)), tick: None}], Skill::PurifyPlusPlus => vec![ConstructEffect { effect: Effect::Pure, duration: 2, meta: Some(EffectMeta::Multiplier(200)), tick: None}], _ => { panic!("{:?} no skill effect", self); }, } } pub fn base_cd(&self) -> Cooldown { match self { Skill::Attack => None, Skill::Block => None, // reduce damage Skill::Buff => None, Skill::Debuff => Some(1), Skill::Stun => Some(2), Skill::Strike=> None, Skill::StrikePlus => None, Skill::StrikePlusPlus => None, Skill::Counter| Skill::CounterPlus | Skill::CounterPlusPlus => None, // avoid all damage Skill::Restrict | Skill::RestrictPlus | Skill::RestrictPlusPlus => Some(2), Skill::Bash | Skill::BashPlus | Skill::BashPlusPlus => Some(2), Skill::Heal=> None, Skill::HealPlus => None, Skill::HealPlusPlus => None, Skill::Triage=> None, // hot Skill::TriagePlus => None, // hot Skill::TriagePlusPlus => None, // hot Skill::Break | // no damage stun, adds vulnerable Skill::BreakPlus | Skill::BreakPlusPlus => Some(1), Skill::Blast | Skill::BlastPlus | Skill::BlastPlusPlus => None, Skill::Chaos | Skill::ChaosPlus | Skill::ChaosPlusPlus => None, Skill::Amplify | Skill::AmplifyPlus | Skill::AmplifyPlusPlus => Some(1), Skill::Hybrid | Skill::HybridPlus | Skill::HybridPlusPlus => Some(1), Skill::Invert | Skill::InvertPlus | Skill::InvertPlusPlus => Some(2), Skill::Decay => None, // dot Skill::DecayPlus => None, Skill::DecayPlusPlus => None, Skill::Siphon| Skill::SiphonPlus | Skill::SiphonPlusPlus => None, Skill::Curse | Skill::CursePlus | Skill::CursePlusPlus => Some(1), Skill::Link | Skill::LinkPlus | Skill::LinkPlusPlus => Some(1), Skill::Silence | Skill::SilencePlus | Skill::SilencePlusPlus => Some(2), Skill::Purify | Skill::PurifyPlus | Skill::PurifyPlusPlus => None, Skill::Purge | Skill::PurgePlus | Skill::PurgePlusPlus => Some(1), Skill::Banish | Skill::BanishPlus | Skill::BanishPlusPlus => Some(1), Skill::Haste | Skill::HastePlus | Skill::HastePlusPlus => Some(1), Skill::Reflect | Skill::ReflectPlus | Skill::ReflectPlusPlus => None, Skill::Recharge | Skill::RechargePlus | Skill::RechargePlusPlus => None, Skill::Ruin | Skill::RuinPlus | Skill::RuinPlusPlus => Some(2), Skill::Slay=> None, Skill::SlayPlus => None, Skill::SlayPlusPlus => None, Skill::Sleep | Skill::SleepPlus | Skill::SleepPlusPlus => Some(2), Skill::Sustain | Skill::SustainPlus | Skill::SustainPlusPlus => Some(1), Skill::Intercept => Some(1), Skill::InterceptPlus => Some(1), Skill::InterceptPlusPlus => Some(1), Skill::Electrify | Skill::ElectrifyPlus | Skill::ElectrifyPlusPlus => None, Skill::Absorb | Skill::AbsorbPlus | Skill::AbsorbPlusPlus => Some(1), //----------- // Never cast directly //--------- // Trigger Skill::HybridBlast | Skill::HasteStrike | Skill::CounterAttack| Skill::CounterAttackPlus | Skill::CounterAttackPlusPlus | // counter Skill::Electrocute| Skill::ElectrocutePlus | Skill::ElectrocutePlusPlus | Skill::Absorption| Skill::AbsorptionPlus | Skill::AbsorptionPlusPlus | // Ticks Skill::ElectrocuteTick| Skill::ElectrocuteTickPlus | Skill::ElectrocuteTickPlusPlus | Skill::DecayTick| Skill::DecayTickPlus | Skill::DecayTickPlusPlus | Skill::SiphonTick| Skill::SiphonTickPlus | Skill::SiphonTickPlusPlus | Skill::TriageTick| Skill::TriageTickPlus | Skill::TriageTickPlusPlus => None, } } pub fn ko_castable(&self) -> bool { match self { Skill::ElectrocuteTick | Skill::ElectrocuteTickPlus | Skill::ElectrocuteTickPlusPlus | Skill::DecayTick | Skill::DecayTickPlus | Skill::DecayTickPlusPlus | Skill::SiphonTick | Skill::SiphonTickPlus | Skill::SiphonTickPlusPlus | Skill::TriageTick | Skill::TriageTickPlus | Skill::TriageTickPlusPlus => true, _ => false, } } pub fn is_tick(&self) -> bool { match self { Skill::ElectrocuteTick | Skill::ElectrocuteTickPlus | Skill::ElectrocuteTickPlusPlus | Skill::DecayTick | Skill::DecayTickPlus | Skill::DecayTickPlusPlus | Skill::SiphonTick | Skill::SiphonTickPlus | Skill::SiphonTickPlusPlus | Skill::TriageTick | Skill::TriageTickPlus | Skill::TriageTickPlusPlus => true, _ => false, } } pub fn speed(&self) -> usize { match self { Skill::SiphonTick | Skill::SiphonTickPlus | Skill::SiphonTickPlusPlus => Skill::Siphon.speed(), Skill::DecayTick | Skill::DecayTickPlus | Skill::DecayTickPlusPlus => Skill::Decay.speed(), Skill::TriageTick | Skill::TriageTickPlus | Skill::TriageTickPlusPlus => Skill::Triage.speed(), Skill::ElectrocuteTick | Skill::ElectrocuteTickPlus | Skill::ElectrocuteTickPlusPlus => Skill::Electrify.speed(), _ => Item::from(*self).speed(), } } pub fn aoe(&self) -> bool { match self { Skill::Ruin | Skill::RuinPlus | Skill::RuinPlusPlus => true, _ => false, } } pub fn defensive(&self) -> bool { match self { Skill::Amplify| Skill::AmplifyPlus | Skill::AmplifyPlusPlus | Skill::Block | Skill::Sustain | Skill::SustainPlus | Skill::SustainPlusPlus | Skill::Electrify | Skill::ElectrifyPlus | Skill::ElectrifyPlusPlus | Skill::Haste | Skill::HastePlus | Skill::HastePlusPlus | Skill::Heal | Skill::HealPlus | Skill::HealPlusPlus | Skill::Absorb | Skill::AbsorbPlus | Skill::AbsorbPlusPlus | Skill::Invert | Skill::InvertPlus | Skill::InvertPlusPlus | Skill::Intercept | Skill::InterceptPlus | Skill::InterceptPlusPlus | Skill::Counter | Skill::CounterPlus | Skill::CounterPlusPlus | Skill::Purify | Skill::PurifyPlus | Skill::PurifyPlusPlus | Skill::Recharge | Skill::RechargePlus | Skill::RechargePlusPlus | Skill::Reflect | Skill::ReflectPlus | Skill::ReflectPlusPlus | Skill::Triage | Skill::TriagePlus | Skill::TriagePlusPlus => true, _ => false, } } fn components(&self) -> Vec { let mut components = Item::from(*self).components(); components.sort_unstable(); return components; } pub fn colours(&self) -> Vec { let mut components = self.components(); let colour_items = [Item::Red, Item::Green, Item::Blue]; components.dedup(); return components.iter() .filter(|i| colour_items.contains(i)) .map(|i| i.into_colour()) .collect::>(); } fn base(&self) -> Skill { let bases = [Item::Attack, Item::Stun, Item::Buff, Item::Debuff, Item::Block]; match self.components() .iter() .find(|i| bases.contains(i)) { Some(i) => i.into_skill().unwrap(), None => panic!("{:?} has no base item", self), } } } pub fn cast_actions(cast: Cast) -> Vec { match cast.skill { Skill::Amplify => vec![ Action::Effect { construct: cast.target, skill: cast.skill, effect: ConstructEffect { effect: Effect::Amplify, duration: 2, meta: Some(EffectMeta::Multiplier(150)), tick: None }, }, ], Skill::AmplifyPlus => vec![ Action::Effect { construct: cast.target, skill: cast.skill, effect: ConstructEffect { effect: Effect::Amplify, duration: 3, meta: Some(EffectMeta::Multiplier(175)), tick: None }, }, ], Skill::AmplifyPlusPlus => vec![ Action::Effect { construct: cast.target, skill: cast.skill, effect: ConstructEffect { effect: Effect::Amplify, duration: 4, meta: Some(EffectMeta::Multiplier(200)), tick: None }, }, ], Skill::Attack => vec![ Action::Damage { construct: cast.target, skill: cast.skill, colour: Colour::Red, values: vec![Value::Stat { construct: cast.source, stat: Stat::RedPower, mult: cast.skill.multiplier() }], }, ], Skill::Banish => vec![ Action::Effect { construct: cast.target, skill: cast.skill, effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None } } ], Skill::BanishPlus => vec![ Action::Effect { construct: cast.target, skill: cast.skill, effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None } } ], Skill::BanishPlusPlus => vec![ Action::Effect { construct: cast.target, skill: cast.skill, effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None } } ], Skill::Bash => vec![ Action::Damage { construct: cast.target, skill: cast.skill, colour: Colour::Red, values: vec![ Value::Stat { construct: cast.source, stat: Stat::RedPower, mult: cast.skill.multiplier() }, Value::Cooldowns { construct: cast.source, mult: 45 }, ], }, Action::Effect { construct: cast.target, skill: cast.skill, effect: ConstructEffect {effect: Effect::Stun, duration: 2, meta: Some(EffectMeta::Skill(Skill::Bash)), tick: None } }, Action::IncreaseCooldowns { construct: cast.target, skill: cast.skill, turns: 1, }, ], // fn bash(source: &mut Construct, target: &mut Construct, skill: Skill) { // skill.effect().into_iter() // .for_each(|e| (game.event(Event::new(source, target).event(target.add_effect(skill, e))))); // if resolutions.iter().any(|r| match r.event { // Event::Effect { effect, skill: effect_skill, duration: _, construct_effects: _ } // => effect == Effect::Stun && skill == effect_skill, // _ => false, // }) { // let mut cds = 0; // for cs in target.skills.iter_mut() { // if cs.skill.base_cd().is_some() { // cs.cd = match cs.cd { // None => Some(1), // Some(i) => Some(i + 1), // }; // cds += 1; // } // } // let amount = source.red_power().pct(skill.multiplier().pct(100 + 45usize.saturating_mul(cds))); // target.deal_red_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // } Skill::Strike | Skill::StrikePlus | Skill::StrikePlusPlus => vec![ Action::Damage { construct: cast.target, skill: cast.skill, colour: Colour::Red, values: vec![Value::Stat { construct: cast.source, stat: Stat::RedPower, mult: cast.skill.multiplier() }], }, ], _ => unimplemented!() } } // fn sleep(source: &mut Construct, target: &mut Construct, skill: Skill) { // skill.effect().into_iter() // .for_each(|e| (game.event(Event::new(source, target).event(target.add_effect(skill, e))))); // let amount = source.green_power().pct(skill.multiplier()); // target.deal_green_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // fn sustain(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); // let red_amount = source.red_power().pct(skill.multiplier()); // target.recharge(skill, red_amount, 0) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // fn intercept(source: &mut Construct, target: &mut Construct, skill: Skill) { // let intercept = skill.effect()[0]; // game.event(Event::new(source, target).event(target.add_effect(skill, intercept))); // let red_amount = source.red_power().pct(skill.multiplier()); // target.recharge(skill, red_amount, 0) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // fn break_(source: &mut Construct, target: &mut Construct, skill: Skill) { // let stun = skill.effect()[0]; // game.event(Event::new(source, target).event(target.add_effect(skill, stun))); // let vuln = skill.effect()[1]; // game.event(Event::new(source, target).event(target.add_effect(skill, vuln)).stages(EventStages::PostOnly)); // } // fn block(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); // } // fn buff(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target) // .event(target.add_effect(skill, skill.effect()[0]))); // } // fn counter(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target) // .event(target.add_effect(skill, skill.effect()[0]))); // } // fn counter_attack(source: &mut Construct, target: &mut Construct, skill: Skill) { // let amount = source.red_power().pct(skill.multiplier()); // target.deal_red_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e))); // } // fn restrict(source: &mut Construct, target: &mut Construct, skill: Skill) { // skill.effect().into_iter() // .for_each(|e| (game.event(Event::new(source, target).event(target.add_effect(skill, e))))); // let s_multi = target.skills // .iter() // .fold(100, |acc, cs| match cs.skill.colours().contains(&Colour::Red) { // true => acc + 35, // false => acc, // }); // let amount = source.red_power().pct(skill.multiplier()).pct(s_multi); // target.deal_red_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // fn slay(source: &mut Construct, target: &mut Construct, skill: Skill) { // let amount = source.red_power().pct(skill.multiplier()) + source.green_power().pct(skill.multiplier()); // let slay_events = target.deal_red_damage(skill, amount); // for e in slay_events { // match e { // Event::Damage { amount, mitigation: _, colour: _, skill: _ } => { // game.event(Event::new(source, target).event(e)); // let heal = source.deal_green_damage(skill, amount.pct(50)); // for h in heal { // game.event(Event::new(source, source).event(h).stages(EventStages::PostOnly)); // }; // }, // _ => game.event(Event::new(source, target).event(e)), // } // } // } // fn heal(source: &mut Construct, target: &mut Construct, skill: Skill) { // let amount = source.green_power().pct(skill.multiplier()); // target.deal_green_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e))); // } // fn triage(source: &mut Construct, target: &mut Construct, skill: Skill) { // let skip_tick = target.effects.iter().any(|e| { // match e.effect { // Effect::Triage => source.skill_speed(skill) <= e.tick.unwrap().speed, // _ => false, // } // }); // let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect()[0]; // let tick_skill = match meta { // Some(EffectMeta::Skill(s)) => s, // _ => panic!("no triage tick skill"), // }; // let triage = ConstructEffect::new(effect, duration).set_tick(Cast::new_tick(source, target, tick_skill)); // game.event(Event::new(source, target).event(target.add_effect(skill, triage))); // match skip_tick { // false => return triage_tick(source, target, resolutions, tick_skill) // } // } // fn triage_tick(source: &mut Construct, target: &mut Construct, skill: Skill) { // let amount = source.green_power().pct(skill.multiplier()); // target.deal_green_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::EndPost))); // } // fn chaos(source: &mut Construct, target: &mut Construct, skill: Skill) { // let mut rng = thread_rng(); // let b_rng: usize = rng.gen_range(100, 130); // let amount = source.blue_power().pct(skill.multiplier()).pct(b_rng); // target.deal_blue_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e))); // let r_rng: usize = rng.gen_range(100, 130); // let amount = source.red_power().pct(skill.multiplier()).pct(r_rng); // target.deal_red_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // fn blast(source: &mut Construct, target: &mut Construct, skill: Skill) { // let amount = source.blue_power().pct(skill.multiplier()); // target.deal_blue_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e))); // } // fn amplify(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); // } // fn haste(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); // } // fn debuff(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); // } // fn decay(source: &mut Construct, target: &mut Construct, skill: Skill) { // let wither = skill.effect()[0]; // game.event(Event::new(source, target).event(target.add_effect(skill, wither))); // let skip_tick = target.effects.iter().any(|e| { // match e.effect { // Effect::Decay => source.skill_speed(skill) <= e.tick.unwrap().speed, // _ => false, // } // }); // let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect()[1]; // let tick_skill = match meta { // Some(EffectMeta::Skill(s)) => s, // _ => panic!("no decay tick skill"), // }; // let decay = ConstructEffect::new(effect, duration).set_tick(Cast::new_tick(source, target, tick_skill)); // game.event(Event::new(source, target) // .event(target.add_effect(skill, decay)) // .stages(EventStages::PostOnly)); // match skip_tick { // false => return decay_tick(source, target, resolutions, tick_skill) // } // } // fn decay_tick(source: &mut Construct, target: &mut Construct, skill: Skill) { // let amount = source.blue_power().pct(skill.multiplier()); // target.deal_blue_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::EndPost))); // } // // electrify is the buff effect // // when attacked it runs electrocute and applies a debuff // fn electrify(source: &mut Construct, target: &mut Construct, skill: Skill) { // let electrify = skill.effect()[0]; // game.event(Event::new(source, target).event(target.add_effect(skill, electrify))); // } // fn electrocute(source: &mut Construct, target: &mut Construct, skill: Skill) { // // Remove electric buff, no need to display if construct is dead // if !source.is_ko() { // let electric = source.effects.iter().position(|e| e.effect == Effect::Electric); // match electric { // Some(eff) => { // let ce = source.effects.remove(eff); // game.event(Event::new(source, source) // .event(Event::Removal { skill, effect: Some(ce.effect), construct_effects: source.effects.clone() }) // .stages(EventStages::PostOnly)); // } // None => () // } // } // let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect()[0]; // let tick_skill = match meta { // Some(EffectMeta::Skill(s)) => s, // _ => panic!("no electrocute tick skill"), // }; // let skip_tick = target.effects.iter().any(|e| { // match e.effect { // Effect::Electrocute => source.skill_speed(skill) <= e.tick.unwrap().speed, // _ => false, // } // }); // let electrocute = ConstructEffect::new(effect, duration).set_tick(Cast::new_tick(source, target, tick_skill)); // game.event(Event::new(source, target) // .event(target.add_effect(skill, electrocute)) // .stages(EventStages::PostOnly)); // match skip_tick { // false => return electrocute_tick(source, target, resolutions, tick_skill) // } // } // fn electrocute_tick(source: &mut Construct, target: &mut Construct, skill: Skill) { // let amount = source.blue_power().pct(skill.multiplier()); // target.deal_blue_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::EndPost))); // } // fn ruin(source: &mut Construct, target: &mut Construct, skill: Skill) { // let amount = source.blue_power().pct(skill.multiplier()); // target.deal_blue_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // game.event(Event::new(source, target) // .event(target.add_effect(skill, skill.effect()[0])) // .stages(EventStages::PostOnly)); // } // fn absorb(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); // let blue_amount = source.blue_power().pct(skill.multiplier()); // target.recharge(skill, 0, blue_amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // fn absorption(source: &mut Construct, target: &mut Construct, reflect_skill: Skill, amount: usize, skill: Skill) { // let absorb = skill.effect()[0].set_meta(EffectMeta::AddedDamage(amount)); // game.event(Event::new(source, target) // .event(target.add_effect(reflect_skill, absorb)) // .stages(EventStages::PostOnly)); // let absorb_index = target.effects.iter().position(|e| e.effect == Effect::Absorb).expect("No absorb"); // let ce = target.effects.remove(absorb_index); // game.event(Event::new(source, target) // .event(Event::Removal { skill, effect: Some(ce.effect), construct_effects: target.effects.clone() }) // .stages(EventStages::PostOnly)); // } // fn curse(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); // } // fn hybrid(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); // } // fn invert(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); // } // fn reflect(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); // let blue_amount = source.blue_power().pct(skill.multiplier()); // target.recharge(skill, 0, blue_amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // fn recharge(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(Event::Skill { skill }).stages(EventStages::StartEnd)); // let red_amount = source.red_power().pct(skill.multiplier()); // let blue_amount = source.blue_power().pct(skill.multiplier()); // target.recharge(skill, red_amount, blue_amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // fn siphon(source: &mut Construct, target: &mut Construct, skill: Skill) { // let skip_tick = target.effects.iter().any(|e| { // match e.effect { // Effect::Siphon => source.skill_speed(skill) <= e.tick.unwrap().speed, // _ => false, // } // }); // let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect()[0]; // let tick_skill = match meta { // Some(EffectMeta::Skill(s)) => s, // _ => panic!("no siphon tick skill"), // }; // let siphon = ConstructEffect::new(effect, duration).set_tick(Cast::new_tick(source, target, tick_skill)); // game.event(Event::new(source, target).event(target.add_effect(skill, siphon))); // match skip_tick { // false => return siphon_tick(source, target, resolutions, tick_skill) // } // } // fn siphon_tick(source: &mut Construct, target: &mut Construct, skill: Skill) { // let amount = source.blue_power().pct(skill.multiplier()) + source.green_power().pct(skill.multiplier()); // let siphon_events = target.deal_blue_damage(skill, amount); // for e in siphon_events { // match e { // Event::Damage { amount, mitigation: _, colour: _, skill: _ } => { // game.event(Event::new(source, target).event(e).stages(EventStages::EndPost)); // let heal = source.deal_green_damage(skill, amount); // for h in heal { // game.event(Event::new(source, source).event(h).stages(EventStages::PostOnly)); // }; // }, // _ => game.event(Event::new(source, target).event(e).stages(EventStages::EndPost)), // } // } // } // fn link(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); // let amount = source.blue_power().pct(skill.multiplier().saturating_mul(target.effects.len() as usize)); // target.deal_blue_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // fn silence(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); // let s_multi = target.skills // .iter() // .fold(100, |acc, cs| match cs.skill.colours().contains(&Colour::Blue) { // true => acc + 45, // false => acc, // }); // let amount = source.blue_power().pct(skill.multiplier()).pct(s_multi); // target.deal_blue_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // fn purge(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(Event::Skill { skill }).stages(EventStages::StartEnd)); // if target.effects.len() > 0 { // target.effects.clear(); // game.event(Event::new(source, target) // .event(Event::Removal { skill, effect: None, construct_effects: target.effects.clone() }) // .stages(EventStages::PostOnly)); // } // let effect = skill.effect()[0]; // game.event(Event::new(source, target).event(target.add_effect(skill, effect)).stages(EventStages::PostOnly)); // } // fn purify(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(Event::Skill { skill }).stages(EventStages::StartEnd)); // if target.effects.len() > 0 { // let amount = source.green_power().pct(skill.multiplier().saturating_mul(target.effects.len() as usize)); // target.effects.clear(); // game.event(Event::new(source, target) // .event(Event::Removal { skill, effect: None, construct_effects: target.effects.clone() }) // .stages(EventStages::PostOnly)); // target.deal_green_damage(skill, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // let effect = skill.effect()[0]; // game.event(Event::new(source, target).event(target.add_effect(skill, effect)).stages(EventStages::PostOnly)); // } // fn banish(source: &mut Construct, target: &mut Construct, skill: Skill) { // game.event(Event::new(source, target).event(Event::Skill { skill }).stages(EventStages::StartEnd)); // let red_damage = target.red_life().pct(skill.multiplier()); // let blue_damage = target.blue_life().pct(skill.multiplier()); // if red_damage > 0 { // target.deal_red_damage(skill, red_damage) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // if blue_damage > 0 { // target.deal_blue_damage(skill, blue_damage) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); // } // game.event(Event::new(source, target).event(target.add_effect(skill, skill.effect()[0])).stages(EventStages::PostOnly)); // } // #[cfg(test)] mod tests { use skill::*; #[test] fn attack_actions_test() { let cast = Cast::new(Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4(), Skill::Attack); let mut actions = cast_actions(cast); match actions.remove(0) { Action::Damage { construct: _, skill, values: _, colour } => { assert_eq!(skill, Skill::Attack); assert_eq!(colour, Colour::Red); }, _ => panic!("{:?}", actions), }; } // #[test] // fn heal_test() { // let mut x = Construct::new() // .named(&"muji".to_string()) // .learn(Skill::Heal); // let mut y = Construct::new() // .named(&"camel".to_string()) // .learn(Skill::Heal); // x.deal_red_damage(Skill::Attack, 5); // heal(&mut y, &mut x, vec![], Skill::Heal); // } // #[test] // fn decay_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // let mut y = Construct::new() // .named(&"camel".to_string()); // decay(&mut x, &mut y, vec![], Skill::Decay); // assert!(y.effects.iter().any(|e| e.effect == Effect::Decay)); // y.reduce_effect_durations(); // let _decay = y.effects.iter().find(|e| e.effect == Effect::Decay); // // assert!(y.green_life() == y.green_life().saturating_sub(decay.unwrap().tick.unwrap().amount)); // } // #[test] // fn block_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // let mut y = Construct::new() // .named(&"camel".to_string()); // // ensure it doesn't have 0 pd // x.red_power.force(100); // y.green_life.force(500); // block(&mut y.clone(), &mut y, vec![], Skill::Block); // assert!(y.effects.iter().any(|e| e.effect == Effect::Block)); // attack(&mut x, &mut y, vec![], Skill::Attack); // let Event { source: _, target: _, event, stages: _ } = resolutions.remove(0); // match event { // Event::Damage { amount, mitigation: _, colour: _, skill: _ } => // assert!(amount < x.red_power().pct(Skill::Attack.multiplier())), // _ => panic!("not damage"), // }; // } // #[test] // fn sustain_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // let mut y = Construct::new() // .named(&"camel".to_string()); // x.red_power.force(10000000000000); // multiplication of int max will cause overflow // y.green_life.force(1024); // make tests more flexible if we change stats // sustain(&mut y.clone(), &mut y, vec![], Skill::Sustain); // assert!(y.affected(Effect::Sustain)); // ruin(&mut x, &mut y, vec![], Skill::Ruin); // let Event { source: _, target: _, event, stages: _ } = resolutions.remove(0); // match event { // Event::Immunity { skill: _, immunity } => assert!(immunity.contains(&Effect::Sustain)), // _ => panic!("not immune cluthc"), // }; // attack(&mut x, &mut y, vec![], Skill::Attack); // assert!(y.green_life() == 1); // let Event { source: _, target: _, event, stages: _ } = resolutions.remove(0); // match event { // Event::Damage { amount, mitigation: _, colour: _, skill: _ } => assert_eq!(amount, 1023), // _ => panic!("not damage"), // }; // } // #[test] // fn invert_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // let mut y = Construct::new() // .named(&"camel".to_string()); // // give red shield but reduce to 0 // y.red_life.force(64); // y.red_life.reduce(64); // x.red_power.force(512); // invert(&mut y.clone(), &mut y, vec![], Skill::Invert); // assert!(y.affected(Effect::Invert)); // // heal should deal green damage // heal(&mut x, &mut y, vec![], Skill::Heal); // assert!(y.green_life() < 1024); // // attack should heal and recharge red shield // attack(&mut x, &mut y, vec![], Skill::Attack); // // match resolutions.remove(0).event { // // Event::Inversion { skill } => assert_eq!(skill, Skill::Attack), // // _ => panic!("not inversion"), // //}; // match resolutions.remove(0).event { // Event::Healing { skill: _, overhealing: _, amount } => assert!(amount > 0), // _ => panic!("not healing from inversion"), // }; // match resolutions.remove(0).event { // Event::Recharge { skill: _, red, blue: _ } => assert!(red > 0), // _ => panic!("not recharge from inversion"), // }; // } // #[test] // fn reflect_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // let mut y = Construct::new() // .named(&"camel".to_string()); // reflect(&mut y.clone(), &mut y, vec![], Skill::Reflect); // assert!(y.affected(Effect::Reflect)); // let mut vec![]; // cast_actions(Skill::Blast, &mut x, &mut y, resolutions); // assert!(x.green_life() < 1024); // let Event { source: _, target: _, event, stages: _ } = resolutions.remove(0); // match event { // Event::Reflection { skill } => assert_eq!(skill, Skill::Blast), // _ => panic!("not reflection"), // }; // let Event { source: _, target: _, event, stages: _ } = resolutions.remove(0); // match event { // Event::Damage { amount, mitigation: _, colour: _, skill: _ } => assert!(amount > 0), // _ => panic!("not damage"), // }; // } // #[test] // fn siphon_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // let mut y = Construct::new() // .named(&"camel".to_string()); // x.blue_power.force(256); // x.green_power.force(220); // x.green_life.force(1024); // y.blue_life.force(0); // x.green_life.reduce(512); // cast_actions(Skill::Siphon, &mut x, &mut y, vec![]); // assert!(y.affected(Effect::Siphon)); // assert!(x.green_life() == (512 + 256.pct(Skill::SiphonTick.multiplier()) + 220.pct(Skill::SiphonTick.multiplier()))); // let Event { source: _, target: _, event, stages: _ } = resolutions.remove(0); // match event { // Event::Effect { effect, skill: _, duration: _, construct_effects: _ } => assert_eq!(effect, Effect::Siphon), // _ => panic!("not siphon"), // }; // let Event { source: _, target: _, event, stages: _ } = resolutions.remove(0); // match event { // Event::Damage { amount, skill: _, mitigation: _, colour: _} => assert_eq!(amount, 256.pct(Skill::SiphonTick.multiplier()) // + 220.pct(Skill::SiphonTick.multiplier())), // _ => panic!("not damage siphon"), // }; // let Event { source: _, target, event, stages: _ } = resolutions.remove(0); // match event { // Event::Healing { amount, skill: _, overhealing: _ } => { // assert_eq!(amount, 256.pct(Skill::SiphonTick.multiplier()) + 220.pct(Skill::SiphonTick.multiplier())); // assert_eq!(target.id, x.id); // }, // _ => panic!("not healing"), // }; // } // #[test] // fn triage_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // let mut y = Construct::new() // .named(&"pretaliation".to_string()); // // ensure it doesn't have 0 sd // x.blue_power.force(50); // // remove all mitigation // y.red_life.force(0); // y.blue_life.force(0); // y.deal_red_damage(Skill::Attack, 5); // let prev_hp = y.green_life(); // triage(&mut x, &mut y, vec![], Skill::Triage); // assert!(y.effects.iter().any(|e| e.effect == Effect::Triage)); // assert!(y.green_life() > prev_hp); // } // #[test] // fn recharge_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // let mut y = Construct::new() // .named(&"pretaliation".to_string()); // y.red_life.force(50); // y.blue_life.force(50); // y.deal_red_damage(Skill::Attack, 5); // y.deal_blue_damage(Skill::Blast, 5); // recharge(&mut x, &mut y, vec![], Skill::Recharge); // resolutions.remove(0); // let Event { source: _, target: _, event, stages: _ } = resolutions.remove(0); // match event { // Event::Recharge { red, blue, skill: _ } => { // assert!(red == 5); // assert!(blue == 5); // } // _ => panic!("result was not recharge"), // } // } // #[test] // fn silence_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // silence(&mut x.clone(), &mut x, vec![], Skill::Silence); // assert!(x.effects.iter().any(|e| e.effect == Effect::Silence)); // assert!(x.disabled(Skill::Silence).is_some()); // } // #[test] // fn amplify_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // x.blue_power.force(50); // amplify(&mut x.clone(), &mut x, vec![], Skill::Amplify); // assert!(x.effects.iter().any(|e| e.effect == Effect::Amplify)); // assert_eq!(x.blue_power(), 75); // } // #[test] // fn purify_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // decay(&mut x.clone(), &mut x, vec![], Skill::Decay); // assert!(x.effects.iter().any(|e| e.effect == Effect::Decay)); // purify(&mut x.clone(), &mut x, vec![], Skill::Purify); // assert!(!x.effects.iter().any(|e| e.effect == Effect::Decay)); // } // #[test] // fn bash_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // let mut y = Construct::new() // .named(&"pretaliation".to_string()) // .learn(Skill::Stun); // let stun_cd = y.skills.iter().find(|cs| cs.skill == Skill::Stun).unwrap().cd.unwrap(); // bash(&mut x, &mut y, vec![], Skill::Bash); // assert!(!x.effects.iter().any(|e| e.effect == Effect::Stun)); // assert!(y.skills.iter().any(|cs| cs.skill == Skill::Stun && cs.cd.unwrap() == stun_cd + 1)); // } // #[test] // fn purge_test() { // let mut x = Construct::new() // .named(&"muji".to_string()); // let mut y = Construct::new() // .named(&"pretaliation".to_string()) // .learn(Skill::Heal) // .learn(Skill::HealPlus); // purge(&mut x, &mut y, vec![], Skill::Purge); // // 2 turns at lvl 1 // assert!(y.effects.iter().any(|e| e.effect == Effect::Purge && e.duration == 2)); // assert!(y.disabled(Skill::Heal).is_some()); // } } // Skill::Strike => game.event( // Event::Damage { // colour: Colour::Red, // amount: SkillPower { construct: cast.source, skill: Skill::Strike } // }, // Event::LifeSteal { // amount: CastDamage { id: cast.id, colour: Colour::Red, target: cast.source }, // }, // ), // Skill::Attack => game.event(Event::Damage { // colour: Colour::Red, // amount: Stat { construct: cast.source, stat: Stat::RedPower, pct: ATTACK_RED_POWER_PCT } // }), // Skill::Bash => game.event(Event::Damage { // colour: Colour::Red, // amounts: vec![ // Stat { construct: cast.source, stat: Stat::RedPower, pct: ATTACK_RED_POWER_PCT }, // Cooldowns { construct: cast.source }, // ], // }) // // we clone the current state of the target and source // // so we can modify them during the resolution // // no more than 1 mutable ref allowed on game // let mut source = game.construct_by_id(cast.source).unwrap().clone(); // let mut target = game.construct_by_id(target_id).unwrap().clone(); // // bail out on ticks that have been removed // if skill.is_tick() && target.effects.iter().find(|ce| match ce.tick { // Some(t) => t.id == cast.id, // None => false, // }).is_none() { // return; // } // if let Some(_disable) = source.disabled(skill) { // game.event(Event::new(source, target).event(Event::Disable { disable, skill }).stages(EventStages::PostOnly)); // return; // } // if target.is_ko() { // game.event(Event::new(source, target).event(Event::TargetKo { skill }).stages(EventStages::PostOnly)); // return; // } // if target.affected(Effect::Reflect) && skill.colours().contains(&Colour::Blue) && !skill.is_tick() { // // guard against overflow // if source.affected(Effect::Reflect) { // } // game.event(Event::new(source, target).event(Event::Reflection { skill })); // return cast_actions(skill, &mut source.clone(), source, resolutions); // } // // haste_strike_check(game) // if source.affected(Effect::Haste) { // match skill { // Skill::Slay | // Skill::SlayPlus | // Skill::SlayPlusPlus | // Skill::Chaos | // Skill::ChaosPlus | // Skill::ChaosPlusPlus | // Skill::Strike | // Skill::StrikePlus | // Skill::StrikePlusPlus => { // let amount = source.speed().pct(Skill::HasteStrike.multiplier()); // target.deal_red_damage(Skill::HasteStrike, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e))); // }, // _ => (), // } // } // if source.affected(Effect::Hybrid) { // match skill { // Skill::Blast| // Skill::BlastPlus | // Skill::BlastPlusPlus | // Skill::Chaos | // Skill::ChaosPlus | // Skill::ChaosPlusPlus | // Skill::Siphon | // Skill::SiphonPlus | // Skill::SiphonPlusPlus => { // let amount = source.green_power().pct(Skill::HybridBlast.multiplier()); // target.deal_blue_damage(Skill::HybridBlast, amount) // .into_iter() // .for_each(|e| game.event(Event::new(source, target).event(e))); // }, // _ => (), // } // } // match self.category() == EffectCategory::Red { // true => { // if let Some(evasion) = target.evade(*self) { // game.event(evasion); // return Event; // } // }, // false => (), // } // match skill { // Skill::Amplify| // Skill::AmplifyPlus | // Skill::AmplifyPlusPlus => amplify(game, skill), // Skill::Banish| // Skill::BanishPlus | // Skill::BanishPlusPlus => banish(game, skill), // Skill::Bash| // Skill::BashPlus | // Skill::BashPlusPlus => bash(game, skill), // Skill::Blast| // Skill::BlastPlus | // Skill::BlastPlusPlus => blast(game, skill), // Skill::Chaos| // Skill::ChaosPlus | // Skill::ChaosPlusPlus => chaos(game, skill), // Skill::Sustain| // Skill::SustainPlus | // Skill::SustainPlusPlus => sustain(game, skill), // Skill::Electrify| // Skill::ElectrifyPlus | // Skill::ElectrifyPlusPlus => electrify(game, skill), // Skill::ElectrocuteTick| // Skill::ElectrocuteTickPlus | // Skill::ElectrocuteTickPlusPlus => electrocute_tick(game, skill), // Skill::Curse| // Skill::CursePlus | // Skill::CursePlusPlus => curse(game, skill), // Skill::Decay| // Skill::DecayPlus | // Skill::DecayPlusPlus => decay(game, skill), // Skill::DecayTick| // Skill::DecayTickPlus | // Skill::DecayTickPlusPlus => decay_tick(game, skill), // Skill::Haste| // Skill::HastePlus | // Skill::HastePlusPlus => haste(game, skill), // Skill::Heal| // Skill::HealPlus | // Skill::HealPlusPlus => heal(game, skill), // Skill::Absorb| // Skill::AbsorbPlus | // Skill::AbsorbPlusPlus => absorb(game, skill), // Skill::Hybrid| // Skill::HybridPlus | // Skill::HybridPlusPlus => hybrid(game, skill), // Skill::Invert| // Skill::InvertPlus | // Skill::InvertPlusPlus => invert(game, skill), // Skill::Counter| // Skill::CounterPlus | // Skill::CounterPlusPlus => counter(game, skill), // Skill::Purge| // Skill::PurgePlus | // Skill::PurgePlusPlus => purge(game, skill), // Skill::Purify| // Skill::PurifyPlus | // Skill::PurifyPlusPlus => purify(game, skill), // Skill::Recharge| // Skill::RechargePlus | // Skill::RechargePlusPlus => recharge(game, skill), // Skill::Reflect| // Skill::ReflectPlus | // Skill::ReflectPlusPlus => reflect(game, skill), // Skill::Ruin| // Skill::RuinPlus | // Skill::RuinPlusPlus => ruin(game, skill), // Skill::Link| // Skill::LinkPlus | // Skill::LinkPlusPlus => link(game, skill), // Skill::Silence| // Skill::SilencePlus | // Skill::SilencePlusPlus => silence(game, skill), // Skill::Siphon| // Skill::SiphonPlus | // Skill::SiphonPlusPlus => siphon(game, skill), // Skill::SiphonTick| // Skill::SiphonTickPlus | // Skill::SiphonTickPlusPlus => siphon_tick(game, skill), // Skill::Slay| // Skill::SlayPlus | // Skill::SlayPlusPlus => slay(game, skill), // Skill::Sleep| // Skill::SleepPlus | // Skill::SleepPlusPlus => sleep(game, skill), // Skill::Restrict| // Skill::RestrictPlus | // Skill::RestrictPlusPlus => restrict(game, skill), // Skill::Strike| // Skill::StrikePlus | // Skill::StrikePlusPlus => strike(game, skill), // Skill::Intercept| // Skill::InterceptPlus | // Skill::InterceptPlusPlus => intercept(game, skill), // Skill::Break| // Skill::BreakPlus | // Skill::BreakPlusPlus => break_(game, skill), // Skill::Triage| // Skill::TriagePlus | // Skill::TriagePlusPlus => triage(game, skill), // Skill::TriageTick| // Skill::TriageTickPlus | // Skill::TriageTickPlusPlus => triage_tick(game, skill), // // Base Skills // Skill::Attack => attack(game, skill), // Skill::Block => block(game, skill), // Skill::Buff => buff(game, skill), // Skill::Debuff => debuff(game, skill), // Skill::Stun => stun(game, skill), // // Triggered // Skill::Electrocute | // Skill::ElectrocutePlus | // Skill::ElectrocutePlusPlus => panic!("should only trigger from electrify hit"), // Skill::HasteStrike => panic!("should only trigger from haste"), // Skill::Absorption| // Skill::AbsorptionPlus | // Skill::AbsorptionPlusPlus => panic!("should only trigger from absorb"), // Skill::HybridBlast => panic!("should only trigger from hybrid"), // Skill::CounterAttack| // Skill::CounterAttackPlus | // Skill::CounterAttackPlusPlus => panic!("should only trigger from counter"), // // Not used // }; // for Event { source: event_source, target: event_target, event, stages: _ } in resolutions.clone() { // let mut source = game.construct_by_id(event_source.id).unwrap().clone(); // let mut target = game.construct_by_id(event_target.id).unwrap().clone(); // match event { // Event::Damage { amount, skill, mitigation, colour: c } => { // if target.affected(Effect::Electric) && !skill.is_tick() { // let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter() // .find(|e| e.effect == Effect::Electric).unwrap().clone(); // match meta { // Some(EffectMeta::Skill(s)) => { // // Gurad against reflect overflow // if !(source.affected(Effect::Reflect) && target.affected(Effect::Reflect)) { // // Check reflect don't bother if electrocute is procing on death // if source.affected(Effect::Reflect) && !target.is_ko() { // game.event(Event::new(&target, &source) // .event(Event::Reflection { skill: s }).stages(EventStages::EndPost)); // electrocute(&mut source, &mut target, resolutions, s); // } else { // electrocute(&mut target, &mut source, resolutions, s); // } // } // }, // _ => panic!("no electrify skill"), // }; // } // if target.affected(Effect::Absorb) && !target.is_ko() { // let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter() // .find(|e| e.effect == Effect::Absorb).unwrap().clone(); // match meta { // Some(EffectMeta::Skill(s)) => { // absorption(&mut source, &mut target, resolutions, skill, amount + mitigation, s); // }, // _ => panic!("no absorb skill"), // }; // } // if c == Colour::Red { // if target.affected(Effect::Counter) && !target.is_ko() { // let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter() // .find(|e| e.effect == Effect::Counter).unwrap().clone(); // match meta { // Some(EffectMeta::Skill(s)) => { // counter_attack(&mut target, &mut source, resolutions, s); // }, // _ => panic!("no counter skill"), // }; // } // } // if target.is_ko() && event_target.green == 0 { // // Make sure target ko is from this event // target.effects.clear(); // game.event(Event::new(&source, &target).event(Event::Ko()).stages(EventStages::PostOnly)); // } // }, // _ => (), // }; // game.update_construct(&mut source); // game.update_construct(&mut target); // };