diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 62874609..a966ca11 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -10,6 +10,7 @@ use failure::err_msg; use account::Account; use rpc::{CrypSpawnParams}; use skill::{Skill, Cooldown, Effect, Tick}; +use game::{Log}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct CrypSkill { @@ -36,8 +37,8 @@ pub struct CrypEffect { } impl CrypEffect { - pub fn tick(&self, cryp: &mut Cryp) -> &CrypEffect { - self.effect.tick(self, cryp); + pub fn tick(&self, cryp: &mut Cryp, log: &mut Log) -> &CrypEffect { + self.effect.tick(self, cryp, log); self } } @@ -175,6 +176,10 @@ impl Cryp { self.hp.value == 0 } + pub fn immune(&self, skill: Skill) -> bool { + self.effects.iter().any(|e| e.effect.immune(skill)) + } + pub fn is_stunned(&self) -> bool { self.effects.iter().any(|s| s.effect == Effect::Stun) } @@ -218,10 +223,10 @@ impl Cryp { self } - pub fn reduce_effect_durations(&mut self) -> &mut 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); + 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 a7f4a521..75e1bcb3 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -357,8 +357,7 @@ impl Game { 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); - self.log.push(skill.resolution.text.clone()); + skill.set_resolution(&mut source, &mut target, &mut self.log); self.resolved.push(skill.clone()); @@ -395,7 +394,7 @@ impl Game { } // always reduce durations - cryp.reduce_effect_durations(); + cryp.reduce_effect_durations(&mut self.log); self.update_cryp(&mut cryp); } diff --git a/server/src/skill.rs b/server/src/skill.rs index 45735472..a378be64 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -1,7 +1,7 @@ use rand::{thread_rng, Rng}; use uuid::Uuid; -// use game::{Log}; +use game::{Log}; use cryp::{Cryp, CrypEffect}; #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] @@ -30,12 +30,12 @@ impl Cast { target_cryp_id, target_team_id, skill, - resolution: Resolution { base: 0, result: None, text: String::new(), }, + resolution: Resolution { base: 0, result: None }, }; } - pub fn set_resolution(&mut self, cryp: &mut Cryp, target: &mut Cryp) -> &mut Cast { - self.resolution = self.skill.resolve(cryp, target); + 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 } @@ -53,7 +53,6 @@ impl Cast { pub struct Resolution { pub base: u64, pub result: Option, - pub text: String, } pub type Cooldown = Option; @@ -72,14 +71,16 @@ pub enum Effect { Fury, Evasion, Blind, + Snare, // magic - Silence, + Hex, Banish, Slow, Haste, Enslave, Mesmerise, + Amplify, // magic immunity Immune, @@ -95,20 +96,21 @@ pub enum Effect { } impl Effect { - pub fn prevents(&self, skill: Skill) -> bool { + pub fn immune(&self, skill: Skill) -> bool { match self { Effect::Block => match skill { - Skill::Stun => true, + Skill::Stun | + Skill::Attack => true, _ => false, }, _ => false, } } - pub fn tick(&self, cryp_effect: &CrypEffect, target: &mut Cryp) -> &Effect { + pub fn tick(&self, cryp_effect: &CrypEffect, target: &mut Cryp, log: &mut Log) -> &Effect { match self { - Effect::Decay => decay_tick(target, cryp_effect), - // Effect::Triage => triage_tick(target, res), + Effect::Decay => decay_tick(target, cryp_effect, log), + Effect::Triage => triage_tick(target, cryp_effect, log), _ => (), } @@ -149,7 +151,7 @@ pub enum Skill { Swarm, Orbit, Repair, - Scout, // track? + Scan, // track? // ----------------- // Nonviolence @@ -175,7 +177,7 @@ pub enum Skill { // ----------------- // Purity // ----------------- - Accuracy, + Precision, Inspire, Slay, Shield, @@ -226,7 +228,7 @@ impl Skill { Skill::Swarm => Some(3), Skill::Orbit => Some(2), Skill::Repair => Some(1), - Skill::Scout => Some(2), // track? + Skill::Scan => Some(2), // track? // ----------------- // Preservation @@ -252,7 +254,7 @@ impl Skill { // ----------------- // Purity // ----------------- - Skill::Accuracy => Some(1), + Skill::Precision => Some(1), Skill::Inspire => Some(2), Skill::Slay => Some(1), Skill::Shield => Some(1), @@ -305,7 +307,7 @@ impl Skill { Skill::Swarm => 3, Skill::Orbit => 2, Skill::Repair => 1, - Skill::Scout => 2, // track? + Skill::Scan => 2, // track? // ----------------- // Preservation @@ -331,7 +333,7 @@ impl Skill { // ----------------- // Purity // ----------------- - Skill::Accuracy => 1, + Skill::Precision => 1, Skill::Inspire => 2, Skill::Slay => 1, Skill::Shield => 1, @@ -357,11 +359,11 @@ impl Skill { } } - pub fn resolve(&self, cryp: &mut Cryp, target: &mut Cryp) -> Resolution { + 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 mut res = Resolution { base, result: None, text: String::new() }; + let res = Resolution { base, result: None }; // println!("{:?}'s stats", self.name); // println!("{:064b} <- finalised", roll.result); @@ -374,19 +376,19 @@ impl Skill { // return Some(roll); match self { - Skill::Attack => attack(cryp, target, &mut res), + Skill::Attack => attack(cryp, target, log), // ----------------- // Nature // ----------------- - Skill::Block => block(cryp, target, &mut res), + Skill::Block => block(cryp, target, log), Skill::Evade => panic!("nyi"), // Skill::Parry => panic!("nyi"), // avoid all dmg - Skill::Snare => panic!("nyi"), + Skill::Snare => snare(cryp, target, log), // TODO prevent physical moves Skill::Paralyse => panic!("nyi"), // no physical moves Skill::Strangle => panic!("nyi"), // no physical moves - Skill::Stun => stun(cryp, target, &mut res), + Skill::Stun => stun(cryp, target, log), Skill::Evasion => panic!("nyi"), // additional layer of dmg avoidance // ----------------- @@ -396,14 +398,14 @@ impl Skill { Skill::Swarm => panic!("nyi"), Skill::Orbit => panic!("nyi"), Skill::Repair => panic!("nyi"), - Skill::Scout => panic!("nyi"), // track? + Skill::Scan => panic!("nyi"), // track? // ----------------- // Preservation // ----------------- - Skill::Heal => heal(cryp, target, &mut res), - Skill::Triage => triage(cryp, target, &mut res), // hot - Skill::Throw => panic!("nyi"), // no dmg stun, adds vulnerable + Skill::Heal => heal(cryp, target, log), + Skill::Triage => triage(cryp, target, log), // hot + Skill::Throw => throw(cryp, target, log), // no dmg stun, adds vulnerable Skill::Charm => panic!("nyi"), Skill::Calm => panic!("nyi"), Skill::Rez => panic!("nyi"), @@ -411,9 +413,9 @@ impl Skill { // ----------------- // Destruction // ----------------- - Skill::Blast => panic!("nyi"), - Skill::Amplify => panic!("nyi"), - Skill::Decay => decay(cryp, target, &mut res), // dot + 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::Plague => panic!("nyi"), // aoe dot @@ -422,7 +424,7 @@ impl Skill { // ----------------- // Purity // ----------------- - Skill::Accuracy => panic!("nyi"), + Skill::Precision => panic!("nyi"), Skill::Inspire => panic!("nyi"), Skill::Slay => panic!("nyi"), Skill::Shield => panic!("nyi"), @@ -433,8 +435,8 @@ impl Skill { // ----------------- // Chaos // ----------------- - Skill::Banish => panic!("nyi"), - Skill::Hex => panic!("nyi"), + Skill::Banish => banish(cryp, target, log), // TODO prevent all actions + Skill::Hex => hex(cryp, target, log), // todo prevent casting Skill::Fear => panic!("nyi"), Skill::Taunt => panic!("nyi"), Skill::Pause => panic!("nyi"), // speed slow @@ -443,8 +445,8 @@ impl Skill { // Test // ----------------- Skill::TestTouch => (), - Skill::TestStun => stun(cryp, target, &mut res), - Skill::TestBlock => block(cryp, target, &mut res), + Skill::TestStun => stun(cryp, target, log), + Skill::TestBlock => block(cryp, target, log), }; return res; @@ -457,6 +459,7 @@ impl Skill { Skill::Block => 1, Skill::Decay => 3, + Skill::Triage => 3, Skill::TestBlock => 1, Skill::TestStun => 2, @@ -480,28 +483,48 @@ impl Skill { } } -fn attack(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { - res.text = format!("{:?} -> {:?} | Attack for {:?}", cryp.name, target.name, cryp.phys_dmg); +fn attack(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + 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, res: &mut Resolution) { - if !target.effects.iter().any(|e| e.effect.prevents(Skill::Stun)) { +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); - res.text = format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration); + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration)); } else { - res.text = format!("{:?} blocks.", target.name); + log.push(format!("{:?} -> {:?} | {:?} immune", cryp.name, target.name, target.name)); } } -fn block(_cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { - let effect = CrypEffect { effect: Effect::Block, duration: Skill::Block.duration(), tick: None }; - target.effects.push(effect); - res.text = format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.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)); + } } -fn heal(cryp: &mut Cryp, target: &mut Cryp, res: &mut 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!("{:?} is {:?} 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)); +} + +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 @@ -510,30 +533,73 @@ fn heal(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { 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; - res.text = format!("{:?} -> {:?} | Heal for {:?} ({:?} OH)", cryp.name, target.name, healing, overhealing); + log.push(format!("{:?} -> {:?} | Heal for {:?} ({:?} OH)", cryp.name, target.name, healing, overhealing)); } -fn triage(_cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { - let effect = CrypEffect { effect: Effect::Triage, duration: Skill::Triage.duration(), tick: None }; +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) }) + }; target.effects.push(effect); - res.text = format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration); + log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); } -fn decay(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { +fn triage_tick(target: &mut Cryp, effect: &CrypEffect, log: &mut Log) { + let tick = effect.tick.expect("no tick for triage"); + let new_hp = *[ + target.hp.value.saturating_add(tick.amount), + 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)); + target.hp.value = new_hp; +} + +fn blast(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + let amount = cryp.spell_dmg.value; + log.push(format!("{:?} -> {:?} | Blast for {:?}", cryp.name, target.name, amount)); + target.hp.reduce(amount); +} + +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)); +} + +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) }) }; target.effects.push(effect); - res.text = format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration); + log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); } -fn decay_tick(target: &mut Cryp, effect: &CrypEffect) { +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 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)); +} + +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)); +} + + #[cfg(test)] mod tests { use skill::*; @@ -554,10 +620,10 @@ mod tests { x.hp.reduce(5); - let mut resolution = Resolution { base: 0, result: None, text: String::new() }; - heal(&mut y, &mut x, &mut resolution); + let mut log = vec![]; + heal(&mut y, &mut x, &mut log); - println!("{:?}", resolution); + println!("{:?}", log); } #[test] @@ -565,7 +631,6 @@ mod tests { let mut x = Cryp::new() .named(&"muji".to_string()) .level(8) - .learn(Skill::Decay) .create(); let mut y = Cryp::new() @@ -573,16 +638,43 @@ mod tests { .level(8) .create(); - let mut resolution = Resolution { base: 0, result: None, text: String::new() }; - decay(&mut x, &mut y, &mut resolution); + let mut log = vec![]; + decay(&mut x, &mut y, &mut log); assert!(y.effects.iter().any(|e| e.effect == Effect::Decay)); - y.reduce_effect_durations(); + 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)); } + #[test] + fn triage_test() { + let mut x = Cryp::new() + .named(&"muji".to_string()) + .level(8) + .create(); + + let mut y = Cryp::new() + .named(&"pretaliation".to_string()) + .level(8) + .create(); + + let mut log = vec![]; + + // ensure it doesn't have 0 sd + x.spell_dmg.value = 50; + y.hp.reduce(5); + + let prev_hp = y.hp.value; + + 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); + } } // #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]