mnml/core/src/skill.rs
2019-12-04 17:26:05 +10:00

2296 lines
85 KiB
Rust

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<Cast> {
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<Effect>;
pub type Immunity = Vec<Effect>;
#[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<ConstructEffect> },
Removal { skill: Skill, effect: Option<Effect>, construct_effects: Vec<ConstructEffect> },
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<ConstructEffect> {
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<Item> {
let mut components = Item::from(*self).components();
components.sort_unstable();
return components;
}
pub fn colours(&self) -> Vec<Colour> {
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::<Vec<Colour>>();
}
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<Action> {
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);
// };