From 12d30fbc8291c2247068e62b9fdcd182d19c15d7 Mon Sep 17 00:00:00 2001 From: ntr Date: Fri, 26 Oct 2018 19:08:00 +1100 Subject: [PATCH 1/8] resolved log --- server/WORKLOG.md | 28 +++++----- server/src/cryp.rs | 14 +++-- server/src/game.rs | 131 ++++++++++++++++++++++++++++++++++++-------- server/src/skill.rs | 19 ++++++- 4 files changed, 150 insertions(+), 42 deletions(-) diff --git a/server/WORKLOG.md b/server/WORKLOG.md index a82abd4b..ea0ecff3 100755 --- a/server/WORKLOG.md +++ b/server/WORKLOG.md @@ -4,30 +4,19 @@ * Prove something # WORK WORK -* QOL - * auto login - * cryp list on spawn - * ws reconnect ✔ -* Levelling ✔ +* move rpc functions out + * unwrap account for all functions except list * Global rolls -* Logins ✔️ - * Cryp Ownership ✔ - -* Matchmaking - * Lobbies - * Create - * Join - * Resolve - * Stats * Scrabble grid * skills * offensive -> choose target ✔ * private fields for opponents + * cooldowns reduce each turn * teach cyps skills * can you attack yourself? * fetch existing battles @@ -50,6 +39,17 @@ * Cryp Generation * +* ws reconnect ✔ +* Levelling ✔ +* Logins ✔️ + * Cryp Ownership ✔ +* Matchmaking ✔ + * Lobbies ✔ + * Create ✔ + * Join ✔ + * Resolve ✔ + + # Db maintenance * delete games when a cryp is deleted * does this need to happen? can have historical games diff --git a/server/src/cryp.rs b/server/src/cryp.rs index dd018cea..5e27aa23 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -26,7 +26,9 @@ impl CrypSkill { Skill::Block => Some(1), Skill::Dodge => Some(1), Skill::Heal => Some(2), - Skill::Stun => None, + Skill::Stun => Some(2), + Skill::TestTouch => None, + Skill::TestStun => None, }; CrypSkill { @@ -190,12 +192,12 @@ impl Cryp { self.skills.iter().any(|s| s.skill == skill) } - pub fn skill_on_cd(&self, skill: Skill) -> bool { - self.skills.iter().any(|s| s.skill == skill && s.cd.is_some()) + pub fn skill_on_cd(&self, skill: Skill) -> Option<&CrypSkill> { + self.skills.iter().find(|s| s.skill == skill && s.cd.is_some()) } pub fn skill_can_cast(&self, skill: Skill) -> bool { - self.skills.iter().any(|s| s.skill == skill && !s.can_cast(self)) + self.skills.iter().any(|s| s.skill == skill && s.can_cast(self)) } pub fn rez(&mut self) -> &mut Cryp { @@ -213,6 +215,10 @@ impl Cryp { Skill::Stun => self.str, Skill::Dodge => self.agi, Skill::Heal => self.int, + + // test skills + Skill::TestTouch => self.int, + Skill::TestStun => self.str, }; let mut roll = Roll { base, result: base }; diff --git a/server/src/game.rs b/server/src/game.rs index 413484ec..94dcf67e 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -18,6 +18,7 @@ pub struct Team { cryps: Vec, skills: Vec, incoming: Vec, + resolved: Vec, } impl Team { @@ -27,6 +28,7 @@ impl Team { cryps: vec![], skills: vec![], incoming: vec![], + resolved: vec![] }; } @@ -41,6 +43,13 @@ impl Team { self } + fn update_cryp(&mut self, updated: Cryp) -> &mut Team { + let index = self.cryps.iter().position(|c| c.id == updated.id).unwrap(); + self.cryps.remove(index); + self.cryps.push(updated); + self + } + pub fn cryp_by_id(&mut self, id: Uuid) -> Option<&mut Cryp> { self.cryps.iter_mut().find(|c| c.id == id) } @@ -129,26 +138,15 @@ impl Game { panic!("cryp not in game"); } - // fn update_team(&mut self, updated: Team) -> &mut Game { - // match self.teams.iter().position(|t| t.id == updated.id) { - // Some(index) => { - // self.teams.remove(index); - // self.teams.push(updated); - // self - // } - // None => panic!("team not in game"), - // } - // } - - fn update_cryp(&mut self, updated: Cryp) -> &mut Game { - for team in self.teams.iter_mut() { - if let Some(index) = team.cryps.iter().position(|c| c.id == updated.id) { - team.cryps.remove(index); - team.cryps.push(updated); - break; + fn update_team(&mut self, updated: &mut Team) -> &mut Game { + match self.teams.iter().position(|t| t.id == updated.id) { + Some(index) => { + self.teams.remove(index); + self.teams.push(updated.clone()); + self } + None => panic!("team not in game"), } - self } fn can_start(&self) -> bool { @@ -214,15 +212,16 @@ impl Game { return Err(err_msg("cryp does not have that skill")); } - if cryp.skill_on_cd(skill) { + if cryp.skill_on_cd(skill).is_some() { return Err(err_msg("abiltity on cooldown")); } - if cryp.skill_can_cast(skill) { + if !cryp.skill_can_cast(skill) { return Err(err_msg("cryp cannot cast spell")); } } + // replace cryp skill if let Some(s) = team.skills.iter_mut().position(|s| s.cryp_id == cryp_id) { team.skills.remove(s); @@ -333,11 +332,13 @@ impl Game { // they better fuckin be there let mut cryp = self.cryp_by_id(incoming.cryp_id).clone(); let mut target_cryp = self.cryp_by_id(incoming.target_cryp_id.unwrap()).clone(); - println!("{:?} is attacking {:?}", cryp.name, target_cryp.name); - incoming.resolve(&mut cryp, &mut target_cryp); - self.update_cryp(target_cryp); + let resolution = incoming.resolve(&mut cryp, &mut target_cryp); + team.resolved.push(*resolution); + team.update_cryp(target_cryp); + self.update_team(team); } } + self } @@ -638,6 +639,55 @@ mod tests { use game::*; use cryp::*; + fn create_test_game() -> Game { + let x = Cryp::new() + .named(&"pronounced \"creeep\"".to_string()) + .level(8) + .learn(Skill::TestStun) + .learn(Skill::TestTouch) + .learn(Skill::Block) + .create(); + + let _x_id = x.id; + + let y = Cryp::new() + .named(&"lemongrass tea".to_string()) + .level(8) + .learn(Skill::TestStun) + .learn(Skill::TestTouch) + .learn(Skill::Block) + .create(); + + let _y_id = y.id; + + let mut game = Game::new(); + + game + .set_team_num(2) + .set_team_size(1) + .set_pve(false); + + let x_team_id = Uuid::new_v4(); + let mut x_team = Team::new(x_team_id); + x_team + .set_cryps(vec![x]); + + let y_team_id = Uuid::new_v4(); + let mut y_team = Team::new(y_team_id); + y_team + .set_cryps(vec![y]); + + game + .add_team(x_team).unwrap() + .add_team(y_team).unwrap(); + + assert!(game.can_start()); + + game.start(); + + return game; + } + #[test] fn game_test() { let x = Cryp::new() @@ -701,4 +751,39 @@ mod tests { return; } + + #[test] + fn cooldown_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_stun_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::TestStun).unwrap(); + let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::TestTouch).unwrap(); + + assert!(game.skill_phase_finished()); + game.target_phase_start(); + + game.add_target(x_team.id, x_cryp.id, y_attack_id).unwrap(); + game.add_target(y_team.id, y_cryp.id, x_stun_id).unwrap(); + + assert!(game.target_phase_finished()); + game.damage_phase_start(); + + // should auto progress back to skill phase + assert!(game.phase == Phase::Skill); + assert!(game.team_by_id(y_team.id).cryps[0].is_stunned()); + assert!(game.team_by_id(y_team.id).skills_required() == 0); + + // should be on cooldown now + assert!(game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::Stun).is_err()); + + println!("{:#?}", game); + // 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()); + } } diff --git a/server/src/skill.rs b/server/src/skill.rs index ab9b5953..cb65ee6e 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -16,6 +16,9 @@ pub enum Skill { Heal, Stun, Dodge, + // used by tests to do no dmg + TestTouch, + TestStun, } @@ -26,6 +29,7 @@ pub struct Cast { pub cryp_id: Uuid, pub target_cryp_id: Option, pub target_team_id: Uuid, + pub roll: Option, } impl Cast { @@ -36,18 +40,31 @@ impl Cast { target_cryp_id: None, target_team_id, skill, + roll: None, }; } pub fn resolve(&mut self, cryp: &mut Cryp, target: &mut Cryp) -> &mut Cast { let roll = cryp.roll(self.skill); + println!("{:?} -> {:?} -> {:?}", cryp.name, self.skill, target.name); + match self.skill { + // the real deal Skill::Stun => target.stun(roll), - _ => target.attack(roll), + Skill::Attack => target.attack(roll), + Skill::Block => target, + Skill::Heal => target, + Skill::Dodge => target, + + // Test Skills + Skill::TestStun => target.stun(roll), + Skill::TestTouch => target, }; println!("{:?} gettin clapped for {:?}", target.name, roll.result); + + self.roll = Some(roll); self } From f9966e1c2bfeb23b926b416af9e9e195c2439ea9 Mon Sep 17 00:00:00 2001 From: ntr Date: Fri, 26 Oct 2018 19:59:14 +1100 Subject: [PATCH 2/8] skill cooldowns working --- server/src/cryp.rs | 30 +++++++++- server/src/game.rs | 141 ++++++++++++++++++++++++++------------------ server/src/skill.rs | 7 ++- 3 files changed, 116 insertions(+), 62 deletions(-) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 5e27aa23..df5b3d8a 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -15,8 +15,8 @@ type Cooldown = Option; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct CrypSkill { - skill: Skill, - cd: Cooldown, + pub skill: Skill, + pub cd: Cooldown, } impl CrypSkill { @@ -200,6 +200,32 @@ impl Cryp { self.skills.iter().any(|s| s.skill == skill && s.can_cast(self)) } + pub fn skill_set_cd(&mut self, skill: Skill) -> &mut Cryp { + let i = self.skills.iter().position(|s| s.skill == skill).unwrap(); + self.skills.remove(i); + self.skills.push(CrypSkill::new(skill)); + + self + } + + pub fn decrease_cooldowns(&mut self) -> &mut Cryp { + for skill in self.skills.iter_mut() { + if let Some(cd) = skill.cd { + // if the cd is 1 it becomes none + if cd == 1 { + println!("{:?} is now off cd", skill.skill); + skill.cd = None; + continue; + } + + // otherwise decrement it + skill.cd = Some(cd.saturating_sub(1)); + } + } + + self + } + pub fn rez(&mut self) -> &mut Cryp { self.hp.set(self.stam.value); self diff --git a/server/src/game.rs b/server/src/game.rs index 94dcf67e..6a0c8500 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -60,6 +60,28 @@ impl Team { None => panic!("abiltity not in game"), } } + + pub fn progress_cooldowns(&mut self) -> &mut Team { + // get a copy of the used skills + for skill in self.skills.clone().iter() { + // copy the creep cause we will replace it + let mut cryp = self.cryp_by_id(skill.cryp_id).unwrap().clone(); + + if skill.used_cooldown() { + cryp.skill_set_cd(skill.skill); + self.update_cryp(cryp); + continue; + } + + cryp.decrease_cooldowns(); + + println!("{:?}", cryp); + + self.update_cryp(cryp); + } + + self + } } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -333,12 +355,20 @@ impl Game { let mut cryp = self.cryp_by_id(incoming.cryp_id).clone(); let mut target_cryp = self.cryp_by_id(incoming.target_cryp_id.unwrap()).clone(); let resolution = incoming.resolve(&mut cryp, &mut target_cryp); + + // push the resolved spell on team.resolved.push(*resolution); team.update_cryp(target_cryp); self.update_team(team); } } + // now damage has all been assigned + // handle cooldowns + for team in self.teams.iter_mut() { + team.progress_cooldowns(); + } + self } @@ -648,8 +678,6 @@ mod tests { .learn(Skill::Block) .create(); - let _x_id = x.id; - let y = Cryp::new() .named(&"lemongrass tea".to_string()) .level(8) @@ -658,8 +686,6 @@ mod tests { .learn(Skill::Block) .create(); - let _y_id = y.id; - let mut game = Game::new(); game @@ -688,69 +714,62 @@ mod tests { return game; } - #[test] - fn game_test() { - let x = Cryp::new() - .named(&"pronounced \"creeep\"".to_string()) - .level(8) - .create(); + // #[test] + // fn phase_test() { + // let mut game = create_test_game(); - let x_id = x.id; + // let x_team = game.teams[0].clone(); + // let y_team = game.teams[1].clone(); - let y = Cryp::new() - .named(&"lemongrass tea".to_string()) - .level(8) - .create(); + // let x_cryp = x_team.cryps[0].clone(); + // let y_cryp = y_team.cryps[0].clone(); - let y_id = y.id; + // let x_attack_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::Attack).unwrap(); + // let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::Attack).unwrap(); - let mut game = Game::new(); + // assert!(game.skill_phase_finished()); - game - .set_team_num(2) - .set_team_size(1) - .set_pve(false); + // game.target_phase_start(); - let x_team_id = Uuid::new_v4(); - let mut x_team = Team::new(x_team_id); - x_team - .set_cryps(vec![x]); + // game.add_target(x_team.id, x_cryp.id, y_attack_id).unwrap(); + // game.add_target(y_team.id, y_cryp.id, x_attack_id).unwrap(); - let y_team_id = Uuid::new_v4(); - let mut y_team = Team::new(y_team_id); - y_team - .set_cryps(vec![y]); + // assert!(game.target_phase_finished()); - game - .add_team(x_team).unwrap() - .add_team(y_team).unwrap(); + // game.damage_phase_start(); - assert!(game.can_start()); + // assert!([Phase::Skill, Phase::Finish].contains(&game.phase)); - game.start(); + // return; + // } - let x_attack_id = game.add_skill(x_team_id, x_id, y_team_id, Skill::Attack); - let y_attack_id = game.add_skill(y_team_id, y_id, x_team_id, Skill::Attack); + // #[test] + // fn stun_test() { + // let mut game = create_test_game(); - assert!(game.skill_phase_finished()); + // let x_team = game.teams[0].clone(); + // let y_team = game.teams[1].clone(); - game.target_phase_start(); + // let x_cryp = x_team.cryps[0].clone(); + // let y_cryp = y_team.cryps[0].clone(); - println!("{:?}", game); + // let x_stun_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::TestStun).unwrap(); + // let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::TestTouch).unwrap(); - game.add_target(x_team_id, x_id, y_attack_id.unwrap()).unwrap(); - game.add_target(y_team_id, y_id, x_attack_id.unwrap()).unwrap(); + // assert!(game.skill_phase_finished()); + // game.target_phase_start(); - assert!(game.target_phase_finished()); + // game.add_target(x_team.id, x_cryp.id, y_attack_id).unwrap(); + // game.add_target(y_team.id, y_cryp.id, x_stun_id).unwrap(); - game.damage_phase_start(); + // assert!(game.target_phase_finished()); + // game.damage_phase_start(); - assert!([Phase::Skill, Phase::Finish].contains(&game.phase)); - - println!("{:?}", game); - - return; - } + // // should auto progress back to skill phase + // assert!(game.phase == Phase::Skill); + // assert!(game.team_by_id(y_team.id).cryps[0].is_stunned()); + // assert!(game.team_by_id(y_team.id).skills_required() == 0); + // } #[test] fn cooldown_test() { @@ -762,28 +781,32 @@ mod tests { let x_cryp = x_team.cryps[0].clone(); let y_cryp = y_team.cryps[0].clone(); - let x_stun_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::TestStun).unwrap(); + let x_stun_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::TestTouch).unwrap(); let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::TestTouch).unwrap(); - assert!(game.skill_phase_finished()); game.target_phase_start(); - game.add_target(x_team.id, x_cryp.id, y_attack_id).unwrap(); game.add_target(y_team.id, y_cryp.id, x_stun_id).unwrap(); - assert!(game.target_phase_finished()); game.damage_phase_start(); // should auto progress back to skill phase assert!(game.phase == Phase::Skill); - assert!(game.team_by_id(y_team.id).cryps[0].is_stunned()); - assert!(game.team_by_id(y_team.id).skills_required() == 0); - // should be on cooldown now - assert!(game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::Stun).is_err()); - - println!("{:#?}", game); // 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()); + + // second round + // now we block and it should go back on cd + let x_block_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::Block).unwrap(); + let y_block_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::Block).unwrap(); + + game.target_phase_start(); + game.add_target(x_team.id, x_cryp.id, y_block_id).unwrap(); + game.add_target(y_team.id, y_cryp.id, x_block_id).unwrap(); + game.damage_phase_start(); + + assert!(game.team_by_id(y_team.id).cryps[0].skill_on_cd(Skill::Block).is_some()); + println!("{:#?}", game); } } diff --git a/server/src/skill.rs b/server/src/skill.rs index cb65ee6e..0e2d2fc3 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -1,7 +1,7 @@ use rand::prelude::*; use uuid::Uuid; -use cryp::{Cryp}; +use cryp::{Cryp, CrypSkill}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct Roll { @@ -72,6 +72,11 @@ impl Cast { self.target_cryp_id = Some(cryp_id); self } + + pub fn used_cooldown(self) -> bool { + let cs = CrypSkill::new(self.skill); + return cs.cd.is_some(); + } } // #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] From b2dd0633823d6252737663bc79241bea62c98caa Mon Sep 17 00:00:00 2001 From: ntr Date: Sat, 27 Oct 2018 17:41:15 +1100 Subject: [PATCH 3/8] rewrite game with skills on the game struct --- server/src/game.rs | 253 ++++++++++++++++++++++---------------------- server/src/skill.rs | 10 +- 2 files changed, 130 insertions(+), 133 deletions(-) diff --git a/server/src/game.rs b/server/src/game.rs index 6a0c8500..69f2d0ff 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -16,9 +16,6 @@ use skill::{Skill, Cast}; pub struct Team { id: Uuid, cryps: Vec, - skills: Vec, - incoming: Vec, - resolved: Vec, } impl Team { @@ -26,9 +23,6 @@ impl Team { return Team { id: account, cryps: vec![], - skills: vec![], - incoming: vec![], - resolved: vec![] }; } @@ -53,35 +47,6 @@ impl Team { pub fn cryp_by_id(&mut self, id: Uuid) -> Option<&mut Cryp> { self.cryps.iter_mut().find(|c| c.id == id) } - - pub fn cast_by_id(&mut self, id: Uuid) -> &mut Cast { - match self.incoming.iter_mut().find(|a| a.id == id) { - Some(a) => a, - None => panic!("abiltity not in game"), - } - } - - pub fn progress_cooldowns(&mut self) -> &mut Team { - // get a copy of the used skills - for skill in self.skills.clone().iter() { - // copy the creep cause we will replace it - let mut cryp = self.cryp_by_id(skill.cryp_id).unwrap().clone(); - - if skill.used_cooldown() { - cryp.skill_set_cd(skill.skill); - self.update_cryp(cryp); - continue; - } - - cryp.decrease_cooldowns(); - - println!("{:?}", cryp); - - self.update_cryp(cryp); - } - - self - } } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -101,6 +66,8 @@ pub struct Game { pub teams: Vec, pub is_pve: bool, pub phase: Phase, + pub skills: Vec, + pub resolutions: Vec, pub log: Vec, } @@ -113,6 +80,8 @@ impl Game { teams: vec![], is_pve: true, phase: Phase::Start, + skills: vec![], + resolutions: vec![], log: vec![], }; } @@ -171,6 +140,19 @@ impl Game { } } + 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) => { + let index = team.cryps.iter().position(|t| t.id == cryp.id).unwrap(); + team.cryps.remove(index); + team.cryps.push(cryp.clone()); + }, + None => panic!("cryp not in game"), + }; + + self + } + fn can_start(&self) -> bool { self.teams.len() == self.team_num } @@ -186,10 +168,8 @@ impl Game { } self.phase = Phase::Skill; - for team in self.teams.iter_mut() { - team.skills.clear(); - team.incoming.clear(); - } + + self.skills.clear(); if self.is_pve { self.pve_add_skills(); @@ -201,13 +181,13 @@ impl Game { fn pve_add_skills(&mut self) -> &mut Game { { let mob_team_id = Uuid::nil(); - let teams = self.teams.clone(); let mobs = self.team_by_id(mob_team_id).clone(); + // TODO attack multiple players based on some criteria - let player_team = teams.iter().find(|t| t.id != mob_team_id).unwrap(); + let player_team_id = self.teams.iter().find(|t| t.id != mob_team_id).unwrap().id; for mob in &mobs.cryps { // doesn't matter if the cryp can't cast - self.add_skill(mob_team_id, mob.id, player_team.id, Skill::Attack).ok(); + self.add_skill(mob_team_id, mob.id, player_team_id, Skill::Attack).ok(); } } @@ -216,15 +196,14 @@ impl Game { // skills can target any team, but we have to check if the caller is the owner of the cryp // and that the cryp has the skill they are trying to add - fn add_skill(&mut self, team_id: Uuid, cryp_id: Uuid, target_team_id: Uuid, skill: Skill) -> Result { + fn add_skill(&mut self, team_id: Uuid, source_cryp_id: Uuid, target_team_id: Uuid, skill: Skill) -> Result { if self.phase != Phase::Skill { return Err(err_msg("game not in skill phase")); } - let team = self.team_by_id(team_id); - { - let cryp = match team.cryp_by_id(cryp_id) { + let team = self.team_by_id(team_id); + let cryp = match team.cryp_by_id(source_cryp_id) { Some(c) => c, None => return Err(err_msg("cryp not in team")), }; @@ -245,18 +224,26 @@ impl Game { // replace cryp skill - if let Some(s) = team.skills.iter_mut().position(|s| s.cryp_id == cryp_id) { - team.skills.remove(s); + if let Some(s) = self.skills.iter_mut().position(|s| s.source_cryp_id == source_cryp_id) { + self.skills.remove(s); } - let skill = Cast::new(cryp_id, target_team_id, skill); - team.skills.push(skill); + let skill = Cast::new(source_cryp_id, team_id, target_team_id, skill); + self.skills.push(skill); return Ok(skill.id); } fn skill_phase_finished(&self) -> bool { - self.teams.iter().all(|t| t.skills.len() == t.skills_required()) + self.teams.iter() + // for every team + .all(|t| self.skills.iter() + // the number of skills they have cast + .filter(|s| s.target_team_id == t.id) + .collect::>() + // should equal the number required this turn + .len() == t.skills_required() + ) } // move all skills into their target team's targets list @@ -267,16 +254,6 @@ impl Game { self.phase = Phase::Target; - // have to clone the teams because we change them while iterating - let teams = self.teams.clone(); - - for mut team in teams { - for skill in team.skills.iter_mut() { - let target_team = self.team_by_id(skill.target_team_id); - target_team.incoming.push(*skill); - } - } - if self.is_pve { self.pve_add_targets(); } @@ -294,33 +271,38 @@ impl Game { let mob_team_id = Uuid::nil(); let mobs = self.team_by_id(mob_team_id).clone(); // TODO attack multiple players based on some criteria - for incoming in &mobs.incoming { - self.add_target(mob_team_id, mobs.cryps[0].id, incoming.id).unwrap(); + for incoming_skill_id in self.skills.clone().iter().filter(|s| s.target_team_id == mob_team_id).map(|s| s.id) { + self.add_target(mob_team_id, mobs.cryps[0].id, incoming_skill_id).unwrap(); } } self } - // targets can only be added by the owner of the team fn add_target(&mut self, team_id: Uuid, cryp_id: Uuid, cast_id: Uuid) -> Result<&mut Cast, Error> { - // whose team is this? - let team = self.team_by_id(team_id); + { + // whose team is this? + let team = self.team_by_id(team_id); - // is the target in the team? - match team.cryp_by_id(cryp_id) { - Some(c) => c, - None => return Err(err_msg("cryp not in team")), - }; + // is the target in the team? + match team.cryp_by_id(cryp_id) { + Some(c) => c, + None => return Err(err_msg("cryp not in team")), + }; + } // set the target - let cast = team.cast_by_id(cast_id); + let cast = match self.skills.iter_mut().find(|s| s.id == cast_id) { + Some(c) => c, + None => return Err(err_msg("cast_id not found")), + }; + Ok(cast.set_target(cryp_id)) } fn target_phase_finished(&self) -> bool { - self.teams.iter().all(|t| t.incoming.iter().all(|i| i.target_cryp_id.is_some())) + self.skills.iter().all(|s| s.target_cryp_id.is_some()) } // requires no input @@ -348,30 +330,44 @@ impl Game { panic!("game not in damage phase"); } - // sometimes... you just gotta - for team in self.teams.clone().iter_mut() { - for incoming in team.incoming.clone().iter_mut() { - // they better fuckin be there - let mut cryp = self.cryp_by_id(incoming.cryp_id).clone(); - let mut target_cryp = self.cryp_by_id(incoming.target_cryp_id.unwrap()).clone(); - let resolution = incoming.resolve(&mut cryp, &mut target_cryp); + for skill in self.skills.clone().iter_mut() { + let mut source = self.cryp_by_id(skill.source_cryp_id).clone(); + let mut target = self.cryp_by_id(skill.target_cryp_id.unwrap()).clone(); + let resolution = skill.resolve(&mut source, &mut target); - // push the resolved spell on - team.resolved.push(*resolution); - team.update_cryp(target_cryp); - self.update_team(team); - } + self.update_cryp(&mut source); + self.update_cryp(&mut target); } // now damage has all been assigned // handle cooldowns - for team in self.teams.iter_mut() { - team.progress_cooldowns(); + self.progress_cooldowns(); + + self + } + + fn progress_cooldowns(&mut self) -> &mut Game { + for skill in self.skills.clone().iter() { + // copy the creep cause we will replace it + let mut cryp = self.cryp_by_id(skill.source_cryp_id).clone(); + + if skill.used_cooldown() { + cryp.skill_set_cd(skill.skill); + self.update_cryp(&mut cryp); + continue; + } + + cryp.decrease_cooldowns(); + + println!("{:?}", cryp); + + self.update_cryp(&mut cryp); } self } + fn is_finished(&self) -> bool { self.teams.iter().any(|t| t.cryps.iter().all(|c| c.is_ko())) } @@ -379,10 +375,7 @@ impl Game { fn finish(&mut self) -> &mut Game { self.phase = Phase::Finish; - for team in self.teams.iter_mut() { - team.skills.clear(); - team.incoming.clear(); - } + self.skills.clear(); self } @@ -714,62 +707,64 @@ mod tests { return game; } - // #[test] - // fn phase_test() { - // let mut game = create_test_game(); + #[test] + fn phase_test() { + let mut game = create_test_game(); - // let x_team = game.teams[0].clone(); - // let y_team = game.teams[1].clone(); + 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_cryp = x_team.cryps[0].clone(); + let y_cryp = y_team.cryps[0].clone(); - // let x_attack_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::Attack).unwrap(); - // let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::Attack).unwrap(); + let x_attack_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::Attack).unwrap(); + let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::Attack).unwrap(); - // assert!(game.skill_phase_finished()); + assert!(game.skill_phase_finished()); - // game.target_phase_start(); + game.target_phase_start(); - // game.add_target(x_team.id, x_cryp.id, y_attack_id).unwrap(); - // game.add_target(y_team.id, y_cryp.id, x_attack_id).unwrap(); + game.add_target(x_team.id, x_cryp.id, y_attack_id).unwrap(); + game.add_target(y_team.id, y_cryp.id, x_attack_id).unwrap(); - // assert!(game.target_phase_finished()); + assert!(game.target_phase_finished()); - // game.damage_phase_start(); + game.damage_phase_start(); - // assert!([Phase::Skill, Phase::Finish].contains(&game.phase)); + assert!([Phase::Skill, Phase::Finish].contains(&game.phase)); - // return; - // } + return; + } - // #[test] - // fn stun_test() { - // let mut game = create_test_game(); + #[test] + fn stun_test() { + let mut game = create_test_game(); - // let x_team = game.teams[0].clone(); - // let y_team = game.teams[1].clone(); + 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_cryp = x_team.cryps[0].clone(); + let y_cryp = y_team.cryps[0].clone(); - // let x_stun_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::TestStun).unwrap(); - // let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::TestTouch).unwrap(); + let x_stun_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::TestStun).unwrap(); + let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::TestTouch).unwrap(); - // assert!(game.skill_phase_finished()); - // game.target_phase_start(); + assert!(game.skill_phase_finished()); + game.target_phase_start(); - // game.add_target(x_team.id, x_cryp.id, y_attack_id).unwrap(); - // game.add_target(y_team.id, y_cryp.id, x_stun_id).unwrap(); + game.add_target(x_team.id, x_cryp.id, y_attack_id).unwrap(); + game.add_target(y_team.id, y_cryp.id, x_stun_id).unwrap(); - // assert!(game.target_phase_finished()); - // game.damage_phase_start(); + assert!(game.target_phase_finished()); + game.damage_phase_start(); - // // should auto progress back to skill phase - // assert!(game.phase == Phase::Skill); - // assert!(game.team_by_id(y_team.id).cryps[0].is_stunned()); - // assert!(game.team_by_id(y_team.id).skills_required() == 0); - // } + // 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); + } #[test] fn cooldown_test() { diff --git a/server/src/skill.rs b/server/src/skill.rs index 0e2d2fc3..d01114c4 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -26,17 +26,19 @@ pub enum Skill { pub struct Cast { pub id: Uuid, pub skill: Skill, - pub cryp_id: Uuid, + pub source_team_id: Uuid, + pub source_cryp_id: Uuid, pub target_cryp_id: Option, pub target_team_id: Uuid, pub roll: Option, } impl Cast { - pub fn new(cryp_id: Uuid, target_team_id: Uuid, skill: Skill) -> Cast { + pub fn new(source_cryp_id: Uuid, source_team_id: Uuid, target_team_id: Uuid, skill: Skill) -> Cast { return Cast { id: Uuid::new_v4(), - cryp_id, + source_cryp_id, + source_team_id, target_cryp_id: None, target_team_id, skill, @@ -62,7 +64,7 @@ impl Cast { Skill::TestTouch => target, }; - println!("{:?} gettin clapped for {:?}", target.name, roll.result); + // println!("{:?} gettin clapped for {:?}", target.name, roll.result); self.roll = Some(roll); self From 433b20fdb5fed91aad1fa29347ffed085b6d2b14 Mon Sep 17 00:00:00 2001 From: ntr Date: Sat, 27 Oct 2018 17:53:05 +1100 Subject: [PATCH 4/8] remove unused fns --- server/src/game.rs | 44 ++++++++++++-------------------------------- server/src/rpc.rs | 2 +- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/server/src/game.rs b/server/src/game.rs index 69f2d0ff..d40a03dc 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -37,13 +37,6 @@ impl Team { self } - fn update_cryp(&mut self, updated: Cryp) -> &mut Team { - let index = self.cryps.iter().position(|c| c.id == updated.id).unwrap(); - self.cryps.remove(index); - self.cryps.push(updated); - self - } - pub fn cryp_by_id(&mut self, id: Uuid) -> Option<&mut Cryp> { self.cryps.iter_mut().find(|c| c.id == id) } @@ -119,25 +112,13 @@ impl Game { } } - fn cryp_by_id(&mut self, id: Uuid) -> &mut Cryp { - for team in self.teams.iter_mut() { - let in_team = team.cryp_by_id(id); - if in_team.is_some() { - return in_team.unwrap(); - } - } - panic!("cryp not in game"); - } - - fn update_team(&mut self, updated: &mut Team) -> &mut Game { - match self.teams.iter().position(|t| t.id == updated.id) { - Some(index) => { - self.teams.remove(index); - self.teams.push(updated.clone()); - self - } - None => panic!("team not in game"), - } + fn cryp_by_id(&mut self, id: Uuid) -> Option<&mut Cryp> { + match self.teams.iter_mut().find(|t| t.cryps.iter().any(|c| c.id == id)) { + Some(team) => { + return team.cryps.iter_mut().find(|c| c.id == id); + }, + None => panic!("cryp not in game"), + }; } fn update_cryp(&mut self, cryp: &mut Cryp) -> &mut Game { @@ -202,8 +183,7 @@ impl Game { } { - let team = self.team_by_id(team_id); - let cryp = match team.cryp_by_id(source_cryp_id) { + let cryp = match self.cryp_by_id(source_cryp_id) { Some(c) => c, None => return Err(err_msg("cryp not in team")), }; @@ -331,8 +311,8 @@ impl Game { } for skill in self.skills.clone().iter_mut() { - let mut source = self.cryp_by_id(skill.source_cryp_id).clone(); - let mut target = self.cryp_by_id(skill.target_cryp_id.unwrap()).clone(); + 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(); let resolution = skill.resolve(&mut source, &mut target); self.update_cryp(&mut source); @@ -349,7 +329,7 @@ impl Game { fn progress_cooldowns(&mut self) -> &mut Game { for skill in self.skills.clone().iter() { // copy the creep cause we will replace it - let mut cryp = self.cryp_by_id(skill.source_cryp_id).clone(); + let mut cryp = self.cryp_by_id(skill.source_cryp_id).unwrap().clone(); if skill.used_cooldown() { cryp.skill_set_cd(skill.skill); @@ -430,7 +410,7 @@ pub fn game_target(params: GameTargetParams, tx: &mut Transaction, account: &Acc let game_bytes: Vec = returned.get("data"); let mut game = from_slice::(&game_bytes)?; - game.add_target(account.id, params.cryp_id, params.cast_id)?; + game.add_target(account.id, params.cryp_id, params.skill_id)?; if game.target_phase_finished() { game.damage_phase_start(); diff --git a/server/src/rpc.rs b/server/src/rpc.rs index a7b3b08a..736560b2 100755 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -355,7 +355,7 @@ struct GameTargetMsg { pub struct GameTargetParams { pub game_id: Uuid, pub cryp_id: Uuid, - pub cast_id: Uuid, + pub skill_id: Uuid, } #[derive(Debug,Clone,Serialize,Deserialize)] From 5549e87fab74e92e23b59897cc74c1737707ef97 Mon Sep 17 00:00:00 2001 From: ntr Date: Sat, 27 Oct 2018 18:26:23 +1100 Subject: [PATCH 5/8] fix ability phase bug; --- client/src/components/game.jsx | 2 +- client/src/socket.jsx | 3 ++- server/src/game.rs | 9 +++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/client/src/components/game.jsx b/client/src/components/game.jsx index 4ac25bff..fc55cb35 100755 --- a/client/src/components/game.jsx +++ b/client/src/components/game.jsx @@ -21,7 +21,7 @@ function GamePanel(props) { const playerTeam = game.teams.find(t => t.id === account.id); - const incoming = playerTeam.incoming.map((inc) => { + const incoming = game.skills.filter(s => s.target_team_id === playerTeam.id).map((inc) => { key.unbind('1'); key('1', () => setActiveIncoming(inc.id)); return ( diff --git a/client/src/socket.jsx b/client/src/socket.jsx index f8f3035e..80ed137b 100755 --- a/client/src/socket.jsx +++ b/client/src/socket.jsx @@ -99,6 +99,7 @@ function createSocket(store) { // Outgoing // ------------- function send(msg) { + console.log('outgoing msg', msg); msg.token = account && account.token; ws.send(cbor.encode(msg)); } @@ -142,7 +143,7 @@ function createSocket(store) { } function sendGameTarget(gameId, crypId, skillId) { - send({ method: 'game_target', params: { game_id: gameId, cryp_id: crypId, cast_id: skillId } }); + send({ method: 'game_target', params: { game_id: gameId, cryp_id: crypId, skill_id: skillId } }); store.dispatch(actions.setActiveIncoming(null)); } diff --git a/server/src/game.rs b/server/src/game.rs index d40a03dc..a6002d79 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -219,7 +219,7 @@ impl Game { // for every team .all(|t| self.skills.iter() // the number of skills they have cast - .filter(|s| s.target_team_id == t.id) + .filter(|s| s.source_team_id == t.id) .collect::>() // should equal the number required this turn .len() == t.skills_required() @@ -228,6 +228,7 @@ impl Game { // move all skills into their target team's targets list fn target_phase_start(&mut self) -> &mut Game { + assert!(self.skill_phase_finished()); if self.phase != Phase::Skill { panic!("game not in skill phase"); } @@ -260,7 +261,7 @@ impl Game { } // targets can only be added by the owner of the team - fn add_target(&mut self, team_id: Uuid, cryp_id: Uuid, cast_id: Uuid) -> Result<&mut Cast, Error> { + fn add_target(&mut self, team_id: Uuid, cryp_id: Uuid, skill_id: Uuid) -> Result<&mut Cast, Error> { { // whose team is this? let team = self.team_by_id(team_id); @@ -273,9 +274,9 @@ impl Game { } // set the target - let cast = match self.skills.iter_mut().find(|s| s.id == cast_id) { + let cast = match self.skills.iter_mut().find(|s| s.id == skill_id) { Some(c) => c, - None => return Err(err_msg("cast_id not found")), + None => return Err(err_msg("skill_id not found")), }; Ok(cast.set_target(cryp_id)) From 28238ee8125af00105e7de6b967d15f402c666fc Mon Sep 17 00:00:00 2001 From: ntr Date: Mon, 29 Oct 2018 16:56:43 +1100 Subject: [PATCH 6/8] remove some cruft --- server/DIARY.md | 11 ----- server/README.md | 103 ++++++-------------------------------------- server/WORKLOG.md | 26 ++++++++++- server/src/cryp.rs | 12 ++++-- server/src/game.rs | 25 +++++++++++ server/src/skill.rs | 2 +- 6 files changed, 73 insertions(+), 106 deletions(-) delete mode 100755 server/DIARY.md diff --git a/server/DIARY.md b/server/DIARY.md deleted file mode 100755 index d32aa776..00000000 --- a/server/DIARY.md +++ /dev/null @@ -1,11 +0,0 @@ -## 02-09-2018 -* went full circle through the last 20 years of the web's problems -* debated using vanilla tcp sockets but realised would be very time consuming -* Struggled a lot with google/tarpc - found the documentation absolutely mad, macros perform most of the functionality - couldn't find any way to keep server running, client stub appears to direcly rely on the server structs - needed a specific version of the rust nightly from several months ago to compile -* found wa-rs, hope was restored, had a websocket server up and running in seconds -* lost hope again when its client doesn't compile into wasm due to unix dependencies in mio - this also prevents any tokio based futures client from working -* realised i'd been reading very out of date documentation and there was plenty of work happening on `stdweb` and `cargo web` diff --git a/server/README.md b/server/README.md index 6f73373e..07b9e5a4 100755 --- a/server/README.md +++ b/server/README.md @@ -2,96 +2,21 @@ ## Setup -``` -rustup default nightly-2018-06-09-x86_64-unknown-linux-gnu -``` +skill phase: +1.1 -> block (sp 10) -> on self +1.2 -> attack (sp 5) -> on team 2 -## Items +2.1 -> hex (sp 3) -> on team 1 +2.2 -> attack (sp 5) -> on team 1 -## Rolling -stat & rng -block hash or totally random -roll server that prints a roll every second? +target phase: +team 2 targets 1.2 on 2.2 -friendship on ties? +team 1 targets 2.1 on 1.1 +team 1 targets 2.2 on 1.1 -Def -0001011011001100110101000000101111100110101111110100100001000001 - roll -0000000000000000000000000000000000000000000000000000000000001111 ^ steel armour -0000000000000000000000000000000000000000000000000000001111000000 ^ stoney trait -0001011011001100110101000000101111100110101111110100101111001111 = modified roll -0000000000000000000000000000000000000000000000000000000111000010 & def attribute -0000000000000000000000000000000000000000000000000000000111000010 = roll w/ stats - -0000000000000000000000000000000000000000000000000000000001000000 = roll w/out stats - - -## missions -also the idea is like -the currency is kinda like path right -you're trying to get like chaos -to reroll some stat -or some item -so maybe like a sword -has 5 bits of damage it can guarantee -but a badman-sabre has 16 bits -and you keep blowing chaos on it til it gets 1111111111 -MashyToday at 9:04 PM -yeah that would be cool -natureToday at 9:05 PM -i feel like that would make it kinda p2w -so probably needs limits -MashyToday at 9:05 PM -I was thinking the p2w more just like making the needs and missions quicker right -so like instead of feeding your dude you get some item where hes fed for 2 days or something -or you could reduce the mission time which is like kinda pay to win but not really -natureToday at 9:06 PM -well what do the missions give you -MashyToday at 9:06 PM -well thats what i was thinking you'd do to get items -so instead of doing a zone in an rpg and getting all this loot -you send your dude out and you got a notification 4 hours later like success / failure this is what you got back -natureToday at 9:07 PM -i was thinking aobut that -i don't really like the possibility of failure on a mission -imagine sending your dude out on a mission for 2 days and it ceoms back like "nope wrong" -i'd just fucken rq -MashyToday at 9:08 PM -Yeah -natureToday at 9:08 PM -BUT -a better thing is like -playing off crypto -MashyToday at 9:08 PM -its something like this https://www.youtube.com/watch?v=bXLW9dF7LN8 -YouTube -Tommy J -WoW: Garrison Mission Chance - How it Works - -natureToday at 9:08 PM -missions are like -MashyToday at 9:08 PM -so you get told before hand the % chance for success -natureToday at 9:08 PM -go and do 2000 overkill damage to some monster -and your dude is out there using his items and rolling -so it can happen quick if you're lucky -or slow if you're not -but still will eventually happen -and then you get like easy limits for missions right -MashyToday at 9:09 PM -yeah that would be better actually -natureToday at 9:09 PM -like a creep that onl deals 100 dmg can't finish that mission -i feel like that would be good -cause then if you have like a defensive cryp -it could have a defensive kinda mission -like go and block 40000 damage in pve -and then it gets some baller shield -and is like speccing into defense -MashyToday at 9:10 PM -sounds better -natureToday at 9:10 PM -and like an offensive cryp could do that too -but it might keep getting KOd -and you hvae to pay to revive it \ No newline at end of file +resolve phase: +1.1 <- block +1.1 <- attack (no effect because of block) +2.2 <- attack (normal resolve) +1.1 <- hexed (no skills for the rest of this turn and next) diff --git a/server/WORKLOG.md b/server/WORKLOG.md index ea0ecff3..9b68c58e 100755 --- a/server/WORKLOG.md +++ b/server/WORKLOG.md @@ -16,7 +16,8 @@ * skills * offensive -> choose target ✔ * private fields for opponents - * cooldowns reduce each turn + * cooldowns reduce each turn ✔ + * statuses reduce each turn * teach cyps skills * can you attack yourself? * fetch existing battles @@ -112,4 +113,25 @@ gem td style attr combinations * 17: You don't have to change much to change everything * 18: Restrictions breed creativity * 19: Your audience is good at recognizing problems and bad at solving them -* 20: All the lessons connect \ No newline at end of file +* 20: All the lessons connect + + + +skill phase: +1.1 -> block (sp 10) -> on self +1.2 -> attack (sp 5) -> on team 2 + +2.1 -> hex (sp 3) -> on team 1 +2.2 -> attack (sp 5) -> on team 1 + +target phase: +team 2 targets 1.2 on 2.2 + +team 1 targets 2.1 on 1.1 +team 1 targets 2.2 on 1.1 + +resolve phase: +1.1 <- block +1.1 <- attack (no effect because of block) +2.2 <- attack (normal resolve) +1.1 <- hexed (no skills for the rest of this turn and next) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index df5b3d8a..b1672a87 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -21,7 +21,7 @@ pub struct CrypSkill { impl CrypSkill { pub fn new(skill: Skill) -> CrypSkill { - let turns = match skill { + let cd = match skill { Skill::Attack => None, Skill::Block => Some(1), Skill::Dodge => Some(1), @@ -33,7 +33,7 @@ impl CrypSkill { CrypSkill { skill, - cd: turns, + cd, } } @@ -49,6 +49,7 @@ impl CrypSkill { pub enum Status { Stunned, Silenced, + Blocking, } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -264,7 +265,7 @@ impl Cryp { return roll; } - pub fn stun(&mut self, roll: Roll) -> &mut Cryp { + pub fn stun(&mut self, _roll: Roll) -> &mut Cryp { self.statuses.push(CrypStatus { status: Status::Stunned, turns: 1 }); self } @@ -274,6 +275,11 @@ impl Cryp { self } + pub fn block(&mut self, _roll: Roll) -> &mut Cryp { + self.statuses.push(CrypStatus { status: Status::Blocking, turns: 1 }); + self + } + } 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 a6002d79..5d36284d 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -316,6 +316,8 @@ impl Game { let mut target = self.cryp_by_id(skill.target_cryp_id.unwrap()).unwrap().clone(); let resolution = skill.resolve(&mut source, &mut target); + // do something with resolution + self.update_cryp(&mut source); self.update_cryp(&mut target); } @@ -785,4 +787,27 @@ mod tests { assert!(game.team_by_id(y_team.id).cryps[0].skill_on_cd(Skill::Block).is_some()); println!("{:#?}", game); } + + #[test] + fn block_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_block_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::TestTouch).unwrap(); + let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::TestTouch).unwrap(); + + game.target_phase_start(); + game.add_target(x_team.id, x_cryp.id, y_attack_id).unwrap(); + game.add_target(y_team.id, y_cryp.id, x_block_id).unwrap(); + + game.damage_phase_start(); + + println!("{:#?}", game); + } + } diff --git a/server/src/skill.rs b/server/src/skill.rs index d01114c4..57e8f3a2 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -55,7 +55,7 @@ impl Cast { // the real deal Skill::Stun => target.stun(roll), Skill::Attack => target.attack(roll), - Skill::Block => target, + Skill::Block => target.block(roll), Skill::Heal => target, Skill::Dodge => target, From b5191e5306124b39a0c28506aa80b44636b8f739 Mon Sep 17 00:00:00 2001 From: ntr Date: Tue, 30 Oct 2018 16:44:10 +1100 Subject: [PATCH 7/8] blocking and speed --- server/src/cryp.rs | 75 +++++++++------------- server/src/game.rs | 149 ++++++++++++++++++++++++++++---------------- server/src/rpc.rs | 2 +- server/src/skill.rs | 90 ++++++++++++++++++++++++-- 4 files changed, 211 insertions(+), 105 deletions(-) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index b1672a87..c87e23b9 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -9,40 +9,23 @@ use failure::err_msg; use account::Account; use rpc::{CrypSpawnParams}; -use skill::{Skill, Roll}; - -type Cooldown = Option; +use skill::{Skill, Cooldown, Roll}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct CrypSkill { pub skill: Skill, + pub self_targeting: bool, pub cd: Cooldown, } impl CrypSkill { pub fn new(skill: Skill) -> CrypSkill { - let cd = match skill { - Skill::Attack => None, - Skill::Block => Some(1), - Skill::Dodge => Some(1), - Skill::Heal => Some(2), - Skill::Stun => Some(2), - Skill::TestTouch => None, - Skill::TestStun => None, - }; - CrypSkill { skill, - cd, + self_targeting: skill.self_targeting(), + cd: skill.cd(), } } - - fn can_cast(&self, cryp: &Cryp) -> bool { - if cryp.is_stunned() { - return false; - } - true - } } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -55,7 +38,7 @@ pub enum Status { #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct CrypStatus { status: Status, - turns: u8, + duration: u8, } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -186,7 +169,7 @@ impl Cryp { } pub fn available_skills(&self) -> Vec<&CrypSkill> { - self.skills.iter().filter(|s| s.can_cast(self)).collect() + self.skills.iter().filter(|s| s.skill.castable(self)).collect() } pub fn knows(&self, skill: Skill) -> bool { @@ -197,10 +180,6 @@ impl Cryp { self.skills.iter().find(|s| s.skill == skill && s.cd.is_some()) } - pub fn skill_can_cast(&self, skill: Skill) -> bool { - self.skills.iter().any(|s| s.skill == skill && s.can_cast(self)) - } - pub fn skill_set_cd(&mut self, skill: Skill) -> &mut Cryp { let i = self.skills.iter().position(|s| s.skill == skill).unwrap(); self.skills.remove(i); @@ -209,9 +188,10 @@ impl Cryp { self } - pub fn decrease_cooldowns(&mut self) -> &mut Cryp { + pub fn reduce_cooldowns(&mut self) -> &mut Cryp { for skill in self.skills.iter_mut() { - if let Some(cd) = skill.cd { + // if used cooldown + 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); @@ -227,6 +207,21 @@ impl Cryp { self } + pub fn reduce_statuses(&mut self) -> &mut Cryp { + self.statuses = self.statuses.clone().into_iter().filter_map(|mut s| { + s.duration = s.duration.saturating_sub(1); + + if s.duration == 0 { + return None; + } + + println!("reduced status {:?}", s); + return Some(s); + }).collect::>(); + + self + } + pub fn rez(&mut self) -> &mut Cryp { self.hp.set(self.stam.value); self @@ -236,24 +231,10 @@ impl Cryp { let mut rng = thread_rng(); let base: u64 = rng.gen(); - let stat = match skill { - Skill::Attack => self.str, - Skill::Block => self.str, - Skill::Stun => self.str, - Skill::Dodge => self.agi, - Skill::Heal => self.int, - - // test skills - Skill::TestTouch => self.int, - Skill::TestStun => self.str, - }; + let stat = skill.stat(self); let mut roll = Roll { base, result: base }; - // // apply skills - // roll = c.skills.iter().fold(roll, |roll, s| s.apply(roll)); - - // finally combine with stat println!("{:?}'s stats", self.name); println!("{:064b} <- finalised", roll.result); roll.result = roll.result & stat.value; @@ -266,7 +247,9 @@ impl Cryp { } pub fn stun(&mut self, _roll: Roll) -> &mut Cryp { - self.statuses.push(CrypStatus { status: Status::Stunned, turns: 1 }); + if !self.statuses.iter().any(|s| s.status == Status::Blocking) { + self.statuses.push(CrypStatus { status: Status::Stunned, duration: Skill::Stun.duration() }); + } self } @@ -276,7 +259,7 @@ impl Cryp { } pub fn block(&mut self, _roll: Roll) -> &mut Cryp { - self.statuses.push(CrypStatus { status: Status::Blocking, turns: 1 }); + self.statuses.push(CrypStatus { status: Status::Blocking, duration: Skill::Block.duration() }); self } diff --git a/server/src/game.rs b/server/src/game.rs index 5d36284d..e5257813 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -59,8 +59,8 @@ pub struct Game { pub teams: Vec, pub is_pve: bool, pub phase: Phase, - pub skills: Vec, - pub resolutions: Vec, + pub stack: Vec, + pub resolved: Vec, pub log: Vec, } @@ -73,8 +73,8 @@ impl Game { teams: vec![], is_pve: true, phase: Phase::Start, - skills: vec![], - resolutions: vec![], + stack: vec![], + resolved: vec![], log: vec![], }; } @@ -150,7 +150,7 @@ impl Game { self.phase = Phase::Skill; - self.skills.clear(); + self.stack.clear(); if self.is_pve { self.pve_add_skills(); @@ -168,7 +168,7 @@ impl Game { let player_team_id = self.teams.iter().find(|t| t.id != mob_team_id).unwrap().id; for mob in &mobs.cryps { // doesn't matter if the cryp can't cast - self.add_skill(mob_team_id, mob.id, player_team_id, Skill::Attack).ok(); + self.add_skill(mob_team_id, mob.id, Some(player_team_id), Skill::Attack).ok(); } } @@ -177,7 +177,7 @@ impl Game { // skills can target any team, but we have to check if the caller is the owner of the cryp // and that the cryp has the skill they are trying to add - fn add_skill(&mut self, team_id: Uuid, source_cryp_id: Uuid, target_team_id: Uuid, skill: Skill) -> Result { + fn add_skill(&mut self, team_id: Uuid, source_cryp_id: Uuid, target_team_id: Option, skill: Skill) -> Result { if self.phase != Phase::Skill { return Err(err_msg("game not in skill phase")); } @@ -197,19 +197,28 @@ impl Game { return Err(err_msg("abiltity on cooldown")); } - if !cryp.skill_can_cast(skill) { + if skill.self_targeting() && target_team_id.is_some() { + return Err(err_msg("skill is self targeting")); + } + + if !skill.self_targeting() && target_team_id.is_none() { + return Err(err_msg("skill requires a target")); + } + + // check here as well so uncastable spells don't go on the stack + if !skill.castable(&cryp) { return Err(err_msg("cryp cannot cast spell")); } } // replace cryp skill - if let Some(s) = self.skills.iter_mut().position(|s| s.source_cryp_id == source_cryp_id) { - self.skills.remove(s); + if let Some(s) = self.stack.iter_mut().position(|s| s.source_cryp_id == source_cryp_id) { + self.stack.remove(s); } let skill = Cast::new(source_cryp_id, team_id, target_team_id, skill); - self.skills.push(skill); + self.stack.push(skill); return Ok(skill.id); } @@ -217,7 +226,7 @@ impl Game { fn skill_phase_finished(&self) -> bool { self.teams.iter() // for every team - .all(|t| self.skills.iter() + .all(|t| self.stack.iter() // the number of skills they have cast .filter(|s| s.source_team_id == t.id) .collect::>() @@ -252,9 +261,11 @@ impl Game { let mob_team_id = Uuid::nil(); let mobs = self.team_by_id(mob_team_id).clone(); // TODO attack multiple players based on some criteria - for incoming_skill_id in self.skills.clone().iter().filter(|s| s.target_team_id == mob_team_id).map(|s| s.id) { - self.add_target(mob_team_id, mobs.cryps[0].id, incoming_skill_id).unwrap(); - } + for incoming_skill_id in self.stack.clone().iter() + .filter(|s| s.target_cryp_id.is_none() && s.target_team_id == mob_team_id) + .map(|s| s.id) { + self.add_target(mob_team_id, mobs.cryps[0].id, incoming_skill_id).unwrap(); + } } self @@ -274,16 +285,24 @@ impl Game { } // set the target - let cast = match self.skills.iter_mut().find(|s| s.id == skill_id) { + let cast = match self.stack.iter_mut().find(|s| s.id == skill_id) { Some(c) => c, None => return Err(err_msg("skill_id not found")), }; + if cast.skill.self_targeting() { + return Err(err_msg("skill is self targeting")); + } + + if cast.target_team_id != team_id { + return Err(err_msg("you cannot target that skill")); + } + Ok(cast.set_target(cryp_id)) } fn target_phase_finished(&self) -> bool { - self.skills.iter().all(|s| s.target_cryp_id.is_some()) + self.stack.iter().all(|s| s.target_cryp_id.is_some()) } // requires no input @@ -311,39 +330,51 @@ impl Game { panic!("game not in damage phase"); } - for skill in self.skills.clone().iter_mut() { + self.stack.sort_unstable_by_key(|s| s.skill.speed()); + self.stack.reverse(); + + // update the stack with the resolved skills + self.stack = self.stack.clone().iter_mut().map(|skill| { 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(); - let resolution = skill.resolve(&mut source, &mut target); - // do something with resolution + let resolution = skill.resolve(&mut source, &mut target); + self.resolved.push(*resolution); self.update_cryp(&mut source); self.update_cryp(&mut target); - } + + return *resolution; + }).collect::>(); // now damage has all been assigned - // handle cooldowns - self.progress_cooldowns(); + // handle cooldowns and statuses + self.progress_durations(); self } - fn progress_cooldowns(&mut self) -> &mut Game { - for skill in self.skills.clone().iter() { - // copy the creep cause we will replace it - let mut cryp = self.cryp_by_id(skill.source_cryp_id).unwrap().clone(); + fn progress_durations(&mut self) -> &mut Game { + // 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::>() { - if skill.used_cooldown() { - cryp.skill_set_cd(skill.skill); - self.update_cryp(&mut cryp); - continue; + println!("progressing durations for {:?}", cryp.name); + + // only reduce cooldowns if no cd was used + // have to borrow self for the skill check + { + let skill = self.stack.iter_mut().find(|s| s.source_cryp_id == cryp.id).unwrap(); + if skill.used_cooldown() { + cryp.skill_set_cd(skill.skill); + } else { + cryp.reduce_cooldowns(); + } } - cryp.decrease_cooldowns(); - - println!("{:?}", cryp); - + // always reduce durations + cryp.reduce_statuses(); self.update_cryp(&mut cryp); } @@ -358,7 +389,7 @@ impl Game { fn finish(&mut self) -> &mut Game { self.phase = Phase::Finish; - self.skills.clear(); + self.stack.clear(); self } @@ -520,7 +551,7 @@ fn generate_mob(plr: &Cryp) -> Cryp { // rng panics on min == max let mob_lvl: u8 = match plr.lvl { 1 => 1, - _ => rng.gen_range(1, plr.lvl) + _ => rng.gen_range(plr.lvl.saturating_sub(2), plr.lvl) }; return Cryp::new() @@ -651,6 +682,7 @@ mod tests { .level(8) .learn(Skill::TestStun) .learn(Skill::TestTouch) + .learn(Skill::TestBlock) .learn(Skill::Block) .create(); @@ -659,6 +691,7 @@ mod tests { .level(8) .learn(Skill::TestStun) .learn(Skill::TestTouch) + .learn(Skill::TestBlock) .learn(Skill::Block) .create(); @@ -700,8 +733,8 @@ mod tests { let x_cryp = x_team.cryps[0].clone(); let y_cryp = y_team.cryps[0].clone(); - let x_attack_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::Attack).unwrap(); - let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::Attack).unwrap(); + let x_attack_id = game.add_skill(x_team.id, x_cryp.id, Some(y_team.id), Skill::Attack).unwrap(); + let y_attack_id = game.add_skill(y_team.id, y_cryp.id, Some(x_team.id), Skill::Attack).unwrap(); assert!(game.skill_phase_finished()); @@ -729,8 +762,8 @@ mod tests { let x_cryp = x_team.cryps[0].clone(); let y_cryp = y_team.cryps[0].clone(); - let x_stun_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::TestStun).unwrap(); - let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::TestTouch).unwrap(); + let x_stun_id = game.add_skill(x_team.id, x_cryp.id, Some(y_team.id), Skill::TestStun).unwrap(); + let y_attack_id = game.add_skill(y_team.id, y_cryp.id, Some(x_team.id), Skill::TestTouch).unwrap(); assert!(game.skill_phase_finished()); game.target_phase_start(); @@ -759,8 +792,8 @@ mod tests { let x_cryp = x_team.cryps[0].clone(); let y_cryp = y_team.cryps[0].clone(); - let x_stun_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::TestTouch).unwrap(); - let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::TestTouch).unwrap(); + let x_stun_id = game.add_skill(x_team.id, x_cryp.id, Some(y_team.id), Skill::TestTouch).unwrap(); + let y_attack_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_attack_id).unwrap(); @@ -776,16 +809,16 @@ mod tests { // second round // now we block and it should go back on cd - let x_block_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::Block).unwrap(); - let y_block_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::Block).unwrap(); + let _x_block_id = game.add_skill(x_team.id, x_cryp.id, None, Skill::Block).unwrap(); + let _y_block_id = game.add_skill(y_team.id, y_cryp.id, None, Skill::Block).unwrap(); game.target_phase_start(); - game.add_target(x_team.id, x_cryp.id, y_block_id).unwrap(); - game.add_target(y_team.id, y_cryp.id, x_block_id).unwrap(); - game.damage_phase_start(); + // game.add_target(x_team.id, x_cryp.id, y_block_id).unwrap(); + // game.add_target(y_team.id, y_cryp.id, x_block_id).unwrap(); + // game.damage_phase_start(); assert!(game.team_by_id(y_team.id).cryps[0].skill_on_cd(Skill::Block).is_some()); - println!("{:#?}", game); + assert!(game.team_by_id(x_team.id).cryps[0].skill_on_cd(Skill::Block).is_some()); } #[test] @@ -798,16 +831,26 @@ mod tests { let x_cryp = x_team.cryps[0].clone(); let y_cryp = y_team.cryps[0].clone(); - let x_block_id = game.add_skill(x_team.id, x_cryp.id, y_team.id, Skill::TestTouch).unwrap(); - let y_attack_id = game.add_skill(y_team.id, y_cryp.id, x_team.id, Skill::TestTouch).unwrap(); + // ensure that you can't pass a target for a block + assert!(game.add_skill(x_team.id, x_cryp.id, Some(y_cryp.id), Skill::TestBlock).is_err()); + + let x_block_id = game.add_skill(x_team.id, x_cryp.id, None, Skill::TestBlock).unwrap(); + let y_attack_id = game.add_skill(y_team.id, y_cryp.id, Some(x_team.id), Skill::TestStun).unwrap(); game.target_phase_start(); + + // ensure you can't target a self targeting skill + assert!(game.add_target(y_team.id, y_cryp.id, x_block_id).is_err()); + + // ensure you can't target another team's skills + assert!(game.add_target(x_team.id, y_cryp.id, y_attack_id).is_err()); + game.add_target(x_team.id, x_cryp.id, y_attack_id).unwrap(); - game.add_target(y_team.id, y_cryp.id, x_block_id).unwrap(); game.damage_phase_start(); - println!("{:#?}", game); + // should not be stunned because of block + assert!(game.team_by_id(x_team.id).cryps[0].is_stunned() == false); } } diff --git a/server/src/rpc.rs b/server/src/rpc.rs index 736560b2..2bf60067 100755 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -368,7 +368,7 @@ struct GameSkillMsg { pub struct GameSkillParams { pub game_id: Uuid, pub cryp_id: Uuid, - pub target_team_id: Uuid, + pub target_team_id: Option, pub skill: Skill, } diff --git a/server/src/skill.rs b/server/src/skill.rs index 57e8f3a2..63e710f9 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -1,7 +1,7 @@ -use rand::prelude::*; +// use rand::prelude::*; use uuid::Uuid; -use cryp::{Cryp, CrypSkill}; +use cryp::{Cryp, CrypSkill, CrypStat}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct Roll { @@ -9,6 +9,8 @@ pub struct Roll { pub result: u64, } +pub type Cooldown = Option; + #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Skill { Attack, @@ -16,9 +18,80 @@ pub enum Skill { Heal, Stun, Dodge, - // used by tests to do no dmg + // used by tests, no cd, no dmg TestTouch, TestStun, + TestBlock, +} + +impl Skill { + pub fn cd(&self) -> Cooldown { + match self { + Skill::Attack => None, + Skill::Block => Some(1), + Skill::Dodge => Some(1), + Skill::Heal => Some(2), + Skill::Stun => Some(2), + Skill::TestTouch => None, + Skill::TestStun => None, + Skill::TestBlock => None, + } + } + + pub fn speed(&self) -> u8 { + match self { + Skill::Attack => 10, + Skill::Block => 5, + Skill::Dodge => 5, + Skill::Heal => 2, + Skill::Stun => 2, + Skill::TestTouch => 10, + Skill::TestStun => 2, + Skill::TestBlock => 5, + } + } + + pub fn stat(&self, cryp: &Cryp) -> CrypStat { + match self { + Skill::Attack => cryp.str, + Skill::Block => cryp.str, + Skill::Stun => cryp.str, + Skill::Dodge => cryp.agi, + Skill::Heal => cryp.int, + + // test skills + Skill::TestTouch => cryp.int, + Skill::TestStun => cryp.str, + Skill::TestBlock => cryp.str, + } + } + + pub fn duration(&self) -> u8 { + match self { + Skill::Dodge => 1, + Skill::Stun => 2, + Skill::Block => 1, + + Skill::TestBlock => 1, + Skill::TestStun => 2, + _ => panic!("{:?} does not have a duration", self), + } + } + + pub fn self_targeting(&self) -> bool { + match self { + Skill::Block => true, + Skill::TestBlock => true, + _ => false, + } + } + + pub fn castable(&self, cryp: &Cryp) -> bool { + if cryp.is_stunned() { + return false; + } + true + } } @@ -34,12 +107,18 @@ pub struct Cast { } impl Cast { - pub fn new(source_cryp_id: Uuid, source_team_id: Uuid, target_team_id: Uuid, skill: Skill) -> Cast { + pub fn new(source_cryp_id: Uuid, source_team_id: Uuid, target_team_id: Option, skill: Skill) -> Cast { + + let (target_cryp_id, target_team_id) = match skill.self_targeting() { + true => (Some(source_cryp_id), source_team_id), + false => (None, target_team_id.unwrap()) + }; + return Cast { id: Uuid::new_v4(), source_cryp_id, source_team_id, - target_cryp_id: None, + target_cryp_id, target_team_id, skill, roll: None, @@ -61,6 +140,7 @@ impl Cast { // Test Skills Skill::TestStun => target.stun(roll), + Skill::TestBlock => target.block(roll), Skill::TestTouch => target, }; From 09b6f455d33a062775a0bede2cb03e5a263a6d80 Mon Sep 17 00:00:00 2001 From: ntr Date: Tue, 30 Oct 2018 20:20:08 +1100 Subject: [PATCH 8/8] self targeting client --- client/.eslintrc.js | 10 +++++----- client/src/components/game.container.js | 11 ++++++++--- client/src/components/game.jsx | 7 +++---- server/src/cryp.rs | 1 + server/src/game.rs | 8 ++++++++ 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/client/.eslintrc.js b/client/.eslintrc.js index 2970baf5..507d7a3a 100755 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -616,11 +616,11 @@ module.exports = { avoidQuotes: true, }], - // suggest using arrow functions as callbacks - 'prefer-arrow-callback': ['error', { - allowNamedFunctions: false, - allowUnboundThis: true, - }], + // // suggest using arrow functions as callbacks + // 'prefer-arrow-callback': ['error', { + // allowNamedFunctions: false, + // allowUnboundThis: true, + // }], // suggest using of const declaration for variables that are never modified after declared 'prefer-const': ['error', { diff --git a/client/src/components/game.container.js b/client/src/components/game.container.js index 10009920..8adac3fd 100644 --- a/client/src/components/game.container.js +++ b/client/src/components/game.container.js @@ -10,11 +10,16 @@ const addState = connect( function selectSkillTarget(targetTeamId) { if (activeSkill) { - return ws.sendGameSkill(game.id, activeSkill.crypId, targetTeamId, activeSkill.skill); + return ws.sendGameSkill(game.id, activeSkill.crypId, targetTeamId, activeSkill.skill.skill); } return false; } + // intercept self casting skills + if (activeSkill && activeSkill.skill.self_targeting) { + ws.sendGameSkill(game.id, activeSkill.crypId, null, activeSkill.skill.skill); + } + function selectIncomingTarget(crypId) { if (activeIncoming) { return ws.sendGameTarget(game.id, crypId, activeIncoming); @@ -27,11 +32,11 @@ const addState = connect( function receiveDispatch(dispatch) { function setActiveSkill(crypId, skill) { - dispatch(actions.setActiveSkill(crypId, skill)) + dispatch(actions.setActiveSkill(crypId, skill)); } function setActiveIncoming(skillId) { - dispatch(actions.setActiveIncoming(skillId)) + dispatch(actions.setActiveIncoming(skillId)); } diff --git a/client/src/components/game.jsx b/client/src/components/game.jsx index fc55cb35..71eeea98 100755 --- a/client/src/components/game.jsx +++ b/client/src/components/game.jsx @@ -21,7 +21,7 @@ function GamePanel(props) { const playerTeam = game.teams.find(t => t.id === account.id); - const incoming = game.skills.filter(s => s.target_team_id === playerTeam.id).map((inc) => { + const incoming = game.stack.filter(s => s.target_team_id === playerTeam.id).map((inc) => { key.unbind('1'); key('1', () => setActiveIncoming(inc.id)); return ( @@ -41,14 +41,14 @@ function GamePanel(props) { const skills = cryp.skills.map((skill, i) => { const hotkey = SKILL_HOT_KEYS[i]; key.unbind(hotkey); - key(hotkey, () => setActiveSkill(cryp.id, skill.skill)); + key(hotkey, () => setActiveSkill(cryp.id, skill)); return ( @@ -59,7 +59,6 @@ function GamePanel(props) {
{status} for {status.turns}T
)); - if (activeIncoming) console.log('should be a pointer'); return (
Result<&mut Cast, Error> { + if self.phase != Phase::Target { + return Err(err_msg("game not in target phase")); + } + { // whose team is this? let team = self.team_by_id(team_id); @@ -414,6 +418,10 @@ pub fn game_skill(params: GameSkillParams, tx: &mut Transaction, account: &Accou let game_bytes: Vec = returned.get("data"); let mut game = from_slice::(&game_bytes)?; + if game.phase != Phase::Skill { + return Err(err_msg("game not in skill phase")) + } + game.add_skill(account.id, params.cryp_id, params.target_team_id, params.skill)?; if game.skill_phase_finished() {