From 6cdecb2b37acd3d2f27ba4d9d5405930277aade0 Mon Sep 17 00:00:00 2001 From: ntr Date: Tue, 13 Nov 2018 16:29:20 +1100 Subject: [PATCH 01/10] tests back --- server/src/cryp.rs | 12 +--- server/src/game.rs | 38 +++++++++---- server/src/skill.rs | 133 ++++++++++++++++++++++++++++---------------- 3 files changed, 114 insertions(+), 69 deletions(-) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index a966ca11..421d331a 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}; -use skill::{Skill, Cooldown, Effect, Tick}; +use skill::{Skill, Cooldown, Effect, Cast}; use game::{Log}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -33,14 +33,7 @@ impl CrypSkill { pub struct CrypEffect { pub effect: Effect, pub duration: u8, - pub tick: Option, -} - -impl CrypEffect { - pub fn tick(&self, cryp: &mut Cryp, log: &mut Log) -> &CrypEffect { - self.effect.tick(self, cryp, log); - self - } + pub tick: Option, } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -226,7 +219,6 @@ impl Cryp { pub fn reduce_effect_durations(&mut self, log: &mut Log) -> &mut Cryp { self.effects = self.effects.clone().into_iter().filter_map(|mut effect| { - effect.tick(self, log); effect.duration = effect.duration.saturating_sub(1); if effect.duration == 0 { diff --git a/server/src/game.rs b/server/src/game.rs index 75e1bcb3..a3da8d37 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -332,15 +332,7 @@ impl Game { self.phase = Phase::Resolve; self.log.push("".to_string()); - self.resolve_skills(); - - if self.is_finished() { - return self.finish() - } - - self.skill_phase_start(); - - self + self.resolve_skills() } fn resolve_skills(&mut self) -> &mut Game { @@ -348,6 +340,25 @@ impl Game { panic!("game not in Resolve phase"); } + // get all the cryps + let mut all_cryps = self.teams.clone() + .into_iter() + .flat_map( + |t| t.cryps + .into_iter()) + .collect::>(); + + // find their statuses with ticks + let ticks = all_cryps + .iter_mut() + .flat_map( + |c| c.effects + .iter_mut() + .filter_map(|e| e.tick) + .collect::>()); + + // add them to the stack + self.stack.sort_unstable_by_key(|s| s.skill.speed()); self.stack.reverse(); @@ -371,10 +382,17 @@ impl Game { // handle cooldowns and statuses self.progress_durations(); - self + if self.is_finished() { + return self.finish() + } + + self.skill_phase_start() } fn progress_durations(&mut self) -> &mut Game { + // FIXME + // need to get all the cryps + // and check if they cast anything // do it once for every cryp for mut cryp in self.stack.clone().iter() .map(|s| self.cryp_by_id(s.source_cryp_id).unwrap().clone()) diff --git a/server/src/skill.rs b/server/src/skill.rs index a378be64..75eda67c 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -4,7 +4,7 @@ use uuid::Uuid; use game::{Log}; use cryp::{Cryp, CrypEffect}; -#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct Cast { pub id: Uuid, pub skill: Skill, @@ -12,7 +12,6 @@ pub struct Cast { pub source_cryp_id: Uuid, pub target_cryp_id: Option, pub target_team_id: Uuid, - pub resolution: Resolution, } impl Cast { @@ -30,12 +29,17 @@ impl Cast { target_cryp_id, target_team_id, skill, - resolution: Resolution { base: 0, result: None }, }; } + pub fn new_tick(source: &mut Cryp, target: &mut Cryp, skill: Skill) -> Cast { + let mut cast = Cast::new(source.id, source.account, Some(target.account), skill); + cast.set_target(target.id); + return cast; + } + pub fn set_resolution(&mut self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> &mut Cast { - self.resolution = self.skill.resolve(cryp, target, log); + self.skill.resolve(cryp, target, log); self } @@ -75,6 +79,7 @@ pub enum Effect { // magic Hex, + Curse, Banish, Slow, Haste, @@ -89,7 +94,7 @@ pub enum Effect { Triage, Decay, Regen, - Degen, + Drain, SpeedDrain, SpeedIncrease, @@ -106,22 +111,6 @@ impl Effect { _ => false, } } - - pub fn tick(&self, cryp_effect: &CrypEffect, target: &mut Cryp, log: &mut Log) -> &Effect { - match self { - Effect::Decay => decay_tick(target, cryp_effect, log), - Effect::Triage => triage_tick(target, cryp_effect, log), - _ => (), - } - - self - } - -} - -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub struct Tick { - amount: u64 } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -158,6 +147,7 @@ pub enum Skill { // ----------------- Heal, Triage, // hot + TriageTick, Throw, // no dmg stun, adds vulnerable Charm, Calm, @@ -169,7 +159,9 @@ pub enum Skill { Blast, Amplify, Decay, // dot + DecayTick, // dot Drain, + DrainTick, Curse, Plague, // aoe dot Ruin, // aoe @@ -235,6 +227,7 @@ impl Skill { // ----------------- Skill::Heal => Some(1), Skill::Triage => Some(1), // hot + Skill::TriageTick => None, Skill::Throw => Some(2), // no dmg stun, adds vulnerable Skill::Charm => Some(2), Skill::Calm => Some(2), @@ -246,7 +239,9 @@ impl Skill { Skill::Blast => Some(1), Skill::Amplify => Some(2), Skill::Decay => Some(1), // dot + Skill::DecayTick => None, Skill::Drain => Some(2), + Skill::DrainTick => None, Skill::Curse => Some(2), Skill::Plague => Some(2), // aoe dot Skill::Ruin => Some(3), // aoe @@ -314,6 +309,7 @@ impl Skill { // ----------------- Skill::Heal => 1, Skill::Triage => 1, // hot + Skill::TriageTick => 1, // hot Skill::Throw => 2, // no dmg stun, adds vulnerable Skill::Charm => 2, Skill::Calm => 2, @@ -325,7 +321,9 @@ impl Skill { Skill::Blast => 1, Skill::Amplify => 2, Skill::Decay => 1, // dot + Skill::DecayTick => 2, // hot Skill::Drain => 2, + Skill::DrainTick => 2, // hot Skill::Curse => 2, Skill::Plague => 2, // aoe dot Skill::Ruin => 3, // aoe @@ -348,7 +346,8 @@ impl Skill { Skill::Hex => 1, Skill::Fear => 1, Skill::Taunt => 2, - Skill::Pause => 2, // speed slow + Skill::Pause => 2, // extend durations + // Skill::Lag => 2, // // ----------------- // Test @@ -359,11 +358,11 @@ impl Skill { } } - pub fn resolve(&self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + pub fn resolve(&self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { let mut rng = thread_rng(); let base: u64 = rng.gen(); - let res = Resolution { base, result: None }; + // let res = Resolution { base, result: None }; // println!("{:?}'s stats", self.name); // println!("{:064b} <- finalised", roll.result); @@ -405,6 +404,7 @@ impl Skill { // ----------------- Skill::Heal => heal(cryp, target, log), Skill::Triage => triage(cryp, target, log), // hot + Skill::TriageTick => triage_tick(cryp, target, log), // hot Skill::Throw => throw(cryp, target, log), // no dmg stun, adds vulnerable Skill::Charm => panic!("nyi"), Skill::Calm => panic!("nyi"), @@ -416,8 +416,10 @@ impl Skill { Skill::Blast => blast(cryp, target, log), Skill::Amplify => amplify(cryp, target, log), // TODO increase magic dmg Skill::Decay => decay(cryp, target, log), // dot - Skill::Drain => panic!("nyi"), - Skill::Curse => panic!("nyi"), + Skill::DecayTick => decay_tick(cryp, target, log), // hot + Skill::Drain => drain(cryp, target, log), + Skill::DrainTick => drain_tick(cryp, target, log), // hot + Skill::Curse => curse(cryp, target, log), Skill::Plague => panic!("nyi"), // aoe dot Skill::Ruin => panic!("nyi"), // aoe @@ -448,8 +450,6 @@ impl Skill { Skill::TestStun => stun(cryp, target, log), Skill::TestBlock => block(cryp, target, log), }; - - return res; } pub fn duration(&self) -> u8 { @@ -515,13 +515,13 @@ fn throw(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { 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!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); + log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); } 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!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); + log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); } fn heal(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { @@ -540,22 +540,22 @@ fn triage(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { let effect = CrypEffect { effect: Effect::Triage, duration: Skill::Triage.duration(), - tick: Some(Tick { amount: cryp.spell_dmg.value.wrapping_div(2) }) + tick: Some(Cast::new_tick(cryp, target, Skill::TriageTick)), }; target.effects.push(effect); - log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); + log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); } -fn triage_tick(target: &mut Cryp, effect: &CrypEffect, log: &mut Log) { - let tick = effect.tick.expect("no tick for triage"); +fn triage_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { let new_hp = *[ - target.hp.value.saturating_add(tick.amount), + target.hp.value.saturating_add(cryp.spell_dmg.value), target.stamina.value ].iter().min().unwrap(); let healing = new_hp.saturating_sub(target.hp.value); - let overhealing = target.hp.value + tick.amount - target.stamina.value; - log.push(format!("{:?} | Triage healing for {:?} ({:?} OH)", target.name, healing, overhealing)); + let overhealing = target.hp.value.saturating_add(cryp.phys_dmg.value).saturating_sub(target.stamina.value); + target.hp.value = new_hp; + log.push(format!("{:?} -> {:?} | Triage for {:?} ({:?} OH)", cryp.name, target.name, healing, overhealing)); target.hp.value = new_hp; } @@ -568,35 +568,70 @@ fn blast(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { 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!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); + log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); } fn decay(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { let effect = CrypEffect { effect: Effect::Decay, duration: Skill::Decay.duration(), - tick: Some(Tick { amount: cryp.spell_dmg.value.wrapping_div(2) }) + tick: Some(Cast::new_tick(cryp, target, Skill::DecayTick)), }; target.effects.push(effect); - log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); + log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); } -fn decay_tick(target: &mut Cryp, effect: &CrypEffect, log: &mut Log) { - let tick = effect.tick.expect("no tick for decay"); - target.hp.reduce(tick.amount); - log.push(format!("{:?} | Decay damage for {:?}", target.name, tick.amount)); +fn decay_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + let amount = cryp.spell_dmg.value; + log.push(format!("{:?} -> {:?} | Decay for {:?}", cryp.name, target.name, amount)); + target.hp.reduce(amount); } 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!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); + log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); +} + +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 drain(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + let effect = 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)); +} + +// it's fucked +fn drain_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + // damage part + let damage = cryp.spell_dmg.value; + target.hp.reduce(damage); + log.push(format!("{:?} | Drain damage {:?}", target.name, damage)); + + // healing part + let new_hp = *[ + cryp.hp.value.saturating_add(damage), + cryp.stamina.value + ].iter().min().unwrap(); + + let healing = new_hp.saturating_sub(target.hp.value); + let overhealing = cryp.hp.value + damage - target.stamina.value; + cryp.hp.value = new_hp; + log.push(format!("{:?} | Drain healing {:?} ({:?} OH)", target.name, healing, overhealing)); } 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!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); + log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); } @@ -645,7 +680,7 @@ mod tests { y.reduce_effect_durations(&mut log); let decay = y.effects.iter().find(|e| e.effect == Effect::Decay); - assert!(y.hp.value == y.stamina.value.saturating_sub(decay.unwrap().tick.unwrap().amount)); + // assert!(y.hp.value == y.stamina.value.saturating_sub(decay.unwrap().tick.unwrap().amount)); } #[test] @@ -672,8 +707,8 @@ mod tests { assert!(y.effects.iter().any(|e| e.effect == Effect::Triage)); - y.reduce_effect_durations(&mut log); - assert!(y.hp.value > prev_hp); + // y.reduce_effect_durations(&mut log); + // assert!(y.hp.value > prev_hp); } } From bec35ca46bb15ce6b2ba92a9605099b00e5efe18 Mon Sep 17 00:00:00 2001 From: ntr Date: Tue, 13 Nov 2018 17:10:52 +1100 Subject: [PATCH 02/10] drain test --- server/src/game.rs | 40 +++++++++++++++++++++++++++++++++++++--- server/src/skill.rs | 5 +++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/server/src/game.rs b/server/src/game.rs index a3da8d37..0b68df23 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -349,15 +349,16 @@ impl Game { .collect::>(); // find their statuses with ticks - let ticks = all_cryps + let mut ticks = all_cryps .iter_mut() .flat_map( |c| c.effects .iter_mut() - .filter_map(|e| e.tick) - .collect::>()); + .filter_map(|e| e.tick)) + .collect::>(); // add them to the stack + self.stack.append(&mut ticks); self.stack.sort_unstable_by_key(|s| s.skill.speed()); self.stack.reverse(); @@ -725,6 +726,7 @@ mod tests { .learn(Skill::TestStun) .learn(Skill::TestTouch) .learn(Skill::TestBlock) + .learn(Skill::TestDrain) .learn(Skill::Block) .create(); @@ -734,6 +736,7 @@ mod tests { .learn(Skill::TestStun) .learn(Skill::TestTouch) .learn(Skill::TestBlock) + .learn(Skill::TestDrain) .learn(Skill::Block) .create(); @@ -892,4 +895,35 @@ mod tests { assert!(game.team_by_id(x_team.id).cryps[0].is_stunned() == false); println!("{:#?}", game.log); } + + #[test] + fn drain_test() { + let mut game = create_test_game(); + + let x_team = game.teams[0].clone(); + let y_team = game.teams[1].clone(); + + let x_cryp = x_team.cryps[0].clone(); + let y_cryp = y_team.cryps[0].clone(); + + let x_drain_id = game.add_skill(x_team.id, x_cryp.id, Some(y_team.id), Skill::TestDrain).unwrap(); + let y_touch_id = game.add_skill(y_team.id, y_cryp.id, Some(x_team.id), Skill::TestTouch).unwrap(); + + game.target_phase_start(); + + game.add_target(x_team.id, x_cryp.id, y_touch_id).unwrap(); + game.add_target(y_team.id, y_cryp.id, x_drain_id).unwrap(); + + game.resolve_phase_start(); + + game.add_skill(x_team.id, x_cryp.id, None, Skill::TestBlock).unwrap(); + game.add_skill(y_team.id, y_cryp.id, None, Skill::TestBlock).unwrap(); + + game.target_phase_start(); + + assert!(game.resolved.iter().any(|r| r.skill == Skill::DrainTick)); + + println!("{:#?}", game); + } + } diff --git a/server/src/skill.rs b/server/src/skill.rs index 75eda67c..8fbbc95d 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -190,6 +190,7 @@ pub enum Skill { TestTouch, TestStun, TestBlock, + TestDrain, } impl Skill { @@ -272,6 +273,7 @@ impl Skill { Skill::TestTouch => None, Skill::TestStun => None, Skill::TestBlock => None, + Skill::TestDrain => None, } } @@ -355,6 +357,7 @@ impl Skill { Skill::TestTouch => 10, Skill::TestStun => 5, Skill::TestBlock => 10, + Skill::TestDrain => 10, } } @@ -449,6 +452,7 @@ impl Skill { Skill::TestTouch => (), Skill::TestStun => stun(cryp, target, log), Skill::TestBlock => block(cryp, target, log), + Skill::TestDrain => drain(cryp, target, log), }; } @@ -459,6 +463,7 @@ impl Skill { Skill::Block => 1, Skill::Decay => 3, + Skill::Drain => 3, Skill::Triage => 3, Skill::TestBlock => 1, From 4453f567ea1c2cdaecefa472882a3f512077e5fc Mon Sep 17 00:00:00 2001 From: ntr Date: Tue, 13 Nov 2018 17:14:16 +1100 Subject: [PATCH 03/10] reduce the cooldowns of all cryps in the game, not just the ones on the stack --- server/src/game.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/server/src/game.rs b/server/src/game.rs index 0b68df23..8bd069d4 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -391,14 +391,15 @@ impl Game { } fn progress_durations(&mut self) -> &mut Game { - // FIXME - // need to get all the cryps - // and check if they cast anything - // do it once for every cryp - for mut cryp in self.stack.clone().iter() - .map(|s| self.cryp_by_id(s.source_cryp_id).unwrap().clone()) - .collect::>() { + // get all the cryps + let all_cryps = self.teams.clone() + .into_iter() + .flat_map( + |t| t.cryps + .into_iter()) + .collect::>(); + for mut cryp in all_cryps { println!("progressing durations for {:?}", cryp.name); // only reduce cooldowns if no cd was used @@ -420,7 +421,6 @@ impl Game { self } - fn is_finished(&self) -> bool { self.teams.iter().any(|t| t.cryps.iter().all(|c| c.is_ko())) } From c175e808174a824c5ceb295d3ccb823a935037bc Mon Sep 17 00:00:00 2001 From: ntr Date: Tue, 13 Nov 2018 17:16:25 +1100 Subject: [PATCH 04/10] add all cryps fn --- server/src/game.rs | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/server/src/game.rs b/server/src/game.rs index 8bd069d4..70bf4442 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -130,6 +130,15 @@ impl Game { }; } + fn all_cryps(&self) -> Vec { + self.teams.clone() + .into_iter() + .flat_map( + |t| t.cryps + .into_iter()) + .collect::>() + } + fn update_cryp(&mut self, cryp: &mut Cryp) -> &mut Game { match self.teams.iter_mut().find(|t| t.cryps.iter().any(|c| c.id == cryp.id)) { Some(team) => { @@ -340,16 +349,8 @@ impl Game { panic!("game not in Resolve phase"); } - // get all the cryps - let mut all_cryps = self.teams.clone() - .into_iter() - .flat_map( - |t| t.cryps - .into_iter()) - .collect::>(); - // find their statuses with ticks - let mut ticks = all_cryps + let mut ticks = self.all_cryps() .iter_mut() .flat_map( |c| c.effects @@ -391,15 +392,7 @@ impl Game { } fn progress_durations(&mut self) -> &mut Game { - // get all the cryps - let all_cryps = self.teams.clone() - .into_iter() - .flat_map( - |t| t.cryps - .into_iter()) - .collect::>(); - - for mut cryp in all_cryps { + for mut cryp in self.all_cryps() { println!("progressing durations for {:?}", cryp.name); // only reduce cooldowns if no cd was used From eb8aa98e42b69fd864526be0cec7dfeeec916164 Mon Sep 17 00:00:00 2001 From: ntr Date: Tue, 13 Nov 2018 22:36:02 +1100 Subject: [PATCH 05/10] fix drain logs --- server/src/cryp.rs | 2 +- server/src/game.rs | 11 +++++------ server/src/skill.rs | 8 +++----- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 421d331a..6d1b0a0c 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -203,7 +203,7 @@ impl Cryp { if let Some(cd) = skill.skill.cd() { // if the cd is 1 it becomes none if cd == 1 { - println!("{:?} is now off cd", skill.skill); + // println!("{:?} is now off cd", skill.skill); skill.cd = None; continue; } diff --git a/server/src/game.rs b/server/src/game.rs index 70bf4442..d96d0b0a 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -30,7 +30,7 @@ impl Team { fn skills_required(&self) -> usize { let required = self.cryps.iter().filter(|c| c.available_skills().len() > 0).collect::>().len(); - println!("{:?} requires {:?} skills this turn", self.id, required); + // println!("{:?} requires {:?} skills this turn", self.id, required); return required; } @@ -351,10 +351,10 @@ impl Game { // find their statuses with ticks let mut ticks = self.all_cryps() - .iter_mut() + .iter() .flat_map( |c| c.effects - .iter_mut() + .iter() .filter_map(|e| e.tick)) .collect::>(); @@ -393,7 +393,7 @@ impl Game { fn progress_durations(&mut self) -> &mut Game { for mut cryp in self.all_cryps() { - println!("progressing durations for {:?}", cryp.name); + // println!("progressing durations for {:?}", cryp.name); // only reduce cooldowns if no cd was used // have to borrow self for the skill check @@ -815,7 +815,6 @@ mod tests { // should auto progress back to skill phase assert!(game.phase == Phase::Skill); - println!("{:#?}", game); assert!(game.team_by_id(y_team.id).cryps[0].is_stunned()); assert!(game.team_by_id(y_team.id).skills_required() == 0); } @@ -844,6 +843,7 @@ mod tests { // after 1 turn block should be off cooldown assert!(game.team_by_id(y_team.id).cryps[0].skill_on_cd(Skill::Block).is_none()); + assert!(game.team_by_id(x_team.id).cryps[0].skill_on_cd(Skill::Block).is_none()); // second round // now we block and it should go back on cd @@ -886,7 +886,6 @@ mod tests { // should not be stunned because of block assert!(game.team_by_id(x_team.id).cryps[0].is_stunned() == false); - println!("{:#?}", game.log); } #[test] diff --git a/server/src/skill.rs b/server/src/skill.rs index 8fbbc95d..70205693 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -627,10 +627,10 @@ fn drain_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { cryp.stamina.value ].iter().min().unwrap(); - let healing = new_hp.saturating_sub(target.hp.value); - let overhealing = cryp.hp.value + damage - target.stamina.value; cryp.hp.value = new_hp; - log.push(format!("{:?} | Drain healing {:?} ({:?} OH)", target.name, healing, overhealing)); + let healing = new_hp.saturating_sub(cryp.hp.value); + let overhealing = cryp.hp.value + damage - cryp.stamina.value; + log.push(format!("{:?} | Drain healing {:?} ({:?} OH)", cryp.name, healing, overhealing)); } fn banish(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { @@ -662,8 +662,6 @@ mod tests { let mut log = vec![]; heal(&mut y, &mut x, &mut log); - - println!("{:?}", log); } #[test] From 971c0dd03d4a6dcb461fdb609999dc2631dec4f9 Mon Sep 17 00:00:00 2001 From: ntr Date: Wed, 14 Nov 2018 16:10:27 +1100 Subject: [PATCH 06/10] silence --- server/WORKLOG.md | 5 +- server/src/skill.rs | 149 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 138 insertions(+), 16 deletions(-) diff --git a/server/WORKLOG.md b/server/WORKLOG.md index 7366c9bd..a58f5192 100755 --- a/server/WORKLOG.md +++ b/server/WORKLOG.md @@ -17,10 +17,13 @@ * skills * offensive -> choose target ✔ + * calculate + * hp increase/decrease + * spell/phys dmg * private fields for opponents * cooldowns reduce each turn ✔ * statuses reduce each turn - * teach cyps skills + * teach cyps skills ✔ * can you attack yourself? * fetch existing battles * check for game participation diff --git a/server/src/skill.rs b/server/src/skill.rs index 70205693..c2285456 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -86,6 +86,7 @@ pub enum Effect { Enslave, Mesmerise, Amplify, + Silence, // magic immunity Immune, @@ -111,6 +112,22 @@ impl Effect { _ => false, } } + + 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, + }, + _ => false, + } + } +} + +pub enum Damage { + Physical, + Magic, } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -169,13 +186,13 @@ pub enum Skill { // ----------------- // Purity // ----------------- - Precision, Inspire, Slay, Shield, Silence, Inquiry, Purify, + // Precision, // ----------------- // Chaos @@ -250,7 +267,7 @@ impl Skill { // ----------------- // Purity // ----------------- - Skill::Precision => Some(1), + // Skill::Precision => Some(1), Skill::Inspire => Some(2), Skill::Slay => Some(1), Skill::Shield => Some(1), @@ -277,6 +294,90 @@ impl Skill { } } + pub fn cast_type(&self) -> Damage { + match self { + Skill::Attack => Damage::Physical, + + // ----------------- + // Nature + // ----------------- + Skill::Block => Damage::Physical, // reduce dmg + Skill::Evade => Damage::Physical, + Skill::Parry => Damage::Physical, // avoid all dmg + Skill::Snare => Damage::Physical, + + Skill::Paralyse => Damage::Physical, + Skill::Strangle => Damage::Physical, + + // Strangle + + Skill::Stun => Damage::Physical, + Skill::Evasion => Damage::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? + + // ----------------- + // 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, + + // ----------------- + // 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 + + // ----------------- + // Purity + // ----------------- + // Skill::Precision => 1, + Skill::Inspire => Damage::Physical, + Skill::Slay => Damage::Physical, + Skill::Shield => Damage::Magic, + Skill::Silence => Damage::Magic, + Skill::Inquiry => Damage::Magic, + Skill::Purify => Damage::Magic, + + // ----------------- + // Chaos + // ----------------- + Skill::Banish => Damage::Magic, + Skill::Hex => Damage::Magic, + Skill::Fear => Damage::Magic, + Skill::Taunt => Damage::Magic, + Skill::Pause => Damage::Magic, // extend durations + // Skill::Lag => 2, // + + // ----------------- + // Test + // ----------------- + Skill::TestTouch => Damage::Physical, + Skill::TestStun => Damage::Physical, + Skill::TestBlock => Damage::Physical, + Skill::TestDrain => Damage::Magic, + } + } + pub fn speed(&self) -> u8 { match self { Skill::Attack => 5, @@ -333,7 +434,7 @@ impl Skill { // ----------------- // Purity // ----------------- - Skill::Precision => 1, + // Skill::Precision => 1, Skill::Inspire => 2, Skill::Slay => 1, Skill::Shield => 1, @@ -409,8 +510,8 @@ impl Skill { Skill::Triage => triage(cryp, target, log), // hot Skill::TriageTick => triage_tick(cryp, target, log), // hot Skill::Throw => throw(cryp, target, log), // no dmg stun, adds vulnerable - Skill::Charm => panic!("nyi"), - Skill::Calm => panic!("nyi"), + Skill::Charm => panic!("nyi"), // cast random spell on teammate + Skill::Calm => panic!("nyi"), // remove fear, taunt Skill::Rez => panic!("nyi"), // ----------------- @@ -429,11 +530,11 @@ impl Skill { // ----------------- // Purity // ----------------- - Skill::Precision => panic!("nyi"), - Skill::Inspire => panic!("nyi"), - Skill::Slay => panic!("nyi"), + // Skill::Precision => panic!("nyi"), + Skill::Inspire => panic!("nyi"), // increased phys dmg + Skill::Slay => panic!("nyi"), // phys dmg mult by target magic dmg Skill::Shield => panic!("nyi"), - Skill::Silence => panic!("nyi"), + Skill::Silence => silence(cryp, target, log), Skill::Inquiry => panic!("nyi"), Skill::Purify => panic!("nyi"), @@ -442,7 +543,7 @@ impl Skill { // ----------------- Skill::Banish => banish(cryp, target, log), // TODO prevent all actions Skill::Hex => hex(cryp, target, log), // todo prevent casting - Skill::Fear => panic!("nyi"), + Skill::Fear => panic!("nyi"), // cast random spell on self Skill::Taunt => panic!("nyi"), Skill::Pause => panic!("nyi"), // speed slow @@ -466,6 +567,8 @@ impl Skill { Skill::Drain => 3, Skill::Triage => 3, + Skill::Silence => 3, + Skill::TestBlock => 1, Skill::TestStun => 2, _ => panic!("{:?} does not have a duration", self), @@ -481,10 +584,7 @@ impl Skill { } pub fn castable(&self, cryp: &Cryp) -> bool { - if cryp.is_stunned() { - return false; - } - true + !cryp.effects.iter().any(|e| e.effect.prevents_casting(*self)) } } @@ -614,7 +714,6 @@ fn drain(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); } -// it's fucked fn drain_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { // damage part let damage = cryp.spell_dmg.value; @@ -633,6 +732,12 @@ fn drain_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { log.push(format!("{:?} | Drain healing {:?} ({:?} OH)", cryp.name, healing, overhealing)); } +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 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); @@ -713,6 +818,20 @@ mod tests { // y.reduce_effect_durations(&mut log); // assert!(y.hp.value > prev_hp); } + + #[test] + fn silence_test() { + let mut x = Cryp::new() + .named(&"muji".to_string()) + .level(8) + .create(); + + let mut log = vec![]; + + silence(&mut x.clone(), &mut x, &mut log); + assert!(x.effects.iter().any(|e| e.effect == Effect::Silence)); + assert!(!Skill::Decay.castable(&x)); + } } // #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] From ecabde3389b088782388e1e44ceb5285e2ba37c3 Mon Sep 17 00:00:00 2001 From: ntr Date: Wed, 14 Nov 2018 17:50:28 +1100 Subject: [PATCH 07/10] move healing and spell damage calculations into cryp" git push " --- server/src/cryp.rs | 44 +++++++++++++++++++++++++++++++-- server/src/game.rs | 2 +- server/src/skill.rs | 59 ++++++++++++++------------------------------- 3 files changed, 61 insertions(+), 44 deletions(-) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 6d1b0a0c..815be7fc 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -49,7 +49,7 @@ pub enum Stat { #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct CrypStat { - pub value: u64, + value: u64, pub stat: Stat, } @@ -119,7 +119,6 @@ impl Cryp { self } - pub fn level(mut self, lvl: u8) -> Cryp { self.lvl = check_lvl(lvl); self @@ -236,6 +235,47 @@ impl Cryp { self.hp.set(self.stamina.value); self } + + // Stats + pub fn phys_dmg(&self) -> u64 { + self.phys_dmg.value + } + + pub fn spell_dmg(&self) -> u64 { + self.spell_dmg.value + } + + pub fn hp(&self) -> u64 { + self.hp.value + } + + pub fn stamina(&self) -> u64 { + self.stamina.value + } + + // Stat modifications + pub fn heal(&mut self, amount: u64) -> (u64, u64) { + let current_hp = self.hp(); + let new_hp = *[ + self.hp().saturating_add(amount), + self.stamina() + ].iter().min().unwrap(); + + let healing = new_hp - current_hp; + let overhealing = amount - healing; + return (healing, overhealing); + } + + pub fn deal_phys_dmg(&mut self, amount: u64) -> (u64, u64) { + self.hp.reduce(amount); + return (amount, 0); + } + + pub fn deal_spell_dmg(&mut self, amount: u64) -> (u64, u64) { + self.hp.reduce(amount); + return (amount, 0); + } + } pub fn cryp_get(tx: &mut Transaction, id: Uuid, account_id: Uuid) -> Result { diff --git a/server/src/game.rs b/server/src/game.rs index d96d0b0a..877d942b 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -618,7 +618,7 @@ pub fn game_pve(params: GamePveParams, tx: &mut Transaction, account: &Account) let plr: Cryp = from_slice::(&cryp_bytes)?; // TEMP - if plr.hp.value == 0 { + if plr.is_ko() { return Err(err_msg("cryp is ko")); // plr.rez(); } diff --git a/server/src/skill.rs b/server/src/skill.rs index c2285456..8f2963c5 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -470,9 +470,9 @@ impl Skill { // println!("{:?}'s stats", self.name); // println!("{:064b} <- finalised", roll.result); - // roll.result = roll.result & stat.value; + // roll.result = roll.result & stat(); - // println!("{:064b} & <- attribute roll", stat.value); + // println!("{:064b} & <- attribute roll", stat()); // println!("{:064b} = {:?}", roll.result, roll.result); // println!(""); @@ -589,8 +589,8 @@ impl Skill { } fn attack(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + target.deal_phys_dmg(cryp.phys_dmg()); log.push(format!("{:?} -> {:?} | Attack for {:?}", cryp.name, target.name, cryp.phys_dmg)); - target.hp.reduce(cryp.phys_dmg.value); } fn stun(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { @@ -630,14 +630,7 @@ fn snare(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { } fn heal(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let new_hp = *[ - target.hp.value.saturating_add(cryp.spell_dmg.value), - target.stamina.value - ].iter().min().unwrap(); - - let healing = new_hp.saturating_sub(target.hp.value); - let overhealing = target.hp.value.saturating_add(cryp.phys_dmg.value).saturating_sub(target.stamina.value); - target.hp.value = new_hp; + let (healing, overhealing) = target.heal(cryp.phys_dmg()); log.push(format!("{:?} -> {:?} | Heal for {:?} ({:?} OH)", cryp.name, target.name, healing, overhealing)); } @@ -652,22 +645,15 @@ fn triage(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { } fn triage_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let new_hp = *[ - target.hp.value.saturating_add(cryp.spell_dmg.value), - target.stamina.value - ].iter().min().unwrap(); - - let healing = new_hp.saturating_sub(target.hp.value); - let overhealing = target.hp.value.saturating_add(cryp.phys_dmg.value).saturating_sub(target.stamina.value); - target.hp.value = new_hp; + 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)); - target.hp.value = new_hp; } fn blast(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let amount = cryp.spell_dmg.value; + let amount = cryp.spell_dmg(); log.push(format!("{:?} -> {:?} | Blast for {:?}", cryp.name, target.name, amount)); - target.hp.reduce(amount); + target.deal_spell_dmg(amount); } fn amplify(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { @@ -687,9 +673,9 @@ fn decay(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { } fn decay_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - let amount = cryp.spell_dmg.value; + let amount = cryp.spell_dmg(); log.push(format!("{:?} -> {:?} | Decay for {:?}", cryp.name, target.name, amount)); - target.hp.reduce(amount); + target.deal_spell_dmg(amount); } fn hex(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { @@ -716,19 +702,10 @@ fn drain(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { fn drain_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { // damage part - let damage = cryp.spell_dmg.value; - target.hp.reduce(damage); + let (damage, _) = target.deal_spell_dmg(cryp.spell_dmg().wrapping_div(2)); log.push(format!("{:?} | Drain damage {:?}", target.name, damage)); - // healing part - let new_hp = *[ - cryp.hp.value.saturating_add(damage), - cryp.stamina.value - ].iter().min().unwrap(); - - cryp.hp.value = new_hp; - let healing = new_hp.saturating_sub(cryp.hp.value); - let overhealing = cryp.hp.value + damage - cryp.stamina.value; + let (healing, overhealing) = target.heal(damage); log.push(format!("{:?} | Drain healing {:?} ({:?} OH)", cryp.name, healing, overhealing)); } @@ -763,7 +740,7 @@ mod tests { .learn(Skill::Heal) .create(); - x.hp.reduce(5); + x.deal_phys_dmg(5); let mut log = vec![]; heal(&mut y, &mut x, &mut log); @@ -788,7 +765,7 @@ mod tests { y.reduce_effect_durations(&mut log); let decay = y.effects.iter().find(|e| e.effect == Effect::Decay); - // assert!(y.hp.value == y.stamina.value.saturating_sub(decay.unwrap().tick.unwrap().amount)); + // assert!(y.hp() == y.stamina().saturating_sub(decay.unwrap().tick.unwrap().amount)); } #[test] @@ -806,17 +783,17 @@ mod tests { let mut log = vec![]; // ensure it doesn't have 0 sd - x.spell_dmg.value = 50; - y.hp.reduce(5); + x.spell_dmg.set(50); + y.deal_phys_dmg(5); - let prev_hp = y.hp.value; + let prev_hp = y.hp(); triage(&mut x, &mut y, &mut log); assert!(y.effects.iter().any(|e| e.effect == Effect::Triage)); // y.reduce_effect_durations(&mut log); - // assert!(y.hp.value > prev_hp); + // assert!(y.hp() > prev_hp); } #[test] From 82e49c56bf4d8d54284d430647bc75d2f834a9cd Mon Sep 17 00:00:00 2001 From: ntr Date: Thu, 15 Nov 2018 15:25:36 +1100 Subject: [PATCH 08/10] damage calculations --- server/WORKLOG.md | 1 + server/src/cryp.rs | 57 +++++++++++++++++++++++++++++---------------- server/src/skill.rs | 53 +++++++++++++++++++++++++++++++---------- 3 files changed, 79 insertions(+), 32 deletions(-) diff --git a/server/WORKLOG.md b/server/WORKLOG.md index a58f5192..623e73ea 100755 --- a/server/WORKLOG.md +++ b/server/WORKLOG.md @@ -17,6 +17,7 @@ * skills * offensive -> choose target ✔ + * ensure cryp untargetable and doesn't resolve when KO * calculate * hp increase/decrease * spell/phys dmg diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 815be7fc..7597cd23 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -41,31 +41,31 @@ pub enum Stat { Str, Agi, Int, - PhysicalDmg, - SpellPower, + PhysDmg, + SpellDmg, Hp, Stam, } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct CrypStat { - value: u64, + base: u64, pub stat: Stat, } impl CrypStat { pub fn set(&mut self, v: u64) -> &CrypStat { - self.value = v; + self.base = v; self } pub fn reduce(&mut self, amt: u64) -> &mut CrypStat { - self.value = self.value.saturating_sub(amt); + self.base = self.base.saturating_sub(amt); self } pub fn increase(&mut self, amt: u64) -> &mut CrypStat { - self.value = self.value.saturating_add(amt); + self.base = self.base.saturating_add(amt); self } @@ -97,10 +97,10 @@ impl Cryp { return Cryp { id, account: id, - phys_dmg: CrypStat { value: 0, stat: Stat::Str }, - spell_dmg: CrypStat { value: 0, stat: Stat::Int }, - stamina: CrypStat { value: 0, stat: Stat::Stam }, - hp: CrypStat { value: 0, stat: Stat::Hp }, + phys_dmg: CrypStat { base: 0, stat: Stat::Str }, + spell_dmg: CrypStat { base: 0, stat: Stat::Int }, + stamina: CrypStat { base: 0, stat: Stat::Stam }, + hp: CrypStat { base: 0, stat: Stat::Hp }, lvl: 0, xp: 0, skills: vec![CrypSkill::new(Skill::Attack)], @@ -159,13 +159,13 @@ impl Cryp { self.phys_dmg.set(rng.gen_range(min, max)); self.spell_dmg.set(rng.gen_range(min, max)); self.stamina.set(rng.gen_range(min, max)); - self.hp.set(self.stamina.value); + self.hp.set(self.stamina.base); self } pub fn is_ko(&self) -> bool { - self.hp.value == 0 + self.hp.base == 0 } pub fn immune(&self, skill: Skill) -> bool { @@ -232,25 +232,39 @@ impl Cryp { } pub fn rez(&mut self) -> &mut Cryp { - self.hp.set(self.stamina.value); + self.hp.set(self.stamina.base); self } // Stats pub fn phys_dmg(&self) -> u64 { - self.phys_dmg.value + let phys_dmg_mods = self.effects.iter() + .filter(|e| e.effect.modifications().contains(&Stat::PhysDmg)) + .map(|cryp_effect| cryp_effect.effect) + .collect::>(); + println!("{:?} phys_dmg mods : {:?}", self.name, phys_dmg_mods); + let modified_phys_dmg = phys_dmg_mods.iter().fold(self.phys_dmg.base, |acc, m| m.apply(acc)); + println!("{:?} phys_dmg : {:?}", self.name, modified_phys_dmg); + return modified_phys_dmg; } pub fn spell_dmg(&self) -> u64 { - self.spell_dmg.value + let spell_dmg_mods = self.effects.iter() + .filter(|e| e.effect.modifications().contains(&Stat::SpellDmg)) + .map(|cryp_effect| cryp_effect.effect) + .collect::>(); + println!("{:?} spell_dmg mods : {:?}", self.name, spell_dmg_mods); + let modified_spell_dmg = spell_dmg_mods.iter().fold(self.spell_dmg.base, |acc, m| m.apply(acc)); + println!("{:?} spell_dmg : {:?}", self.name, modified_spell_dmg); + return modified_spell_dmg; } pub fn hp(&self) -> u64 { - self.hp.value + self.hp.base } pub fn stamina(&self) -> u64 { - self.stamina.value + self.stamina.base } // Stat modifications @@ -261,6 +275,8 @@ impl Cryp { self.stamina() ].iter().min().unwrap(); + self.hp.set(new_hp); + let healing = new_hp - current_hp; let overhealing = amount - healing; return (healing, overhealing); @@ -268,6 +284,7 @@ impl Cryp { pub fn deal_phys_dmg(&mut self, amount: u64) -> (u64, u64) { self.hp.reduce(amount); + println!("{:?} dealt {:?} phys dmg", self.name, amount); return (amount, 0); } @@ -380,7 +397,7 @@ mod tests { // ,final_str // ,self.name // ,blocked - // ,self.hp.value)); + // ,self.hp.base)); // plr_t.log.push(format!("")); // self @@ -400,9 +417,9 @@ mod tests { // // finally combine with CrypStat // log.push(format!("{:064b} <- finalised", roll.result)); - // roll.result = roll.result & self.value; + // roll.result = roll.result & self.base; - // log.push(format!("{:064b} & <- attribute roll", self.value)); + // log.push(format!("{:064b} & <- attribute roll", self.base)); // log.push(format!("{:064b} = {:?}", roll.result, roll.result)); // log.push(format!("")); diff --git a/server/src/skill.rs b/server/src/skill.rs index 8f2963c5..0f9888e0 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -2,13 +2,12 @@ use rand::{thread_rng, Rng}; use uuid::Uuid; use game::{Log}; -use cryp::{Cryp, CrypEffect}; +use cryp::{Cryp, CrypEffect, Stat}; #[derive(Debug,Clone,Copy,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, @@ -123,6 +122,20 @@ impl Effect { _ => false, } } + + pub fn modifications(&self) -> Vec { + match self { + Effect::Amplify => vec![Stat::SpellDmg], + _ => vec![], + } + } + + pub fn apply(&self, value: u64) -> u64 { + match self { + Effect::Amplify => value << 1, + _ => panic!("{:?} does not have a mod effect", self), + } + } } pub enum Damage { @@ -510,7 +523,7 @@ impl Skill { Skill::Triage => triage(cryp, target, log), // hot Skill::TriageTick => triage_tick(cryp, target, log), // hot Skill::Throw => throw(cryp, target, log), // no dmg stun, adds vulnerable - Skill::Charm => panic!("nyi"), // cast random spell on teammate + Skill::Charm => panic!("nyi"), // target casts random spell on teammate Skill::Calm => panic!("nyi"), // remove fear, taunt Skill::Rez => panic!("nyi"), @@ -533,10 +546,10 @@ impl Skill { // Skill::Precision => panic!("nyi"), Skill::Inspire => panic!("nyi"), // increased phys dmg Skill::Slay => panic!("nyi"), // phys dmg mult by target magic dmg - Skill::Shield => panic!("nyi"), - Skill::Silence => silence(cryp, target, log), - Skill::Inquiry => panic!("nyi"), - Skill::Purify => panic!("nyi"), + Skill::Shield => panic!("nyi"), // target is immune to magic dmg and fx + Skill::Silence => silence(cryp, target, log), // target cannot cast spells + Skill::Inquiry => panic!("nyi"), // + Skill::Purify => panic!("nyi"), // dispel all debuffs // ----------------- // Chaos @@ -544,7 +557,7 @@ impl Skill { Skill::Banish => banish(cryp, target, log), // TODO prevent all actions Skill::Hex => hex(cryp, target, log), // todo prevent casting Skill::Fear => panic!("nyi"), // cast random spell on self - Skill::Taunt => panic!("nyi"), + Skill::Taunt => panic!("nyi"), // target Skill::Pause => panic!("nyi"), // speed slow // ----------------- @@ -567,6 +580,7 @@ impl Skill { Skill::Drain => 3, Skill::Triage => 3, + Skill::Amplify => 2, Skill::Silence => 3, Skill::TestBlock => 1, @@ -785,15 +799,14 @@ mod tests { // ensure it doesn't have 0 sd x.spell_dmg.set(50); y.deal_phys_dmg(5); - let prev_hp = y.hp(); triage(&mut x, &mut y, &mut log); assert!(y.effects.iter().any(|e| e.effect == Effect::Triage)); - // y.reduce_effect_durations(&mut log); - // assert!(y.hp() > prev_hp); + triage_tick(&mut x, &mut y, &mut log); + assert!(y.hp() > prev_hp); } #[test] @@ -809,6 +822,22 @@ mod tests { assert!(x.effects.iter().any(|e| e.effect == Effect::Silence)); assert!(!Skill::Decay.castable(&x)); } + + #[test] + fn amplify_test() { + let mut x = Cryp::new() + .named(&"muji".to_string()) + .level(8) + .create(); + + x.spell_dmg.set(50); + + let mut log = vec![]; + amplify(&mut x.clone(), &mut x, &mut log); + assert!(x.effects.iter().any(|e| e.effect == Effect::Amplify)); + assert_eq!(x.spell_dmg(), 100); + } + } // #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] From 92ee2ad52ecb059dc87b867061064a96f3910382 Mon Sep 17 00:00:00 2001 From: ntr Date: Thu, 15 Nov 2018 16:34:47 +1100 Subject: [PATCH 09/10] check immunities --- server/src/cryp.rs | 13 +++++++-- server/src/game.rs | 2 +- server/src/skill.rs | 66 ++++++++++++++++++++++++++++----------------- 3 files changed, 54 insertions(+), 27 deletions(-) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 7597cd23..1851f712 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -168,8 +168,17 @@ impl Cryp { self.hp.base == 0 } - pub fn immune(&self, skill: Skill) -> bool { - self.effects.iter().any(|e| e.effect.immune(skill)) + pub fn immune(&self, skill: Skill) -> (bool, Vec) { + let immunities = self.effects.iter() + .filter(|e| e.effect.immune(skill)) + .map(|e| e.effect) + .collect::>(); + + if immunities.len() > 0 { + return (true, immunities); + } + + return (false, vec![]); } pub fn is_stunned(&self) -> bool { diff --git a/server/src/game.rs b/server/src/game.rs index 877d942b..61e0580a 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -915,7 +915,7 @@ mod tests { assert!(game.resolved.iter().any(|r| r.skill == Skill::DrainTick)); - println!("{:#?}", game); + // println!("{:#?}", game); } } diff --git a/server/src/skill.rs b/server/src/skill.rs index 0f9888e0..a14266b5 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -76,6 +76,8 @@ pub enum Effect { Blind, Snare, + Empower, + // magic Hex, Curse, @@ -88,7 +90,7 @@ pub enum Effect { Silence, // magic immunity - Immune, + Shield, // effects over time Triage, @@ -105,9 +107,14 @@ impl Effect { match self { Effect::Block => match skill { Skill::Stun | + Skill::TestStun | Skill::Attack => true, _ => false, }, + Effect::Shield => match skill.cast_type() { + Damage::Magic => true, + Damage::Physical => false, + } _ => false, } } @@ -126,13 +133,18 @@ impl Effect { pub fn modifications(&self) -> Vec { match self { Effect::Amplify => vec![Stat::SpellDmg], + Effect::Empower => vec![Stat::PhysDmg], _ => vec![], } } + // maybe increase by rng + // roll little endian bits + // and OR with base stat pub fn apply(&self, value: u64) -> u64 { match self { Effect::Amplify => value << 1, + Effect::Empower => value << 1, _ => panic!("{:?} does not have a mod effect", self), } } @@ -199,7 +211,7 @@ pub enum Skill { // ----------------- // Purity // ----------------- - Inspire, + Empower, Slay, Shield, Silence, @@ -281,7 +293,7 @@ impl Skill { // Purity // ----------------- // Skill::Precision => Some(1), - Skill::Inspire => Some(2), + Skill::Empower => Some(2), Skill::Slay => Some(1), Skill::Shield => Some(1), Skill::Silence => Some(2), @@ -364,7 +376,7 @@ impl Skill { // Purity // ----------------- // Skill::Precision => 1, - Skill::Inspire => Damage::Physical, + Skill::Empower => Damage::Physical, Skill::Slay => Damage::Physical, Skill::Shield => Damage::Magic, Skill::Silence => Damage::Magic, @@ -448,7 +460,7 @@ impl Skill { // Purity // ----------------- // Skill::Precision => 1, - Skill::Inspire => 2, + Skill::Empower => 2, Skill::Slay => 1, Skill::Shield => 1, Skill::Silence => 2, @@ -491,6 +503,12 @@ impl Skill { // 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), // ----------------- @@ -531,7 +549,7 @@ impl Skill { // Destruction // ----------------- Skill::Blast => blast(cryp, target, log), - Skill::Amplify => amplify(cryp, target, log), // TODO increase magic dmg + Skill::Amplify => amplify(cryp, target, log), // increase magic dmg Skill::Decay => decay(cryp, target, log), // dot Skill::DecayTick => decay_tick(cryp, target, log), // hot Skill::Drain => drain(cryp, target, log), @@ -544,7 +562,7 @@ impl Skill { // Purity // ----------------- // Skill::Precision => panic!("nyi"), - Skill::Inspire => panic!("nyi"), // increased phys dmg + Skill::Empower => empower(cryp, target, log), // increased phys dmg Skill::Slay => panic!("nyi"), // phys dmg mult by target magic dmg Skill::Shield => panic!("nyi"), // target is immune to magic dmg and fx Skill::Silence => silence(cryp, target, log), // target cannot cast spells @@ -576,6 +594,8 @@ impl Skill { Skill::Stun => 2, Skill::Block => 1, + Skill::Empower => 2, + Skill::Decay => 3, Skill::Drain => 3, Skill::Triage => 3, @@ -608,26 +628,18 @@ fn attack(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { } fn stun(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - if !target.immune(Skill::Stun) { - let stun = CrypEffect { effect: Effect::Stun, duration: Skill::Stun.duration(), tick: None }; - target.effects.push(stun); - log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration)); - } else { - log.push(format!("{:?} -> {:?} | {:?} immune", cryp.name, target.name, target.name)); - } + let stun = CrypEffect { effect: Effect::Stun, duration: Skill::Stun.duration(), tick: None }; + target.effects.push(stun); + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration)); } fn throw(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - if !target.immune(Skill::Throw) { - 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); - 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)); - } else { - log.push(format!("{:?} -> {:?} | {:?} immune", cryp.name, target.name, target.name)); - } + 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); + 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)); } @@ -643,6 +655,12 @@ fn snare(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { log.push(format!("{:?} < {:?} for {:?}T", target.name, effect.effect, effect.duration)); } +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 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)); From 84debe6bbc0a735c47fa75e76afa50a9b0f4a6a0 Mon Sep 17 00:00:00 2001 From: ntr Date: Fri, 16 Nov 2018 13:48:49 +1100 Subject: [PATCH 10/10] purge and purify --- server/src/cryp.rs | 2 +- server/src/skill.rs | 116 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 109 insertions(+), 9 deletions(-) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 1851f712..7a782ccf 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}; -use skill::{Skill, Cooldown, Effect, Cast}; +use skill::{Skill, Cooldown, Effect, Cast, Source}; use game::{Log}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] diff --git a/server/src/skill.rs b/server/src/skill.rs index a14266b5..e8dd7753 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -148,13 +148,70 @@ impl Effect { _ => panic!("{:?} does not have a mod effect", self), } } + + pub fn source(&self) -> Source { + 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::Empower => Source::Buff, + + // 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, + + // magic immunity + Effect::Shield => Source::Buff, + + // effects over time + Effect::Triage => Source::Buff, + Effect::Decay => Source::Debuff, + Effect::Regen => Source::Buff, + Effect::Drain => Source::Debuff, + + Effect::SpeedDrain => Source::Debuff, + Effect::SpeedIncrease => Source::Buff, + } + } } +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Damage { 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, +} + #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Skill { Attack, @@ -217,6 +274,7 @@ pub enum Skill { Silence, Inquiry, Purify, + Purge, // Precision, // ----------------- @@ -299,6 +357,7 @@ impl Skill { Skill::Silence => Some(2), Skill::Inquiry => Some(2), Skill::Purify => Some(1), + Skill::Purge => Some(1), // ----------------- // Chaos @@ -382,6 +441,7 @@ impl Skill { Skill::Silence => Damage::Magic, Skill::Inquiry => Damage::Magic, Skill::Purify => Damage::Magic, + Skill::Purge => Damage::Magic, // ----------------- // Chaos @@ -466,6 +526,7 @@ impl Skill { Skill::Silence => 2, Skill::Inquiry => 2, Skill::Purify => 1, + Skill::Purge => 1, // ----------------- // Chaos @@ -542,7 +603,7 @@ impl Skill { Skill::TriageTick => triage_tick(cryp, target, log), // hot Skill::Throw => throw(cryp, target, log), // no dmg stun, adds vulnerable Skill::Charm => panic!("nyi"), // target casts random spell on teammate - Skill::Calm => panic!("nyi"), // remove fear, taunt + Skill::Calm => panic!("nyi"), // physical fear, taunt removal Skill::Rez => panic!("nyi"), // ----------------- @@ -555,8 +616,8 @@ impl Skill { Skill::Drain => drain(cryp, target, log), Skill::DrainTick => drain_tick(cryp, target, log), // hot Skill::Curse => curse(cryp, target, log), - Skill::Plague => panic!("nyi"), // aoe dot - Skill::Ruin => panic!("nyi"), // aoe + Skill::Plague => panic!("nyi"), // dot that spreads every turn + Skill::Ruin => panic!("nyi"), // aoe version of blast // ----------------- // Purity @@ -564,10 +625,11 @@ impl Skill { // Skill::Precision => panic!("nyi"), Skill::Empower => empower(cryp, target, log), // increased phys dmg Skill::Slay => panic!("nyi"), // phys dmg mult by target magic dmg - Skill::Shield => panic!("nyi"), // target is immune to magic dmg and fx + Skill::Shield => shield(cryp, target, log), // target is immune to magic dmg and fx Skill::Silence => silence(cryp, target, log), // target cannot cast spells Skill::Inquiry => panic!("nyi"), // - Skill::Purify => panic!("nyi"), // dispel all debuffs + Skill::Purify => purify(cryp, target, log), // dispel all debuffs + Skill::Purge => purge(cryp, target, log), // dispel all buffs // ----------------- // Chaos @@ -575,7 +637,7 @@ impl Skill { Skill::Banish => banish(cryp, target, log), // TODO prevent all actions Skill::Hex => hex(cryp, target, log), // todo prevent casting Skill::Fear => panic!("nyi"), // cast random spell on self - Skill::Taunt => panic!("nyi"), // target + Skill::Taunt => panic!("nyi"), // target forced to attack Skill::Pause => panic!("nyi"), // speed slow // ----------------- @@ -733,20 +795,44 @@ fn drain(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { } fn drain_tick(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { - // damage part + // Damage part let (damage, _) = target.deal_spell_dmg(cryp.spell_dmg().wrapping_div(2)); - log.push(format!("{:?} | Drain damage {:?}", target.name, damage)); + log.push(format!("{:?} | Drain Damage {:?}", target.name, damage)); let (healing, overhealing) = target.heal(damage); log.push(format!("{:?} | Drain healing {:?} ({:?} OH)", cryp.name, healing, overhealing)); } +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 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 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 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 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); @@ -856,6 +942,20 @@ mod tests { assert_eq!(x.spell_dmg(), 100); } + #[test] + fn purify_test() { + let mut x = Cryp::new() + .named(&"muji".to_string()) + .level(8) + .create(); + + let mut log = vec![]; + decay(&mut x.clone(), &mut x, &mut log); + assert!(x.effects.iter().any(|e| e.effect == Effect::Decay)); + + purify(&mut x.clone(), &mut x, &mut log); + assert!(!x.effects.iter().any(|e| e.effect == Effect::Decay)); + } } // #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]