From 2a002d08d4f37e39d123d884c9a1d27ab6bd15c2 Mon Sep 17 00:00:00 2001 From: ntr Date: Mon, 26 Nov 2018 23:07:07 +1100 Subject: [PATCH] goddamn borrow checker --- server/src/cryp.rs | 10 +- server/src/game.rs | 11 +- server/src/skill.rs | 728 +++++++++++++++++++++++++++++++------------- 3 files changed, 533 insertions(+), 216 deletions(-) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 72aaa149..fbe75b83 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -9,7 +9,7 @@ use failure::err_msg; use account::Account; use rpc::{CrypSpawnParams, CrypLearnParams, CrypForgetParams}; -use skill::{Skill, Cooldown, Effect, Cast, Source}; +use skill::{Skill, Cooldown, Effect, Cast, Immunity}; use game::{Log}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -29,7 +29,7 @@ impl CrypSkill { } } -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct CrypEffect { pub effect: Effect, pub duration: u8, @@ -178,17 +178,17 @@ impl Cryp { self.hp.base == 0 } - pub fn immune(&self, skill: Skill) -> (bool, Vec) { + pub fn immune(&self, skill: Skill) -> Immunity { let immunities = self.effects.iter() .filter(|e| e.effect.immune(skill)) .map(|e| e.effect) .collect::>(); if immunities.len() > 0 { - return (true, immunities); + return Immunity { immune: true, effects: immunities}; } - return (false, vec![]); + return Immunity { immune: false, effects: vec![]}; } pub fn is_stunned(&self) -> bool { diff --git a/server/src/game.rs b/server/src/game.rs index 8c5c546f..8d8cc35f 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -389,11 +389,9 @@ impl Game { let mut source = self.cryp_by_id(skill.source_cryp_id).unwrap().clone(); let mut target = self.cryp_by_id(skill.target_cryp_id.unwrap()).unwrap().clone(); - // self.log.push(format!("{:?} uses {:?} on {:?}", source.name, skill.skill, target.name)); skill.set_resolution(&mut source, &mut target, &mut self.log); self.resolved.push(skill.clone()); - self.update_cryp(&mut source); self.update_cryp(&mut target); @@ -444,6 +442,15 @@ impl Game { fn finish(&mut self) -> &mut Game { self.phase = Phase::Finish; + self.log.push(format!("Game finished.")); + + { + let winner = self.teams.iter().find(|t| t.cryps.iter().any(|c| !c.is_ko())); + match winner { + Some(w) => self.log.push(format!("Winner: {:?}", w.id)), + None => self.log.push(format!("Game was drawn.")), + }; + } self.stack.clear(); diff --git a/server/src/skill.rs b/server/src/skill.rs index 24077b5e..3b038521 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -4,13 +4,15 @@ use uuid::Uuid; use game::{Log}; use cryp::{Cryp, CrypEffect, Stat}; -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct Cast { pub id: Uuid, - pub skill: Skill, pub source_team_id: Uuid, + pub skill: Skill, + pub source_team_id: Uuid, pub source_cryp_id: Uuid, pub target_cryp_id: Option, pub target_team_id: Uuid, + pub resolution: Resolution, } impl Cast { @@ -28,6 +30,7 @@ impl Cast { target_cryp_id, target_team_id, skill, + resolution: Resolution { skill, results: vec![] }, }; } @@ -38,7 +41,7 @@ impl Cast { } pub fn set_resolution(&mut self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> &mut Cast { - self.skill.resolve(cryp, target, log); + self.resolution = self.skill.resolve(cryp, target, log); self } @@ -52,15 +55,29 @@ impl Cast { } } + +#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] +pub struct Immunity { + pub immune: bool, + pub effects: Vec +} + +#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] +pub enum ResolutionResult { + Damage { amount: u64, category: Category , immunity: Immunity }, + Effect { effect: Effect, duration: u8, immunity: Immunity }, + Removal { effect: Effect, immunity: Immunity }, +} + #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct Resolution { - pub base: u64, - pub result: Option, + pub skill: Skill, + pub results: Vec, } pub type Cooldown = Option; -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub enum Effect { // physical Stun, @@ -111,9 +128,10 @@ impl Effect { Skill::Attack => true, _ => false, }, - Effect::Shield => match skill.cast_type() { - Damage::Magic => true, - Damage::Physical => false, + Effect::Shield => match skill.category() { + Category::Spell => true, + Category::Physical => false, + _ => false, }, Effect::Banish => true, _ => false, @@ -123,9 +141,10 @@ impl Effect { pub fn prevents_casting(&self, skill: Skill) -> bool { match self { Effect::Stun => true, - Effect::Silence => match skill.cast_type() { - Damage::Magic => true, - Damage::Physical => false, + Effect::Silence => match skill.category() { + Category::Spell => true, + Category::Physical => false, + _ => false, }, _ => false, } @@ -150,67 +169,62 @@ impl Effect { } } - pub fn source(&self) -> Source { + pub fn category(&self) -> Category { match self { // physical - Effect::Stun => Source::Debuff, - Effect::Block => Source::Buff, - Effect::Bleed => Source::Debuff, - Effect::Leech => Source::Debuff, - Effect::Airborne => Source::Buff, - Effect::Untouchable => Source::Buff, - Effect::Deadly => Source::Buff, - Effect::Vulnerable => Source::Debuff, - Effect::Fury => Source::Buff, - Effect::Evasion => Source::Buff, - Effect::Blind => Source::Debuff, - Effect::Snare => Source::Debuff, + Effect::Stun => Category::PhysDebuff, + Effect::Block => Category::PhysBuff, + Effect::Bleed => Category::PhysDebuff, + Effect::Leech => Category::PhysDebuff, + Effect::Airborne => Category::PhysDebuff, + Effect::Untouchable => Category::PhysBuff, + Effect::Deadly => Category::PhysBuff, + Effect::Vulnerable => Category::PhysDebuff, + Effect::Fury => Category::PhysBuff, + Effect::Evasion => Category::PhysBuff, + Effect::Blind => Category::PhysDebuff, + Effect::Snare => Category::PhysDebuff, - Effect::Empower => Source::Buff, + Effect::Empower => Category::PhysBuff, // magic - Effect::Hex => Source::Debuff, - Effect::Curse => Source::Debuff, - Effect::Banish => Source::Debuff, // todo randomise - Effect::Slow => Source::Debuff, - Effect::Haste => Source::Buff, - Effect::Enslave => Source::Debuff, - Effect::Mesmerise => Source::Debuff, - Effect::Amplify => Source::Buff, - Effect::Silence => Source::Debuff, + Effect::Hex => Category::SpellDebuff, + Effect::Curse => Category::SpellDebuff, + Effect::Banish => Category::SpellDebuff, // todo randomise + Effect::Slow => Category::SpellDebuff, + Effect::Haste => Category::SpellBuff, + Effect::Enslave => Category::SpellDebuff, + Effect::Mesmerise => Category::SpellDebuff, + Effect::Amplify => Category::SpellBuff, + Effect::Silence => Category::SpellDebuff, // magic immunity - Effect::Shield => Source::Buff, + Effect::Shield => Category::SpellBuff, // effects over time - Effect::Triage => Source::Buff, - Effect::Decay => Source::Debuff, - Effect::Regen => Source::Buff, - Effect::Drain => Source::Debuff, + Effect::Triage => Category::SpellBuff, + Effect::Decay => Category::SpellDebuff, + Effect::Regen => Category::SpellBuff, + Effect::Drain => Category::SpellDebuff, - Effect::SpeedDrain => Source::Debuff, - Effect::SpeedIncrease => Source::Buff, + Effect::SpeedDrain => Category::SpellDebuff, + Effect::SpeedIncrease => Category::SpellBuff, } } } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub enum Damage { +pub enum Category { Physical, - Magic, -} - -// #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -// enum Style { -// Offensive, -// Defensive, -// } - -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub enum Source { - Buff, - Debuff, - Stat, + PhysHeal, + PhysDmg, + PhysDebuff, + PhysBuff, + Spell, + SpellDmg, + SpellHeal, + SpellDebuff, + SpellBuff, } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -379,88 +393,88 @@ impl Skill { } } - pub fn cast_type(&self) -> Damage { + pub fn category(&self) -> Category { match self { - Skill::Attack => Damage::Physical, + Skill::Attack => Category::Physical, // ----------------- // Nature // ----------------- - Skill::Block => Damage::Physical, // reduce dmg - Skill::Evade => Damage::Physical, - Skill::Parry => Damage::Physical, // avoid all dmg - Skill::Snare => Damage::Physical, + Skill::Block => Category::Physical, // reduce dmg + Skill::Evade => Category::Physical, + Skill::Parry => Category::Physical, // avoid all dmg + Skill::Snare => Category::Physical, - Skill::Paralyse => Damage::Physical, - Skill::Strangle => Damage::Physical, + Skill::Paralyse => Category::Physical, + Skill::Strangle => Category::Physical, // Strangle - Skill::Stun => Damage::Physical, - Skill::Evasion => Damage::Physical, // additional layer of dmg avoidance + Skill::Stun => Category::Physical, + Skill::Evasion => Category::Physical, // additional layer of dmg avoidance // ----------------- // Technology // ----------------- - Skill::Replicate => Damage::Physical, - Skill::Swarm => Damage::Physical, - Skill::Orbit => Damage::Physical, - Skill::Repair => Damage::Physical, - Skill::Scan => Damage::Physical, // track? + Skill::Replicate => Category::Physical, + Skill::Swarm => Category::Physical, + Skill::Orbit => Category::Physical, + Skill::Repair => Category::Physical, + Skill::Scan => Category::Physical, // track? // ----------------- // Preservation // ----------------- - Skill::Heal => Damage::Physical, - Skill::Triage => Damage::Physical, // hot - Skill::TriageTick => Damage::Physical, // hot - Skill::Throw => Damage::Physical, // no dmg stun, adds vulnerable - Skill::Charm => Damage::Physical, - Skill::Calm => Damage::Physical, - Skill::Rez => Damage::Physical, + Skill::Heal => Category::Physical, + Skill::Triage => Category::Physical, // hot + Skill::TriageTick => Category::Physical, // hot + Skill::Throw => Category::Physical, // no dmg stun, adds vulnerable + Skill::Charm => Category::Physical, + Skill::Calm => Category::Physical, + Skill::Rez => Category::Physical, // ----------------- // Destruction // ----------------- - Skill::Blast => Damage::Magic, - Skill::Amplify => Damage::Magic, - Skill::Decay => Damage::Magic, // dot - Skill::DecayTick => Damage::Magic, // hot - Skill::Drain => Damage::Magic, - Skill::DrainTick => Damage::Magic, // hot - Skill::Curse => Damage::Magic, - Skill::Plague => Damage::Magic, // aoe dot - Skill::Ruin => Damage::Magic, // aoe + Skill::Blast => Category::Spell, + Skill::Amplify => Category::Spell, + Skill::Decay => Category::Spell, // dot + Skill::DecayTick => Category::Spell, // hot + Skill::Drain => Category::Spell, + Skill::DrainTick => Category::Spell, // hot + Skill::Curse => Category::Spell, + Skill::Plague => Category::Spell, // aoe dot + Skill::Ruin => Category::Spell, // aoe // ----------------- // Purity // ----------------- // Skill::Precision => 1, - Skill::Empower => Damage::Physical, - Skill::Slay => Damage::Physical, - Skill::Shield => Damage::Magic, - Skill::Silence => Damage::Magic, - Skill::Inquiry => Damage::Magic, - Skill::Purify => Damage::Magic, - Skill::Purge => Damage::Magic, + Skill::Empower => Category::Physical, + Skill::Slay => Category::Physical, + Skill::Shield => Category::Spell, + Skill::Silence => Category::Spell, + Skill::Inquiry => Category::Spell, + Skill::Purify => Category::Spell, + Skill::Purge => Category::Spell, // ----------------- // Chaos // ----------------- - Skill::Banish => Damage::Magic, - Skill::Hex => Damage::Magic, - Skill::Fear => Damage::Magic, - Skill::Taunt => Damage::Magic, - Skill::Pause => Damage::Magic, // extend durations + Skill::Banish => Category::Spell, + Skill::Hex => Category::Spell, + Skill::Fear => Category::Spell, + Skill::Taunt => Category::Spell, + Skill::Pause => Category::Spell, // extend durations // Skill::Lag => 2, // // ----------------- // Test // ----------------- - Skill::TestTouch => Damage::Physical, - Skill::TestStun => Damage::Physical, - Skill::TestBlock => Damage::Physical, - Skill::TestDrain => Damage::Magic, + Skill::TestTouch => Category::Physical, + Skill::TestStun => Category::Physical, + Skill::TestBlock => Category::Physical, + Skill::TestDrain => Category::Spell, } } @@ -549,28 +563,10 @@ impl Skill { } } - pub fn resolve(&self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + pub fn resolve(&self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { let mut rng = thread_rng(); let base: u64 = rng.gen(); - // let res = Resolution { base, result: None }; - - // println!("{:?}'s stats", self.name); - // println!("{:064b} <- finalised", roll.result); - // roll.result = roll.result & stat(); - - // println!("{:064b} & <- attribute roll", stat()); - // println!("{:064b} = {:?}", roll.result, roll.result); - // println!(""); - - // return Some(roll); - - let (immune, reason) = target.immune(*self); - if immune { - log.push(format!("{:?} -> {:?} | {:?} immune: {:?}", cryp.name, target.name, self, reason)); - return; - } - match self { Skill::Attack => attack(cryp, target, log), // ----------------- @@ -644,11 +640,11 @@ impl Skill { // ----------------- // Test // ----------------- - Skill::TestTouch => (), + Skill::TestTouch => Resolution { skill: Skill::TestTouch, results: vec![] }, Skill::TestStun => stun(cryp, target, log), Skill::TestBlock => block(cryp, target, log), Skill::TestDrain => drain(cryp, target, log), - }; + } } pub fn duration(&self) -> u8 { @@ -685,159 +681,473 @@ impl Skill { } } -fn attack(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - target.deal_phys_dmg(cryp.phys_dmg()); +fn attack(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let amount = cryp.phys_dmg(); + let immunity = target.immune(Skill::Attack); + + let attack_result = ResolutionResult::Damage { + amount, + category: Category::PhysDmg, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Attack, results: vec![attack_result] }; + + if !immunity.immune { + target.deal_phys_dmg(amount); + } + log.push(format!("{:?} -> {:?} | Attack for {:?}", cryp.name, target.name, cryp.phys_dmg)); + + return resolution; } -fn stun(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { +fn stun(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { let stun = CrypEffect { effect: Effect::Stun, duration: Skill::Stun.duration(), tick: None }; - target.effects.push(stun); + let immunity = target.immune(Skill::Stun); + + let attack_result = ResolutionResult::Effect { + effect: stun.effect, + duration: stun.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Stun, results: vec![attack_result] }; + + if !immunity.immune { + target.effects.push(stun); + } + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration)); + return resolution; } -fn throw(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { +fn throw(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { let stun = CrypEffect { effect: Effect::Stun, duration: Skill::Stun.duration(), tick: None }; let vulnerable = CrypEffect { effect: Effect::Vulnerable, duration: Skill::Stun.duration(), tick: None }; - target.effects.push(stun); - target.effects.push(vulnerable); + let immunity = target.immune(Skill::Throw); + + let stun_result = ResolutionResult::Effect { + effect: stun.effect, + duration: stun.duration, + immunity, + }; + + let vulnerable_result = ResolutionResult::Effect { + effect: vulnerable.effect, + duration: vulnerable.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Throw, results: vec![stun_result, vulnerable_result] }; + + if !immunity.immune { + target.effects.push(stun); + target.effects.push(vulnerable); + } + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration)); - log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, vulnerable.effect, vulnerable.duration)); + return resolution; } -fn block(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let effect = CrypEffect { effect: Effect::Block, duration: Skill::Block.duration(), tick: None }; - target.effects.push(effect); - log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); +fn block(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let block = CrypEffect { effect: Effect::Block, duration: Skill::Block.duration(), tick: None }; + let immunity = target.immune(Skill::Block); + + let block_result = ResolutionResult::Effect { + effect: block.effect, + duration: block.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Block, results: vec![block_result] }; + + if !immunity.immune { + target.effects.push(block); + } + + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", target.name, target.name, block.effect, block.duration)); + return resolution; } -fn snare(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let effect = CrypEffect { effect: Effect::Snare, duration: Skill::Snare.duration(), tick: None }; - target.effects.push(effect); - log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); +fn snare(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let snare = CrypEffect { effect: Effect::Snare, duration: Skill::Snare.duration(), tick: None }; + let immunity = target.immune(Skill::Snare); + + let snare_result = ResolutionResult::Effect { + effect: snare.effect, + duration: snare.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Snare, results: vec![snare_result] }; + + if !immunity.immune { + target.effects.push(snare); + } + + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, snare.effect, snare.duration)); + return resolution; } -fn empower(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let effect = CrypEffect { effect: Effect::Empower, duration: Skill::Empower.duration(), tick: None }; - target.effects.push(effect); - log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); +fn empower(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let empower = CrypEffect { effect: Effect::Empower, duration: Skill::Empower.duration(), tick: None }; + let immunity = target.immune(Skill::Empower); + + let snare_result = ResolutionResult::Effect { + effect: empower.effect, + duration: empower.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Empower, results: vec![snare_result] }; + + if !immunity.immune { + target.effects.push(empower); + } + + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, empower.effect, empower.duration)); + return resolution; } -fn heal(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let (healing, overhealing) = target.heal(cryp.phys_dmg()); - log.push(format!("{:?} -> {:?} | Heal for {:?} ({:?} OH)", cryp.name, target.name, healing, overhealing)); +// TODO put overhealing back +fn heal(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let amount = cryp.phys_dmg(); + let immunity = target.immune(Skill::Heal); + + let heal_result = ResolutionResult::Damage { + amount, + category: Category::PhysHeal, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Heal, results: vec![heal_result] }; + + if !immunity.immune { + let (healing, overhealing) = target.heal(amount); + } + + log.push(format!("{:?} -> {:?} | Attack for {:?}", cryp.name, target.name, amount)); + + return resolution; + } -fn triage(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let effect = CrypEffect { +fn triage(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let triage = CrypEffect { effect: Effect::Triage, duration: Skill::Triage.duration(), tick: Some(Cast::new_tick(cryp, target, Skill::TriageTick)), }; - target.effects.push(effect); - log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); + let immunity = target.immune(Skill::Triage); + + let snare_result = ResolutionResult::Effect { + effect: triage.effect, + duration: triage.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Triage, results: vec![snare_result] }; + + if !immunity.immune { + target.effects.push(triage); + } + + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, triage.effect, triage.duration)); + return resolution; } -fn triage_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { +fn triage_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { let amount = cryp.spell_dmg().wrapping_div(2); - let (healing, overhealing) = target.heal(amount); - log.push(format!("{:?} -> {:?} | Triage for {:?} ({:?} OH)", cryp.name, target.name, healing, overhealing)); + let immunity = target.immune(Skill::TriageTick); + + let heal_result = ResolutionResult::Damage { + amount, + category: Category::PhysHeal, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::TriageTick, results: vec![heal_result] }; + + if !immunity.immune { + let (healing, overhealing) = target.heal(amount); + } + + log.push(format!("{:?} -> {:?} | Attack for {:?}", cryp.name, target.name, amount)); + + return resolution; } -fn blast(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { +fn blast(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { let amount = cryp.spell_dmg(); - log.push(format!("{:?} -> {:?} | Blast for {:?}", cryp.name, target.name, amount)); - target.deal_spell_dmg(amount); + let immunity = target.immune(Skill::Blast); + + let blast_result = ResolutionResult::Damage { + amount, + category: Category::SpellDmg, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Blast, results: vec![blast_result] }; + + if !immunity.immune { + target.deal_spell_dmg(amount); + } + + return resolution; } -fn amplify(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let effect = CrypEffect { effect: Effect::Amplify, duration: Skill::Amplify.duration(), tick: None }; - target.effects.push(effect); - log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); +fn amplify(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let amplify = CrypEffect { effect: Effect::Amplify, duration: Skill::Amplify.duration(), tick: None }; + let immunity = target.immune(Skill::Amplify); + + let amplify_result = ResolutionResult::Effect { + effect: amplify.effect, + duration: amplify.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Amplify, results: vec![amplify_result] }; + + if !immunity.immune { + target.effects.push(amplify); + } + + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, amplify.effect, amplify.duration)); + return resolution;; } -fn decay(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let effect = CrypEffect { +fn decay(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let decay = CrypEffect { effect: Effect::Decay, duration: Skill::Decay.duration(), tick: Some(Cast::new_tick(cryp, target, Skill::DecayTick)), }; - target.effects.push(effect); - log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); + let immunity = target.immune(Skill::Empower); + + let decay_result = ResolutionResult::Effect { + effect: decay.effect, + duration: decay.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Decay, results: vec![decay_result] }; + + if !immunity.immune { + target.effects.push(decay); + } + + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, decay.effect, decay.duration)); + return resolution; } -fn decay_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { +fn decay_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { let amount = cryp.spell_dmg(); - log.push(format!("{:?} -> {:?} | Decay for {:?}", cryp.name, target.name, amount)); - target.deal_spell_dmg(amount); + let immunity = target.immune(Skill::DecayTick); + + let decay_tick_result = ResolutionResult::Damage { + amount, + category: Category::SpellDmg, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::DecayTick, results: vec![decay_tick_result] }; + + if !immunity.immune { + target.deal_spell_dmg(amount); + } + + return resolution; } -fn hex(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let effect = CrypEffect { effect: Effect::Hex, duration: Skill::Hex.duration(), tick: None }; - target.effects.push(effect); - log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); +fn hex(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let hex = CrypEffect { effect: Effect::Hex, duration: Skill::Hex.duration(), tick: None }; + let immunity = target.immune(Skill::Hex); + + let hex_result = ResolutionResult::Effect { + effect: hex.effect, + duration: hex.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Hex, results: vec![hex_result] }; + + if !immunity.immune { + target.effects.push(hex); + } + + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, hex.effect, hex.duration)); + return resolution;; } -fn curse(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let effect = CrypEffect { effect: Effect::Curse, duration: Skill::Curse.duration(), tick: None }; - target.effects.push(effect); - log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); +fn curse(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let curse = CrypEffect { effect: Effect::Curse, duration: Skill::Curse.duration(), tick: None }; + let immunity = target.immune(Skill::Curse); + + let curse_result = ResolutionResult::Effect { + effect: curse.effect, + duration: curse.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Curse, results: vec![curse_result] }; + + if !immunity.immune { + target.effects.push(curse); + } + + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, curse.effect, curse.duration)); + return resolution;; } -fn drain(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let effect = CrypEffect { +fn drain(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let drain = CrypEffect { effect: Effect::Drain, duration: Skill::Drain.duration(), tick: Some(Cast::new_tick(cryp, target, Skill::DrainTick)), }; - target.effects.push(effect); - log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); + let immunity = target.immune(Skill::Drain); + + let drain_result = ResolutionResult::Effect { + effect: drain.effect, + duration: drain.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Drain, results: vec![drain_result] }; + + if !immunity.immune { + target.effects.push(drain); + } + + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, drain.effect, drain.duration)); + return resolution;; } -fn drain_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - // Damage part - let (damage, _) = target.deal_spell_dmg(cryp.spell_dmg().wrapping_div(2)); - log.push(format!("{:?} | Drain Damage {:?}", target.name, damage)); +// TODO is fukt +fn drain_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let amount = cryp.spell_dmg().wrapping_div(2); + let immunity = target.immune(Skill::DrainTick); - let (healing, overhealing) = target.heal(damage); - log.push(format!("{:?} | Drain healing {:?} ({:?} OH)", cryp.name, healing, overhealing)); + let drain_tick_dmg_result = ResolutionResult::Damage { + amount, + category: Category::SpellDmg, + immunity, + }; + + let drain_tick_heal_result = ResolutionResult::Damage { + amount, + category: Category::SpellHeal, + immunity, + }; + + + let mut resolution = Resolution { skill: Skill::DrainTick, results: vec![drain_tick_dmg_result, drain_tick_heal_result] }; + + if !immunity.immune { + // need to saturate amount and check caster immunity to healing + target.deal_spell_dmg(amount); + let (healing, overhealing) = cryp.heal(amount); + } + + return resolution; } -fn shield(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let effect = CrypEffect { effect: Effect::Shield, duration: Skill::Shield.duration(), tick: None }; - target.effects.push(effect); - log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); +fn shield(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let shield = CrypEffect { effect: Effect::Shield, duration: Skill::Shield.duration(), tick: None }; + let immunity = target.immune(Skill::Shield); + + let shield_result = ResolutionResult::Effect { + effect: shield.effect, + duration: shield.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Shield, results: vec![shield_result] }; + + if !immunity.immune { + target.effects.push(shield); + } + + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, shield.effect, shield.duration)); + return resolution; } -fn silence(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let effect = CrypEffect { effect: Effect::Silence, duration: Skill::Silence.duration(), tick: None }; - target.effects.push(effect); - log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); +fn silence(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let silence = CrypEffect { effect: Effect::Silence, duration: Skill::Silence.duration(), tick: None }; + let immunity = target.immune(Skill::Silence); + + let silence_result = ResolutionResult::Effect { + effect: silence.effect, + duration: silence.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Silence, results: vec![silence_result] }; + + if !immunity.immune { + target.effects.push(silence); + } + + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, silence.effect, silence.duration)); + return resolution; } -fn purge(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - for (i, ce) in target.effects.clone().iter_mut().enumerate() { - if ce.effect.source() == Source::Buff { - target.effects.remove(i); - log.push(format!("{:?} < {:?} purged", target.name, ce.effect)); +fn purge(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let immunity = target.immune(Skill::Purge); + + let mut resolution = Resolution { skill: Skill::Silence, results: vec![] }; + + if !immunity.immune { + for (i, ce) in target.effects.clone().iter_mut().enumerate() { + if ce.effect.category() == Category::SpellBuff { + target.effects.remove(i); + resolution.results.push(ResolutionResult::Removal { effect: ce.effect, immunity }); + log.push(format!("{:?} < {:?} purged", target.name, ce.effect)); + } } } + + return resolution; } -fn purify(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - for (i, ce) in target.effects.clone().iter_mut().enumerate() { - if ce.effect.source() == Source::Debuff { - target.effects.remove(i); - log.push(format!("{:?} < {:?} purified", target.name, ce.effect)); +fn purify(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let immunity = target.immune(Skill::Purify); + + let mut resolution = Resolution { skill: Skill::Silence, results: vec![] }; + + if !immunity.immune { + for (i, ce) in target.effects.clone().iter_mut().enumerate() { + if ce.effect.category() == Category::SpellDebuff { + target.effects.remove(i); + resolution.results.push(ResolutionResult::Removal { effect: ce.effect, immunity }); + log.push(format!("{:?} < {:?} purified", target.name, ce.effect)); + } } } + + return resolution; } -fn banish(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let effect = CrypEffect { effect: Effect::Banish, duration: Skill::Banish.duration(), tick: None }; - target.effects.push(effect); - log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); +fn banish(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let banish = CrypEffect { effect: Effect::Banish, duration: Skill::Banish.duration(), tick: None }; + let immunity = target.immune(Skill::Banish); + + let banish_result = ResolutionResult::Effect { + effect: banish.effect, + duration: banish.duration, + immunity, + }; + + let mut resolution = Resolution { skill: Skill::Banish, results: vec![banish_result] }; + + if !immunity.immune { + target.effects.push(banish); + } + + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, banish.effect, banish.duration)); + return resolution; }