use rand::{thread_rng, Rng}; use uuid::Uuid; use cryp::{Cryp, CrypEffect, Stat}; #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct Cast { pub id: Uuid, pub skill: Skill, pub source_team_id: Uuid, pub source_cryp_id: Uuid, pub target_cryp_id: Uuid, pub resolution: Resolution, } impl Cast { pub fn new(source_cryp_id: Uuid, source_team_id: Uuid, target_cryp_id: Uuid, skill: Skill) -> Cast { return Cast { id: Uuid::new_v4(), source_cryp_id, source_team_id, target_cryp_id, skill, resolution: Resolution::new(skill), }; } pub fn new_tick(source: &mut Cryp, target: &mut Cryp, skill: Skill) -> Cast { Cast::new(source.id, source.account, target.id, skill) } pub fn set_resolution(&mut self, cryp: &mut Cryp, target: &mut Cryp) -> &mut Cast { self.resolution = self.skill.resolve(cryp, target); self } pub fn used_cooldown(&self) -> bool { return self.skill.base_cd().is_some(); } } #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct Immunity { pub immune: bool, pub effects: Vec } impl Immunity { fn new() -> Immunity { Immunity { immune: false, effects: vec![] } } } #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct Disable { pub disabled: bool, pub effects: Vec } impl Disable { fn new() -> Disable { Disable { disabled: false, effects: vec![] } } } #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub enum ResolutionResult { Damage { amount: u64, mitigation: u64, category: Category , immunity: Immunity }, Healing { amount: u64, overhealing: u64, category: Category , immunity: Immunity }, Effect { effect: Effect, duration: u8, immunity: Immunity }, Removal { effect: Effect, immunity: Immunity }, Evasion { skill: Skill, evasion_rating: u64 }, } #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct Resolution { pub skill: Skill, pub disable: Disable, pub speed: u64, pub results: Vec, } impl Resolution { fn new(skill: Skill) -> Resolution { Resolution { skill, results: vec![], disable: Disable::new(), speed: 0 } } } 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, Empower, // magic Hex, Curse, Banish, Slow, Haste, Enslave, Mesmerise, Amplify, Silence, // magic immunity Shield, // effects over time Triage, Decay, Regen, Drain, SpeedDrain, 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::Shield => match skill.category() { Category::Blue => true, Category::Red => false, _ => false, }, Effect::Banish => true, _ => false, } } pub fn disables_skill(&self, skill: Skill) -> bool { match self { Effect::Stun => true, Effect::Hex => true, Effect::Banish => true, 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, _ => true, }, _ => false, } } pub fn modifications(&self) -> Vec { match self { Effect::Empower => vec![Stat::RedDamage], Effect::Vulnerable => vec![Stat::RedDamageTaken], Effect::Block => vec![Stat::RedDamageTaken], Effect::Amplify => vec![Stat::BlueDamage], Effect::Curse => vec![Stat::BlueDamageTaken], Effect::Haste => vec![Stat::Speed], Effect::Slow => vec![Stat::Speed], _ => vec![], } } // maybe increase by rng // roll little endian bits // and OR with base stat pub fn apply(&self, value: u64) -> u64 { match self { Effect::Empower => value << 1, Effect::Vulnerable => value << 1, Effect::Block => value >> 1, Effect::Amplify => value << 1, Effect::Curse => value << 1, Effect::Haste => value << 1, Effect::Slow => value >> 1, _ => { println!("{:?} does not have a mod effect", self); return value; }, } } pub fn category(&self) -> Category { match self { // physical Effect::Stun => Category::RedDebuff, Effect::Block => Category::RedBuff, Effect::Parry => Category::RedBuff, Effect::Bleed => Category::RedDebuff, Effect::Leech => Category::RedDebuff, Effect::Airborne => Category::RedDebuff, Effect::Untouchable => Category::RedBuff, Effect::Deadly => Category::RedBuff, Effect::Vulnerable => Category::RedDebuff, Effect::Fury => Category::RedBuff, Effect::Blind => Category::RedDebuff, Effect::Snare => Category::RedDebuff, Effect::Empower => Category::RedBuff, // magic Effect::Hex => Category::BlueDebuff, Effect::Curse => Category::BlueDebuff, Effect::Banish => Category::BlueDebuff, // todo randomise Effect::Slow => Category::BlueDebuff, Effect::Haste => Category::BlueBuff, Effect::Enslave => Category::BlueDebuff, Effect::Mesmerise => Category::BlueDebuff, Effect::Amplify => Category::BlueBuff, Effect::Silence => Category::BlueDebuff, // magic immunity Effect::Shield => Category::BlueBuff, // effects over time Effect::Triage => Category::BlueBuff, Effect::Decay => Category::BlueDebuff, Effect::Regen => Category::BlueBuff, Effect::Drain => Category::BlueDebuff, Effect::SpeedDrain => Category::BlueDebuff, Effect::SpeedIncrease => Category::BlueBuff, Effect::Ko => Category::Ko, } } pub fn duration(&self) -> u8 { match self { Effect::Stun => 2, Effect::Block => 1, Effect::Parry => 1, Effect::Vulnerable => 2, Effect::Snare => 2, Effect::Empower => 2, Effect::Hex => 2, Effect::Curse => 2, Effect::Banish => 1, Effect::Slow => 2, Effect::Haste => 2, Effect::Amplify => 2, Effect::Silence => 2, Effect::Shield => 2, Effect::Triage => 3, Effect::Decay => 3, Effect::Drain => 2, _ => { println!("{:?} does not have a duration", self); return 1; }, } } } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Category { Red, RedHeal, RedDamage, RedDebuff, RedBuff, RedTick, Blue, BlueDamage, BlueHeal, BlueDebuff, BlueBuff, BlueTick, Ko, } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Skill { Attack, // ----------------- // Nature // ----------------- Block, // reduce damage Parry, // avoid all damage Snare, Paralyse, Strangle, // physical dot and disable Stun, // Evade, // actively evade // ----------------- // Technology // ----------------- Replicate, Swarm, Orbit, Repair, Scan, // track? // ----------------- // Nonviolence // ----------------- Heal, Triage, // hot TriageTick, Throw, // no damage stun, adds vulnerable Charm, Calm, Rez, // Sleep, // Nightmare, // ------------------- // Destruction // ------------------- Blast, Amplify, Decay, // dot DecayTick, // dot Drain, DrainTick, Curse, Plague, // aoe dot Ruin, // aoe // ----------------- // Purity // ----------------- Empower, Slay, Shield, Silence, Inquiry, Purify, Purge, // Precision, // ----------------- // Chaos // ----------------- Banish, Hex, Fear, Taunt, Pause, // speed slow Haste, Slow, // used by tests, no cd, no damage TestTouch, TestStun, TestBlock, TestParry, TestDrain, } impl Skill { pub fn base_cd(&self) -> Cooldown { match self { Skill::Attack => None, // ----------------- // Nature // ----------------- Skill::Block => None, // reduce damage Skill::Parry => None, // avoid all damage Skill::Snare => Some(1), Skill::Paralyse => Some(1), Skill::Strangle => Some(2), // Strangle Skill::Stun => Some(1), // ----------------- // Technology // ----------------- Skill::Replicate => None, Skill::Swarm => Some(2), Skill::Orbit => Some(1), Skill::Repair => None, Skill::Scan => Some(1), // track? // ----------------- // Preservation // ----------------- Skill::Heal => None, Skill::Triage => None, // hot Skill::TriageTick => None, Skill::Throw => Some(1), // no damage stun, adds vulnerable Skill::Charm => Some(1), Skill::Calm => None, Skill::Rez => Some(2), // ----------------- // Destruction // ----------------- Skill::Blast => None, Skill::Amplify => Some(1), Skill::Decay => None, // dot Skill::DecayTick => None, Skill::Drain => Some(1), Skill::DrainTick => None, Skill::Curse => Some(1), Skill::Plague => Some(1), // aoe dot Skill::Ruin => Some(2), // aoe // ----------------- // Purity // ----------------- // Skill::Precision => None, Skill::Empower => Some(1), Skill::Slay => None, Skill::Shield => None, Skill::Silence => Some(1), Skill::Inquiry => Some(1), Skill::Purify => None, Skill::Purge => None, // ----------------- // Chaos // ----------------- Skill::Banish => Some(1), Skill::Hex => None, Skill::Fear => None, Skill::Taunt => Some(1), Skill::Pause => Some(1), // speed slow Skill::Haste => None, Skill::Slow => None, // ----------------- // Test // ----------------- Skill::TestTouch => None, Skill::TestStun => None, Skill::TestBlock => None, Skill::TestDrain => None, Skill::TestParry => None, } } pub fn category(&self) -> Category { match self { Skill::Attack => Category::Red, // ----------------- // Nature // ----------------- Skill::Block => Category::Red, // reduce damage Skill::Parry => Category::Red, // avoid all damage Skill::Snare => Category::Red, Skill::Paralyse => Category::Red, Skill::Strangle => Category::Red, // Strangle Skill::Stun => Category::Red, // ----------------- // Technology // ----------------- Skill::Replicate => Category::Red, Skill::Swarm => Category::Red, Skill::Orbit => Category::Red, Skill::Repair => Category::Red, Skill::Scan => Category::Red, // track? // ----------------- // Preservation // ----------------- Skill::Heal => Category::Red, Skill::Triage => Category::Blue, // hot Skill::TriageTick => Category::BlueTick, // hot Skill::Throw => Category::Red, // no damage stun, adds vulnerable Skill::Charm => Category::Blue, Skill::Calm => Category::Red, Skill::Rez => Category::Blue, // ----------------- // Destruction // ----------------- Skill::Blast => Category::Blue, Skill::Amplify => Category::Blue, Skill::Decay => Category::Blue, // dot Skill::DecayTick => Category::BlueTick, // hot Skill::Drain => Category::Blue, Skill::DrainTick => Category::BlueTick, // hot Skill::Curse => Category::Blue, Skill::Plague => Category::Blue, // aoe dot Skill::Ruin => Category::Blue, // aoe // ----------------- // Purity // ----------------- // Skill::Precision => 1, Skill::Empower => Category::Red, Skill::Slay => Category::Red, Skill::Shield => Category::Blue, Skill::Silence => Category::Blue, Skill::Inquiry => Category::Blue, Skill::Purify => Category::Blue, Skill::Purge => Category::Blue, // ----------------- // Chaos // ----------------- Skill::Banish => Category::Blue, Skill::Hex => Category::Blue, Skill::Fear => Category::Blue, Skill::Taunt => Category::Blue, Skill::Pause => Category::Blue, // extend durations // Skill::Lag => 2, // Skill::Haste => Category::Blue, Skill::Slow => Category::Blue, // ----------------- // Test // ----------------- Skill::TestTouch => Category::Red, Skill::TestStun => Category::Red, Skill::TestParry => Category::Red, Skill::TestBlock => Category::Red, Skill::TestDrain => Category::Blue, } } pub fn ko_castable(&self) -> bool { match self { Skill::TriageTick => true, Skill::DecayTick => true, Skill::DrainTick => true, _ => false, } } pub fn speed(&self) -> u8 { match self { // defensive block Skill::Block => 10, // reduce damage Skill::Parry => 10, // avoid all damage Skill::Snare => 10, Skill::Shield => 10, // avoid magic damage, // fast phys combat Skill::Attack => 5, Skill::Paralyse => 5, Skill::Strangle => 5, Skill::Banish => 5, Skill::Blast => 5, Skill::Decay => 5, // dot // magic combat trickery Skill::Triage => 3, // hot Skill::Slow => 3, Skill::Fear => 2, Skill::Amplify => 3, Skill::Curse => 3, Skill::Empower => 3, Skill::Haste => 3, // general combat Skill::DecayTick => 2, // hot Skill::Drain => 2, Skill::DrainTick => 2, // hot Skill::Hex => 2, Skill::Pause => 2, // extend durations Skill::Plague => 2, // aoe dot Skill::Silence => 2, Skill::Stun => 2, Skill::Throw => 2, // no damage stun, adds vulnerable Skill::TriageTick => 2, // hot Skill::Heal => 1, Skill::Purify => 1, Skill::Purge => 1, // unimplemented // Skill::Precision => 1, // Skill::Lag => 2, // Skill::Taunt => 10, Skill::Ruin => 3, // aoe Skill::Slay => 1, Skill::Charm => 2, Skill::Calm => 2, Skill::Inquiry => 2, Skill::Rez => 4, Skill::Replicate => 1, Skill::Swarm => 3, Skill::Orbit => 2, Skill::Repair => 1, Skill::Scan => 2, // track? // ----------------- // Test // ----------------- Skill::TestTouch => 10, Skill::TestStun => 5, Skill::TestBlock => 10, Skill::TestParry => 10, Skill::TestDrain => 10, } } pub fn resolve(&self, source: &mut Cryp, target: &mut Cryp) -> Resolution { let mut rng = thread_rng(); let _base: u64 = rng.gen(); let speed = source.skill_speed(*self); let mut resolution = Resolution { skill: *self, results: vec![], disable: source.disabled(*self), speed }; if target.is_ko() { return resolution; } if resolution.disable.disabled { return resolution; } match self.category() == Category::Red { true => { if let Some(evasion) = target.evade(*self) { resolution.results.push(evasion); return resolution; } }, false => (), } match self { Skill::Attack => attack(source, target, resolution), // ----------------- // Nature // ----------------- Skill::Block => block(source, target, resolution), Skill::Parry => parry(source, target, resolution), Skill::Snare => snare(source, target, resolution), // TODO prevent physical moves Skill::Paralyse => panic!("nyi"), // no physical moves Skill::Strangle => panic!("nyi"), // no physical moves Skill::Stun => stun(source, target, resolution), // ----------------- // Technology // ----------------- Skill::Replicate => panic!("nyi"), Skill::Swarm => panic!("nyi"), Skill::Orbit => panic!("nyi"), Skill::Repair => panic!("nyi"), Skill::Scan => panic!("nyi"), // track? // ----------------- // Preservation // ----------------- Skill::Heal => heal(source, target, resolution), Skill::Triage => triage(source, target, resolution), // hot Skill::TriageTick => triage_tick(source, target, resolution), // hot Skill::Throw => throw(source, target, resolution), // no damage stun, adds vulnerable Skill::Charm => panic!("nyi"), // target casts random spell on teammate Skill::Calm => panic!("nyi"), // physical fear, taunt removal Skill::Rez => panic!("nyi"), // ----------------- // Destruction // ----------------- Skill::Blast => blast(source, target, resolution), Skill::Amplify => amplify(source, target, resolution), // increase magic damage Skill::Decay => decay(source, target, resolution), // dot Skill::DecayTick => decay_tick(source, target, resolution), // dot Skill::Drain => drain(source, target, resolution), Skill::DrainTick => drain_tick(source, target, resolution), // hot Skill::Curse => curse(source, target, resolution), Skill::Plague => panic!("nyi"), // dot that spreads every turn Skill::Ruin => panic!("nyi"), // aoe version of blast // ----------------- // Purity // ----------------- // Skill::Precision => panic!("nyi"), Skill::Empower => empower(source, target, resolution), // increased phys damage Skill::Slay => panic!("nyi"), // phys damage mult by target magic damage Skill::Shield => shield(source, target, resolution), // target is immune to magic damage and fx Skill::Silence => silence(source, target, resolution), // target cannot cast spells Skill::Inquiry => panic!("nyi"), // Skill::Purify => purify(source, target, resolution), // dispel all debuffs Skill::Purge => purge(source, target, resolution), // dispel all buffs // ----------------- // Chaos // ----------------- Skill::Banish => banish(source, target, resolution), // TODO prevent all actions Skill::Hex => hex(source, target, resolution), // todo prevent casting Skill::Fear => panic!("nyi"), // cast random spell on self Skill::Taunt => panic!("nyi"), // target forced to attack Skill::Pause => panic!("nyi"), // speed slow Skill::Haste => haste(source, target, resolution), // speed slow Skill::Slow => slow(source, target, resolution), // speed slow // ----------------- // Test // ----------------- Skill::TestTouch => Resolution { skill: Skill::TestTouch, results: vec![], disable: Disable::new(), speed: 0 }, Skill::TestStun => stun(source, target, resolution), Skill::TestBlock => block(source, target, resolution), Skill::TestParry => parry(source, target, resolution), Skill::TestDrain => drain(source, target, resolution), } } pub fn self_targeting(&self) -> bool { match self { Skill::Block => true, Skill::Parry => true, Skill::TestBlock => true, Skill::TestParry => true, _ => false, } } pub fn defensive(&self) -> bool { match self { Skill::Heal | Skill::Triage | Skill::Empower | Skill::Purify | Skill::Calm | Skill::Parry | Skill::Block => true, _ => false, } } } fn attack(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let amount = cryp.red_damage(); resolution.results.push(target.deal_red_damage(Skill::Attack, amount)); return resolution; } fn stun(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let effect = CrypEffect { effect: Effect::Stun, duration: Effect::Stun.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Stun, effect)); return resolution; } fn throw(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let stun = CrypEffect { effect: Effect::Stun, duration: Effect::Stun.duration(), tick: None }; let vulnerable = CrypEffect { effect: Effect::Vulnerable, duration: Effect::Vulnerable.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Throw, stun)); resolution.results.push(target.add_effect(Skill::Throw, vulnerable)); return resolution; } fn block(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let block = CrypEffect { effect: Effect::Block, duration: Effect::Block.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Block, block)); return resolution; } fn parry(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let effect = CrypEffect { effect: Effect::Parry, duration: Effect::Parry.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Parry, effect)); return resolution; } fn snare(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let snare = CrypEffect { effect: Effect::Snare, duration: Effect::Snare.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Snare, snare)); return resolution; } fn empower(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let empower = CrypEffect { effect: Effect::Empower, duration: Effect::Empower.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Empower, empower)); return resolution; } fn heal(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let amount = cryp.blue_damage(); resolution.results.push(target.heal(Skill::Heal, amount)); return resolution; } fn triage(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let triage = CrypEffect { effect: Effect::Triage, duration: Effect::Triage.duration(), tick: Some(Cast::new_tick(cryp, target, Skill::TriageTick)), }; let immunity = target.immune(Skill::Triage); let immune = immunity.immune; let snare_result = ResolutionResult::Effect { effect: triage.effect, duration: triage.duration, immunity, }; resolution.results.push(snare_result); if !immune { target.effects.push(triage); } return resolution; } fn triage_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let amount = cryp.blue_damage().wrapping_div(2); resolution.results.push(target.heal(Skill::TriageTick, amount)); return resolution; } fn blast(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let amount = cryp.blue_damage(); resolution.results.push(target.deal_blue_damage(Skill::Blast, amount)); return resolution; } fn amplify(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let amplify = CrypEffect { effect: Effect::Amplify, duration: Effect::Amplify.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Amplify, amplify)); return resolution;; } fn haste(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let effect = CrypEffect { effect: Effect::Haste, duration: Effect::Haste.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Haste, effect)); return resolution;; } fn slow(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let effect = CrypEffect { effect: Effect::Slow, duration: Effect::Slow.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Slow, effect)); return resolution;; } fn decay(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let decay = CrypEffect { effect: Effect::Decay, duration: Effect::Decay.duration(), tick: Some(Cast::new_tick(cryp, target, Skill::DecayTick)), }; resolution.results.push(target.add_effect(Skill::Decay, decay)); return resolution; } fn decay_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let amount = cryp.blue_damage(); resolution.results.push(target.deal_blue_damage(Skill::DecayTick, amount)); return resolution; } fn hex(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let hex = CrypEffect { effect: Effect::Hex, duration: Effect::Hex.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Hex, hex)); return resolution;; } fn curse(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let curse = CrypEffect { effect: Effect::Curse, duration: Effect::Curse.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Curse, curse)); return resolution;; } fn drain(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let drain = CrypEffect { effect: Effect::Drain, duration: Effect::Drain.duration(), tick: Some(Cast::new_tick(cryp, target, Skill::DrainTick)), }; resolution.results.push(target.add_effect(Skill::Drain, drain)); return resolution;; } fn drain_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let amount = cryp.blue_damage(); let drain_damage = target.deal_blue_damage(Skill::DrainTick, amount); resolution.results.push(drain_damage.clone()); match drain_damage { ResolutionResult::Damage { amount, mitigation, category: _, immunity } => { if !immunity.immune { resolution.results.push(cryp.heal(Skill::Heal, amount)); } }, _ => panic!("drain tick damage not dealt {:?}", drain_damage), } return resolution; } fn shield(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let shield = CrypEffect { effect: Effect::Shield, duration: Effect::Shield.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Shield, shield)); return resolution; } fn silence(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let silence = CrypEffect { effect: Effect::Silence, duration: Effect::Silence.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Silence, silence)); return resolution; } fn purge(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let immunity = target.immune(Skill::Purge); let immune = immunity.immune; if !immune { for (i, ce) in target.effects.clone().iter_mut().enumerate() { if ce.effect.category() == Category::BlueBuff { target.effects.remove(i); resolution.results.push(ResolutionResult::Removal { effect: ce.effect, immunity: immunity.clone() }); } } } return resolution; } fn purify(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let immunity = target.immune(Skill::Purify); let immune = immunity.immune; if !immune { for (i, ce) in target.effects.clone().iter_mut().enumerate() { if ce.effect.category() == Category::BlueDebuff { target.effects.remove(i); resolution.results.push(ResolutionResult::Removal { effect: ce.effect, immunity: immunity.clone() }); } } } return resolution; } fn banish(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { let banish = CrypEffect { effect: Effect::Banish, duration: Effect::Banish.duration(), tick: None }; resolution.results.push(target.add_effect(Skill::Banish, banish)); return resolution; } #[cfg(test)] mod tests { use skill::*; #[test] fn heal_test() { let mut x = Cryp::new() .named(&"muji".to_string()) .level(8) .learn(Skill::Heal) .create(); let mut y = Cryp::new() .named(&"camel".to_string()) .level(8) .learn(Skill::Heal) .create(); x.deal_red_damage(Skill::Attack, 5); heal(&mut y, &mut x, Resolution::new(Skill::Heal)); } #[test] fn decay_test() { let mut x = Cryp::new() .named(&"muji".to_string()) .level(8) .create(); let mut y = Cryp::new() .named(&"camel".to_string()) .level(8) .create(); let mut log = vec![]; decay(&mut x, &mut y, Resolution::new(Skill::Triage)); assert!(y.effects.iter().any(|e| e.effect == Effect::Decay)); y.reduce_effect_durations(&mut log); let _decay = y.effects.iter().find(|e| e.effect == Effect::Decay); // assert!(y.hp() == y.stamina().saturating_sub(decay.unwrap().tick.unwrap().amount)); } #[test] fn block_test() { let mut x = Cryp::new() .named(&"muji".to_string()) .level(8) .create(); let mut y = Cryp::new() .named(&"camel".to_string()) .level(8) .create(); // ensure it doesn't have 0 pd x.red_damage.force(100); y.hp.force(500); block(&mut y.clone(), &mut y, Resolution::new(Skill::Block)); assert!(y.effects.iter().any(|e| e.effect == Effect::Block)); let res = attack(&mut x, &mut y, Resolution::new(Skill::Attack)); match res.results[0] { ResolutionResult::Damage { amount, mitigation: _, category: _, immunity: _ } => assert_eq!(amount, 50), _ => panic!("not damage"), }; } #[test] fn triage_test() { let mut x = Cryp::new() .named(&"muji".to_string()) .level(8) .create(); let mut y = Cryp::new() .named(&"pretaliation".to_string()) .level(8) .create(); // ensure it doesn't have 0 sd x.blue_damage.force(50); // remove all mitigation y.red_shield.force(0); y.blue_shield.force(0); y.deal_red_damage(Skill::Attack, 5); let prev_hp = y.hp(); let res = Resolution::new(Skill::Triage); triage(&mut x, &mut y, res); assert!(y.effects.iter().any(|e| e.effect == Effect::Triage)); let res = Resolution::new(Skill::TriageTick); triage_tick(&mut x, &mut y, res); assert!(y.hp() > prev_hp); } #[test] fn silence_test() { let mut x = Cryp::new() .named(&"muji".to_string()) .level(8) .create(); silence(&mut x.clone(), &mut x, Resolution::new(Skill::Silence)); assert!(x.effects.iter().any(|e| e.effect == Effect::Silence)); assert!(x.disabled(Skill::Silence).disabled); } #[test] fn amplify_test() { let mut x = Cryp::new() .named(&"muji".to_string()) .level(8) .create(); x.blue_damage.force(50); amplify(&mut x.clone(), &mut x, Resolution::new(Skill::Amplify)); assert!(x.effects.iter().any(|e| e.effect == Effect::Amplify)); assert_eq!(x.blue_damage(), 100); } #[test] fn purify_test() { let mut x = Cryp::new() .named(&"muji".to_string()) .level(8) .create(); decay(&mut x.clone(), &mut x, Resolution::new(Skill::Decay)); assert!(x.effects.iter().any(|e| e.effect == Effect::Decay)); purify(&mut x.clone(), &mut x, Resolution::new(Skill::Purify)); assert!(!x.effects.iter().any(|e| e.effect == Effect::Decay)); } }