mnml/server/src/skill.rs
ntr ce703ff473 Revert "merge stash"
This reverts commit 6057d267744a5e9322b93155918293f04db4eecb.
2019-05-28 14:21:45 +10:00

2248 lines
80 KiB
Rust

use rand::{thread_rng, Rng};
use uuid::Uuid;
use util::{IntPct};
use construct::{Construct, ConstructEffect, EffectMeta, Stat};
use item::{Item};
use game::{Game};
pub fn resolution_steps(cast: &Cast, game: &mut Game) -> Resolutions {
let mut resolutions = vec![];
resolutions = pre_resolve(cast, game, resolutions);
return resolutions;
}
pub fn pre_resolve(cast: &Cast, game: &mut Game, mut resolutions: Resolutions) -> Resolutions {
let skill = cast.skill;
let source = game.construct_by_id(cast.source_construct_id).unwrap().clone();
let targets = game.get_targets(cast.skill, &source, cast.target_construct_id);
if skill.aoe() { // Send an aoe skill event for anims
resolutions.push(Resolution::new(&source,
&game.construct_by_id(cast.target_construct_id).unwrap().clone()).event(Event::AoeSkill { skill }));
}
for target_id in targets {
// 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_construct_id).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() {
continue;
}
resolutions = resolve(cast.skill, &mut source, &mut target, resolutions);
// save the changes to the game
game.update_construct(&mut source);
game.update_construct(&mut target);
// do additional steps
resolutions = post_resolve(cast.skill, game, resolutions);
}
return resolutions;
}
pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut resolutions: Vec<Resolution>) -> Resolutions {
if let Some(disable) = source.disabled(skill) {
resolutions.push(Resolution::new(source, target).event(Event::Disable { disable, skill }));
return resolutions;
}
if target.is_ko() {
resolutions.push(Resolution::new(source, target).event(Event::TargetKo { skill }));
return resolutions;
}
if target.affected(Effect::Reflect) {
// guard against overflow
if source.affected(Effect::Reflect) {
return resolutions;
}
resolutions.push(Resolution::new(source, target).event(Event::Reflection { skill }));
return resolve(skill, target, source, resolutions);
}
if source.affected(Effect::Haste) {
match skill {
Skill::Attack |
Skill::SlayI |
Skill::ChaosI |
Skill::StrikeI => {
let amount = source.speed().pct(Skill::HasteStrike.multiplier());
target.deal_red_damage(Skill::HasteStrike, amount)
.into_iter()
.for_each(|e| resolutions.push(Resolution::new(source, target).event(e)));
},
_ => (),
}
}
if source.affected(Effect::Impurity) {
match skill {
Skill::BlastI |
Skill::ChaosI |
Skill::SiphonI => {
let amount = source.green_power().pct(Skill::ImpureBlast.multiplier());
target.deal_blue_damage(Skill::ImpureBlast, amount)
.into_iter()
.for_each(|e| resolutions.push(Resolution::new(source, target).event(e)));
},
_ => (),
}
}
// match self.category() == EffectCategory::Red {
// true => {
// if let Some(evasion) = target.evade(*self) {
// resolutions.push(evasion);
// return Event;
// }
// },
// false => (),
// }
resolutions = match skill {
Skill::AmplifyI |
Skill::AmplifyII |
Skill::AmplifyIII => amplify(source, target, resolutions, skill),
Skill::BanishI |
Skill::BanishII |
Skill::BanishIII => banish(source, target, resolutions, skill), // TODO prevent all actions
Skill::BlastI |
Skill::BlastII |
Skill::BlastIII => blast(source, target, resolutions, skill),
Skill::ChaosI |
Skill::ChaosII |
Skill::ChaosIII => chaos(source, target, resolutions, skill),
Skill::ClutchI |
Skill::ClutchII |
Skill::ClutchIII => clutch(source, target, resolutions, skill),
Skill::CorruptI |
Skill::CorruptII |
Skill::CorruptIII => corrupt(source, target, resolutions, skill),
Skill::CorruptionTickI |
Skill::CorruptionTickII |
Skill::CorruptionTickIII => corruption_tick(source, target, resolutions, skill),
Skill::CurseI |
Skill::CurseII |
Skill::CurseIII => curse(source, target, resolutions, skill),
Skill::DecayI |
Skill::DecayII |
Skill::DecayIII => decay(source, target, resolutions, skill), // dot
Skill::DecayTickI |
Skill::DecayTickII |
Skill::DecayTickIII => decay_tick(source, target, resolutions, skill), // dot
Skill::HasteI |
Skill::HasteII |
Skill::HasteIII => haste(source, target, resolutions, skill), // speed slow
Skill::HealI |
Skill::HealII |
Skill::HealIII => heal(source, target, resolutions, skill),
Skill::HexI |
Skill::HexII |
Skill::HexIII => hex(source, target, resolutions, skill),
Skill::HostilityI |
Skill::HostilityII |
Skill::HostilityIII => hostility(source, target, resolutions, skill),
Skill::ImpurityI |
Skill::ImpurityII |
Skill::ImpurityIII => impurity(source, target, resolutions, skill),
Skill::InvertI |
Skill::InvertII |
Skill::InvertIII => invert(source, target, resolutions, skill),
Skill::ParryI |
Skill::ParryII |
Skill::ParryIII => parry(source, target, resolutions, skill),
Skill::PurgeI |
Skill::PurgeII |
Skill::PurgeIII => purge(source, target, resolutions, skill), // dispel all buffs
Skill::PurifyI |
Skill::PurifyII |
Skill::PurifyIII => purify(source, target, resolutions, skill),
Skill::RechargeI |
Skill::RechargeII |
Skill::RechargeIII => recharge(source, target, resolutions, skill),
Skill::ReflectI |
Skill::ReflectII |
Skill::ReflectIII => reflect(source, target, resolutions, skill),
Skill::RuinI |
Skill::RuinII |
Skill::RuinIII => ruin(source, target, resolutions, skill),
Skill::ScatterI |
Skill::ScatterII |
Skill::ScatterIII => scatter(source, target, resolutions, skill), // target is immune to magic damage and fx
Skill::SilenceI |
Skill::SilenceII |
Skill::SilenceIII => silence(source, target, resolutions, skill), // target cannot cast spells
Skill::SiphonI |
Skill::SiphonII |
Skill::SiphonIII => siphon(source, target, resolutions, skill), // dot
Skill::SiphonTickI |
Skill::SiphonTickII |
Skill::SiphonTickIII => siphon_tick(source, target, resolutions, skill), // dot
Skill::SlayI |
Skill::SlayII |
Skill::SlayIII => slay(source, target, resolutions, skill), // hybrid dmg self heal
Skill::SleepI |
Skill::SleepII |
Skill::SleepIII => sleep(source, target, resolutions, skill), // heal stun
Skill::SnareI |
Skill::SnareII |
Skill::SnareIII => snare(source, target, resolutions, skill),
Skill::StrangleI |
Skill::StrangleII |
Skill::StrangleIII => strangle(source, target, resolutions, skill),
Skill::StrangleTickI |
Skill::StrangleTickII |
Skill::StrangleTickIII => strangle_tick(source, target, resolutions, skill),
Skill::StrikeI |
Skill::StrikeII |
Skill::StrikeIII => strike(source, target, resolutions, skill),
Skill::TauntI |
Skill::TauntII |
Skill::TauntIII => taunt(source, target, resolutions, skill),
Skill::ThrowI |
Skill::ThrowII |
Skill::ThrowIII => throw(source, target, resolutions, skill), // no damage stun, adds vulnerable
Skill::TriageI |
Skill::TriageII |
Skill::TriageIII => triage(source, target, resolutions, skill), // hot
Skill::TriageTickI |
Skill::TriageTickII |
Skill::TriageTickIII => triage_tick(source, target, resolutions, skill), // hot
// Base Skills
Skill::Attack => attack(source, target, resolutions, skill),
Skill::Block => block(source, target, resolutions, skill),
Skill::Buff => buff(source, target, resolutions, skill),
Skill::Debuff => debuff(source, target, resolutions, skill), // speed slow
Skill::Stun => stun(source, target, resolutions, skill),
//Triggered
Skill::CorruptionI |
Skill::CorruptionII |
Skill::CorruptionIII => panic!("should only trigger from corrupt hit"),
Skill::HasteStrike => panic!("should only trigger from haste"),
Skill::HatredI |
Skill::HatredII |
Skill::HatredIII => panic!("should only trigger from hatred"),
Skill::ImpureBlast => panic!("should only trigger from impurity"),
Skill::RiposteI |
Skill::RiposteII |
Skill::RiposteIII => panic!("should only trigger from parry"),
// Not used
Skill::Injure => injure(source, target, resolutions, skill),
// -----------------
// Test
// -----------------
Skill::TestAttack => attack(source, target, resolutions, skill),
Skill::TestHeal => heal(source, target, resolutions, skill),
Skill::TestTouch => touch(source, target, resolutions, skill),
Skill::TestStun => stun(source, target, resolutions, Skill::Stun),
Skill::TestBlock => block(source, target, resolutions, Skill::Block),
Skill::TestParry => parry(source, target, resolutions, Skill::ParryI),
Skill::TestSiphon => siphon(source, target, resolutions, Skill::SiphonI),
};
return resolutions;
}
fn post_resolve(_skill: Skill, game: &mut Game, mut resolutions: Resolutions) -> Resolutions {
for Resolution { source, target, event } in resolutions.clone() {
let mut source = game.construct_by_id(source.id).unwrap().clone();
let mut target = game.construct_by_id(target.id).unwrap().clone();
match event {
Event::Damage { amount, skill, mitigation: _, colour: _ } => {
if target.affected(Effect::Corrupt) {
let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter()
.find(|e| e.effect == Effect::Corrupt).unwrap().clone();
match meta {
Some(EffectMeta::Skill(s)) => {
resolutions = corruption(&mut target, &mut source, resolutions, s);
},
_ => panic!("no corrupt skill"),
};
}
if target.affected(Effect::Hostility) {
let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter()
.find(|e| e.effect == Effect::Hostility).unwrap().clone();
match meta {
Some(EffectMeta::Skill(s)) => {
resolutions = hatred(&mut target, &mut source, resolutions, skill, amount, s);
},
_ => panic!("no hatred skill"),
};
}
// beware that scatter doesn't cause any damage
// because then applying it will proc this
if target.affected(Effect::Scatter) {
resolutions = scatter_hit(&source, &target, resolutions, game, event)
}
},
Event::Immunity { skill: _, immunity } => match immunity.contains(&Effect::Parry) {
true => {
let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter()
.find(|e| e.effect == Effect::Parry).unwrap().clone();
match meta {
Some(EffectMeta::Skill(s)) => {
resolutions = riposte(&mut target, &mut source, resolutions, s);
},
_ => panic!("no parry skill"),
};
},
false => (),
},
_ => (),
};
game.update_construct(&mut source);
game.update_construct(&mut target);
};
return resolutions;
}
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub struct Cast {
pub id: Uuid,
pub source_player_id: Uuid,
pub source_construct_id: Uuid,
pub target_construct_id: Uuid,
pub skill: Skill,
pub speed: u64,
}
impl Cast {
pub fn new(source_construct_id: Uuid, source_player_id: Uuid, target_construct_id: Uuid, skill: Skill) -> Cast {
return Cast {
id: Uuid::new_v4(),
source_construct_id,
source_player_id,
target_construct_id,
skill,
speed: 0,
};
}
pub fn new_tick(source: &mut Construct, target: &mut Construct, skill: Skill) -> Cast {
Cast {
id: Uuid::new_v4(),
source_construct_id: source.id,
source_player_id: source.account,
target_construct_id: target.id,
skill,
speed: 0,
}
}
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 LogConstruct {
pub id: Uuid,
pub name: String,
}
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub struct Resolution {
pub source: LogConstruct,
pub target: LogConstruct,
pub event: Event,
}
impl Resolution {
fn new(source: &Construct, target: &Construct) -> Resolution {
Resolution {
source: LogConstruct { id: source.id, name: source.name.clone() },
target: LogConstruct { id: target.id, name: target.name.clone() },
event: Event::Incomplete,
}
}
fn event(mut self, e: Event) -> Resolution {
self.event = e;
self
}
}
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub enum Event {
Disable { skill: Skill, disable: Disable },
Immunity { skill: Skill, immunity: Immunity },
Damage { skill: Skill, amount: u64, mitigation: u64, colour: Colour },
Healing { skill: Skill, amount: u64, overhealing: u64 },
Recharge { skill: Skill, red: u64, blue: u64 },
Inversion { skill: Skill },
Reflection { skill: Skill },
Effect { skill: Skill, effect: Effect, duration: u8 },
AoeSkill { skill: Skill },
Skill { skill: Skill },
Removal { effect: Effect },
TargetKo { skill: Skill },
// skill not necessary but makes it neater as all events are arrays in js
Ko { skill: Skill },
Incomplete,
// not used
Evasion { skill: Skill, evasion_rating: u64 },
}
type Resolutions = Vec<Resolution>;
pub type Cooldown = Option<u8>;
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
enum EffectCategory {
Buff,
Debuff,
Ko,
}
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum Effect {
Amplify,
Banish,
Block,
Buff,
Clutch,
Curse,
Haste,
Hex,
Impurity,
Invert,
Parry,
Reflect,
Slow,
Snare,
Strangle,
Strangling,
Stun,
Taunt,
Vulnerable,
Silence,
Wither, // Reduce green dmg (healing) taken
// corrupt is the buff that applies
// corruption the dmg debuff
Corrupt,
Corruption,
// hostility is the buff
// hatred is the increased damage
Hostility,
Hatred,
// magic immunity
Scatter,
// effects over time
Triage,
Decay,
Regen,
Siphon,
Injured,
// Airborne,
// Boost
// Bleed,
// Blind,
// Deadly,
// Enslave,
// Fury,
// Injured,
// Leech,
// Mesmerise,
// Untouchable,
// SpeedSiphon,
// SpeedIncrease,
Ko,
}
impl Effect {
pub fn immune(&self, skill: Skill) -> bool {
match self {
Effect::Parry => match skill {
Skill::Attack => true,
Skill::Stun => true,
_ => skill.colours().contains(&Colour::Red)
},
Effect::Banish => true,
Effect::Clutch => [
Skill::Stun,
Skill::HexI,
Skill::HexII,
Skill::HexIII,
Skill::SilenceI,
Skill::SilenceII,
Skill::SilenceIII,
Skill::RuinI,
Skill::RuinII,
Skill::RuinIII,
Skill::StrangleI,
Skill::StrangleII,
Skill::StrangleIII,
Skill::SnareI,
Skill::SnareII,
Skill::SnareIII
].contains(&skill),
Effect::Injured => skill.colours().contains(&Colour::Green),
_ => false,
}
}
pub fn disables_skill(&self, skill: Skill) -> bool {
if skill.is_tick() {
return false;
}
match self {
Effect::Stun => true,
Effect::Hex => true,
Effect::Banish => true,
Effect::Strangle => true,
Effect::Strangling => match skill {
Skill::StrangleTickI |
Skill::StrangleTickII |
Skill::StrangleTickIII => false,
_ => true,
},
Effect::Silence => skill.colours().contains(&Colour::Blue),
Effect::Snare => skill.colours().contains(&Colour::Red),
Effect::Ko => skill.ko_castable(),
_ => false,
}
}
pub fn modifications(&self) -> Vec<Stat> {
match self {
Effect::Vulnerable => vec![Stat::RedDamageTaken],
Effect::Block => vec![Stat::RedDamageTaken],
Effect::Buff => vec![Stat::RedPower, Stat::Speed],
Effect::Hatred => vec![Stat::RedPower, Stat::BluePower],
Effect::Amplify => vec![Stat::RedPower, Stat::BluePower],
Effect::Curse => vec![Stat::BlueDamageTaken],
Effect::Impurity => vec![Stat::GreenPower],
Effect::Wither => vec![Stat::GreenDamageTaken],
Effect::Haste => vec![Stat::Speed],
Effect::Slow => vec![Stat::Speed],
Effect::Scatter => vec![Stat::BlueDamageTaken, Stat::GreenDamageTaken, Stat::RedDamageTaken],
_ => vec![],
}
}
pub fn apply(&self, value: u64, meta: Option<EffectMeta>) -> u64 {
match self {
Effect::Amplify |
Effect::Vulnerable |
Effect::Block |
Effect::Buff |
Effect::Curse |
Effect::Haste |
Effect::Slow |
Effect::Impurity |
Effect::Wither => value.pct(match meta {
Some(EffectMeta::Multiplier(d)) => d,
_ => 100,
}),
Effect::Scatter => value >> 1,
Effect::Hatred => value + match meta {
Some(EffectMeta::AddedDamage(d)) => d,
_ => panic!("hatred meta not damage"),
},
_ => {
info!("{:?} does not have a mod effect", self);
return value;
},
}
}
fn category(&self) -> EffectCategory {
match self {
// physical
Effect::Stun => EffectCategory::Debuff,
Effect::Block => EffectCategory::Buff,
Effect::Buff => EffectCategory::Buff,
Effect::Parry => EffectCategory::Buff,
Effect::Vulnerable => EffectCategory::Debuff,
Effect::Snare => EffectCategory::Debuff,
Effect::Clutch => EffectCategory::Buff,
Effect::Taunt => EffectCategory::Buff,
Effect::Strangle => EffectCategory::Debuff,
Effect::Strangling => EffectCategory::Buff,
// magic
Effect::Hex => EffectCategory::Debuff,
Effect::Curse => EffectCategory::Debuff,
Effect::Banish => EffectCategory::Debuff, // todo randomise
// Effect::Banish => rng.gen_bool(0.5),
Effect::Slow => EffectCategory::Debuff,
Effect::Haste => EffectCategory::Buff,
Effect::Hatred => EffectCategory::Buff,
Effect::Reflect => EffectCategory::Buff,
Effect::Amplify => EffectCategory::Buff,
Effect::Silence => EffectCategory::Debuff,
Effect::Wither => EffectCategory::Debuff,
Effect::Corrupt => EffectCategory::Buff,
Effect::Corruption => EffectCategory::Debuff,
Effect::Hostility => EffectCategory::Buff,
// magic
Effect::Impurity => EffectCategory::Buff,
Effect::Scatter => EffectCategory::Buff,
Effect::Invert => EffectCategory::Buff,
// effects over time
Effect::Triage => EffectCategory::Buff,
Effect::Decay => EffectCategory::Debuff,
Effect::Regen => EffectCategory::Buff,
Effect::Siphon => EffectCategory::Debuff,
// not in game
Effect::Injured => EffectCategory::Debuff,
Effect::Ko => EffectCategory::Ko,
}
}
}
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum Colour {
Red,
Blue,
Green,
}
#[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,
AmplifyI,
AmplifyII,
AmplifyIII,
BanishI,
BanishII,
BanishIII,
BlastI,
BlastII,
BlastIII,
ChaosI,
ChaosII,
ChaosIII,
ClutchI,
ClutchII,
ClutchIII,
CorruptI,
CorruptII,
CorruptIII,
CorruptionI,
CorruptionII,
CorruptionIII,
CorruptionTickI,
CorruptionTickII,
CorruptionTickIII,
CurseI,
CurseII,
CurseIII,
DecayI, // dot
DecayII,
DecayIII,
DecayTickI, // dot
DecayTickII,
DecayTickIII,
HasteI,
HasteII,
HasteIII,
HasteStrike,
HealI,
HealII,
HealIII,
HexI,
HexII,
HexIII,
HatredI,
HatredII,
HatredIII,
HostilityI,
HostilityII,
HostilityIII,
ImpureBlast,
ImpurityI,
ImpurityII,
ImpurityIII,
Injure,
InvertI,
InvertII,
InvertIII,
ParryI, // avoid all damage
ParryII,
ParryIII,
PurgeI,
PurgeII,
PurgeIII,
PurifyI,
PurifyII,
PurifyIII,
RechargeI,
RechargeII,
RechargeIII,
ReflectI,
ReflectII,
ReflectIII,
RiposteI,
RiposteII,
RiposteIII,
RuinI,
RuinII,
RuinIII,
ScatterI,
ScatterII,
ScatterIII,
SilenceI,
SilenceII,
SilenceIII,
SiphonI,
SiphonII,
SiphonIII,
SiphonTickI,
SiphonTickII,
SiphonTickIII,
SlayI,
SlayII,
SlayIII,
SleepI,
SleepII,
SleepIII,
SnareI,
SnareII,
SnareIII,
StrangleI,
StrangleII,
StrangleIII,
StrangleTickI,
StrangleTickII,
StrangleTickIII,
StrikeI,
StrikeII,
StrikeIII,
TauntI,
TauntII,
TauntIII,
ThrowI, // no damage stun, adds vulnerable
ThrowII,
ThrowIII,
TriageI, // hot
TriageII,
TriageIII,
TriageTickI,
TriageTickII,
TriageTickIII,
// used by tests, no cd, 100% multiplier
TestAttack,
TestHeal,
TestTouch, // No damage
TestStun,
TestBlock,
TestParry,
TestSiphon,
}
impl Skill {
pub fn multiplier(&self) -> u64 {
match self {
// Attack Base
Skill::Attack => 80, // Base
Skill::BlastI => 110, // BB
Skill::BlastII => 130, // BB
Skill::BlastIII => 150, // BB
Skill::ChaosI => 40, // BR
Skill::ChaosII => 50, // BR
Skill::ChaosIII => 60, // BR
Skill::HealI => 130, //GG
Skill::HealII => 160, //GG
Skill::HealIII => 200, //GG
Skill::SiphonTickI => 40, // GB
Skill::SiphonTickII => 70,
Skill::SiphonTickIII => 110,
Skill::SlayI => 70, // RG
Skill::SlayII => 90,
Skill::SlayIII => 120,
Skill::StrikeI => 90, //RR
Skill::StrikeII => 110,
Skill::StrikeIII => 140,
// Block Base
Skill::CorruptionTickI => 80,
Skill::CorruptionTickII => 100,
Skill::CorruptionTickIII => 130,
Skill::ParryI => 110,
Skill::ParryII => 145,
Skill::ParryIII => 200,
Skill::RiposteI => 70,
Skill::RiposteII => 95,
Skill::RiposteIII => 120,
Skill::PurifyI => 45, //Green dmg (heal)
Skill::PurifyII => 70,
Skill::PurifyIII => 105,
Skill::ReflectI => 45, //restore blue life (heal)
Skill::ReflectII => 70,
Skill::ReflectIII => 100,
Skill::RechargeI => 85, //restore red and blue life (heal)
Skill::RechargeII => 130,
Skill::RechargeIII => 200,
// Stun Base
Skill::SleepI => 240, //Green dmg (heal)
Skill::SleepII => 300,
Skill::SleepIII => 400,
Skill::StrangleTickI => 65,
Skill::StrangleTickII => 95,
Skill::StrangleTickIII => 140,
// Debuff Base
Skill::DecayTickI => 25,
Skill::DecayTickII => 45,
Skill::DecayTickIII => 70,
Skill::SilenceI => 55, // Deals more per blue skill on target
Skill::SilenceII => 80,
Skill::SilenceIII => 110,
Skill::SnareI => 40, // Deals more per red skill on target
Skill::SnareII => 65,
Skill::SnareIII => 100,
// Buff base
Skill::ImpureBlast => 25,
Skill::HasteStrike => 30,
Skill::ScatterI => 140,
Skill::ScatterII => 200,
Skill::ScatterIII => 300,
Skill::TauntI => 80,
Skill::TauntII => 110,
Skill::TauntIII => 150,
Skill::TriageTickI => 75,
Skill::TriageTickII => 110,
Skill::TriageTickIII => 140,
_ => 100,
}
}
pub fn effect(&self) -> Vec<ConstructEffect> {
match self {
// Modifiers
Skill::AmplifyI => vec![ConstructEffect {effect: Effect::Amplify, duration: 2,
meta: Some(EffectMeta::Multiplier(150)), tick: None}],
Skill::AmplifyII => vec![ConstructEffect {effect: Effect::Amplify, duration: 3,
meta: Some(EffectMeta::Multiplier(175)), tick: None}],
Skill::AmplifyIII => vec![ConstructEffect {effect: Effect::Amplify, duration: 4,
meta: Some(EffectMeta::Multiplier(200)), tick: None}],
Skill::BanishI => vec![ConstructEffect {effect: Effect::Banish, duration: 1,meta: None, tick: None}],
Skill::BanishII => vec![ConstructEffect {effect: Effect::Banish, duration: 2,meta: None, tick: None}],
Skill::BanishIII => vec![ConstructEffect {effect: Effect::Banish, duration: 3,meta: None, tick: None}],
Skill::Block => vec![ConstructEffect {effect: Effect::Block, duration: 1,
meta: Some(EffectMeta::Multiplier(50)), tick: None}],
Skill::Buff => vec![ConstructEffect {effect: Effect::Buff, duration: 2,
meta: Some(EffectMeta::Multiplier(125)), tick: None }],
Skill::CorruptI => vec![ConstructEffect {effect: Effect::Corrupt, duration: 2,
meta: Some(EffectMeta::Skill(Skill::CorruptionI)), tick: None}],
Skill::CorruptII => vec![ConstructEffect {effect: Effect::Corrupt, duration: 3,
meta: Some(EffectMeta::Skill(Skill::CorruptionII)), tick: None}],
Skill::CorruptIII => vec![ConstructEffect {effect: Effect::Corrupt, duration: 4,
meta: Some(EffectMeta::Skill(Skill::CorruptionIII)), tick: None}],
Skill::CorruptionI => vec![ConstructEffect {effect: Effect::Corruption, duration: 3,
meta: Some(EffectMeta::Skill(Skill::CorruptionTickI)), tick: None}],
Skill::CorruptionII => vec![ConstructEffect {effect: Effect::Corruption, duration: 4,
meta: Some(EffectMeta::Skill(Skill::CorruptionTickII)), tick: None}],
Skill::CorruptionIII => vec![ConstructEffect {effect: Effect::Corruption, duration: 5,
meta: Some(EffectMeta::Skill(Skill::CorruptionTickIII)), tick: None}],
Skill::ClutchI => vec![ConstructEffect {effect: Effect::Clutch, duration: 1, meta: None, tick: None }],
Skill::ClutchII => vec![ConstructEffect {effect: Effect::Clutch, duration: 2, meta: None, tick: None }],
Skill::ClutchIII => vec![ConstructEffect {effect: Effect::Clutch, duration: 3, meta: None, tick: None }],
Skill::CurseI => vec![ConstructEffect {effect: Effect::Curse, duration: 2,
meta: Some(EffectMeta::Multiplier(150)), tick: None}],
Skill::CurseII => vec![ConstructEffect {effect: Effect::Curse, duration: 2,
meta: Some(EffectMeta::Multiplier(200)), tick: None}],
Skill::CurseIII => vec![ConstructEffect {effect: Effect::Curse, duration: 3,
meta: Some(EffectMeta::Multiplier(250)), tick: None}],
Skill::Debuff => vec![ConstructEffect {effect: Effect::Slow, duration: 3,
meta: Some(EffectMeta::Multiplier(50)), tick: None }],
Skill::DecayI => 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::DecayTickI)), tick: None}],
Skill::DecayII => 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::DecayTickII)), tick: None}],
Skill::DecayIII => 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::DecayTickIII)), tick: None}],
Skill::HasteI => vec![ConstructEffect {effect: Effect::Haste, duration: 2,
meta: Some(EffectMeta::Multiplier(150)), tick: None }],
Skill::HasteII => vec![ConstructEffect {effect: Effect::Haste, duration: 3,
meta: Some(EffectMeta::Multiplier(175)), tick: None }],
Skill::HasteIII => vec![ConstructEffect {effect: Effect::Haste, duration: 4,
meta: Some(EffectMeta::Multiplier(225)), tick: None }],
Skill::HexI => vec![ConstructEffect {effect: Effect::Hex, duration: 2, meta: None, tick: None}],
Skill::HexII => vec![ConstructEffect {effect: Effect::Hex, duration: 3, meta: None, tick: None}],
Skill::HexIII => vec![ConstructEffect {effect: Effect::Hex, duration: 4, meta: None, tick: None}],
Skill::HostilityI => vec![ConstructEffect {effect: Effect::Hostility, duration: 2,
meta: Some(EffectMeta::Skill(Skill::HatredI)), tick: None}],
Skill::HostilityII => vec![ConstructEffect {effect: Effect::Hostility, duration: 3,
meta: Some(EffectMeta::Skill(Skill::HatredII)), tick: None}],
Skill::HostilityIII => vec![ConstructEffect {effect: Effect::Hostility, duration: 4,
meta: Some(EffectMeta::Skill(Skill::HatredIII)), tick: None}],
Skill::HatredI => vec![ConstructEffect {effect: Effect::Hatred, duration: 5, meta: None, tick: None}],
Skill::HatredII => vec![ConstructEffect {effect: Effect::Hatred, duration: 7, meta: None, tick: None}],
Skill::HatredIII => vec![ConstructEffect {effect: Effect::Hatred, duration: 9, meta: None, tick: None}],
Skill::ImpurityI => vec![ConstructEffect {effect: Effect::Impurity, duration: 2,
meta: Some(EffectMeta::Multiplier(150)), tick: None }],
Skill::ImpurityII => vec![ConstructEffect {effect: Effect::Impurity, duration: 3,
meta: Some(EffectMeta::Multiplier(175)), tick: None }],
Skill::ImpurityIII => vec![ConstructEffect {effect: Effect::Impurity, duration: 4,
meta: Some(EffectMeta::Multiplier(225)), tick: None }],
Skill::InvertI => vec![ConstructEffect {effect: Effect::Invert, duration: 2, meta: None, tick: None}],
Skill::InvertII => vec![ConstructEffect {effect: Effect::Invert, duration: 3, meta: None, tick: None}],
Skill::InvertIII => vec![ConstructEffect {effect: Effect::Invert, duration: 4, meta: None, tick: None}],
Skill::ParryI => vec![ConstructEffect {effect: Effect::Parry, duration: 2,
meta: Some(EffectMeta::Skill(Skill::RiposteI)), tick: None}],
Skill::ParryII => vec![ConstructEffect {effect: Effect::Parry, duration: 2,
meta: Some(EffectMeta::Skill(Skill::RiposteII)), tick: None}],
Skill::ParryIII => vec![ConstructEffect {effect: Effect::Parry, duration: 2,
meta: Some(EffectMeta::Skill(Skill::RiposteIII)), tick: None}],
Skill::ReflectI => vec![ConstructEffect {effect: Effect::Reflect, duration: 1, meta: None, tick: None }],
Skill::ReflectII => vec![ConstructEffect {effect: Effect::Reflect, duration: 2, meta: None, tick: None }],
Skill::ReflectIII => vec![ConstructEffect {effect: Effect::Reflect, duration: 3, meta: None, tick: None }],
Skill::ThrowI => 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::ThrowII => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None},
ConstructEffect {effect: Effect::Vulnerable, duration: 4, meta: Some(EffectMeta::Multiplier(200)), tick: None}],
Skill::ThrowIII => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None},
ConstructEffect {effect: Effect::Vulnerable, duration: 4, meta: Some(EffectMeta::Multiplier(250)), tick: None}],
Skill::RuinI => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
Skill::RuinII => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
Skill::RuinIII => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}],
Skill::ScatterI => vec![ConstructEffect {effect: Effect::Scatter, duration: 2, meta: None, tick: None}],
Skill::ScatterII => vec![ConstructEffect {effect: Effect::Scatter, duration: 3, meta: None, tick: None}],
Skill::ScatterIII => vec![ConstructEffect {effect: Effect::Scatter, duration: 4, meta: None, tick: None}],
Skill::SilenceI => vec![ConstructEffect {effect: Effect::Silence, duration: 2, meta: None, tick: None}],
Skill::SilenceII => vec![ConstructEffect {effect: Effect::Silence, duration: 3, meta: None, tick: None}],
Skill::SilenceIII => vec![ConstructEffect {effect: Effect::Silence, duration: 4, meta: None, tick: None}],
Skill::SiphonI => vec![ConstructEffect {effect: Effect::Siphon, duration: 2,
meta: Some(EffectMeta::Skill(Skill::SiphonTickI)), tick: None}],
Skill::SiphonII => vec![ConstructEffect {effect: Effect::Siphon, duration: 3,
meta: Some(EffectMeta::Skill(Skill::SiphonTickII)), tick: None}],
Skill::SiphonIII => vec![ConstructEffect {effect: Effect::Siphon, duration: 4,
meta: Some(EffectMeta::Skill(Skill::SiphonTickIII)), tick: None}],
Skill::SleepI => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}],
Skill::SleepII => vec![ConstructEffect {effect: Effect::Stun, duration: 3, meta: None, tick: None}],
Skill::SleepIII => vec![ConstructEffect {effect: Effect::Stun, duration: 4, meta: None, tick: None}],
Skill::SnareI => vec![ConstructEffect {effect: Effect::Snare, duration: 2, meta: None, tick: None}],
Skill::SnareII => vec![ConstructEffect {effect: Effect::Snare, duration: 3, meta: None, tick: None}],
Skill::SnareIII => vec![ConstructEffect {effect: Effect::Snare, duration: 4, meta: None, tick: None}],
Skill::StrangleI => vec![ConstructEffect {effect: Effect::Strangle, duration: 2,
meta: Some(EffectMeta::Skill(Skill::StrangleTickI)), tick: None}],
Skill::StrangleII => vec![ConstructEffect {effect: Effect::Strangle, duration: 2,
meta: Some(EffectMeta::Skill(Skill::StrangleTickII)), tick: None}],
Skill::StrangleIII => vec![ConstructEffect {effect: Effect::Strangle, duration: 2,
meta: Some(EffectMeta::Skill(Skill::StrangleTickIII)), tick: None}],
Skill::Stun => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}],
Skill::TauntI => vec![ConstructEffect {effect: Effect::Taunt, duration: 2, meta: None, tick: None}],
Skill::TauntII => vec![ConstructEffect {effect: Effect::Taunt, duration: 3, meta: None, tick: None}],
Skill::TauntIII => vec![ConstructEffect {effect: Effect::Taunt, duration: 4, meta: None, tick: None}],
Skill::TriageI => vec![ConstructEffect {effect: Effect::Triage, duration: 2,
meta: Some(EffectMeta::Skill(Skill::TriageTickI)), tick: None}],
Skill::TriageII => vec![ConstructEffect {effect: Effect::Triage, duration: 3,
meta: Some(EffectMeta::Skill(Skill::TriageTickII)), tick: None}],
Skill::TriageIII => vec![ConstructEffect {effect: Effect::Triage, duration: 4,
meta: Some(EffectMeta::Skill(Skill::TriageTickIII)), tick: None}],
//Unused
Skill::Injure => vec![ConstructEffect {effect: Effect::Injured, duration: 2, meta: None, tick: None }],
_ => {
panic!("{:?} no skill effect", self);
},
}
}
pub fn base_cd(&self) -> Cooldown {
match self {
Skill::Attack => None,
Skill::Debuff => Some(1),
Skill::Buff => None,
Skill::StrikeI => None,
Skill::StrikeII => None,
Skill::StrikeIII => None,
Skill::Block => None, // reduce damage
Skill::ParryI |
Skill::ParryII |
Skill::ParryIII => Some(2), // avoid all damage
Skill::SnareI => Some(2),
Skill::SnareII => Some(2),
Skill::SnareIII => Some(2),
Skill::Stun => Some(2),
Skill::HealI => None,
Skill::HealII => None,
Skill::HealIII => None,
Skill::TriageI => None, // hot
Skill::TriageII => None, // hot
Skill::TriageIII => None, // hot
Skill::ThrowI => Some(1), // no damage stun, adds vulnerable
Skill::ThrowII => Some(1),
Skill::ThrowIII => Some(1),
Skill::BlastI => None,
Skill::BlastII => None,
Skill::BlastIII => None,
Skill::ChaosI => None,
Skill::ChaosII => None,
Skill::ChaosIII => None,
Skill::AmplifyI => Some(1),
Skill::AmplifyII => Some(1),
Skill::AmplifyIII => Some(1),
Skill::ImpurityI |
Skill::ImpurityII |
Skill::ImpurityIII => Some(3),
Skill::InvertI => Some(2),
Skill::InvertII => Some(2),
Skill::InvertIII => Some(2),
Skill::DecayI => Some(1), // dot
Skill::DecayII => Some(1),
Skill::DecayIII => Some(1),
Skill::SiphonI |
Skill::SiphonII |
Skill::SiphonIII => None,
Skill::CurseI => Some(1),
Skill::CurseII => Some(1),
Skill::CurseIII => Some(1),
Skill::ScatterI => Some(2),
Skill::ScatterII => Some(2),
Skill::ScatterIII => Some(2),
Skill::SilenceI => Some(3),
Skill::SilenceII => Some(2),
Skill::SilenceIII => Some(2),
Skill::PurifyI => None,
Skill::PurifyII => None,
Skill::PurifyIII => None,
Skill::PurgeI => None,
Skill::PurgeII => None,
Skill::PurgeIII => None,
Skill::BanishI => Some(1),
Skill::BanishII => Some(1),
Skill::BanishIII => Some(1),
Skill::HexI => Some(1),
Skill::HexII => Some(2),
Skill::HexIII => Some(2),
Skill::HasteI => Some(2),
Skill::HasteII => Some(2),
Skill::HasteIII => Some(2),
Skill::ReflectI => Some(2),
Skill::ReflectII => Some(2),
Skill::ReflectIII => Some(2),
Skill::RechargeI => Some(2),
Skill::RechargeII => Some(2),
Skill::RechargeIII => Some(2),
Skill::RuinI => Some(3),
Skill::RuinII => Some(2),
Skill::RuinIII => Some(2),
Skill::SlayI => None,
Skill::SlayII => None,
Skill::SlayIII => None,
Skill::SleepI => Some(3),
Skill::SleepII => Some(3),
Skill::SleepIII => Some(3),
Skill::StrangleI => Some(2),
Skill::StrangleII => Some(2),
Skill::StrangleIII => Some(2),
Skill::ClutchI => Some(1),
Skill::ClutchII => Some(2),
Skill::ClutchIII => Some(3),
Skill::TauntI => Some(2),
Skill::TauntII => Some(2),
Skill::TauntIII => Some(2),
Skill::Injure => Some(2),
Skill::CorruptI =>Some(1),
Skill::CorruptII =>Some(1),
Skill::CorruptIII =>Some(1),
Skill::HostilityI |
Skill::HostilityII |
Skill::HostilityIII => Some(1),
//-----------
// Never cast directly
//---------
// Trigger
Skill::ImpureBlast |
Skill::HasteStrike |
Skill::RiposteI |
Skill::RiposteII |
Skill::RiposteIII | // parry
Skill::CorruptionI |
Skill::CorruptionII |
Skill::CorruptionIII |
Skill::HatredI |
Skill::HatredII |
Skill::HatredIII |
// Ticks
Skill::CorruptionTickI |
Skill::CorruptionTickII |
Skill::CorruptionTickIII |
Skill::DecayTickI |
Skill::DecayTickII |
Skill::DecayTickIII |
Skill::SiphonTickI |
Skill::SiphonTickII |
Skill::SiphonTickIII |
Skill::StrangleTickI |
Skill::StrangleTickII |
Skill::StrangleTickIII |
Skill::TriageTickI |
Skill::TriageTickII |
Skill::TriageTickIII => None,
// Triggers
// -----------------
// Test
// -----------------
Skill::TestAttack => None,
Skill::TestHeal => None,
Skill::TestTouch => None,
Skill::TestStun => None,
Skill::TestBlock => None,
Skill::TestSiphon => None,
Skill::TestParry => None,
}
}
pub fn ko_castable(&self) -> bool {
match self {
Skill::CorruptionTickI |
Skill::CorruptionTickII |
Skill::CorruptionTickIII |
Skill::DecayTickI |
Skill::DecayTickII |
Skill::DecayTickIII |
Skill::SiphonTickI |
Skill::SiphonTickII |
Skill::SiphonTickIII |
Skill::TriageTickI |
Skill::TriageTickII |
Skill::TriageTickIII => true,
_ => false,
}
}
pub fn is_tick(&self) -> bool {
match self {
Skill::CorruptionTickI |
Skill::CorruptionTickII |
Skill::CorruptionTickIII |
Skill::DecayTickI |
Skill::DecayTickII |
Skill::DecayTickIII |
Skill::SiphonTickI |
Skill::SiphonTickII |
Skill::SiphonTickIII |
Skill::StrangleTickI |
Skill::StrangleTickII |
Skill::StrangleTickIII |
Skill::TriageTickI |
Skill::TriageTickII |
Skill::TriageTickIII => true,
_ => false,
}
}
pub fn speed(&self) -> u8 {
match self {
// -----------------
// Test
// -----------------
Skill::TestTouch => 10,
Skill::TestStun => 5,
Skill::TestBlock => 10,
Skill::TestParry => 10,
Skill::TestSiphon => 10,
Skill::StrikeI => u8::max_value(),
Skill::StrikeII => Skill::StrikeI.speed(),
Skill::StrikeIII => Skill::StrikeI.speed(),
Skill::SiphonTickI |
Skill::SiphonTickII |
Skill::SiphonTickIII => Skill::SiphonI.speed(),
Skill::DecayTickI |
Skill::DecayTickII |
Skill::DecayTickIII => Skill::DecayI.speed(),
Skill::TriageTickI |
Skill::TriageTickII |
Skill::TriageTickIII => Skill::TriageI.speed(),
Skill::StrangleTickI |
Skill::StrangleTickII |
Skill::StrangleTickIII => Skill::StrangleI.speed(),
Skill::CorruptionTickI |
Skill::CorruptionTickII |
Skill::CorruptionTickIII => Skill::CorruptI.speed(),
_ => Item::from(*self).speed(),
}
}
pub fn aoe(&self) -> bool {
match self {
Skill::RuinI |
Skill::RuinII |
Skill::RuinIII => true,
_ => false,
}
}
pub fn self_targeting(&self) -> bool {
match self {
Skill::Block |
Skill::CorruptI |
Skill::CorruptII |
Skill::CorruptIII |
Skill::ClutchI |
Skill::ClutchII |
Skill::ClutchIII |
Skill::ParryI |
Skill::ParryII |
Skill::ParryIII |
Skill::TestBlock |
Skill::TestParry => true,
_ => false,
}
}
pub fn defensive(&self) -> bool {
let mut rng = thread_rng();
match self {
Skill::AmplifyI |
Skill::AmplifyII |
Skill::AmplifyIII |
Skill::Block |
Skill::ClutchI |
Skill::ClutchII |
Skill::ClutchIII |
Skill::CorruptI |
Skill::CorruptII |
Skill::CorruptIII |
Skill::HasteI |
Skill::HasteII |
Skill::HasteIII |
Skill::HealI |
Skill::HealII |
Skill::HealIII |
Skill::HostilityI |
Skill::HostilityII |
Skill::HostilityIII |
Skill::InvertI |
Skill::InvertII |
Skill::InvertIII |
Skill::ParryI |
Skill::ParryII |
Skill::ParryIII |
Skill::PurifyI |
Skill::PurifyII |
Skill::PurifyIII |
Skill::RechargeI |
Skill::RechargeII |
Skill::RechargeIII |
Skill::ReflectI |
Skill::ReflectII |
Skill::ReflectIII |
Skill::ScatterI |
Skill::ScatterII |
Skill::ScatterIII |
Skill::TriageI |
Skill::TriageII |
Skill::TriageIII => true,
Skill::BanishI |
Skill::BanishII |
Skill::BanishIII => rng.gen_bool(0.5),
_ => false,
}
}
fn components(&self) -> Vec<Item> {
let mut components = Item::from(*self).components();
components.sort_unstable();
return components;
}
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),
}
}
}
fn touch(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
target.deal_red_damage(skill, 0)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
return results;
}
fn attack(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.red_power().pct(skill.multiplier());
target.deal_red_damage(skill, amount)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
return results;
}
fn strike(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.red_power().pct(skill.multiplier());
target.deal_red_damage(skill, amount)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
return results;
}
fn injure(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.red_power().pct(skill.multiplier());
target.deal_red_damage(skill, amount)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;
}
fn stun(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;
}
fn sleep(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::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| results.push(Resolution::new(source, target).event(e)));
return results;
}
fn clutch(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;
}
fn taunt(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let red_amount = source.red_power().pct(skill.multiplier());
results.push(Resolution::new(source, target).event(target.recharge(skill, red_amount, 0)));
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;
}
fn throw(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;
}
fn strangle(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect().first().unwrap().clone();
let tick_skill = match meta {
Some(EffectMeta::Skill(s)) => s,
_ => panic!("no strangle tick skill"),
};
let strangle = ConstructEffect::new(effect, duration).set_tick(Cast::new_tick(source, target, tick_skill));
results.push(Resolution::new(source, target).event(target.add_effect(skill, strangle)));
let attacker_strangle = ConstructEffect::new(Effect::Strangling, duration);
results.push(Resolution::new(source, source).event(source.add_effect(skill, attacker_strangle)));
return strangle_tick(source, target, results, tick_skill);
}
fn strangle_tick(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.red_power().pct(skill.multiplier());
target.deal_red_damage(skill, amount)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
// remove immunity if target ko
if target.is_ko() {
let i = source.effects
.iter()
.position(|e| e.effect == Effect::Strangling)
.expect("no strangling on construct");
source.effects.remove(i);
results.push(Resolution::new(source, source).event(Event::Removal { effect: Effect::Strangling }));
}
return results;
}
fn block(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;
}
fn buff(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;
}
fn parry(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let red_amount = source.red_power().pct(skill.multiplier());
results.push(Resolution::new(source, target).event(target.recharge(skill, red_amount, 0)));
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;
}
fn riposte(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.red_power().pct(skill.multiplier());
target.deal_red_damage(skill, amount)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
return results;
}
fn snare(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::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| results.push(Resolution::new(source, target).event(e)));
return results;
}
fn slay(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.red_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: _ } => {
results.push(Resolution::new(source, target).event(e));
let heal = source.deal_green_damage(skill, amount);
for h in heal {
results.push(Resolution::new(source, source).event(h));
};
},
_ => results.push(Resolution::new(source, target).event(e)),
}
}
return results;
}
fn heal(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.green_power().pct(skill.multiplier());
target.deal_green_damage(skill, amount)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
return results;
}
fn triage(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect().first().unwrap().clone();
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));
results.push(Resolution::new(source, target).event(target.add_effect(skill, triage)));
return triage_tick(source, target, results, tick_skill);
}
fn triage_tick(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.green_power().pct(skill.multiplier());
target.deal_green_damage(skill, amount)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
return results;
}
fn chaos(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let mut rng = thread_rng();
let b_rng: u64 = 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| results.push(Resolution::new(source, target).event(e)));
let r_rng: u64 = 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| results.push(Resolution::new(source, target).event(e)));
return results;
}
fn blast(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.blue_power().pct(skill.multiplier());
target.deal_blue_damage(skill, amount)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
return results;
}
fn amplify(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;;
}
fn haste(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;;
}
fn debuff(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;;
}
fn decay(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let wither = skill.effect().first().unwrap().clone();
results.push(Resolution::new(source, target).event(target.add_effect(skill, wither)));
let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect().last().unwrap().clone();
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));
results.push(Resolution::new(source, target).event(target.add_effect(skill, decay)));
return decay_tick(source, target, results, tick_skill);
}
fn decay_tick(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.blue_power().pct(skill.multiplier());
target.deal_blue_damage(skill, amount)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
return results;
}
// corrupt is the buff effect
// when attacked it runs corruption and applies a debuff
fn corrupt(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let corrupt = skill.effect().first().unwrap().clone();
results.push(Resolution::new(source, target).event(target.add_effect(skill, corrupt)));
return results;;
}
fn corruption(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect().first().unwrap().clone();
let tick_skill = match meta {
Some(EffectMeta::Skill(s)) => s,
_ => panic!("no corruption tick skill"),
};
let corruption = ConstructEffect::new(effect, duration).set_tick(Cast::new_tick(source, target, tick_skill));
results.push(Resolution::new(source, target).event(target.add_effect(skill, corruption)));
return corruption_tick(source, target, results, tick_skill);
}
fn corruption_tick(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.blue_power().pct(skill.multiplier());
target.deal_blue_damage(skill, amount)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
return results;
}
fn ruin(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;;
}
fn hex(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;;
}
fn hostility(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let hostility = skill.effect().first().unwrap().clone();
results.push(Resolution::new(source, target).event(target.add_effect(skill, hostility)));
return results;;
}
fn hatred(source: &mut Construct, target: &mut Construct, mut results: Resolutions, reflect_skill: Skill, amount: u64, skill: Skill) -> Resolutions {
let hatred = skill.effect().first().unwrap().clone()
.set_meta(EffectMeta::AddedDamage(amount));
results.push(Resolution::new(source, target).event(target.add_effect(reflect_skill, hatred)));
return results;;
}
fn curse(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;;
}
fn impurity(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;;
}
fn invert(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;;
}
fn reflect(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
let blue_amount = source.blue_power().pct(skill.multiplier());
results.push(Resolution::new(source, target).event(target.recharge(skill, 0, blue_amount)));
return results;;
}
fn recharge(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let red_amount = source.red_power().pct(skill.multiplier());
let blue_amount = source.blue_power().pct(skill.multiplier());
results.push(Resolution::new(source, target).event(target.recharge(skill, red_amount, blue_amount)));
return results;
}
fn siphon(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect().first().unwrap().clone();
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));
results.push(Resolution::new(source, target).event(target.add_effect(skill, siphon)));
return siphon_tick(source, target, results, tick_skill);
}
fn siphon_tick(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.blue_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: _ } => {
results.push(Resolution::new(source, target).event(e));
let heal = source.deal_green_damage(skill, amount);
for h in heal {
results.push(Resolution::new(source, source).event(h));
};
},
_ => results.push(Resolution::new(source, target).event(e)),
}
}
return results;
}
fn scatter(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let blue_amount = source.blue_power().pct(skill.multiplier());
results.push(Resolution::new(source, target).event(target.recharge(skill, 0, blue_amount)));
skill.effect().into_iter().for_each(|e| {
let scatter = e.clone().set_meta(EffectMeta::ScatterTarget(target.id));
results.push(Resolution::new(source, target).event(source.add_effect(skill, scatter)));
});
return results;
}
fn scatter_hit(source: &Construct, target: &Construct, mut results: Resolutions, game: &mut Game, event: Event) -> Resolutions {
match event {
Event::Damage { amount, skill, mitigation: _, colour } => {
let scatter = target.effects.iter().find(|e| e.effect == Effect::Scatter).unwrap();
if let Some(EffectMeta::ScatterTarget(scatter_target_id)) = scatter.meta {
let mut scatter_target = game.construct_by_id(scatter_target_id).unwrap();
let res = match colour {
Colour::Red => scatter_target.deal_red_damage(skill, amount),
Colour::Blue => scatter_target.deal_blue_damage(skill, amount),
Colour::Green => scatter_target.deal_green_damage(skill, amount),
};
results.push(Resolution::new(target, scatter_target).event(Event::Skill { skill: Skill::ScatterI }));
res.into_iter().for_each(|e| results.push(Resolution::new(&source, &scatter_target).event(e)));
} else {
panic!("not a scatter target {:?}", scatter);
}
return results;
},
_ => panic!("{:?} scatter hit not damage event", event),
}
}
fn silence(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::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::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| results.push(Resolution::new(source, target).event(e)));
return results;
}
fn purge(source: &mut Construct, target: &mut Construct, mut results: Resolutions, _skill: Skill) -> Resolutions {
while let Some(i) = target.effects
.iter()
.position(|ce| ce.effect.category() == EffectCategory::Buff) {
let ce = target.effects.remove(i);
results.push(Resolution::new(source, target).event(Event::Removal { effect: ce.effect }));
}
return results;
}
fn purify(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
results.push(Resolution::new(source, target).event(Event::Skill { skill }));
let amount = source.green_power().pct(skill.multiplier());
while let Some(i) = target.effects
.iter()
.position(|ce| ce.effect.category() == EffectCategory::Debuff) {
let ce = target.effects.remove(i);
results.push(Resolution::new(source, target).event(Event::Removal { effect: ce.effect }));
target.deal_green_damage(skill, amount)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
}
return results;
}
fn banish(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter()
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
return results;
}
#[cfg(test)]
mod tests {
use skill::*;
#[test]
fn heal_test() {
let mut x = Construct::new()
.named(&"muji".to_string())
.learn(Skill::HealI);
let mut y = Construct::new()
.named(&"camel".to_string())
.learn(Skill::HealI);
x.deal_red_damage(Skill::Attack, 5);
heal(&mut y, &mut x, vec![], Skill::HealI);
}
#[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::DecayI);
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));
let mut results = attack(&mut x, &mut y, vec![], Skill::TestAttack);
let Resolution { source: _, target: _, event } = results.remove(0);
match event {
Event::Damage { amount, mitigation: _, colour: _, skill: _ } => assert_eq!(amount, 50),
_ => panic!("not damage"),
};
}
#[test]
fn clutch_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
clutch(&mut y.clone(), &mut y, vec![], Skill::ClutchI);
assert!(y.affected(Effect::Clutch));
let mut results = hex(&mut x, &mut y, vec![], Skill::HexI);
let Resolution { source: _, target: _, event } = results.remove(0);
match event {
Event::Immunity { skill: _, immunity } => assert!(immunity.contains(&Effect::Clutch)),
_ => panic!("not immune cluthc"),
};
let mut results = attack(&mut x, &mut y, vec![], Skill::Attack);
assert!(y.green_life() == 1);
let Resolution { source: _, target: _, event } = results.remove(0);
match event {
Event::Damage { amount, mitigation: _, colour: _, skill: _ } => assert_eq!(amount, 1023),
_ => panic!("not damage"),
};
}
#[test]
fn injure_test() {
let mut x = Construct::new()
.named(&"muji".to_string());
let mut y = Construct::new()
.named(&"camel".to_string());
resolve(Skill::Injure, &mut x, &mut y, vec![]);
assert!(y.immune(Skill::HealI).is_some());
// resolutions = heal(&mut y.clone(), &mut y, resolutions);
}
#[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(256 + 64);
invert(&mut y.clone(), &mut y, vec![], Skill::InvertI);
assert!(y.affected(Effect::Invert));
// heal should deal green damage
heal(&mut x, &mut y, vec![], Skill::TestHeal);
assert!(y.green_life() == 768);
// attack should heal and recharge red shield
let mut results = attack(&mut x, &mut y, vec![], Skill::TestAttack);
assert!(y.green_life() == 1024);
match results.remove(0).event {
Event::Inversion { skill } => assert_eq!(skill, Skill::TestAttack),
_ => panic!("not inversion"),
};
match results.remove(0).event {
Event::Healing { skill: _, overhealing: _, amount } => assert_eq!(amount, 256),
_ => panic!("not healing from inversion"),
};
match results.remove(0).event {
Event::Recharge { skill: _, red, blue: _ } => assert_eq!(red, 64),
_ => 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::ReflectI);
assert!(y.affected(Effect::Reflect));
let mut results = vec![];
results = resolve(Skill::TestAttack, &mut x, &mut y, results);
assert!(x.green_life() == 768);
let Resolution { source: _, target: _, event } = results.remove(0);
match event {
Event::Reflection { skill } => assert_eq!(skill, Skill::TestAttack),
_ => panic!("not reflection"),
};
let Resolution { source: _, target: _, event } = results.remove(0);
match event {
Event::Damage { amount, mitigation: _, colour: _, skill: _ } => assert_eq!(amount, 256),
_ => 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.green_life.reduce(512);
let mut results = resolve(Skill::SiphonI, &mut x, &mut y, vec![]);
assert!(y.affected(Effect::Siphon));
assert!(x.green_life() == (512 + 256.pct(Skill::SiphonTickI.multiplier())));
let Resolution { source: _, target: _, event } = results.remove(0);
match event {
Event::Effect { effect, skill: _, duration: _ } => assert_eq!(effect, Effect::Siphon),
_ => panic!("not siphon"),
};
let Resolution { source: _, target: _, event } = results.remove(0);
match event {
Event::Damage { amount, skill: _, mitigation: _, colour: _} => assert_eq!(amount, 256.pct(Skill::SiphonTickI.multiplier())),
_ => panic!("not damage siphon"),
};
let Resolution { source: _, target, event } = results.remove(0);
match event {
Event::Healing { amount, skill: _, overhealing: _ } => {
assert_eq!(amount, 256.pct(Skill::SiphonTickI.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::TriageI);
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::BlastI, 5);
let mut results = recharge(&mut x, &mut y, vec![], Skill::RechargeI);
let Resolution { source: _, target: _, event } = results.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::SilenceI);
assert!(x.effects.iter().any(|e| e.effect == Effect::Silence));
assert!(x.disabled(Skill::SilenceI).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::AmplifyI);
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::DecayI);
assert!(x.effects.iter().any(|e| e.effect == Effect::Decay));
purify(&mut x.clone(), &mut x, vec![], Skill::PurifyI);
assert!(!x.effects.iter().any(|e| e.effect == Effect::Decay));
}
}