diff --git a/core/src/construct.rs b/core/src/construct.rs index abab88e2..05482807 100644 --- a/core/src/construct.rs +++ b/core/src/construct.rs @@ -98,7 +98,7 @@ impl ConstructEffect { pub fn get_skill(&self) -> Option { match self.meta { - Some(EffectMeta::CastTick { source, target, skill, speed, amount }) => Some(skill), + Some(EffectMeta::CastTick { source: _, target: _, skill, speed: _, amount: _ }) => Some(skill), Some(EffectMeta::CastOnHit(s)) => Some(s), _ => None, } @@ -534,7 +534,7 @@ impl Construct { fn tick_damage(&self, effect: Effect) -> usize { match self.effects.iter().find_map(|ce| match ce.effect == effect { true => match ce.meta { - Some(EffectMeta::CastTick { source, target, skill, speed, amount }) => Some(amount), + Some(EffectMeta::CastTick { source: _, target: _, skill: _, speed: _, amount }) => Some(amount), _ => None, }, false => None, diff --git a/core/src/game.rs b/core/src/game.rs index dd80a286..01cc35fa 100644 --- a/core/src/game.rs +++ b/core/src/game.rs @@ -12,7 +12,6 @@ use failure::err_msg; use construct::{Construct, ConstructEffect, Stat, EffectMeta}; use skill::{Skill, Cast}; use effect::{Effect}; -use util::{IntPct}; use player::{Player}; use instance::{TimeControl}; @@ -631,7 +630,7 @@ impl Game { Value::Removals { construct } => self.events.iter().fold(0, |dmg, e| match e { - Event::Damage { construct: event_construct, amount, mitigation:_, colour: event_colour, display: _ } => + Event::Damage { construct: event_construct, amount, mitigation:_, colour: _event_colour, display: _ } => match construct == *event_construct { true => dmg + amount, false => dmg, @@ -1250,6 +1249,351 @@ mod tests { assert!(game.player_by_id(y_player.id).unwrap().constructs[0].skill_on_cd(Skill::Block).is_none()); } +// #[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 actions = cast.actions(Game::); + + // match actions[0] { + // Action::Cast => (), + // _ => panic!("{:?}", actions), + // }; + + // match actions[1] { + // Action::Hit => (), + // _ => panic!("{:?}", actions), + // }; + + // match actions[2] { + // Action::Damage { construct: _, amount: _, colour } => { + // 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::Heal { 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::Heal { 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()); + // } +// } + + // #[test] // fn sleep_cooldown_test() { // let mut game = create_test_game(); @@ -1874,11 +2218,11 @@ mod tests { _ => false, })); - assert!(match game.players[1].constructs[0].effects[0].meta { +/* assert!(match game.players[1].constructs[0].effects[0].meta { Some(EffectMeta::AddedDamage(d)) => d, _ => 0 // 320 base blue power and 125 base blue life - } == 320.pct(Skill::Blast.multiplier()) - 125); + } == 320.pct(Skill::Blast.multiplier()) - 125);*/ } @@ -1896,11 +2240,11 @@ mod tests { game.new_resolve(Cast::new(source, player_id, target, Skill::Absorb)); game.new_resolve(Cast::new(source, player_id, target, Skill::Blast)); - assert!(match game.players[1].constructs[0].effects[0].meta { + /*assert!(match game.players[1].constructs[0].effects[0].meta { Some(EffectMeta::AddedDamage(d)) => d, _ => 0 // 320 base blue power and 125 base blue life - } == 320.pct(Skill::Blast.multiplier()) - 125); + } == 320.pct(Skill::Blast.multiplier()) - 125);*/ } #[test] diff --git a/core/src/item.rs b/core/src/item.rs index fbdd7e68..d84abe7d 100644 --- a/core/src/item.rs +++ b/core/src/item.rs @@ -590,7 +590,7 @@ impl Item { Item::Red => format!("Combine two colours with a white base item to create a new combo. \n Fast speed, physical type. Chaos and momentum."), // base skills - Item::Attack => format!("Deal {:?}% RedPower as red damage.", +/* Item::Attack => format!("Deal {:?}% RedPower as red damage.", self.into_skill().unwrap().multiplier()), Item::Block => format!("Reduce red damage and blue damage taken by {:?}%. Block lasts {:?}T", 100 - self.into_skill().unwrap().effect()[0].get_multiplier(), @@ -605,7 +605,7 @@ impl Item { Item::Debuff => format!("Slows the target reducing SpeedStat by {:?}%. Debuff lasts {:?}T", 100 - self.into_skill().unwrap().effect()[0].get_multiplier(), - self.into_skill().unwrap().effect()[0].get_duration()), + self.into_skill().unwrap().effect()[0].get_duration()),*/ // specs // Base Item::Power => format!("Increases all power stats by {:?}%. @@ -710,8 +710,7 @@ impl Item { If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), - // Skills <- need to move effect mulltipliers into skills - Item::Amplify| +/* Item::Amplify| Item::AmplifyPlus | Item::AmplifyPlusPlus => format!("Increase RedPower BluePower by {:?}%. Lasts {:?}T.", self.into_skill().unwrap().effect()[0].get_multiplier() - 100, @@ -944,6 +943,8 @@ impl Item { "Heals target for {:?}% GreenPower each turn. Lasts {:?}T.", self.into_skill().unwrap().effect()[0].get_skill().unwrap().multiplier(), self.into_skill().unwrap().effect()[0].get_duration()), +*/ + _ => format!("Missing"), } } diff --git a/core/src/skill.rs b/core/src/skill.rs index 01276c2f..328e0cc5 100644 --- a/core/src/skill.rs +++ b/core/src/skill.rs @@ -4,8 +4,8 @@ use uuid::Uuid; use util::{IntPct}; use item::{Item}; -use game::{Game, Colour, Value, Action, Event}; -use construct::{Construct, ConstructEffect, EffectMeta, Stat}; +use game::{Game, Colour, Value, Action}; +use construct::{ConstructEffect, EffectMeta, Stat}; use effect::{Effect, Cooldown}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -18,8 +18,6 @@ pub struct Cast { pub speed: usize, } -fn start() {} - impl Cast { pub fn new(source: Uuid, player: Uuid, target: Uuid, skill: Skill) -> Cast { return Cast { @@ -52,149 +50,147 @@ impl Cast { } match self.skill { - Skill::Attack => attack(self, game), - Skill::Block => block(self, game), - Skill::Buff => buff(self, game), - Skill::Debuff => debuff(self, game), - Skill::Stun => stun(self, game), + Skill::Attack => attack(self, game, Attack::Base), + Skill::Block => block(self, game, Block::Base), + Skill::Buff => buff(self, game, Buff::Base), + Skill::Debuff => debuff(self, game, Debuff::Base), + Skill::Stun => stun(self, game, Stun::Base), - Skill::Amplify => amplify(self, game), - Skill::AmplifyPlus => amplify_plus(self, game), - Skill::AmplifyPlusPlus => amplify_plus_plus(self, game), + Skill::Amplify => amplify(self, game, Amplify::Base), + Skill::AmplifyPlus => amplify(self, game, Amplify::Plus), + Skill::AmplifyPlusPlus => amplify(self, game, Amplify::PlusPlus), - Skill::Absorb => absorb(self, game), - Skill::AbsorbPlus => absorb_plus(self, game), - Skill::AbsorbPlusPlus => absorb_plus_plus(self, game), - Skill::Absorption => absorption(self, game), - Skill::AbsorptionPlus => absorption_plus(self, game), - Skill::AbsorptionPlusPlus => absorption_plus_plus(self, game), + Skill::Absorb => absorb(self, game, Absorb::Base), + Skill::AbsorbPlus => absorb(self, game, Absorb::Plus), + Skill::AbsorbPlusPlus => absorb(self, game, Absorb::PlusPlus), + Skill::Absorption => absorption(self, game, Absorption::Base), + Skill::AbsorptionPlus => absorption(self, game, Absorption::Plus), + Skill::AbsorptionPlusPlus => absorption(self, game, Absorption::PlusPlus), - Skill::Banish | - Skill::BanishPlus | - Skill::BanishPlusPlus => banish(self, game), + Skill::Banish => banish(self, game, Banish::Base), + Skill::BanishPlus => banish(self, game, Banish::Plus), + Skill::BanishPlusPlus => banish(self, game, Banish::PlusPlus), - Skill::Bash | - Skill::BashPlus | - Skill::BashPlusPlus => bash(self, game), + Skill::Bash => bash(self, game, Bash::Base), + Skill::BashPlus => bash(self, game, Bash::Plus), + Skill::BashPlusPlus => bash(self, game, Bash::PlusPlus), - Skill::Blast | - Skill::BlastPlus | - Skill::BlastPlusPlus => blast(self, game), + Skill::Blast => blast(self, game, Blast::Base), + Skill::BlastPlus => blast(self, game, Blast::Plus), + Skill::BlastPlusPlus => blast(self, game, Blast::PlusPlus), - Skill::Break => break_zero(self, game), - Skill::BreakPlus => break_plus(self, game), - Skill::BreakPlusPlus => break_plus_plus(self, game), + Skill::Break => break_fn(self, game, Break::Base), + Skill::BreakPlus => break_fn(self, game, Break::Plus), + Skill::BreakPlusPlus => break_fn(self, game, Break::PlusPlus), - Skill::Curse => curse(self, game), - Skill::CursePlus => curse_plus(self, game), - Skill::CursePlusPlus => curse_plus_plus(self, game), + Skill::Curse => curse(self, game, Curse::Base), + Skill::CursePlus => curse(self, game, Curse::Plus), + Skill::CursePlusPlus => curse(self, game, Curse::PlusPlus), - Skill::Chaos | - Skill::ChaosPlus | - Skill::ChaosPlusPlus => chaos(self, game), + Skill::Chaos => chaos(self, game, Chaos::Base), + Skill::ChaosPlus => chaos(self, game, Chaos::Plus), + Skill::ChaosPlusPlus => chaos(self, game, Chaos::PlusPlus), - Skill::Counter => counter(self, game), - Skill::CounterPlus => counter_plus(self, game), - Skill::CounterPlusPlus => counter_plus_plus(self, game), - Skill::CounterAttack | - Skill::CounterAttackPlus | - Skill::CounterAttackPlusPlus => counter_attack(self, game), + Skill::Counter => counter(self, game, Counter::Base), + Skill::CounterPlus => counter(self, game, Counter::Plus), + Skill::CounterPlusPlus => counter(self, game, Counter::PlusPlus), + Skill::CounterAttack => counter_attack(self, game, CounterAttack::Base), + Skill::CounterAttackPlus => counter_attack(self, game, CounterAttack::Plus), + Skill::CounterAttackPlusPlus => counter_attack(self, game, CounterAttack::PlusPlus), - Skill::Decay => decay(self, game, Decay::Decay), - Skill::DecayPlus => decay(self, game, Decay::DecayPlus), - Skill::DecayPlusPlus => decay(self, game, Decay::DecayPlusPlus), + Skill::Decay => decay(self, game, Decay::Base), + Skill::DecayPlus => decay(self, game, Decay::Plus), + Skill::DecayPlusPlus => decay(self, game, Decay::PlusPlus), Skill::DecayTick => decay_tick(self, game), - Skill::Electrify => electrify(self, game), - Skill::ElectrifyPlus => electrify_plus(self, game), - Skill::ElectrifyPlusPlus => electrify_plus_plus(self, game), - Skill::Electrocute => electrocute(self, game), - Skill::ElectrocutePlus => electrocute_plus(self, game), - Skill::ElectrocutePlusPlus => electrocute_plus_plus(self, game), - Skill::ElectrocuteTick => electrocute_tick(self, game), + Skill::Electrify => electrify(self, game, Electrify::Base), + Skill::ElectrifyPlus => electrify(self, game, Electrify::Plus), + Skill::ElectrifyPlusPlus => electrify(self, game, Electrify::PlusPlus), + Skill::Electrocute => electrocute(self, game, Electrocute::Base), + Skill::ElectrocutePlus => electrocute(self, game, Electrocute::Plus), + Skill::ElectrocutePlusPlus => electrocute(self, game, Electrocute::PlusPlus), + Skill::ElectrocuteTick => electrocute_tick(self, game), - Skill::Heal | - Skill::HealPlus | - Skill::HealPlusPlus => heal(self, game), + Skill::Heal => heal(self, game, Heal::Base), + Skill::HealPlus => heal(self, game, Heal::Plus), + Skill::HealPlusPlus => heal(self, game, Heal::PlusPlus), - Skill::Haste => haste(self, game), - Skill::HastePlus => haste_plus(self, game), - Skill::HastePlusPlus => haste_plus_plus(self, game), + Skill::Haste => haste(self, game, Haste::Base), + Skill::HastePlus => haste(self, game, Haste::Plus), + Skill::HastePlusPlus => haste(self, game, Haste::PlusPlus), + Skill::HasteStrike => strike(self, game, Strike::Haste), - Skill::HasteStrike => haste_strike(self, game), + Skill::Hybrid => hybrid(self, game, Hybrid::Base), + Skill::HybridPlus => hybrid(self, game, Hybrid::Plus), + Skill::HybridPlusPlus => hybrid(self, game, Hybrid::PlusPlus), - Skill::Hybrid => hybrid(self, game), - Skill::HybridPlus => hybrid_plus(self, game), - Skill::HybridPlusPlus => hybrid_plus_plus(self, game), + Skill::HybridBlast => blast(self, game, Blast::Hybrid), - Skill::HybridBlast => hybrid_blast(self, game), + Skill::Intercept => intercept(self, game, Intercept::Base), + Skill::InterceptPlus => intercept(self, game, Intercept::Plus), + Skill::InterceptPlusPlus => intercept(self, game, Intercept::PlusPlus), - Skill::Intercept | - Skill::InterceptPlus | - Skill::InterceptPlusPlus => intercept(self, game), + Skill::Invert => invert(self, game, Invert::Base), + Skill::InvertPlus => invert(self, game, Invert::Plus), + Skill::InvertPlusPlus => invert(self, game, Invert::PlusPlus), - Skill::Invert => invert(self, game), - Skill::InvertPlus => invert_plus(self, game), - Skill::InvertPlusPlus => invert_plus_plus(self, game), + Skill::Link => link(self, game, Link::Base), + Skill::LinkPlus => link(self, game, Link::Plus), + Skill::LinkPlusPlus => link(self, game, Link::PlusPlus), - Skill::Link | - Skill::LinkPlus | - Skill::LinkPlusPlus => link(self, game), + Skill::Purge => purge(self, game, Purge::Base), + Skill::PurgePlus => purge(self, game, Purge::Plus), + Skill::PurgePlusPlus => purge(self, game, Purge::PlusPlus), - Skill::Purge => purge(self, game), - Skill::PurgePlus => purge_plus(self, game), - Skill::PurgePlusPlus => purge_plus_plus(self, game), + Skill::Purify => purify(self, game, Purify::Base), + Skill::PurifyPlus => purify(self, game, Purify::Plus), + Skill::PurifyPlusPlus => purify(self, game, Purify::PlusPlus), - Skill::Purify => purify(self, game), - Skill::PurifyPlus => purify_plus(self, game), - Skill::PurifyPlusPlus => purify_plus_plus(self, game), + Skill::Recharge => recharge(self, game, Recharge::Base), + Skill::RechargePlus => recharge(self, game, Recharge::Plus), + Skill::RechargePlusPlus => recharge(self, game, Recharge::PlusPlus), - Skill::Recharge | - Skill::RechargePlus | - Skill::RechargePlusPlus => recharge(self, game), + Skill::Reflect => reflect(self, game, Reflect::Base), + Skill::ReflectPlus => reflect(self, game, Reflect::Plus), + Skill::ReflectPlusPlus => reflect(self, game, Reflect::PlusPlus), - Skill::Reflect | - Skill::ReflectPlus | - Skill::ReflectPlusPlus => reflect(self, game), + Skill::Restrict => restrict(self, game, Restrict::Base), + Skill::RestrictPlus => restrict(self, game, Restrict::Plus), + Skill::RestrictPlusPlus => restrict(self, game, Restrict::PlusPlus), - Skill::Restrict | - Skill::RestrictPlus | - Skill::RestrictPlusPlus => restrict(self, game), + Skill::Ruin => ruin(self, game, Ruin::Base), + Skill::RuinPlus => ruin(self, game, Ruin::Plus), + Skill::RuinPlusPlus => ruin(self, game, Ruin::PlusPlus), + Skill::Siphon => siphon(self, game, Siphon::Base), + Skill::SiphonPlus => siphon(self, game, Siphon::Plus), + Skill::SiphonPlusPlus => siphon(self, game, Siphon::PlusPlus), + Skill::SiphonTick => siphon_tick(self, game), - Skill::Ruin | - Skill::RuinPlus | - Skill::RuinPlusPlus => ruin(self, game), + Skill::Slay => slay(self, game, Slay::Base), + Skill::SlayPlus => slay(self, game, Slay::Plus), + Skill::SlayPlusPlus => slay(self, game, Slay::PlusPlus), + Skill::Sleep => sleep(self, game, Sleep::Base), + Skill::SleepPlus => sleep(self, game, Sleep::Plus), + Skill::SleepPlusPlus => sleep(self, game, Sleep::PlusPlus), - Skill::Siphon => siphon(self, game), - Skill::SiphonPlus => siphon_plus(self, game), - Skill::SiphonPlusPlus => siphon_plus_plus(self, game), - Skill::SiphonTick=> siphon_tick(self, game), + Skill::Silence => silence(self, game, Silence::Base), + Skill::SilencePlus => silence(self, game, Silence::Plus), + Skill::SilencePlusPlus => silence(self, game, Silence::PlusPlus), - Skill::Slay | - Skill::SlayPlus | - Skill::SlayPlusPlus => slay(self, game), + Skill::Strike => strike(self, game, Strike::Base), + Skill::StrikePlus => strike(self, game, Strike::Plus), + Skill::StrikePlusPlus => strike(self, game, Strike::PlusPlus), - Skill::Sleep => sleep(self, game), - Skill::SleepPlus => sleep_plus(self, game), - Skill::SleepPlusPlus => sleep_plus_plus(self, game), + Skill::Sustain => sustain(self, game, Sustain::Base), + Skill::SustainPlus => sustain(self, game, Sustain::Plus), + Skill::SustainPlusPlus => sustain(self, game, Sustain::PlusPlus), - Skill::Silence | - Skill::SilencePlus | - Skill::SilencePlusPlus => silence(self, game), + Skill::Triage => triage(self, game, Triage::Base), + Skill::TriagePlus => triage(self, game, Triage::Plus), + Skill::TriagePlusPlus => triage(self, game, Triage::PlusPlus), - Skill::Strike | - Skill::StrikePlus | - Skill::StrikePlusPlus => strike(self, game), - - Skill::Sustain | - Skill::SustainPlus | - Skill::SustainPlusPlus => sustain(self, game), - - Skill::Triage => triage(self, game), - Skill::TriagePlus => triage_plus(self, game), - Skill::TriagePlusPlus => triage_plus_plus(self, game), Skill::TriageTick => triage_tick(self, game), }; @@ -439,287 +435,6 @@ pub enum Skill { } 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::Siphon => 25, // GB - Skill::SiphonPlus => 27, - Skill::SiphonPlusPlus => 30, - - Skill::Slay => 40, // RG - Skill::SlayPlus => 50, - Skill::SlayPlusPlus => 65, - - Skill::Strike => 90, //RR - Skill::StrikePlus => 110, - Skill::StrikePlusPlus => 140, - - // Block Base - Skill::Electrocute => 80, - Skill::ElectrocutePlus => 90, - Skill::ElectrocutePlusPlus => 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::Decay => 33, - Skill::DecayPlus => 37, - Skill::DecayPlusPlus => 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::Triage => 75, - Skill::TriagePlus => 90, - Skill::TriagePlusPlus => 110, - - _ => 100, - } - } - - pub fn effect(&self) -> Vec { - match self { - // Modifiers - Skill::Amplify => vec![ConstructEffect { effect: Effect::Amplify, duration: 2, - meta: Some(EffectMeta::Multiplier(150)) }], - Skill::AmplifyPlus => vec![ConstructEffect { effect: Effect::Amplify, duration: 3, - meta: Some(EffectMeta::Multiplier(175)) }], - Skill::AmplifyPlusPlus => vec![ConstructEffect { effect: Effect::Amplify, duration: 4, - meta: Some(EffectMeta::Multiplier(200)) }], - - Skill::Banish => vec![ConstructEffect { effect: Effect::Banish, duration: 2, meta: None }], - Skill::BanishPlus => vec![ConstructEffect { effect: Effect::Banish, duration: 2, meta: None }], - Skill::BanishPlusPlus => vec![ConstructEffect { effect: Effect::Banish, duration: 2, meta: None }], - - Skill::Block => vec![ConstructEffect { effect: Effect::Block, duration: 1, - meta: Some(EffectMeta::Multiplier(35)) }], - Skill::Buff => vec![ConstructEffect { effect: Effect::Buff, duration: 3, - meta: Some(EffectMeta::Multiplier(130)) }], - - Skill::Electrify => vec![ConstructEffect { effect: Effect::Electric, duration: 1, - meta: Some(EffectMeta::CastOnHit(Skill::Electrocute)) }], - Skill::ElectrifyPlus => vec![ConstructEffect { effect: Effect::Electric, duration: 1, - meta: Some(EffectMeta::CastOnHit(Skill::ElectrocutePlus)) }], - Skill::ElectrifyPlusPlus => vec![ConstructEffect { effect: Effect::Electric, duration: 1, - meta: Some(EffectMeta::CastOnHit(Skill::ElectrocutePlusPlus)) }], - Skill::Electrocute => vec![ConstructEffect { effect: Effect::Electrocute, duration: 2, - meta: Some(EffectMeta::CastOnHit(Skill::ElectrocuteTick)) }], - Skill::ElectrocutePlus => vec![ConstructEffect { effect: Effect::Electrocute, duration: 3, - meta: Some(EffectMeta::CastOnHit(Skill::ElectrocuteTick)) }], - Skill::ElectrocutePlusPlus => vec![ConstructEffect { effect: Effect::Electrocute, duration: 4, - meta: Some(EffectMeta::CastOnHit(Skill::ElectrocuteTick)) }], - - Skill::Sustain => vec![ConstructEffect { effect: Effect::Sustain, duration: 1, meta: None }], - Skill::SustainPlus => vec![ConstructEffect { effect: Effect::Sustain, duration: 1, meta: None }], - Skill::SustainPlusPlus => vec![ConstructEffect { effect: Effect::Sustain, duration: 1, meta: None }], - - Skill::Curse => vec![ConstructEffect { effect: Effect::Curse, duration: 2, - meta: Some(EffectMeta::Multiplier(150)) }], - Skill::CursePlus => vec![ConstructEffect { effect: Effect::Curse, duration: 2, - meta: Some(EffectMeta::Multiplier(175)) }], - Skill::CursePlusPlus => vec![ConstructEffect { effect: Effect::Curse, duration: 3, - meta: Some(EffectMeta::Multiplier(200)) }], - - Skill::Debuff => vec![ConstructEffect { effect: Effect::Slow, duration: 3, - meta: Some(EffectMeta::Multiplier(50)) }], - - Skill::Decay => vec![ConstructEffect { effect: Effect::Wither, duration: 3, - meta: Some(EffectMeta::Multiplier(50)) }, - ConstructEffect { effect: Effect::Decay, duration: 3, - meta: Some(EffectMeta::CastOnHit(Skill::DecayTick)) }], - Skill::DecayPlus => vec![ConstructEffect { effect: Effect::Wither, duration: 3, - meta: Some(EffectMeta::Multiplier(35)) }, - ConstructEffect { effect: Effect::Decay, duration: 3, - meta: Some(EffectMeta::CastOnHit(Skill::DecayTick)) }], - Skill::DecayPlusPlus => vec![ConstructEffect { effect: Effect::Wither, duration: 4, - meta: Some(EffectMeta::Multiplier(20)) }, - ConstructEffect { effect: Effect::Decay, duration: 4, - meta: Some(EffectMeta::CastOnHit(Skill::DecayTick)) }], - - Skill::Haste => vec![ConstructEffect { effect: Effect::Haste, duration: 3, - meta: Some(EffectMeta::Multiplier(150)) }], - Skill::HastePlus => vec![ConstructEffect { effect: Effect::Haste, duration: 4, - meta: Some(EffectMeta::Multiplier(175)) }], - Skill::HastePlusPlus => vec![ConstructEffect { effect: Effect::Haste, duration: 5, - meta: Some(EffectMeta::Multiplier(225)) }], - - Skill::Absorb => vec![ConstructEffect { effect: Effect::Absorb, duration: 1, - meta: Some(EffectMeta::CastOnHit(Skill::Absorption)) }], - Skill::AbsorbPlus => vec![ConstructEffect { effect: Effect::Absorb, duration: 1, - meta: Some(EffectMeta::CastOnHit(Skill::AbsorptionPlus)) }], - Skill::AbsorbPlusPlus => vec![ConstructEffect { effect: Effect::Absorb, duration: 1, - meta: Some(EffectMeta::CastOnHit(Skill::AbsorptionPlusPlus)) }], - - Skill::Absorption => vec![ConstructEffect { effect: Effect::Absorption, duration: 3, meta: None }], - Skill::AbsorptionPlus => vec![ConstructEffect { effect: Effect::Absorption, duration: 4, meta: None }], - Skill::AbsorptionPlusPlus => vec![ConstructEffect { effect: Effect::Absorption, duration: 5, meta: None }], - - Skill::Hybrid => vec![ConstructEffect { effect: Effect::Hybrid, duration: 3, - meta: Some(EffectMeta::Multiplier(150)) }], - Skill::HybridPlus => vec![ConstructEffect { effect: Effect::Hybrid, duration: 4, - meta: Some(EffectMeta::Multiplier(175)) }], - Skill::HybridPlusPlus => vec![ConstructEffect { effect: Effect::Hybrid, duration: 5, - meta: Some(EffectMeta::Multiplier(200)) }], - - Skill::Invert => vec![ConstructEffect { effect: Effect::Invert, duration: 2, meta: None }], - Skill::InvertPlus => vec![ConstructEffect { effect: Effect::Invert, duration: 3, meta: None }], - Skill::InvertPlusPlus => vec![ConstructEffect { effect: Effect::Invert, duration: 4, meta: None }], - - Skill::Counter => vec![ConstructEffect { effect: Effect::Counter, duration: 1, - meta: Some(EffectMeta::CastOnHit(Skill::CounterAttack)) }], - Skill::CounterPlus => vec![ConstructEffect { effect: Effect::Counter, duration: 1, - meta: Some(EffectMeta::CastOnHit(Skill::CounterAttackPlus)) }], - Skill::CounterPlusPlus => vec![ConstructEffect { effect: Effect::Counter, duration: 1, - meta: Some(EffectMeta::CastOnHit(Skill::CounterAttackPlusPlus)) }], - - Skill::Reflect => vec![ConstructEffect { effect: Effect::Reflect, duration: 1, meta: None }], - Skill::ReflectPlus => vec![ConstructEffect { effect: Effect::Reflect, duration: 1, meta: None }], - Skill::ReflectPlusPlus => vec![ConstructEffect { effect: Effect::Reflect, duration: 1, meta: None }], - - Skill::Break => vec![ConstructEffect { effect: Effect::Stun, duration: 1, meta: None }, - ConstructEffect { effect: Effect::Vulnerable, duration: 3, - meta: Some(EffectMeta::Multiplier(150)) }], - Skill::BreakPlus => vec![ConstructEffect { effect: Effect::Stun, duration: 1, meta: None }, - ConstructEffect { effect: Effect::Vulnerable, duration: 4, - meta: Some(EffectMeta::Multiplier(175)) }], - Skill::BreakPlusPlus => vec![ConstructEffect { effect: Effect::Stun, duration: 2, meta: None }, - ConstructEffect { effect: Effect::Vulnerable, duration: 4, - meta: Some(EffectMeta::Multiplier(200)) }], - - Skill::Ruin => vec![ConstructEffect { effect: Effect::Stun, duration: 1, meta: None }], - Skill::RuinPlus => vec![ConstructEffect { effect: Effect::Stun, duration: 1, meta: None }], - Skill::RuinPlusPlus => vec![ConstructEffect { effect: Effect::Stun, duration: 1, meta: None }], - - Skill::Purge => vec![ConstructEffect { effect: Effect::Purge, duration: 2, meta: None }], - Skill::PurgePlus => vec![ConstructEffect { effect: Effect::Purge, duration: 3, meta: None }], - Skill::PurgePlusPlus => vec![ConstructEffect { effect: Effect::Purge, duration: 4, meta: None }], - - Skill::Link => vec![ConstructEffect { effect: Effect::Stun, duration: 1, meta: None }], - Skill::LinkPlus => vec![ConstructEffect { effect: Effect::Stun, duration: 1, meta: None }], - Skill::LinkPlusPlus => vec![ConstructEffect { effect: Effect::Stun, duration: 1, meta: None }], - - Skill::Silence => vec![ConstructEffect { effect: Effect::Silence, duration: 2, meta: None }], - Skill::SilencePlus => vec![ConstructEffect { effect: Effect::Silence, duration: 2, meta: None }], - Skill::SilencePlusPlus => vec![ConstructEffect { effect: Effect::Silence, duration: 2, meta: None }], - - Skill::Siphon => vec![ConstructEffect { effect: Effect::Siphon, duration: 2, - meta: Some(EffectMeta::CastOnHit(Skill::SiphonTick)) }], - Skill::SiphonPlus => vec![ConstructEffect { effect: Effect::Siphon, duration: 3, - meta: Some(EffectMeta::CastOnHit(Skill::SiphonTick)) }], - Skill::SiphonPlusPlus => vec![ConstructEffect { effect: Effect::Siphon, duration: 4, - meta: Some(EffectMeta::CastOnHit(Skill::SiphonTick)) }], - - Skill::Sleep => vec![ConstructEffect { effect: Effect::Stun, duration: 2, meta: None }], - Skill::SleepPlus => vec![ConstructEffect { effect: Effect::Stun, duration: 3, meta: None }], - Skill::SleepPlusPlus => vec![ConstructEffect { effect: Effect::Stun, duration: 4, meta: None }], - - Skill::Restrict => vec![ConstructEffect { effect: Effect::Restrict, duration: 2, meta: None }], - Skill::RestrictPlus => vec![ConstructEffect { effect: Effect::Restrict, duration: 2, meta: None }], - Skill::RestrictPlusPlus => vec![ConstructEffect { effect: Effect::Restrict, duration: 2, meta: None }], - - Skill::Bash => vec![ConstructEffect { effect: Effect::Stun, duration: 2, - meta: Some(EffectMeta::CastOnHit(Skill::Bash)) }], - Skill::BashPlus => vec![ConstructEffect { effect: Effect::Stun, duration: 2, - meta: Some(EffectMeta::CastOnHit(Skill::BashPlus)) }], - Skill::BashPlusPlus => vec![ConstructEffect { effect: Effect::Stun, duration: 2, - meta: Some(EffectMeta::CastOnHit(Skill::BashPlusPlus)) }], - Skill::Stun => vec![ConstructEffect { effect: Effect::Stun, duration: 2, meta: None }], - - Skill::Intercept => vec![ConstructEffect { effect: Effect::Intercept, duration: 1, meta: None }], - Skill::InterceptPlus => vec![ConstructEffect { effect: Effect::Intercept, duration: 1, meta: None }], - Skill::InterceptPlusPlus => vec![ConstructEffect { effect: Effect::Intercept, duration: 1, meta: None }], - - Skill::Triage => vec![ConstructEffect { effect: Effect::Triage, duration: 2, - meta: Some(EffectMeta::CastOnHit(Skill::TriageTick)) }], - Skill::TriagePlus => vec![ConstructEffect { effect: Effect::Triage, duration: 3, - meta: Some(EffectMeta::CastOnHit(Skill::TriageTick)) }], - Skill::TriagePlusPlus => vec![ConstructEffect { effect: Effect::Triage, duration: 4, - meta: Some(EffectMeta::CastOnHit(Skill::TriageTick)) }], - - Skill::Purify => vec![ConstructEffect { effect: Effect::Pure, duration: 2, - meta: Some(EffectMeta::Multiplier(150)) }], - Skill::PurifyPlus => vec![ConstructEffect { effect: Effect::Pure, duration: 2, - meta: Some(EffectMeta::Multiplier(175)) }], - Skill::PurifyPlusPlus => vec![ConstructEffect { effect: Effect::Pure, duration: 2, - meta: Some(EffectMeta::Multiplier(200)) }], - - _ => { - panic!("{:?} no skill effect", self); - }, - } - } - pub fn base_cd(&self) -> Cooldown { match self { Skill::Attack => None, @@ -1037,130 +752,184 @@ impl Skill { } } -fn attack(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Attack { Base } +impl Attack { + fn dmg_multiplier(self) -> usize { + match self { Attack::Base => 80 } + } +} + +fn attack(cast: Cast, game: &mut Game, values: Attack) { game.action( cast, Action::Damage { construct: cast.target, colour: Colour::Red, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(cast.skill.multiplier()), + amount: game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(values.dmg_multiplier()), }, ); } -fn block(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Block { Base } +impl Block { + fn damage_reduction(self) -> usize { + match self { Block::Base => 35 } + } + fn duration(self) -> u8 { + match self { Block::Base => 1 } + } +} + +fn block(cast: Cast, game: &mut Game, values: Block) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Block, duration: 1, meta: Some(EffectMeta::Multiplier(35)) }, + effect: ConstructEffect { + effect: Effect::Block, + duration: values.duration(), + meta: Some(EffectMeta::Multiplier(values.damage_reduction())) + }, }, ); } -fn buff(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Buff { Base } +impl Buff { + fn multiplier(self) -> usize { // RedPower / BluePower / Speed + match self { Buff::Base => 130 } + } + fn duration(self) -> u8 { + match self { Buff::Base => 3 } + } +} + +fn buff(cast: Cast, game: &mut Game, values: Buff) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Buff, duration: 3, meta: Some(EffectMeta::Multiplier(130)) }, + effect: ConstructEffect { + effect: Effect::Buff, + duration: values.duration(), + meta: Some(EffectMeta::Multiplier(values.multiplier())) + }, }, ); } -fn debuff(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Debuff { Base } +impl Debuff { + fn multiplier(self) -> usize { // Speed + match self { Debuff::Base => 50 } + } + fn duration(self) -> u8 { + match self { Debuff::Base => 3 } + } +} + +fn debuff(cast: Cast, game: &mut Game, values: Debuff) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Slow, duration: 3, meta: Some(EffectMeta::Multiplier(50)) }, + effect: ConstructEffect { + effect: Effect::Slow, + duration: values.duration(), + meta: Some(EffectMeta::Multiplier(values.multiplier())) + }, }, ); } -fn stun(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Stun { Base } +impl Stun { + fn duration(self) -> u8 { + match self { Stun::Base => 2 } + } +} + +fn stun(cast: Cast, game: &mut Game, values: Stun) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Stun, duration: 2, meta: None }, + effect: ConstructEffect { effect: Effect::Stun, duration: values.duration(), meta: None }, }, ); } +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Amplify { Base, Plus, PlusPlus } +impl Amplify { + fn multiplier(self) -> usize { // Blue and Red Power + match self { Amplify::Base => 150, Amplify::Plus => 175, Amplify::PlusPlus => 200 } + } + fn duration(self) -> u8 { + match self { Amplify::Base => 2, Amplify::Plus => 3, Amplify::PlusPlus => 4 } + } +} -fn amplify(cast: Cast, game: &mut Game) { +fn amplify(cast: Cast, game: &mut Game, values: Amplify) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Amplify, duration: 2, meta: Some(EffectMeta::Multiplier(150)) }, + effect: ConstructEffect { + effect: Effect::Amplify, + duration: values.duration(), + meta: Some(EffectMeta::Multiplier(values.multiplier())) + }, }, ); } -fn amplify_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Amplify, duration: 3, meta: Some(EffectMeta::Multiplier(175)) }, - }, - ); +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Absorb { Base, Plus, PlusPlus } +impl Absorb { + fn blue_heal_multi(self) -> usize { // Blue and Red Power + match self { Absorb::Base => 95, Absorb::Plus => 110, Absorb::PlusPlus => 140 } + } + fn duration(self) -> u8 { + match self { Absorb::Base => 1, Absorb::Plus => 1, Absorb::PlusPlus => 1 } + } + fn absorption_skill(self) -> Skill { + match self { + Absorb::Base => Skill::Absorption, + Absorb::Plus => Skill::AbsorptionPlus, + Absorb::PlusPlus => Skill::AbsorptionPlusPlus + } + } } -fn amplify_plus_plus(cast: Cast, game: &mut Game) { +fn absorb(cast: Cast, game: &mut Game, values: Absorb) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Amplify, duration: 4, meta: Some(EffectMeta::Multiplier(200)) }, - }, - ); -} - -fn absorb(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Absorb, duration: 1, meta: Some(EffectMeta::CastOnHit(Skill::Absorption)) }, + effect: ConstructEffect { + effect: Effect::Absorb, + duration: values.duration(), + meta: Some(EffectMeta::CastOnHit(values.absorption_skill())) }, } ); game.action(cast, Action::Heal { construct: cast.target, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier()), + amount: game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(values.blue_heal_multi()), colour: Colour::Blue, }, ); } -fn absorb_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Absorb, duration: 1, meta: Some(EffectMeta::CastOnHit(Skill::AbsorptionPlus)) }, - } - ); - game.action(cast, - Action::Heal { - construct: cast.target, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier()), - colour: Colour::Blue, - }, - ); +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Absorption { Base, Plus, PlusPlus } +impl Absorption { + fn duration(self) -> u8 { + match self { Absorption::Base => 3, Absorption::Plus => 4, Absorption::PlusPlus => 5 } + } } -fn absorb_plus_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Absorb, duration: 1, meta: Some(EffectMeta::CastOnHit(Skill::AbsorptionPlusPlus)) }, - } - ); - game.action(cast, - Action::Heal { - construct: cast.target, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier()), - colour: Colour::Blue, - }, - ); -} - -fn absorption(cast: Cast, game: &mut Game) { +fn absorption(cast: Cast, game: &mut Game, values: Absorption) { game.action(cast, Action::Remove { construct: cast.source, @@ -1170,7 +939,7 @@ fn absorption(cast: Cast, game: &mut Game) { game.action(cast, Action::Effect { construct: cast.source, - effect: ConstructEffect { effect: Effect::Absorption, duration: 3, meta: Some(EffectMeta::AddedDamage(0)) }, + effect: ConstructEffect { effect: Effect::Absorption, duration: values.duration(), meta: Some(EffectMeta::AddedDamage(0)) }, } ); game.action(cast, @@ -1182,89 +951,56 @@ fn absorption(cast: Cast, game: &mut Game) { ); } -fn absorption_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Remove { - construct: cast.source, - effect: Effect::Absorb, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Absorption, duration: 4, meta: Some(EffectMeta::AddedDamage(0)) }, - } - ); - game.action(cast, - Action::SetEffectMeta { - construct: cast.target, - effect: Effect::Absorption, - amount: game.value(Value::DamageReceived { construct: cast.target, colour: Colour::Blue }).pct(100), - }, - ); +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Banish { Base, Plus, PlusPlus } +impl Banish { + fn life_multiplier(self) -> usize { + match self { Banish::Base => 40, Banish::Plus => 55, Banish::PlusPlus => 80 } + } + fn duration(self) -> u8 { + match self { Banish::Base => 2, Banish::Plus => 2, Banish::PlusPlus => 2 } + } } -fn absorption_plus_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Remove { - construct: cast.source, - effect: Effect::Absorb, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Absorption, duration: 5, meta: Some(EffectMeta::AddedDamage(0)) }, - } - ); - game.action(cast, - Action::SetEffectMeta { - construct: cast.target, - effect: Effect::Absorption, - amount: game.value(Value::DamageReceived { construct: cast.target, colour: Colour::Blue }), - }, - ); -} - -fn banish(cast: Cast, game: &mut Game) { +fn banish(cast: Cast, game: &mut Game, values: Banish) { game.action(cast, Action::Damage { construct: cast.target, colour: Colour::Red, - amount: game.value(Value::Stat { construct: cast.target, stat: Stat::RedLife }).pct(cast.skill.multiplier()), + amount: game.value(Value::Stat { construct: cast.target, stat: Stat::RedLife }).pct(values.life_multiplier()), } ); game.action(cast, Action::Damage { construct: cast.target, colour: Colour::Blue, - amount: game.value(Value::Stat { construct: cast.target, stat: Stat::BlueLife }).pct(cast.skill.multiplier()), + amount: game.value(Value::Stat { construct: cast.target, stat: Stat::BlueLife }).pct(values.life_multiplier()), } ); game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None } + effect: ConstructEffect { effect: Effect::Banish, duration: values.duration(), meta: None } } ); } -fn bash(cast: Cast, game: &mut Game) { - let cds = game.value(Value::Cooldowns { construct: cast.source }); - let red_power = game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }); - let amount = red_power.pct(cast.skill.multiplier().pct(100 + 45usize.saturating_mul(cds))); +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Bash { Base, Plus, PlusPlus } +impl Bash { + fn damage_multiplier(self) -> usize { + match self { Bash::Base => 45, Bash::Plus => 55, Bash::PlusPlus => 70 } + } + fn duration(self) -> u8 { + match self { Bash::Base => 2, Bash::Plus => 2, Bash::PlusPlus => 2 } + } +} - game.action(cast, - Action::Damage { - construct: cast.target, - colour: Colour::Red, - amount, - } - ); +fn bash(cast: Cast, game: &mut Game, values: Bash) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Stun, duration: 2, meta: None } + effect: ConstructEffect { effect: Effect::Stun, duration: values.duration(), meta: None } } ); game.action(cast, @@ -1273,100 +1009,121 @@ fn bash(cast: Cast, game: &mut Game) { turns: 1, }, ); + + let cds = game.value(Value::Cooldowns { construct: cast.source }); + let red_power = game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }); + let amount = red_power.pct(values.damage_multiplier().pct(100 + 45usize.saturating_mul(cds))); + + game.action(cast, + Action::Damage { + construct: cast.target, + colour: Colour::Red, + amount, + } + ); } -fn blast(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Blast { Base, Plus, PlusPlus, Hybrid } +impl Blast { + fn dmg_multi(self) -> usize { + match self { + Blast::Base => 105, + Blast::Plus => 125, + Blast::PlusPlus => 145, + Blast::Hybrid => 50, + } + } +} + +fn blast(cast: Cast, game: &mut Game, values: Blast) { + let amount = match values { + Blast::Hybrid => game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(values.dmg_multi()), + _ => game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(values.dmg_multi()) + }; + game.action(cast, Action::Damage { construct: cast.target, colour: Colour::Blue, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier()), + amount, }, ); } -fn break_zero(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Break { Base, Plus, PlusPlus } +impl Break { + fn stun_duration(self) -> u8 { + match self { Break::Base => 1, Break::Plus => 1, Break::PlusPlus => 2 } + } + fn vulnerable_duration(self) -> u8 { + match self { Break::Base => 3, Break::Plus => 4, Break::PlusPlus => 5 } + } + fn vulnerable_multiplier(self) -> usize { + match self { Break::Base => 150, Break::Plus => 175, Break::PlusPlus => 200 } + } +} + +fn break_fn(cast: Cast, game: &mut Game, values: Break) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Stun, duration: 1, meta: None }, + effect: ConstructEffect { effect: Effect::Stun, duration: values.stun_duration(), meta: None }, } ); game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Vulnerable, duration: 3, meta: Some(EffectMeta::Multiplier(150)), }, + effect: ConstructEffect { + effect: Effect::Vulnerable, + duration: values.vulnerable_duration(), + meta: Some(EffectMeta::Multiplier(values.vulnerable_multiplier())) + }, }, ); } -fn break_plus(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Curse { Base, Plus, PlusPlus } +impl Curse { + fn curse_duration(self) -> u8 { + match self { Curse::Base => 2, Curse::Plus => 2, Curse::PlusPlus => 3 } + } + fn curse_multi(self) -> usize { + match self { Curse::Base => 150, Curse::Plus => 175, Curse::PlusPlus => 200 } + } +} + +fn curse(cast: Cast, game: &mut Game, values: Curse) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Stun, duration: 1, meta: None }, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Vulnerable, duration: 4, meta: Some(EffectMeta::Multiplier(175)), }, + effect: ConstructEffect { + effect: Effect::Curse, + duration: values.curse_duration(), + meta: Some(EffectMeta::Multiplier(values.curse_multi())) + }, }, ); } -fn break_plus_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Stun, duration: 2, meta: None }, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Vulnerable, duration: 4, meta: Some(EffectMeta::Multiplier(200)), }, - }, - ); +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Chaos { Base, Plus, PlusPlus } +impl Chaos { + fn dmg_multi(self) -> usize { + match self { Chaos::Base => 40, Chaos::Plus => 50, Chaos::PlusPlus => 65 } + } } - -fn curse(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Curse, duration: 2, meta: Some(EffectMeta::Multiplier(150)) }, - }, - ); -} - -fn curse_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Curse, duration: 2, meta: Some(EffectMeta::Multiplier(175)) }, - }, - ); -} - -fn curse_plus_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Curse, duration: 3, meta: Some(EffectMeta::Multiplier(200)) }, - }, - ); -} - - -fn chaos(cast: Cast, game: &mut Game) { +fn chaos(cast: Cast, game: &mut Game, values: Chaos) { let mut rng = thread_rng(); game.action(cast, Action::Damage { construct: cast.target, colour: Colour::Red, amount: - game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(cast.skill.multiplier() + rng.gen_range(0, 30)) + game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(values.dmg_multi().pct(rng.gen_range(100, 130))) } ); game.action(cast, @@ -1374,39 +1131,48 @@ fn chaos(cast: Cast, game: &mut Game) { construct: cast.target, colour: Colour::Blue, amount: - game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier() + rng.gen_range(0, 30)) + game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(values.dmg_multi().pct(rng.gen_range(100, 130))) }, ); } -fn counter(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Counter { Base, Plus, PlusPlus } +impl Counter { + fn duration(self) -> u8 { + match self { Counter::Base => 1, Counter::Plus => 1, Counter::PlusPlus => 2 } + } + fn counter_skill(self) -> Skill { + match self { + Counter::Base => Skill::CounterAttack, + Counter::Plus => Skill::CounterAttackPlus, + Counter::PlusPlus => Skill::CounterAttackPlusPlus + } + } +} + +fn counter(cast: Cast, game: &mut Game, values: Counter) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Counter, duration: 1, meta: Some(EffectMeta::CastOnHit(Skill::CounterAttack)) }, + effect: ConstructEffect { + effect: Effect::Counter, + duration: values.duration(), + meta: Some(EffectMeta::CastOnHit(values.counter_skill())) + }, }, ); } -fn counter_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Counter, duration: 1, meta: Some(EffectMeta::CastOnHit(Skill::CounterAttackPlus)) }, - }, - ); +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum CounterAttack { Base, Plus, PlusPlus } +impl CounterAttack { + fn dmg_multi(self) -> usize { + match self { CounterAttack::Base => 115, CounterAttack::Plus => 130, CounterAttack::PlusPlus => 160 } + } } -fn counter_plus_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Counter, duration: 1, meta: Some(EffectMeta::CastOnHit(Skill::CounterAttackPlusPlus)) }, - }, - ); -} - -fn counter_attack(cast: Cast, game: &mut Game) { +fn counter_attack(cast: Cast, game: &mut Game, values: CounterAttack) { // effect has to be first so that the loop doesn't occur game.action(cast, Action::Effect { @@ -1418,40 +1184,35 @@ fn counter_attack(cast: Cast, game: &mut Game) { Action::Damage { construct: cast.target, colour: Colour::Red, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(cast.skill.multiplier()), + amount: game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(values.dmg_multi()), }, ); } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -enum Decay { - Decay, - DecayPlus, - DecayPlusPlus, -} - +enum Decay { Base, Plus, PlusPlus } impl Decay { pub fn decay_dmg_multiplier(self) -> usize { match self { - Decay::Decay => 33, Decay::DecayPlus => 37, Decay::DecayPlusPlus => 45, + Decay::Base => 33, Decay::Plus => 37, Decay::PlusPlus => 45, } } pub fn decay_duration(self) -> u8 { match self { - Decay::Decay => 3, Decay::DecayPlus => 4, Decay::DecayPlusPlus => 5, + Decay::Base => 3, Decay::Plus => 4, Decay::PlusPlus => 5, } } pub fn wither_multiplier(self) -> usize { match self { - Decay::Decay => 50, Decay::DecayPlus => 35, Decay::DecayPlusPlus => 20, + Decay::Base => 50, Decay::Plus => 35, Decay::PlusPlus => 20, } } pub fn wither_duration(self) -> u8 { match self { - Decay::Decay => 3, Decay::DecayPlus => 4, Decay::DecayPlusPlus => 5, + Decay::Base => 3, Decay::Plus => 4, Decay::PlusPlus => 5, } } } @@ -1508,35 +1269,41 @@ fn decay_tick(cast: Cast, game: &mut Game) { ); } -fn electrify(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Electrify { Base, Plus, PlusPlus } +impl Electrify { + pub fn electrocute(self) -> Skill { + match self { + Electrify::Base => Skill::Electrocute, + Electrify::Plus => Skill::ElectrocutePlus, + Electrify::PlusPlus => Skill::ElectrocutePlusPlus, + } + } +} + +fn electrify(cast: Cast, game: &mut Game, values: Electrify) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Electric, duration: 1, meta: Some(EffectMeta::CastOnHit(Skill::Electrocute)) }, + effect: ConstructEffect { effect: Effect::Electric, duration: 1, meta: Some(EffectMeta::CastOnHit(values.electrocute())) }, }, ); } -fn electrify_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Electric, duration: 1, meta: Some(EffectMeta::CastOnHit(Skill::ElectrocutePlus)) }, - }, - ); +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Electrocute { Base, Plus, PlusPlus } +impl Electrocute { + pub fn damage_multiplier(self) -> usize { + match self { Electrocute::Base => 80, Electrocute::Plus => 90, Electrocute::PlusPlus => 100 } + } + + pub fn duration(self) -> u8 { + match self { Electrocute::Base => 2, Electrocute::Plus => 3, Electrocute::PlusPlus => 4 } + } } -fn electrify_plus_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Electric, duration: 1, meta: Some(EffectMeta::CastOnHit(Skill::ElectrocutePlusPlus)) }, - }, - ); -} - -fn electrocute(cast: Cast, game: &mut Game) { - let amount = game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier()); +fn electrocute(cast: Cast, game: &mut Game, values: Electrocute) { + let amount = game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(values.damage_multiplier()); game.action(cast, Action::Remove { @@ -1547,42 +1314,7 @@ fn electrocute(cast: Cast, game: &mut Game) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Electric, duration: 2, meta: - Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::ElectrocuteTick, speed: cast.speed, amount }) }, - }, - ); -} -fn electrocute_plus(cast: Cast, game: &mut Game) { - let amount = game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier()); - - game.action(cast, - Action::Remove { - construct: cast.source, - effect: Effect::Electric, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Electric, duration: 3, meta: - Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::ElectrocuteTick, speed: cast.speed, amount }) }, - }, - ); -} - -fn electrocute_plus_plus(cast: Cast, game: &mut Game) { - let amount = game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier()); - - game.action(cast, - Action::Remove { - construct: cast.source, - effect: Effect::Electric, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Electric, duration: 4, meta: + effect: ConstructEffect { effect: Effect::Electric, duration: values.duration(), meta: Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::ElectrocuteTick, speed: cast.speed, amount }) }, }, ); @@ -1598,134 +1330,127 @@ fn electrocute_tick(cast: Cast, game: &mut Game) { ); } -fn heal(cast: Cast, game: &mut Game) { +enum Heal { Base, Plus, PlusPlus } +impl Heal { + pub fn heal_multiplier(self) -> usize { + match self { Heal::Base => 115, Heal::Plus => 135, Heal::PlusPlus => 160 } + } +} + +fn heal(cast: Cast, game: &mut Game, values: Heal) { game.action(cast, Action::Heal { construct: cast.target, colour: Colour::Green, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(cast.skill.multiplier()), + amount: game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(values.heal_multiplier()), }, ); } -fn haste(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Haste { Base, Plus, PlusPlus } +impl Haste { + pub fn speed_multi(self) -> usize { + match self { Haste::Base => 150, Haste::Plus => 175, Haste::PlusPlus => 225 } + } + + pub fn duration(self) -> u8 { + match self { Haste::Base => 3, Haste::Plus => 3, Haste::PlusPlus => 3 } + } +} + +fn haste(cast: Cast, game: &mut Game, values: Haste) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Haste, duration: 3, meta: Some(EffectMeta::Multiplier(150)) }, + effect: ConstructEffect { + effect: Effect::Haste, + duration: values.duration(), + meta: Some(EffectMeta::Multiplier(values.speed_multi())) + }, }, ); } -fn haste_plus(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Hybrid { Base, Plus, PlusPlus } +impl Hybrid { + pub fn green_multi(self) -> usize { + match self { Hybrid::Base => 150, Hybrid::Plus => 175, Hybrid::PlusPlus => 200 } + } + + pub fn duration(self) -> u8 { + match self { Hybrid::Base => 3, Hybrid::Plus => 4, Hybrid::PlusPlus => 5 } + } +} + +fn hybrid(cast: Cast, game: &mut Game, values: Hybrid) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Haste, duration: 3, meta: Some(EffectMeta::Multiplier(175)) }, + effect: ConstructEffect { + effect: Effect::Hybrid, + duration: values.duration(), + meta: Some(EffectMeta::Multiplier(values.green_multi())) + }, }, ); } -fn haste_plus_plus(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Intercept { Base, Plus, PlusPlus } +impl Intercept { + pub fn red_heal(self) -> usize { + match self { Intercept::Base => 85, Intercept::Plus => 100, Intercept::PlusPlus => 125 } + } + + pub fn duration(self) -> u8 { + match self { Intercept::Base => 1, Intercept::Plus => 1, Intercept::PlusPlus => 1 } + } +} + +fn intercept(cast: Cast, game: &mut Game, values: Intercept) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Haste, duration: 3, meta: Some(EffectMeta::Multiplier(225)) }, - }, - ); -} - -fn haste_strike(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Damage { - construct: cast.target, - colour: Colour::Red, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::Speed }).pct(cast.skill.multiplier()), - }, - ); -} - -fn hybrid(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Hybrid, duration: 3, meta: Some(EffectMeta::Multiplier(150)) }, - }, - ); -} - -fn hybrid_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Hybrid, duration: 4, meta: Some(EffectMeta::Multiplier(175)) }, - }, - ); -} - -fn hybrid_plus_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Hybrid, duration: 5, meta: Some(EffectMeta::Multiplier(200)) }, - }, - ); -} - -fn hybrid_blast(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Damage { - construct: cast.target, - colour: Colour::Blue, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(cast.skill.multiplier()), - }, - ); -} - -fn intercept(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Intercept, duration: 1, meta: None }, + effect: ConstructEffect { effect: Effect::Intercept, duration: values.duration(), meta: None }, } ); game.action(cast, Action::Heal { construct: cast.target, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(cast.skill.multiplier()), + amount: game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(values.red_heal()), colour: Colour::Red, }, ); } -fn invert(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Invert { Base, Plus, PlusPlus } +impl Invert { + pub fn duration(self) -> u8 { + match self { Invert::Base => 2, Invert::Plus => 3, Invert::PlusPlus => 4 } + } +} + +fn invert(cast: Cast, game: &mut Game, values: Invert) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Invert, duration: 2, meta: None }, + effect: ConstructEffect { effect: Effect::Invert, duration: values.duration(), meta: None }, }, ); } -fn invert_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Invert, duration: 3, meta: None }, - }, - ); +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Link { Base, Plus, PlusPlus } +impl Link { + pub fn blue_dmg_base(self) -> usize { + match self { Link::Base => 25, Link::Plus => 35, Link::PlusPlus => 45 } + } } -fn invert_plus_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Invert, duration: 4, meta: None }, - }, - ); -} - -fn link(cast: Cast, game: &mut Game) { +fn link(cast: Cast, game: &mut Game, values: Link) { game.action(cast, Action::Effect { construct: cast.target, @@ -1736,12 +1461,20 @@ fn link(cast: Cast, game: &mut Game) { Action::Damage { construct: cast.target, colour: Colour::Blue, - amount: game.value(Value::Effects { construct: cast.target }).pct(cast.skill.multiplier()), + amount: game.value(Value::Effects { construct: cast.target }).pct(values.blue_dmg_base()), }, ); } -fn purge(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Purge { Base, Plus, PlusPlus } +impl Purge { + pub fn duration(self) -> u8 { + match self { Purge::Base => 2, Purge::Plus => 3, Purge::PlusPlus => 4 } + } +} + +fn purge(cast: Cast, game: &mut Game, values: Purge) { game.action(cast, Action::RemoveAll { construct: cast.target, @@ -1750,43 +1483,31 @@ fn purge(cast: Cast, game: &mut Game) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Purge, duration: 2, meta: None } + effect: ConstructEffect { effect: Effect::Purge, duration: values.duration(), meta: None } }, ); } -fn purge_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::RemoveAll { - construct: cast.target, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Purge, duration: 3, meta: None } - }, - ); +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Purify { Base, Plus, PlusPlus } +impl Purify { + pub fn green_heal_base(self) -> usize { + match self { Purify::Base => 45, Purify::Plus => 60, Purify::PlusPlus => 85 } + } + + pub fn duration(self) -> u8 { + match self { Purify::Base => 2, Purify::Plus => 3, Purify::PlusPlus => 4 } + } + + pub fn pure_multi(self) -> usize { + match self { Purify::Base => 150, Purify::Plus => 175, Purify::PlusPlus => 200 } + } } -fn purge_plus_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::RemoveAll { - construct: cast.target, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Purge, duration: 4, meta: None } - }, - ); -} - -fn purify(cast: Cast, game: &mut Game) { +fn purify(cast: Cast, game: &mut Game, values: Purify) { let gp = game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }); let rms = game.value(Value::Removals { construct: cast.target }); - let amount = gp.pct(cast.skill.multiplier().saturating_mul(rms)); + let amount = gp.pct(values.green_heal_base().saturating_mul(rms)); game.action(cast, Action::RemoveAll { @@ -1803,79 +1524,49 @@ fn purify(cast: Cast, game: &mut Game) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Pure, duration: 2, meta: Some(EffectMeta::Multiplier(150)) } + effect: ConstructEffect { + effect: Effect::Pure, + duration: values.duration(), + meta: Some(EffectMeta::Multiplier(values.pure_multi())) + } }, ); } -fn purify_plus(cast: Cast, game: &mut Game) { - let gp = game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }); - let rms = game.value(Value::Removals { construct: cast.target }); - let amount = gp.pct(cast.skill.multiplier().saturating_mul(rms)); - - game.action(cast, - Action::RemoveAll { - construct: cast.target, - } - ); - game.action(cast, - Action::Heal { - construct: cast.target, - colour: Colour::Green, - amount, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Pure, duration: 2, meta: Some(EffectMeta::Multiplier(175)) } - }, - ); +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Recharge { Base, Plus, PlusPlus } +impl Recharge { + pub fn heal_multi(self) -> usize { + match self { Recharge::Base => 45, Recharge::Plus => 60, Recharge::PlusPlus => 85 } + } } -fn purify_plus_plus(cast: Cast, game: &mut Game) { - let gp = game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }); - let rms = game.value(Value::Removals { construct: cast.target }); - let amount = gp.pct(cast.skill.multiplier().saturating_mul(rms)); - - game.action(cast, - Action::RemoveAll { - construct: cast.target, - } - ); - game.action(cast, - Action::Heal { - construct: cast.target, - colour: Colour::Green, - amount, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Pure, duration: 2, meta: Some(EffectMeta::Multiplier(200)) } - }, - ); -} - -fn recharge(cast: Cast, game: &mut Game) { +fn recharge(cast: Cast, game: &mut Game, values: Recharge) { game.action(cast, Action::Heal { construct: cast.target, colour: Colour::Red, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(cast.skill.multiplier()), + amount: game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(values.heal_multi()), } ); game.action(cast, Action::Heal { construct: cast.target, colour: Colour::Blue, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier()), + amount: game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(values.heal_multi()), }, ); } -fn reflect(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Reflect { Base, Plus, PlusPlus } +impl Reflect { + pub fn heal_multi(self) -> usize { + match self { Reflect::Base => 45, Reflect::Plus => 70, Reflect::PlusPlus => 100 } + } +} + +fn reflect(cast: Cast, game: &mut Game, values: Reflect) { game.action(cast, Action::Effect { construct: cast.target, @@ -1885,123 +1576,94 @@ fn reflect(cast: Cast, game: &mut Game) { game.action(cast, Action::Heal { construct: cast.target, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier()), + amount: game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(values.heal_multi()), colour: Colour::Blue, }, ); } -fn restrict(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Restrict { Base, Plus, PlusPlus } +impl Restrict { + pub fn dmg_multi(self) -> usize { + match self { Restrict::Base => 40, Restrict::Plus => 55, Restrict::PlusPlus => 70 } + } + + pub fn duration(self) -> u8 { + match self { Restrict::Base => 2, Restrict::Plus => 2, Restrict::PlusPlus => 3 } + } +} + +fn restrict(cast: Cast, game: &mut Game, values: Restrict) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Restrict, duration: 2, meta: None } + effect: ConstructEffect { effect: Effect::Restrict, duration: values.duration(), meta: None } } ); game.action(cast, Action::Damage { construct: cast.target, colour: Colour::Red, - amount: game.value(Value::ColourSkills { construct: cast.source, colour: Colour::Red }).pct(cast.skill.multiplier()), + amount: game.value(Value::ColourSkills { construct: cast.source, colour: Colour::Red }).pct(values.dmg_multi()), }, ); } +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Ruin { Base, Plus, PlusPlus } +impl Ruin { + pub fn dmg_multi(self) -> usize { + match self { Ruin::Base => 40, Ruin::Plus => 55, Ruin::PlusPlus => 70 } + } + + pub fn duration(self) -> u8 { + match self { Ruin::Base => 1, Ruin::Plus => 1, Ruin::PlusPlus => 2 } + } +} + +fn ruin(cast: Cast, game: &mut Game, values: Ruin) { + game.action(cast, + Action::Effect { + construct: cast.target, + effect: ConstructEffect { effect: Effect::Stun, duration: values.duration(), meta: None } + } + ); -fn ruin(cast: Cast, game: &mut Game) { game.action(cast, Action::Damage { construct: cast.target, colour: Colour::Blue, - amount: game.value(Value::Stat { construct: cast.target, stat: Stat::BluePower }).pct(cast.skill.multiplier()), - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Stun, duration: 1, meta: None } + amount: game.value(Value::Stat { construct: cast.target, stat: Stat::BluePower }).pct(values.dmg_multi()), } ); } +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Siphon { Base, Plus, PlusPlus } +impl Siphon { + pub fn blue_dmg_multi(self) -> usize { + match self { Siphon::Base => 25, Siphon::Plus => 27, Siphon::PlusPlus => 30 } + } -fn siphon(cast: Cast, game: &mut Game) { - let amount = - game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier()) - + game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(cast.skill.multiplier()); + pub fn green_dmg_multi(self) -> usize { + match self { Siphon::Base => 25, Siphon::Plus => 27, Siphon::PlusPlus => 30 } + } - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Siphon, duration: 2, meta: - Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::SiphonTick, speed: cast.speed, amount }) }, - } - ); - game.action(cast, - Action::Damage { - construct: cast.target, - colour: Colour::Blue, - amount, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Siphoned, duration: 1, meta: None }, // immunity to additional ticks - } - ); - game.action(cast, - Action::Heal { - construct: cast.source, - colour: Colour::Green, - amount: game.value(Value::DamageReceived { construct: cast.target, colour: Colour::Blue }).pct(100), - }, - ); + pub fn duration(self) -> u8 { + match self { Siphon::Base => 2, Siphon::Plus => 3, Siphon::PlusPlus => 4 } + } } -fn siphon_plus(cast: Cast, game: &mut Game) { +fn siphon(cast: Cast, game: &mut Game, values: Siphon) { let amount = - game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier()) - + game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(cast.skill.multiplier()); + game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(values.blue_dmg_multi()) + + game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(values.green_dmg_multi()); game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Siphon, duration: 3, meta: - Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::SiphonTick, speed: cast.speed, amount }) }, - } - ); - game.action(cast, - Action::Damage { - construct: cast.target, - colour: Colour::Blue, - amount, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Siphoned, duration: 1, meta: None }, // immunity to additional ticks - } - ); - game.action(cast, - Action::Heal { - construct: cast.source, - colour: Colour::Green, - amount: game.value(Value::DamageReceived { construct: cast.target, colour: Colour::Blue }).pct(100), - }, - ); -} - -fn siphon_plus_plus(cast: Cast, game: &mut Game) { - let amount = - game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(cast.skill.multiplier()) - + game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(cast.skill.multiplier()); - - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Siphon, duration: 4, meta: + effect: ConstructEffect { effect: Effect::Siphon, duration: values.duration(), meta: Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::SiphonTick, speed: cast.speed, amount }) }, } ); @@ -2050,12 +1712,28 @@ fn siphon_tick(cast: Cast, game: &mut Game) { ); } -fn slay(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Slay { Base, Plus, PlusPlus } +impl Slay { + pub fn blue_dmg_multi(self) -> usize { + match self { Slay::Base => 40, Slay::Plus => 50, Slay::PlusPlus => 65 } + } + + pub fn green_dmg_multi(self) -> usize { + match self { Slay::Base => 40, Slay::Plus => 50, Slay::PlusPlus => 65 } + } +} + +fn slay(cast: Cast, game: &mut Game, values: Slay) { + let amount = + game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(values.blue_dmg_multi()) + + game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(values.green_dmg_multi()); + game.action(cast, Action::Damage { construct: cast.target, colour: Colour::Red, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(cast.skill.multiplier()), + amount, } ); game.action(cast, @@ -2067,7 +1745,15 @@ fn slay(cast: Cast, game: &mut Game) { ); } -fn sleep(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Sleep { Base, Plus, PlusPlus } +impl Sleep { + pub fn green_heal_multi(self) -> usize { + match self { Sleep::Base => 160, Sleep::Plus => 200, Sleep::PlusPlus => 240 } + } +} + +fn sleep(cast: Cast, game: &mut Game, values: Sleep) { game.action(cast, Action::Effect { construct: cast.target, @@ -2077,55 +1763,36 @@ fn sleep(cast: Cast, game: &mut Game) { game.action(cast, Action::Heal { construct: cast.target, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(cast.skill.multiplier()), + amount: game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(values.green_heal_multi()), colour: Colour::Blue, }, ); } -fn sleep_plus(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Stun, duration: 2, meta: None }, - } - ); - game.action(cast, - Action::Heal { - construct: cast.target, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(cast.skill.multiplier()), - colour: Colour::Blue, - }, - ); +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Silence { Base, Plus, PlusPlus } +impl Silence { + pub fn dmg_multi(self) -> usize { + match self { Silence::Base => 55, Silence::Plus => 65, Silence::PlusPlus => 80 } + } + + pub fn duration(self) -> u8 { + match self { Silence::Base => 2, Silence::Plus => 2, Silence::PlusPlus => 3 } + } } -fn sleep_plus_plus(cast: Cast, game: &mut Game) { +fn silence(cast: Cast, game: &mut Game, values: Silence) { game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Stun, duration: 2, meta: None }, - } - ); - game.action(cast, - Action::Heal { - construct: cast.target, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(cast.skill.multiplier()), - colour: Colour::Blue, - }, - ); -} - -fn silence(cast: Cast, game: &mut Game) { - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Silence, duration: 2, meta: None }, + effect: ConstructEffect { effect: Effect::Silence, duration: values.duration(), meta: None }, } ); - let bp = game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }); + let bp = game.value(Value::Stat { construct: cast.source, stat: Stat::BluePower }).pct(values.dmg_multi()); let silences = game.value(Value::ColourSkills { construct: cast.target, colour: Colour::Blue }); - let amount = bp.pct(45usize.saturating_mul(silences)); + let amount = bp.pct(100 + 45usize.saturating_mul(silences)); + game.action(cast, Action::Damage { construct: cast.target, @@ -2135,17 +1802,43 @@ fn silence(cast: Cast, game: &mut Game) { ); } -fn strike(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Strike { Base, Plus, PlusPlus, Haste } +impl Strike { + pub fn dmg_multi(self) -> usize { + match self { + Strike::Base => 90, + Strike::Plus => 110, + Strike::PlusPlus => 140, + Strike::Haste => 60, + } + } +} + +fn strike(cast: Cast, game: &mut Game, values: Strike) { + let amount = match values { + Strike::Haste => game.value(Value::Stat { construct: cast.source, stat: Stat::Speed }).pct(values.dmg_multi()), + _ => game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(values.dmg_multi()) + }; + game.action(cast, Action::Damage { construct: cast.target, colour: Colour::Red, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(cast.skill.multiplier()), + amount, }, ); } -fn sustain(cast: Cast, game: &mut Game) { +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Sustain { Base, Plus, PlusPlus } +impl Sustain { + pub fn red_heal(self) -> usize { + match self { Sustain::Base => 110, Sustain::Plus => 130, Sustain::PlusPlus => 150 } + } +} + +fn sustain(cast: Cast, game: &mut Game, values: Sustain) { game.action(cast, Action::Effect { construct: cast.target, @@ -2155,69 +1848,31 @@ fn sustain(cast: Cast, game: &mut Game) { game.action(cast, Action::Heal { construct: cast.target, - amount: game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(cast.skill.multiplier()), + amount: game.value(Value::Stat { construct: cast.source, stat: Stat::RedPower }).pct(values.red_heal()), colour: Colour::Red, }, ); } -fn triage(cast: Cast, game: &mut Game) { - let amount = game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(cast.skill.multiplier()); +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum Triage { Base, Plus, PlusPlus } +impl Triage { + pub fn heal_multi(self) -> usize { + match self { Triage::Base => 75, Triage::Plus => 90, Triage::PlusPlus => 110 } + } - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Triage, duration: 2, meta: - Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::TriageTick, speed: cast.speed, amount }) }, - } - ); - game.action(cast, - Action::Heal { - construct: cast.target, - colour: Colour::Green, - amount, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Triaged, duration: 1, meta: None }, - }, - ); + pub fn duration(self) -> u8 { + match self { Triage::Base => 2, Triage::Plus => 2, Triage::PlusPlus => 4 } + } } -fn triage_plus(cast: Cast, game: &mut Game) { - let amount = game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(cast.skill.multiplier()); +fn triage(cast: Cast, game: &mut Game, values: Triage) { + let amount = game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(values.heal_multi()); game.action(cast, Action::Effect { construct: cast.target, - effect: ConstructEffect { effect: Effect::Triage, duration: 3, meta: - Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::TriageTick, speed: cast.speed, amount }) }, - } - ); - game.action(cast, - Action::Heal { - construct: cast.target, - colour: Colour::Green, - amount, - } - ); - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Triaged, duration: 1, meta: None }, - }, - ); -} - -fn triage_plus_plus(cast: Cast, game: &mut Game) { - let amount = game.value(Value::Stat { construct: cast.source, stat: Stat::GreenPower }).pct(cast.skill.multiplier()); - - game.action(cast, - Action::Effect { - construct: cast.target, - effect: ConstructEffect { effect: Effect::Triage, duration: 4, meta: + effect: ConstructEffect { effect: Effect::Triage, duration: values.duration(), meta: Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::TriageTick, speed: cast.speed, amount }) }, } ); @@ -2250,349 +1905,4 @@ fn triage_tick(cast: Cast, game: &mut Game) { effect: ConstructEffect { effect: Effect::Triaged, duration: 1, meta: None }, }, ); -} - - -// #[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 actions = cast.actions(Game::); - - // match actions[0] { - // Action::Cast => (), - // _ => panic!("{:?}", actions), - // }; - - // match actions[1] { - // Action::Hit => (), - // _ => panic!("{:?}", actions), - // }; - - // match actions[2] { - // Action::Damage { construct: _, amount: _, colour } => { - // 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::Heal { 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::Heal { 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()); - // } -} +} \ No newline at end of file