use rand::{thread_rng, Rng}; use uuid::Uuid; use util::{IntPct}; use cryp::{Cryp, CrypEffect, EffectMeta, Stat}; use vbox::{Var}; use game::{Game}; pub fn resolution_steps(cast: &Cast, game: &mut Game) -> Resolutions { let mut resolutions = vec![]; resolutions = pre_resolve(cast, game, resolutions); return resolutions; } pub fn pre_resolve(cast: &Cast, game: &mut Game, mut resolutions: Resolutions) -> Resolutions { let skill = cast.skill; let source = game.cryp_by_id(cast.source_cryp_id).unwrap().clone(); let targets = game.get_targets(cast.skill, &source, cast.target_cryp_id); if skill.aoe() { // Send an aoe skill event for anims resolutions.push(Resolution::new(&source, &game.cryp_by_id(cast.target_cryp_id).unwrap().clone()).event(Event::AoeSkill { skill })); } for target_id in targets { // we clone the current state of the target and source // so we can modify them during the resolution // no more than 1 mutable ref allowed on game let mut source = game.cryp_by_id(cast.source_cryp_id).unwrap().clone(); let mut target = game.cryp_by_id(target_id).unwrap().clone(); // bail out on ticks that have been removed if cast.is_tick && target.effects.iter().find(|ce| match ce.tick { Some(t) => t.id == cast.id, None => false, }).is_none() { continue; } resolutions = resolve(cast.skill, &mut source, &mut target, resolutions); // save the changes to the game game.update_cryp(&mut source); game.update_cryp(&mut target); // do additional steps resolutions = post_resolve(cast.skill, game, resolutions); } return resolutions; } pub fn resolve(skill: Skill, source: &mut Cryp, target: &mut Cryp, mut resolutions: Vec) -> Resolutions { if let Some(disable) = source.disabled(skill) { resolutions.push(Resolution::new(source, target).event(Event::Disable { disable, skill })); return resolutions; } if target.is_ko() { resolutions.push(Resolution::new(source, target).event(Event::TargetKo { skill })); return resolutions; } if target.affected(Effect::Reflect) { // guard against overflow if source.affected(Effect::Reflect) { return resolutions; } resolutions.push(Resolution::new(source, target).event(Event::Reflection { skill })); return resolve(skill, target, source, resolutions); } if source.affected(Effect::Haste) { match skill { Skill::Attack | Skill::Slay | Skill::Chaos | Skill::Strike => { let amount = source.speed().pct(Skill::HasteStrike.multiplier()); target.deal_red_damage(Skill::HasteStrike, amount) .into_iter() .for_each(|e| resolutions.push(Resolution::new(source, target).event(e))); }, _ => (), } } if source.affected(Effect::Impurity) { match skill { Skill::Blast | Skill::Chaos | Skill::Siphon => { let amount = source.green_damage().pct(Skill::ImpureBlast.multiplier()); target.deal_blue_damage(Skill::ImpureBlast, amount) .into_iter() .for_each(|e| resolutions.push(Resolution::new(source, target).event(e))); }, _ => (), } } // match self.category() == Category::Red { // true => { // if let Some(evasion) = target.evade(*self) { // resolutions.push(evasion); // return Event; // } // }, // false => (), // } resolutions = match skill { Skill::Amplify => amplify(source, target, resolutions, skill), // increase magic damage Skill::Attack => attack(source, target, resolutions, skill), Skill::Banish => banish(source, target, resolutions, skill), // TODO prevent all actions Skill::Blast => blast(source, target, resolutions, skill), Skill::Block => block(source, target, resolutions, skill), Skill::Chaos => chaos(source, target, resolutions, skill), Skill::Clutch => clutch(source, target, resolutions, skill), Skill::Corrupt => corrupt(source, target, resolutions, skill), Skill::CorruptionTick => corruption_tick(source, target, resolutions, skill), Skill::Curse => curse(source, target, resolutions, skill), Skill::Debuff => debuff(source, target, resolutions, skill), // speed slow Skill::Decay => decay(source, target, resolutions, skill), // dot Skill::DecayTick => decay_tick(source, target, resolutions, skill), // dot Skill::Haste => haste(source, target, resolutions, skill), // speed slow Skill::HasteStrike => panic!("haste strike should not be caste"), Skill::Heal => heal(source, target, resolutions, skill), Skill::Hex => hex(source, target, resolutions, skill), Skill::Hostility => hostility(source, target, resolutions, skill), Skill::Impurity => impurity(source, target, resolutions, skill), Skill::ImpureBlast => panic!("impure blast should not be caste"), Skill::Invert => invert(source, target, resolutions, skill), Skill::Injure => injure(source, target, resolutions, skill), Skill::Parry => parry(source, target, resolutions, skill), Skill::Purge => purge(source, target, resolutions, skill), // dispel all buffs Skill::Purify => purify(source, target, resolutions, skill), // dispel all debuffs Skill::Recharge => recharge(source, target, resolutions, skill), // target is immune to magic damage and fx Skill::Reflect => reflect(source, target, resolutions, skill), Skill::Riposte => panic!("riposte should not be caste"), Skill::Ruin => ruin(source, target, resolutions, skill), Skill::Scatter => scatter(source, target, resolutions, skill), // target is immune to magic damage and fx Skill::Silence => silence(source, target, resolutions, skill), // target cannot cast spells Skill::Siphon => siphon(source, target, resolutions, skill), Skill::SiphonTick => siphon_tick(source, target, resolutions, skill), // hot Skill::Slay => slay(source, target, resolutions, skill), // hybrid dmg self heal Skill::Sleep => sleep(source, target, resolutions, skill), // speed slow Skill::Snare => snare(source, target, resolutions, skill), Skill::Strangle => strangle(source, target, resolutions, skill), Skill::StrangleTick => strangle_tick(source, target, resolutions, skill), Skill::Strike => strike(source, target, resolutions, skill), Skill::StrikeII => strike(source, target, resolutions, skill), Skill::StrikeIII => strike(source, target, resolutions, skill), Skill::Stun => stun(source, target, resolutions, skill), Skill::Taunt => taunt(source, target, resolutions, skill), Skill::Throw => throw(source, target, resolutions, skill), // no damage stun, adds vulnerable Skill::Triage => triage(source, target, resolutions, skill), // hot Skill::TriageTick => triage_tick(source, target, resolutions, skill), // hot // ----------------- // Test // ----------------- Skill::TestAttack => attack(source, target, resolutions, skill), Skill::TestHeal => heal(source, target, resolutions, skill), Skill::TestTouch => touch(source, target, resolutions, skill), Skill::TestStun => stun(source, target, resolutions, Skill::Stun), Skill::TestBlock => block(source, target, resolutions, Skill::Block), Skill::TestParry => parry(source, target, resolutions, Skill::Parry), Skill::TestSiphon => siphon(source, target, resolutions, Skill::Siphon), }; return resolutions; } fn post_resolve(_skill: Skill, game: &mut Game, mut resolutions: Resolutions) -> Resolutions { for Resolution { source, target, event } in resolutions.clone() { let mut source = game.cryp_by_id(source.id).unwrap().clone(); let mut target = game.cryp_by_id(target.id).unwrap().clone(); match event { Event::Damage { amount, skill, mitigation: _, colour: _ } => { if target.affected(Effect::Corrupt) { resolutions = corruption(&mut target, &mut source, resolutions, Skill::Corrupt); } if target.affected(Effect::Hostility) { resolutions = hatred(&mut source, &mut target, resolutions, skill, amount, Skill::Hostility); } // beware that scatter doesn't cause any damage // because then applying it will proc this if target.affected(Effect::Scatter) { resolutions = scatter_hit(&source, &target, resolutions, game, event) } }, Event::Immunity { skill: _, immunity } => match immunity.contains(&Effect::Parry) { true => { resolutions = riposte(&mut target, &mut source, resolutions, Skill::Riposte); } false => (), }, _ => (), }; game.update_cryp(&mut source); game.update_cryp(&mut target); }; return resolutions; } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct Cast { pub id: Uuid, pub source_player_id: Uuid, pub source_cryp_id: Uuid, pub target_cryp_id: Uuid, pub skill: Skill, pub speed: u64, pub is_tick: bool, } impl Cast { pub fn new(source_cryp_id: Uuid, source_player_id: Uuid, target_cryp_id: Uuid, skill: Skill) -> Cast { return Cast { id: Uuid::new_v4(), source_cryp_id, source_player_id, target_cryp_id, skill, speed: 0, is_tick: false, }; } pub fn new_tick(source: &mut Cryp, target: &mut Cryp, skill: Skill) -> Cast { Cast { id: Uuid::new_v4(), source_cryp_id: source.id, source_player_id: source.account, target_cryp_id: target.id, skill, speed: 0, is_tick: true } } pub fn used_cooldown(&self) -> bool { return self.skill.base_cd().is_some(); } } pub type Disable = Vec; pub type Immunity = Vec; #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct LogCryp { pub id: Uuid, pub name: String, } #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct Resolution { pub source: LogCryp, pub target: LogCryp, pub event: Event, } impl Resolution { fn new(source: &Cryp, target: &Cryp) -> Resolution { Resolution { source: LogCryp { id: source.id, name: source.name.clone() }, target: LogCryp { id: target.id, name: target.name.clone() }, event: Event::Incomplete, } } fn event(mut self, e: Event) -> Resolution { self.event = e; self } } #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub enum Event { Disable { skill: Skill, disable: Disable }, Immunity { skill: Skill, immunity: Immunity }, Damage { skill: Skill, amount: u64, mitigation: u64, colour: Category }, Healing { skill: Skill, amount: u64, overhealing: u64 }, Recharge { skill: Skill, red: u64, blue: u64 }, Inversion { skill: Skill }, Reflection { skill: Skill }, Effect { skill: Skill, effect: Effect, duration: u8 }, AoeSkill { skill: Skill }, Skill { skill: Skill }, Removal { effect: Effect }, TargetKo { skill: Skill }, // skill not necessary but makes it neater as all events are arrays in js Ko { skill: Skill }, Incomplete, // not used Evasion { skill: Skill, evasion_rating: u64 }, } type Resolutions = Vec; pub type Cooldown = Option; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Effect { // physical Stun, Parry, Block, Bleed, Leech, Airborne, Untouchable, Deadly, Vulnerable, Fury, Blind, Snare, Clutch, Injured, Reflect, Taunt, Impurity, Invert, Strangle, Strangling, // magic Hex, Ruin, Curse, Banish, Slow, Haste, Enslave, Mesmerise, Amplify, Silence, Wither, // Reduce green dmg (healing) taken // corrupt is the buff that applies // corruption the dmg debuff Corrupt, Corruption, // hostility is the buff // hatred is the increased damage Hostility, Hatred, // magic immunity Scatter, // effects over time Triage, Decay, Regen, Siphon, SpeedSiphon, SpeedIncrease, Ko, } impl Effect { pub fn immune(&self, skill: Skill) -> bool { match self { Effect::Parry => match skill.category() { Category::Blue => false, Category::Red => true, _ => false, }, Effect::Banish => true, Effect::Injured => match skill.category() { Category::Green => true, Category::GreenTick => true, _ => false, }, _ => false, } } pub fn disables_skill(&self, skill: Skill) -> bool { match self { Effect::Stun => true, Effect::Hex => true, Effect::Ruin => true, Effect::Banish => true, Effect::Strangle => true, Effect::Strangling => skill != Skill::StrangleTick, Effect::Silence => match skill.category() { Category::Blue => true, Category::Red => false, _ => false, }, Effect::Snare => match skill.category() { Category::Blue => false, Category::Red => true, _ => false, }, Effect::Ko => match skill.category() { Category::BlueTick => false, Category::GreenTick => false, _ => true, }, _ => false, } } pub fn modifications(&self) -> Vec { match self { Effect::Vulnerable => vec![Stat::RedDamageTaken], Effect::Block => vec![Stat::RedDamageTaken], Effect::Hatred => vec![Stat::RedDamage, Stat::BlueDamage], Effect::Amplify => vec![Stat::RedDamage, Stat::BlueDamage], Effect::Curse => vec![Stat::BlueDamageTaken], Effect::Impurity => vec![Stat::GreenDamage], Effect::Wither => vec![Stat::GreenDamageTaken], Effect::Haste => vec![Stat::Speed], Effect::Slow => vec![Stat::Speed], Effect::Scatter => vec![Stat::BlueDamageTaken, Stat::GreenDamageTaken, Stat::RedDamageTaken], _ => vec![], } } pub fn apply(&self, value: u64, meta: Option) -> u64 { match self { Effect::Vulnerable => value.pct(150), Effect::Block => value.pct(50), Effect::Amplify => value.pct(150), Effect::Curse => value.pct(150), Effect::Haste => value.pct(150), Effect::Slow => value.pct(50), Effect::Impurity => value.pct(150), Effect::Wither => value.pct(50), Effect::Scatter => value >> 1, Effect::Hatred => value + match meta { Some(EffectMeta::AddedDamage(d)) => d, _ => panic!("hatred meta not damage"), }, _ => { info!("{:?} does not have a mod effect", self); return value; }, } } pub fn category(&self) -> Category { match self { // physical Effect::Stun => Category::Debuff, Effect::Block => Category::Buff, Effect::Parry => Category::Buff, Effect::Bleed => Category::Debuff, Effect::Leech => Category::Debuff, Effect::Airborne => Category::Debuff, Effect::Untouchable => Category::Buff, Effect::Deadly => Category::Buff, Effect::Vulnerable => Category::Debuff, Effect::Fury => Category::Buff, Effect::Blind => Category::Debuff, Effect::Snare => Category::Debuff, Effect::Clutch => Category::Buff, Effect::Taunt => Category::Buff, Effect::Injured => Category::Debuff, Effect::Strangle => Category::Debuff, Effect::Strangling => Category::Buff, // magic Effect::Hex => Category::Debuff, Effect::Ruin => Category::Debuff, Effect::Curse => Category::Debuff, Effect::Banish => Category::Debuff, // todo randomise Effect::Slow => Category::Debuff, Effect::Haste => Category::Buff, Effect::Hatred => Category::Buff, Effect::Reflect => Category::Buff, Effect::Enslave => Category::Debuff, Effect::Mesmerise => Category::Debuff, Effect::Amplify => Category::Buff, Effect::Silence => Category::Debuff, Effect::Wither => Category::Debuff, Effect::Corrupt => Category::Buff, Effect::Corruption => Category::Debuff, Effect::Hostility => Category::Buff, // magic Effect::Impurity => Category::Buff, Effect::Scatter => Category::Buff, Effect::Invert => Category::Buff, // effects over time Effect::Triage => Category::Buff, Effect::Decay => Category::Debuff, Effect::Regen => Category::Buff, Effect::Siphon => Category::Debuff, Effect::SpeedSiphon => Category::Debuff, Effect::SpeedIncrease => Category::Buff, Effect::Ko => Category::Ko, } } } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Category { Red, Blue, Green, RedDamage, BlueDamage, GreenDamage, RedTick, BlueTick, GreenTick, Buff, Debuff, Ko, } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Skill { Attack, Debuff, Block, // reduce damage Stun, // ----------------- // Nature // ----------------- Parry, // avoid all damage Riposte, Snare, Injure, Recharge, Reflect, Ruin, Slay, Sleep, Clutch, Taunt, Impurity, ImpureBlast, Invert, Strangle, StrangleTick, Strike, StrikeII, StrikeIII, // Evade, // actively evade // ----------------- // Nonviolence // ----------------- Heal, Triage, // hot TriageTick, Throw, // no damage stun, adds vulnerable // Sleep, // Nightmare, // ------------------- // Destruction // ------------------- Blast, Amplify, Decay, // dot DecayTick, // dot Siphon, SiphonTick, Curse, Hostility, Corrupt, CorruptionTick, // ----------------- // Purity // ----------------- Scatter, Silence, Purify, Purge, // ----------------- // Chaos // ----------------- Banish, Chaos, Hex, Haste, HasteStrike, // used by tests, no cd, 100% multiplier TestAttack, TestHeal, TestTouch, // No damage TestStun, TestBlock, TestParry, TestSiphon, } impl Skill { pub fn multiplier(&self) -> u64 { match self { // Attack Base Skill::Attack => 80, // Base Skill::Blast => 110, // BB Skill::Chaos => 40, // BR Skill::Heal => 130, //GG Skill::SiphonTick => 40, // GB Skill::Slay => 70, // RG Skill::Strike => 90, //RR Skill::StrikeII => 110, Skill::StrikeIII => 130, // Block Base Skill::CorruptionTick => 80, Skill::Purify => 45, //Green dmg (heal) Skill::Recharge => 85, //restore red and blue life (heal) Skill::Reflect => 45, //restore blue life (heal) Skill::Parry => 110, Skill::Riposte => 70, // Stun Base Skill::Sleep => 240, //Green dmg (heal) Skill::StrangleTick => 65, // Debuff Base Skill::Silence => 55, // Deals more per blue skill on target Skill::Snare => 40, // Deals more per red skill on target Skill::DecayTick => 25, // Buff base Skill::ImpureBlast => 25, Skill::HasteStrike => 30, Skill::Taunt => 80, Skill::TriageTick => 75, Skill::Scatter => 140, _ => 100, } } pub fn duration(&self) -> u8 { match self { Skill::Block => 1, Skill::Parry => 2, Skill::Clutch => 1, Skill::Debuff => 3, Skill::Reflect => 1, Skill::Injure => 2, Skill::Strangle => 2, Skill::Stun => 2, Skill::Sleep => 3, Skill::Throw => 1, Skill::Snare => 3, Skill::Taunt => 2, Skill::Impurity => 3, Skill::Invert => 1, Skill::Hex => 2, Skill::Ruin => 1, Skill::Curse => 2, Skill::Banish => 1, Skill::Haste => 2, Skill::Amplify => 2, Skill::Silence => 3, Skill::Hostility => 2, // Primary Buff Skill::Corrupt => 2, // Primary Buff Skill::Scatter => 2, Skill::Triage => 3, Skill::Decay => 3, Skill::Siphon => 2, _ => { info!("{:?} does not have a duration", self); return 1; }, } } pub fn secondary_duration(&self) -> u8 { match self { Skill::Hostility => 5, // Increased dmg buff Skill::Corrupt => 3, // Damage over time Skill::Throw => 3, // Inc dmg taken debuff _ => { info!("{:?} does not have a secondary duration", self); return 1; }, } } pub fn base_cd(&self) -> Cooldown { match self { Skill::Attack => None, Skill::Debuff => Some(1), Skill::Strike => None, Skill::StrikeII => None, Skill::StrikeIII => None, Skill::Block => None, // reduce damage Skill::Parry => Some(2), // avoid all damage Skill::Riposte => None, // used on parry Skill::Snare => Some(2), Skill::Stun => Some(2), Skill::Heal => None, Skill::Triage => None, // hot Skill::TriageTick => None, Skill::Throw => Some(1), // no damage stun, adds vulnerable Skill::Blast => None, Skill::Chaos => None, Skill::Amplify => Some(1), Skill::Impurity => Some(3), Skill::ImpureBlast => None, Skill::Invert => Some(2), Skill::Decay => Some(1), // dot Skill::DecayTick => None, Skill::Siphon => None, Skill::SiphonTick => None, Skill::Curse => Some(1), Skill::Scatter => Some(2), Skill::Silence => Some(2), Skill::Purify => None, Skill::Purge => None, Skill::Banish => Some(1), Skill::Hex => Some(1), Skill::Haste => Some(2), Skill::HasteStrike => None, // Used in haste Skill::Reflect => Some(2), Skill::Recharge => Some(2), Skill::Ruin => Some(3), Skill::Slay => None, Skill::Sleep => Some(3), Skill::Strangle => Some(2), Skill::StrangleTick => None, Skill::Clutch => Some(2), Skill::Taunt => Some(2), Skill::Injure => Some(2), Skill::Corrupt => Some(1), Skill::CorruptionTick => None, Skill::Hostility => Some(1), // ----------------- // Test // ----------------- Skill::TestAttack => None, Skill::TestHeal => None, Skill::TestTouch => None, Skill::TestStun => None, Skill::TestBlock => None, Skill::TestSiphon => None, Skill::TestParry => None, } } pub fn category(&self) -> Category { match self { Skill::Attack => Category::Red, Skill::Strike => Category::Red, Skill::StrikeII => Category::Red, Skill::StrikeIII => Category::Red, Skill::Injure => Category::Red, Skill::Strangle => Category::Red, Skill::StrangleTick => Category::Red, Skill::Block => Category::Red, // reduce damage Skill::Parry => Category::Red, // avoid all damage Skill::Riposte => Category::Red, // avoid all damage Skill::Snare => Category::Red, Skill::Clutch => Category::Red, Skill::Stun => Category::Red, Skill::Slay => Category::Red, Skill::Taunt => Category::Red, Skill::HasteStrike => Category::Red, Skill::Heal => Category::Green, Skill::Triage => Category::Green, // hot Skill::TriageTick => Category::GreenTick, // hot Skill::Throw => Category::Green, Skill::Purify => Category::Green, Skill::Recharge => Category::Green, Skill::Reflect => Category::Green, Skill::Haste => Category::Green, Skill::Impurity => Category::Green, Skill::Invert => Category::Green, Skill::Sleep => Category::Green, Skill::ImpureBlast => Category::Blue, Skill::Scatter => Category::Blue, Skill::Blast => Category::Blue, Skill::Chaos => Category::Blue, Skill::Amplify => Category::Blue, Skill::Decay => Category::Blue, // dot Skill::DecayTick => Category::BlueTick, // hot Skill::Siphon => Category::Blue, Skill::SiphonTick => Category::BlueTick, // hot Skill::Curse => Category::Blue, Skill::Silence => Category::Blue, Skill::Purge => Category::Blue, Skill::Banish => Category::Blue, Skill::Hex => Category::Blue, Skill::Debuff => Category::Blue, Skill::Ruin => Category::Blue, Skill::Hostility => Category::Blue, Skill::Corrupt => Category::Blue, Skill::CorruptionTick => Category::Blue, // ----------------- // Test // ----------------- Skill::TestAttack => Category::Red, Skill::TestHeal => Category::Green, Skill::TestTouch => Category::Red, Skill::TestStun => Category::Red, Skill::TestParry => Category::Red, Skill::TestBlock => Category::Red, Skill::TestSiphon => Category::Blue, } } pub fn ko_castable(&self) -> bool { match self { Skill::TriageTick => true, Skill::DecayTick => true, Skill::SiphonTick => true, Skill::CorruptionTick => true, _ => false, } } pub fn speed(&self) -> u8 { match self { // ----------------- // Test // ----------------- Skill::TestTouch => 10, Skill::TestStun => 5, Skill::TestBlock => 10, Skill::TestParry => 10, Skill::TestSiphon => 10, Skill::Strike => u8::max_value(), Skill::StrikeII => Skill::Strike.speed(), Skill::StrikeIII => Skill::Strike.speed(), Skill::SiphonTick => Var::from(Skill::Siphon).speed(), Skill::DecayTick => Var::from(Skill::Decay).speed(), Skill::TriageTick => Var::from(Skill::Triage).speed(), Skill::StrangleTick => Var::from(Skill::Strangle).speed(), Skill::CorruptionTick => Var::from(Skill::Corrupt).speed(), _ => Var::from(*self).speed(), } } pub fn aoe(&self) -> bool { match self { Skill::Ruin => true, _ => false, } } pub fn self_targeting(&self) -> bool { match self { Skill::Block => true, Skill::Parry => true, Skill::Clutch => true, Skill::Corrupt => true, Skill::TestBlock => true, Skill::TestParry => true, _ => false, } } pub fn defensive(&self) -> bool { let mut rng = thread_rng(); match self { Skill::Heal | Skill::Triage | Skill::Purify | Skill::Parry | Skill::Clutch | Skill::Scatter | Skill::Recharge | Skill::Reflect | Skill::Haste | Skill::Invert | Skill::Amplify | Skill::Hostility | Skill::Corrupt | Skill::Block => true, Skill::Banish => rng.gen_bool(0.5), _ => false, } } } fn touch(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { target.deal_red_damage(skill, 0) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } fn attack(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amount = source.red_damage().pct(skill.multiplier()); target.deal_red_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } fn strike(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amount = source.red_damage().pct(skill.multiplier()); target.deal_red_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } fn injure(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amount = source.red_damage().pct(skill.multiplier()); target.deal_red_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); let effect = CrypEffect::new(Effect::Injured, 2); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return results; } fn stun(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Stun, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return results; } fn sleep(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Stun, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); let amount = source.green_damage().pct(skill.multiplier()); target.deal_green_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } fn clutch(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Clutch, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return results; } fn taunt(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let red_amount = source.red_damage().pct(skill.multiplier()); results.push(Resolution::new(source, target).event(target.recharge(skill, red_amount, 0))); let effect = CrypEffect::new(Effect::Taunt, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return results; } fn throw(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let stun = CrypEffect::new(Effect::Stun, skill.duration()); let vulnerable = CrypEffect::new(Effect::Vulnerable, skill.secondary_duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, stun))); results.push(Resolution::new(source, target).event(target.add_effect(skill, vulnerable))); return results; } fn strangle(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let target_stun = CrypEffect::new(Effect::Strangle, skill.duration()) .set_tick(Cast::new_tick(source, target, Skill::StrangleTick)); let attacker_immunity = CrypEffect::new(Effect::Strangling, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, target_stun))); results.push(Resolution::new(source, source).event(source.add_effect(skill, attacker_immunity))); return strangle_tick(source, target, results, Skill::StrangleTick); } fn strangle_tick(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amount = source.red_damage().pct(skill.multiplier()); target.deal_red_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); // remove immunity if target ko if target.is_ko() { let i = source.effects .iter() .position(|e| e.effect == Effect::Strangling) .expect("no strangling on cryp"); source.effects.remove(i); results.push(Resolution::new(source, source).event(Event::Removal { effect: Effect::Strangling })); } return results; } fn block(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let block = CrypEffect::new(Effect::Block, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, block))); return results; } fn parry(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let red_amount = source.red_damage().pct(skill.multiplier()); results.push(Resolution::new(source, target).event(target.recharge(skill, red_amount, 0))); let effect = CrypEffect::new(Effect::Parry, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return results; } fn riposte(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amount = source.red_damage().pct(skill.multiplier()); target.deal_red_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } fn snare(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let snare = CrypEffect::new(Effect::Snare, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, snare))); let s_multi = target.skills .iter() .fold(100, |acc, cs| match cs.skill.category() { Category::Red => acc + 45, _ => acc, }); let amount = source.red_damage().pct(skill.multiplier()).pct(s_multi); target.deal_red_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } fn slay(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amount = source.red_damage().pct(skill.multiplier()); let slay_events = target.deal_red_damage(skill, amount); for e in slay_events { match e { Event::Damage { amount, mitigation: _, colour: _, skill: _ } => { results.push(Resolution::new(source, target).event(e)); let heal = source.deal_green_damage(skill, amount); for h in heal { results.push(Resolution::new(source, source).event(h)); }; }, _ => results.push(Resolution::new(source, target).event(e)), } } return results; } fn heal(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amount = source.green_damage().pct(skill.multiplier()); target.deal_green_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } fn triage(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Triage, skill.duration()) .set_tick(Cast::new_tick(source, target, Skill::TriageTick)); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return triage_tick(source, target, results, Skill::TriageTick); } fn triage_tick(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amount = source.green_damage().pct(skill.multiplier()); target.deal_green_damage(Skill::TriageTick, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } fn chaos(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let mut rng = thread_rng(); let b_rng: u64 = rng.gen_range(0, 30); let amount = source.blue_damage().pct(skill.multiplier() + b_rng); target.deal_blue_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); let r_rng: u64 = rng.gen_range(0, 30); let amount = source.red_damage().pct(skill.multiplier() + r_rng); target.deal_red_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } fn blast(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amount = source.blue_damage().pct(skill.multiplier()); target.deal_blue_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } fn amplify(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amplify = CrypEffect::new(Effect::Amplify, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, amplify))); return results;; } fn haste(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Haste, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return results;; } fn debuff(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Slow, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return results;; } fn decay(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let wither = CrypEffect::new(Effect::Wither, skill.duration()); let decay = CrypEffect::new(Effect::Decay, skill.duration()) .set_tick(Cast::new_tick(source, target, Skill::DecayTick)); results.push(Resolution::new(source, target).event(target.add_effect(skill, decay))); results.push(Resolution::new(source, target).event(target.add_effect(skill, wither))); return decay_tick(source, target, results, Skill::DecayTick); } fn decay_tick(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amount = source.blue_damage().pct(skill.multiplier()); target.deal_blue_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } // corrupt is the buff effect // when attacked it runs corruption and applies a debuff fn corrupt(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Corrupt, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return results;; } fn corruption(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Corruption, skill.secondary_duration()) .set_tick(Cast::new_tick(source, target, Skill::CorruptionTick)); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return corruption_tick(source, target, results, Skill::CorruptionTick); } fn corruption_tick(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amount = source.blue_damage().pct(skill.multiplier()); target.deal_blue_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } fn ruin(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Ruin, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return results;; } fn hex(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let hex = CrypEffect::new(Effect::Hex, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, hex))); return results;; } fn hostility(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Hostility, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return results;; } fn hatred(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, reflect_skill: Skill, amount: u64, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Hatred, skill.secondary_duration()) .set_meta(EffectMeta::AddedDamage(amount)); results.push(Resolution::new(source, target).event(target.add_effect(reflect_skill, effect))); return results;; } fn curse(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let curse = CrypEffect::new(Effect::Curse, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(Skill::Curse, curse))); return results;; } fn impurity(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Impurity, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return results;; } fn invert(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Invert, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); return results;; } fn reflect(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Reflect, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, effect))); let blue_amount = source.blue_damage().pct(skill.multiplier()); results.push(Resolution::new(source, target).event(target.recharge(skill, 0, blue_amount))); return results;; } fn recharge(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let red_amount = source.red_damage().pct(skill.multiplier()); let blue_amount = source.blue_damage().pct(skill.multiplier()); results.push(Resolution::new(source, target).event(target.recharge(skill, red_amount, blue_amount))); return results; } fn siphon(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let siphon = CrypEffect::new(Effect::Siphon, skill.duration()) .set_tick(Cast::new_tick(source, target, Skill::SiphonTick)); results.push(Resolution::new(source, target).event(target.add_effect(skill, siphon))); return siphon_tick(source, target, results, Skill::SiphonTick); } fn siphon_tick(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let amount = source.blue_damage().pct(skill.multiplier()); let siphon_events = target.deal_blue_damage(Skill::SiphonTick, amount); for e in siphon_events { match e { Event::Damage { amount, mitigation: _, colour: _, skill: _ } => { results.push(Resolution::new(source, target).event(e)); let heal = source.deal_green_damage(Skill::SiphonTick, amount); for h in heal { results.push(Resolution::new(source, source).event(h)); }; }, _ => results.push(Resolution::new(source, target).event(e)), } } return results; } fn scatter(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let effect = CrypEffect::new(Effect::Scatter, skill.duration()) .set_meta(EffectMeta::ScatterTarget(target.id)); let blue_amount = source.blue_damage().pct(skill.multiplier()); results.push(Resolution::new(source, target).event(target.recharge(skill, 0, blue_amount))); results.push(Resolution::new(source, target).event(source.add_effect(skill, effect))); return results; } fn scatter_hit(source: &Cryp, target: &Cryp, mut results: Resolutions, game: &mut Game, event: Event) -> Resolutions { match event { Event::Damage { amount, skill, mitigation: _, colour } => { let scatter = target.effects.iter().find(|e| e.effect == Effect::Scatter).unwrap(); if let Some(EffectMeta::ScatterTarget(scatter_target_id)) = scatter.meta { let mut scatter_target = game.cryp_by_id(scatter_target_id).unwrap(); let res = match colour { Category::RedDamage => scatter_target.deal_red_damage(skill, amount), Category::BlueDamage => scatter_target.deal_blue_damage(skill, amount), Category::GreenDamage => scatter_target.deal_green_damage(skill, amount), _ => panic!("{:?} unknown damage type", colour), }; results.push(Resolution::new(target, scatter_target).event(Event::Skill { skill: Skill::Scatter })); res.into_iter().for_each(|e| results.push(Resolution::new(&source, &scatter_target).event(e))); } else { panic!("not a scatter target {:?}", scatter); } return results; }, _ => panic!("{:?} scatter hit not damage event", event), } } fn silence(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let silence = CrypEffect::new(Effect::Silence, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, silence))); let s_multi = target.skills .iter() .fold(100, |acc, cs| match cs.skill.category() { Category::Blue => acc + 45, _ => acc, }); let amount = source.blue_damage().pct(skill.multiplier()).pct(s_multi); target.deal_blue_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); return results; } fn purge(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { while let Some(i) = target.effects .iter() .position(|ce| [Category::Buff, Category::Buff].contains(&ce.effect.category())) { let ce = target.effects.remove(i); results.push(Resolution::new(source, target).event(Event::Removal { effect: ce.effect })); } return results; } fn purify(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { results.push(Resolution::new(source, target).event(Event::Skill { skill })); let amount = source.green_damage().pct(skill.multiplier()); while let Some(i) = target.effects .iter() .position(|ce| Category::Debuff == ce.effect.category()) { let ce = target.effects.remove(i); results.push(Resolution::new(source, target).event(Event::Removal { effect: ce.effect })); target.deal_green_damage(skill, amount) .into_iter() .for_each(|e| results.push(Resolution::new(source, target).event(e))); } return results; } fn banish(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { let banish = CrypEffect::new(Effect::Banish, skill.duration()); results.push(Resolution::new(source, target).event(target.add_effect(skill, banish))); return results; } #[cfg(test)] mod tests { use skill::*; #[test] fn heal_test() { let mut x = Cryp::new() .named(&"muji".to_string()) .learn(Skill::Heal); let mut y = Cryp::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 = Cryp::new() .named(&"muji".to_string()); let mut y = Cryp::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 = Cryp::new() .named(&"muji".to_string()); let mut y = Cryp::new() .named(&"camel".to_string()); // ensure it doesn't have 0 pd x.red_damage.force(100); y.green_life.force(500); block(&mut y.clone(), &mut y, vec![], Skill::Block); assert!(y.effects.iter().any(|e| e.effect == Effect::Block)); let mut results = attack(&mut x, &mut y, vec![], Skill::TestAttack); let Resolution { source: _, target: _, event } = results.remove(0); match event { Event::Damage { amount, mitigation: _, colour: _, skill: _ } => assert_eq!(amount, 50), _ => panic!("not damage"), }; } #[test] fn clutch_test() { let mut x = Cryp::new() .named(&"muji".to_string()); let mut y = Cryp::new() .named(&"camel".to_string()); x.red_damage.force(10000000000000); // multiplication of int max will cause overflow clutch(&mut y.clone(), &mut y, vec![], Skill::Clutch); assert!(y.affected(Effect::Clutch)); let mut results = attack(&mut x, &mut y, vec![], Skill::Attack); assert!(y.green_life() == 1); let Resolution { source: _, target: _, event } = results.remove(0); match event { Event::Damage { amount, mitigation: _, colour: _, skill: _ } => assert_eq!(amount, 1023), _ => panic!("not damage"), }; } #[test] fn injure_test() { let mut x = Cryp::new() .named(&"muji".to_string()); let mut y = Cryp::new() .named(&"camel".to_string()); resolve(Skill::Injure, &mut x, &mut y, vec![]); assert!(y.immune(Skill::Heal).is_some()); // resolutions = heal(&mut y.clone(), &mut y, resolutions); } #[test] fn invert_test() { let mut x = Cryp::new() .named(&"muji".to_string()); let mut y = Cryp::new() .named(&"camel".to_string()); // give red shield but reduce to 0 y.red_life.force(64); y.red_life.reduce(64); x.red_damage.force(256 + 64); 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::TestHeal); assert!(y.green_life() == 768); // attack should heal and recharge red shield let mut results = attack(&mut x, &mut y, vec![], Skill::TestAttack); assert!(y.green_life() == 1024); match results.remove(0).event { Event::Inversion { skill } => assert_eq!(skill, Skill::TestAttack), _ => panic!("not inversion"), }; match results.remove(0).event { Event::Healing { skill: _, overhealing: _, amount } => assert_eq!(amount, 256), _ => panic!("not healing from inversion"), }; match results.remove(0).event { Event::Recharge { skill: _, red, blue: _ } => assert_eq!(red, 64), _ => panic!("not recharge from inversion"), }; } #[test] fn reflect_test() { let mut x = Cryp::new() .named(&"muji".to_string()); let mut y = Cryp::new() .named(&"camel".to_string()); reflect(&mut y.clone(), &mut y, vec![], Skill::Reflect); assert!(y.affected(Effect::Reflect)); let mut results = vec![]; results = resolve(Skill::TestAttack, &mut x, &mut y, results); assert!(x.green_life() == 768); let Resolution { source: _, target: _, event } = results.remove(0); match event { Event::Reflection { skill } => assert_eq!(skill, Skill::TestAttack), _ => panic!("not reflection"), }; let Resolution { source: _, target: _, event } = results.remove(0); match event { Event::Damage { amount, mitigation: _, colour: _, skill: _ } => assert_eq!(amount, 256), _ => panic!("not damage"), }; } #[test] fn siphon_test() { let mut x = Cryp::new() .named(&"muji".to_string()); let mut y = Cryp::new() .named(&"camel".to_string()); x.green_life.reduce(512); let mut results = resolve(Skill::Siphon, &mut x, &mut y, vec![]); assert!(y.affected(Effect::Siphon)); assert!(x.green_life() == (512 + 256.pct(Skill::SiphonTick.multiplier()))); let Resolution { source: _, target: _, event } = results.remove(0); match event { Event::Effect { effect, skill: _, duration: _ } => assert_eq!(effect, Effect::Siphon), _ => panic!("not siphon"), }; let Resolution { source: _, target: _, event } = results.remove(0); match event { Event::Damage { amount, skill: _, mitigation: _, colour: _} => assert_eq!(amount, 256.pct(Skill::SiphonTick.multiplier())), _ => panic!("not damage siphon"), }; let Resolution { source: _, target, event } = results.remove(0); match event { Event::Healing { amount, skill: _, overhealing: _ } => { assert_eq!(amount, 256.pct(Skill::SiphonTick.multiplier())); assert_eq!(target.id, x.id); }, _ => panic!("not healing"), }; } #[test] fn triage_test() { let mut x = Cryp::new() .named(&"muji".to_string()); let mut y = Cryp::new() .named(&"pretaliation".to_string()); // ensure it doesn't have 0 sd x.blue_damage.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 = Cryp::new() .named(&"muji".to_string()); let mut y = Cryp::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); let mut results = recharge(&mut x, &mut y, vec![], Skill::Recharge); let Resolution { source: _, target: _, event } = results.remove(0); match event { Event::Recharge { red, blue, skill: _ } => { assert!(red == 5); assert!(blue == 5); } _ => panic!("result was not recharge"), } } #[test] fn silence_test() { let mut x = Cryp::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 = Cryp::new() .named(&"muji".to_string()); x.blue_damage.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_damage(), 75); } #[test] fn purify_test() { let mut x = Cryp::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)); } }