diff --git a/client/src/scenes/menu.navigation.js b/client/src/scenes/menu.navigation.js index 0fef15cd..234cc270 100644 --- a/client/src/scenes/menu.navigation.js +++ b/client/src/scenes/menu.navigation.js @@ -67,7 +67,6 @@ class MenuNavigation extends Phaser.Scene { pveText.destroy(); pvp.destroy(); pvpText.destroy(); - ws.sendGameJoinableList(); }); const cancel = this.add diff --git a/client/src/socket.js b/client/src/socket.js index 13b21d01..249a183f 100644 --- a/client/src/socket.js +++ b/client/src/socket.js @@ -77,10 +77,6 @@ function createSocket(events) { send({ method: 'game_join', params: { game_id: gameId, cryp_ids: crypIds } }); } - function sendGameJoinableList() { - send({ method: 'game_joinable_list', params: { } }); - } - function sendSpecForget(id, spec) { send({ method: 'cryp_unspec', params: { id, spec } }); } @@ -105,18 +101,10 @@ function createSocket(events) { send({ method: 'player_vbox_combine', params: { instance_id: instanceId, indices } }); } - function sendVboxDrop(instanceId, index) { send({ method: 'player_vbox_drop', params: { instance_id: instanceId, index } }); } - - function sendPressR() { - send({ method: 'press_r', params: { } }); - } - - window.pressR = sendPressR; - function sendGameSkill(gameId, crypId, targetCrypId, skill) { send({ method: 'game_skill', @@ -154,7 +142,6 @@ function createSocket(events) { account = login; events.setAccount(login); sendAccountCryps(); - // sendGameJoinableList(); } function accountCryps(response) { @@ -167,11 +154,6 @@ function createSocket(events) { events.setGame(game); } - function gameJoinableList(response) { - const [structName, gameList] = response; - events.setGameList(gameList); - } - function crypSpawn(response) { const [structName, cryp] = response; } @@ -202,7 +184,6 @@ function createSocket(events) { cryp_learn: () => true, game_pve: gamePve, game_state: gameState, - game_joinable_list: gameJoinableList, account_login: accountLogin, account_create: accountLogin, account_cryps: accountCryps, @@ -290,7 +271,6 @@ function createSocket(events) { sendGamePve, sendGamePvp, sendGameJoin, - sendGameJoinableList, sendGameSkill, sendGameTarget, sendCrypSpawn, diff --git a/server/src/game.rs b/server/src/game.rs index 8f4ca467..1f6e115f 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -185,10 +185,6 @@ impl Game { self } - fn joinable(&self) -> bool { - self.phase == Phase::Start - } - fn can_start(&self) -> bool { return self.teams.len() == self.team_num && self.teams.iter().all(|t| t.cryps.len() == self.team_size) @@ -607,13 +603,13 @@ pub fn game_write(game: &Game, tx: &mut Transaction) -> Result<(), Error> { let game_bytes = to_vec(&game)?; let query = " - INSERT INTO games (id, joinable, data) - VALUES ($1, $2, $3) + INSERT INTO games (id, data) + VALUES ($1, $2) RETURNING id; "; let result = tx - .query(query, &[&game.id, &game.joinable(), &game_bytes])?; + .query(query, &[&game.id, &game_bytes])?; result.iter().next().ok_or(format_err!("no game written"))?; @@ -653,13 +649,13 @@ pub fn game_update(game: &Game, tx: &mut Transaction) -> Result<(), Error> { let query = " UPDATE games - SET data = $1, joinable = $2 - WHERE id = $3 + SET data = $1 + WHERE id = $2 RETURNING id, data; "; let result = tx - .query(query, &[&game_bytes, &game.joinable(), &game.id])?; + .query(query, &[&game_bytes, &game.id])?; result.iter().next().ok_or(format_err!("game {:?} could not be written", game))?; diff --git a/server/src/game_target_phase.rs b/server/src/game_target_phase.rs deleted file mode 100644 index 83412b1d..00000000 --- a/server/src/game_target_phase.rs +++ /dev/null @@ -1,1281 +0,0 @@ -use uuid::Uuid; -use rand::prelude::*; -use rand::distributions::Alphanumeric; - -use std::iter; - -// Db Commons -use serde_cbor::{from_slice, to_vec}; -use postgres::transaction::Transaction; -use failure::Error; -use failure::err_msg; - -use account::Account; -use rpc::{GameStateParams, GameSkillParams, GamePveParams, GamePvpParams, GameTargetParams, GameJoinParams}; -use cryp::{Cryp, cryp_get}; -use skill::{Skill, Cast, ResolutionResult}; -use item::{item_drop}; -use zone::{node_finish}; - -pub type Log = Vec; - -#[derive(Debug,Clone,Serialize,Deserialize)] -pub enum GameMode { - Boss, - Normal, -} - -#[derive(Debug,Clone,Serialize,Deserialize)] -pub struct Team { - pub id: Uuid, - cryps: Vec, -} - -impl Team { - pub fn new(account: Uuid) -> Team { - return Team { - id: account, - cryps: vec![], - }; - } - - fn skills_required(&self) -> usize { - let required = self.cryps.iter() - .filter(|c| !c.is_ko()) - .filter(|c| c.available_skills().len() > 0) - .collect::>().len(); - // println!("{:} requires {:} skills this turn", self.id, required); - return required; - } - - pub fn set_cryps(&mut self, cryps: Vec) -> &mut Team { - self.cryps = cryps; - self - } - - pub fn cryp_by_id(&mut self, id: Uuid) -> Option<&mut Cryp> { - self.cryps.iter_mut().find(|c| c.id == id) - } -} - -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub enum Phase { - Start, - Skill, - Target, - Resolve, - Finish, -} - -#[derive(Debug,Clone,Serialize,Deserialize)] -pub struct Game { - pub id: Uuid, - pub team_size: usize, - pub team_num: usize, - pub teams: Vec, - pub is_pve: bool, - pub phase: Phase, - pub stack: Vec, - pub resolved: Vec, - pub log: Vec, - pub zone: Option<(Uuid, u32)>, -} - -impl Game { - pub fn new() -> Game { - return Game { - id: Uuid::new_v4(), - team_size: 0, - team_num: 0, - teams: vec![], - is_pve: true, - phase: Phase::Start, - stack: vec![], - resolved: vec![], - log: vec![], - zone: None, - }; - } - - pub fn set_team_num(&mut self, size: usize) -> &mut Game { - self.team_num = size; - self - } - - pub fn set_team_size(&mut self, size: usize) -> &mut Game { - self.team_size = size; - self - } - - pub fn set_pve(&mut self, pve: bool) -> &mut Game { - self.is_pve = pve; - self - } - - pub fn set_zone(&mut self, id: Uuid, node: u32) -> &mut Game { - self.zone = Some((id, node)); - self - } - - fn already_joined(&self, team_id: Uuid) -> bool { - self.teams.iter().any(|t| t.id == team_id) - } - - pub fn team_add(&mut self, team: Team) -> Result<&mut Game, Error> { - if self.teams.len() == self.team_num { - return Err(err_msg("maximum number of teams")); - } - - if self.teams.iter().any(|t| t.id == team.id) { - return Err(err_msg("team already in game")); - } - - let team_description = team.cryps.iter().map(|c| c.name.clone()).collect::>().join(", "); - self.log.push(format!("{:} has joined the game.", team_description)); - - self.teams.push(team); - - Ok(self) - } - - // handle missing team properly - fn team_by_id(&mut self, id: Uuid) -> &mut Team { - match self.teams.iter_mut().find(|t| t.id == id) { - Some(t) => t, - None => panic!("id not in game {:}", id), - } - } - - 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 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) => { - 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 joinable(&self) -> bool { - self.phase == Phase::Start - } - - fn can_start(&self) -> bool { - return self.teams.len() == self.team_num - && self.teams.iter().all(|t| t.cryps.len() == self.team_size) - } - - fn start(&mut self) -> &mut Game { - self.log.push("Game starting...".to_string()); - - self.skill_phase_start(); - self - } - - fn skill_phase_start(&mut self) -> &mut Game { - self.log.push("".to_string()); - - if ![Phase::Start, Phase::Resolve].contains(&self.phase) { - panic!("game not in Resolve or start phase"); - } - - self.phase = Phase::Skill; - - self.stack.clear(); - - if self.is_pve { - self.pve_add_skills(); - } - - self - } - - fn pve_add_skills(&mut self) -> &mut 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 - 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, Some(player_team_id), Skill::Attack).ok(); - } - } - - self - } - - 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")); - } - - { - let cryp = match self.cryp_by_id(source_cryp_id) { - Some(c) => c, - None => return Err(err_msg("cryp not in team")), - }; - - if cryp.is_ko() { - return Err(err_msg("cryp is ko")); - } - - // check the cryp has the skill - if !cryp.knows(skill) { - return Err(err_msg("cryp does not have that skill")); - } - - if cryp.skill_on_cd(skill).is_some() { - return Err(err_msg("abiltity on cooldown")); - } - - // 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 - let check = cryp.disabled(skill); - if check.disabled { - return Err(err_msg("cryp cannot cast that skill")); - } - } - - - // replace cryp skill - 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); - let skill_id = skill.id; - self.stack.push(skill); - - return Ok(skill_id); - } - - fn skill_phase_finished(&self) -> bool { - self.teams.iter() - // for every team - .all(|t| self.stack.iter() - // the number of skills they have cast - .filter(|s| s.source_team_id == t.id).collect::>() - // should equal the number required this turn - .len() == t.skills_required() - ) - } - - fn target_phase_start(&mut self) -> &mut Game { - assert!(self.skill_phase_finished()); - self.log.push("".to_string()); - - if self.phase != Phase::Skill { - panic!("game not in skill phase"); - } - - self.phase = Phase::Target; - - if self.is_pve { - self.pve_add_targets(); - } - - // all cryps are stunned or otherwise inactive - if self.target_phase_finished() { - self.resolve_phase_start(); - } - - self - } - - fn pve_add_targets(&mut self) -> &mut 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 (i, incoming_skill_id) in self.stack.clone().iter() - .filter(|s| s.target_cryp_id.is_none() && s.target_team_id == mob_team_id) - .enumerate() - .map(|(i, s)| (i, s.id)) { - let targets = mobs.cryps - .iter() - .filter(|c| self.cryp_targetable(mob_team_id, c.id).is_ok()) - .collect::>(); - - if targets.len() == 0 { - panic!("could not find a targetable pve cryp"); - } - - let target_id = targets[i % targets.len()].id; - self.add_target(mob_team_id, target_id, incoming_skill_id).unwrap(); - } - } - - self - } - - // each cryp can be the target of - // incomingSkills / activeCryps rounded up - // maybe a problem with friendly / self targeting skills - fn cryp_targetable(&mut self, team_id: Uuid, cryp_id: Uuid) -> Result<(), Error> { - // whose team is this? - let team = self.teams.iter() - .find(|t| t.id == team_id) - .ok_or(err_msg("team not found"))?; - - // is the target in the team? - let cryp = team.cryps.iter() - .find(|c| c.id == cryp_id) - .ok_or(err_msg("cryp not in team"))?; - - if cryp.is_ko() { - return Err(err_msg("you cannot target ko cryps")); - } - - // let incoming = self.stack.iter() - // .filter(|i| i.target_team_id == team.id) - // .count(); - - // let incoming = incoming as u32 as f64; - - // let active_cryps = team.cryps.iter() - // .filter(|c| !c.is_ko()) - // .count(); - - // let active_cryps = active_cryps as u32 as f64; - // let max_targets = (incoming / active_cryps).ceil(); - - // // println!("targets {:} / {:} = {:}", incoming, active_cryps, max_targets); - - // let targeted = self.stack.iter() - // .filter(|s| s.target_cryp_id.is_some()) - // .filter(|s| s.target_cryp_id.unwrap() == cryp_id) - // .count(); - - // if targeted >= max_targets as usize { - // return Err(format_err!("cryp target of maximum number of skills ({:})", max_targets)); - // } - - return Ok(()); - } - - // targets can only be added by the owner of the team - fn add_target(&mut self, team_id: Uuid, cryp_id: Uuid, skill_id: Uuid) -> Result<&mut Cast, Error> { - if self.phase != Phase::Target { - return Err(err_msg("game not in target phase")); - } - - self.cryp_targetable(team_id, cryp_id)?; - - // set the target - 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.stack.iter().all(|s| s.target_cryp_id.is_some()) - } - - // requires no input - // just do it - fn resolve_phase_start(&mut self) -> &mut Game { - if self.phase != Phase::Target { - panic!("game not in target phase"); - } - assert!(self.target_phase_finished()); - - self.phase = Phase::Resolve; - self.log.push("".to_string()); - - self.resolve_skills() - } - - fn log_resolution(&mut self, source: &mut Cryp, target: &mut Cryp, cast: &Cast) -> &mut Game { - match cast.resolution.disable.disabled { - true => { - self.log.push(format!("{:} {:?} {:} disabled {:?}", source.name, cast.skill, target.name, cast.resolution.disable.effects)); - return self; - }, - false => (), - }; - - for result in cast.resolution.results.iter() { - match result { - ResolutionResult::Damage { amount, category: _, immunity } => { - match immunity.immune { - true => self.log.push(format!("{:} {:?} {:} immune {:?}", source.name, cast.skill, target.name, immunity.effects)), - false => self.log.push(format!("{:} {:?} {:} {:}", source.name, cast.skill, target.name, amount)), - } - }, - ResolutionResult::Healing { amount, overhealing, category: _, immunity } => { - match immunity.immune { - true => self.log.push(format!("{:} {:?} {:} immune {:?}", source.name, cast.skill, target.name, immunity.effects)), - false => self.log.push(format!("{:} {:?} {:} {:} ({:}OH)", source.name, cast.skill, target.name, amount, overhealing)), - } - }, - ResolutionResult::Effect { effect, duration, immunity } => { - match immunity.immune { - true => self.log.push(format!("{:} {:?} {:} immune {:?}", source.name, cast.skill, target.name, immunity.effects)), - false => self.log.push(format!("{:} {:?} {:} {:?} {:}T", source.name, cast.skill, target.name, effect, duration)), - } - }, - ResolutionResult::Removal { effect, immunity } => { - match immunity.immune { - true => self.log.push(format!("{:} {:?} {:} immune {:?}", source.name, cast.skill, target.name, immunity.effects)), - false => self.log.push(format!("{:?} removed {:} {:?}", source.name, target.name, effect)), - } - }, - } - } - - self - } - - fn resolve_skills(&mut self) -> &mut Game { - if self.phase != Phase::Resolve { - panic!("game not in Resolve phase"); - } - - // find their statuses with ticks - let mut ticks = self.all_cryps() - .iter() - .flat_map( - |c| c.effects - .iter() - .cloned() - .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(); - - // update the stack with the resolved skills - self.stack = self.stack.clone().iter_mut().map(|skill| { - // println!("{:} resolving ", 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(); - - skill.set_resolution(&mut source, &mut target); - - self.log_resolution(&mut source, &mut target, skill); - - self.resolved.push(skill.clone()); - - if target.is_ko() && !target.ko_logged { - self.log.push(format!("{:} KO", target.name)); - target.effects.clear(); - target.ko_logged = true; - } - - if source.is_ko() && !source.ko_logged { - self.log.push(format!("{:} KO", source.name)); - source.effects.clear(); - source.ko_logged = true; - } - - self.update_cryp(&mut source); - self.update_cryp(&mut target); - - return skill.clone(); - }).collect::>(); - - // now Resolve has all been assigned - // handle cooldowns and statuses - self.progress_durations(); - - if self.finished() { - return self.finish() - } - - self.skill_phase_start() - } - - fn progress_durations(&mut self) -> &mut Game { - for mut cryp in self.all_cryps() { - // println!("progressing durations for {:}", cryp.name); - - if cryp.is_ko() { - continue; - } - - // only reduce cooldowns if no cd was used - // have to borrow self for the skill check - { - if let Some(skill) = self.stack.iter_mut().find(|s| s.source_cryp_id == cryp.id) { - if skill.used_cooldown() { - cryp.skill_set_cd(skill.skill); - } else { - cryp.reduce_cooldowns(); - } - } else { - cryp.reduce_cooldowns(); - } - } - - // always reduce durations - cryp.reduce_effect_durations(&mut self.log); - self.update_cryp(&mut cryp); - } - - self - } - - fn finished(&self) -> bool { - self.teams.iter().any(|t| t.cryps.iter().all(|c| c.is_ko())) - } - - pub fn winner(&self) -> Option<&Team> { - self.teams.iter().find(|t| t.cryps.iter().any(|c| !c.is_ko())) - } - - fn finish(&mut self) -> &mut Game { - self.phase = Phase::Finish; - self.log.push(format!("Game finished.")); - self.stack.clear(); - - { - let winner = self.teams.iter().find(|t| t.cryps.iter().any(|c| !c.is_ko())); - match winner { - Some(w) => self.log.push(format!("Winner: {:}", w.id)), - None => self.log.push(format!("Game was drawn.")), - }; - } - - self - } -} - -pub fn game_skill(params: GameSkillParams, tx: &mut Transaction, account: &Account) -> Result { - let query = " - SELECT * - FROM games - WHERE id = $1 - "; - - let result = tx - .query(query, &[¶ms.game_id])?; - - let returned = match result.iter().next() { - Some(row) => row, - None => return Err(err_msg("game not found")), - }; - - // tells from_slice to cast into a cryp - 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() { - game.target_phase_start(); - } - - game_update(&game, tx)?; - - Ok(game) -} - -pub fn game_target(params: GameTargetParams, tx: &mut Transaction, account: &Account) -> Result { - let query = " - SELECT * - FROM games - WHERE id = $1 - "; - - let result = tx - .query(query, &[¶ms.game_id])?; - - let returned = match result.iter().next() { - Some(row) => row, - None => return Err(err_msg("game not found")), - }; - - // tells from_slice to cast into a cryp - let game_bytes: Vec = returned.get("data"); - let mut game = from_slice::(&game_bytes)?; - - game.add_target(account.id, params.cryp_id, params.skill_id)?; - - if game.target_phase_finished() { - game.resolve_phase_start(); - } - - game_update(&game, tx)?; - - Ok(game) -} - -pub fn game_write(game: &Game, tx: &mut Transaction) -> Result<(), Error> { - let game_bytes = to_vec(&game)?; - - let query = " - INSERT INTO games (id, joinable, data) - VALUES ($1, $2, $3) - RETURNING id; - "; - - let result = tx - .query(query, &[&game.id, &game.joinable(), &game_bytes])?; - - result.iter().next().ok_or(format_err!("no game written"))?; - - // println!("{:} wrote game", game.id); - - return Ok(()); -} - -pub fn game_state(params: GameStateParams, tx: &mut Transaction, _account: &Account) -> Result { - return game_get(tx, params.id) -} - -pub fn game_get(tx: &mut Transaction, id: Uuid) -> Result { - let query = " - SELECT * - FROM games - WHERE id = $1 - "; - - let result = tx - .query(query, &[&id])?; - - let returned = match result.iter().next() { - Some(row) => row, - None => return Err(err_msg("game not found")), - }; - - // tells from_slice to cast into a cryp - let game_bytes: Vec = returned.get("data"); - let game = from_slice::(&game_bytes)?; - - return Ok(game); -} - -pub fn players_write(account: &Account, game_id: Uuid, tx: &mut Transaction) -> Result<(), Error> { - // pve - let id = Uuid::new_v4(); - - let query = " - INSERT INTO players (id, game, account) - VALUES ($1, $2, $3) - RETURNING id, account; - "; - - let result = tx - .query(query, &[&id, &game_id, &account.id])?; - - let _returned = result.iter().next().expect("no row written"); - - println!("wrote player {:} joined game: {:}", account.name, game_id); - - return Ok(()); -} - -pub fn game_update(game: &Game, tx: &mut Transaction) -> Result<(), Error> { - let game_bytes = to_vec(&game)?; - - let query = " - UPDATE games - SET data = $1, joinable = $2 - WHERE id = $3 - RETURNING id, data; - "; - - let result = tx - .query(query, &[&game_bytes, &game.joinable(), &game.id])?; - - result.iter().next().ok_or(format_err!("game {:?} could not be written", game))?; - - if game.finished() { - if let Some(t) = game.winner() { - if !t.id.is_nil() { - item_drop(tx, t.id)?; - } - } - - // check for zone update - if let Some((z, i)) = game.zone { - node_finish(game, z, i, tx)?; - } - - } - - return Ok(()); -} - -fn generate_mob(lvl: u8) -> Cryp { - let mut rng = thread_rng(); - - let name: String = iter::repeat(()) - .map(|()| rng.sample(Alphanumeric)) - .take(8) - .collect(); - - // rng panics on min == max - // let mob_lvl: u8 = match lvl { - // 1 => 1, - // _ => rng.gen_range(lvl.saturating_sub(2), lvl) - // }; - - return Cryp::new() - .named(&name) - .level(lvl) - .create(); - -} - -fn generate_mob_team(mode: GameMode, cryps: &Vec) -> Team { - let mut mob_team = Team::new(Uuid::nil()); - - // Default settings - let mut team_size = 1; - - // Modify the NPC cryps for game mode settings - let mob_lvl = match mode { - GameMode::Normal => { - team_size = cryps.len(); - cryps.iter().max_by_key(|c| c.lvl).unwrap().lvl - }, - GameMode::Boss => cryps.iter().max_by_key(|c| c.lvl).unwrap().lvl + 2, - }; - - // Generate and return the NPC team based on settings - let mobs = iter::repeat_with(|| generate_mob(mob_lvl).set_account(Uuid::nil())) - .take(team_size) - .collect::>(); - mob_team.set_cryps(mobs); - - return mob_team; - -} - -pub fn game_pve_new(cryp_ids: Vec, mode: GameMode, tx: &mut Transaction, account: &Account) -> Result { - let cryps = cryp_ids - .iter() - .map(|id| cryp_get(tx, *id, account.id)) - .collect::, Error>>()?; - - if cryps.len() > 3 { - return Err(err_msg("team size too large (3 max)")); - } - - // create the game - let mut game = Game::new(); - // let game_id = game.id; - - game - .set_pve(true) - .set_team_num(2) - .set_team_size(cryps.len()); - - // create the mob team - let mob_team = generate_mob_team(mode, &cryps); - - // add the players - let mut plr_team = Team::new(account.id); - plr_team - .set_cryps(cryps); - - - game - .team_add(plr_team)? - .team_add(mob_team)?; - - game.start(); - - return Ok(game); -} - -pub fn game_pve(params: GamePveParams, tx: &mut Transaction, account: &Account) -> Result { - let game = game_pve_new(params.cryp_ids, params.mode, tx, account)?; - - // persist - game_write(&game, tx)?; - - Ok(game) -} - -pub fn game_pvp(params: GamePvpParams, tx: &mut Transaction, account: &Account) -> Result { - let cryps = params.cryp_ids - .iter() - .map(|id| cryp_get(tx, *id, account.id)) - .collect::, Error>>()?; - - // create the game - let mut game = Game::new(); - let game_id = game.id; - - game - .set_pve(false) - .set_team_num(2) - .set_team_size(cryps.len()); - - // create the initiators team - let mut team = Team::new(account.id); - team.set_cryps(cryps); - - game.team_add(team)?; - - // persist - game_write(&game, tx)?; - players_write(account, game_id, tx)?; - - Ok(game) -} - -pub fn game_join(params: GameJoinParams, tx: &mut Transaction, account: &Account) -> Result { - let mut game = game_get(tx, params.game_id)?; - - // rejoining a game from the FE list - if game.already_joined(account.id) { - return Ok(game); - } - - // ok actually adding a new team - let game_id = game.id; - - let cryps = params.cryp_ids - .iter() - .map(|id| cryp_get(tx, *id, account.id)) - .collect::, Error>>()?; - - if cryps.len() != game.team_size { - return Err(format_err!("incorrect team size. ({:})", game.team_size)); - } - - let mut team = Team::new(account.id); - team.set_cryps(cryps); - game.team_add(team)?; - - if game.can_start() { - game.start(); - } - - game_update(&game, tx)?; - players_write(account, game_id, tx)?; - - Ok(game) -} - -pub fn game_joinable_list(tx: &mut Transaction, _account: &Account) -> Result, Error> { - let query = " - SELECT games.data - FROM games - WHERE joinable; - "; - - let result = tx - .query(query, &[])?; - - let games: Result, _> = result.iter().map(|row| { - let cryp_bytes: Vec = row.get(0); - from_slice::(&cryp_bytes) - }).collect(); - - // catch any errors - if games.is_err() { - return Err(err_msg("could not deserialize a game")); - } - - // now unwrap is safe - return Ok(games.unwrap()); -} - - -#[cfg(test)] -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::TestBlock) - .learn(Skill::TestParry) - .learn(Skill::TestSiphon) - .learn(Skill::Empower) - .learn(Skill::Block) - .create(); - - let y = Cryp::new() - .named(&"lemongrass tea".to_string()) - .level(8) - .learn(Skill::TestStun) - .learn(Skill::TestTouch) - .learn(Skill::TestBlock) - .learn(Skill::TestParry) - .learn(Skill::TestSiphon) - .learn(Skill::Empower) - .learn(Skill::Block) - .create(); - - 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 - .team_add(x_team).unwrap() - .team_add(y_team).unwrap(); - - assert!(game.can_start()); - - game.start(); - - return game; - } - - fn create_2v2_test_game() -> Game { - let i = Cryp::new() - .named(&"pretaliate".to_string()) - .level(8) - .learn(Skill::TestTouch) - .create(); - - let j = Cryp::new() - .named(&"poy sian".to_string()) - .level(8) - .learn(Skill::TestTouch) - .create(); - - let x = Cryp::new() - .named(&"pronounced \"creeep\"".to_string()) - .level(8) - .learn(Skill::TestTouch) - .create(); - - let y = Cryp::new() - .named(&"lemongrass tea".to_string()) - .level(8) - .learn(Skill::TestTouch) - .create(); - - let mut game = Game::new(); - - game - .set_team_num(2) - .set_team_size(2) - .set_pve(false); - - let i_team_id = Uuid::new_v4(); - let mut i_team = Team::new(i_team_id); - i_team - .set_cryps(vec![i,j]); - - let x_team_id = Uuid::new_v4(); - let mut x_team = Team::new(x_team_id); - x_team - .set_cryps(vec![x,y]); - - game - .team_add(i_team).unwrap() - .team_add(x_team).unwrap(); - - assert!(game.can_start()); - - game.start(); - - return 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_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, 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()); - - 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(); - - assert!(game.target_phase_finished()); - - game.resolve_phase_start(); - - assert!([Phase::Skill, Phase::Finish].contains(&game.phase)); - - return; - } - - #[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_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, 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(); - - 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.resolve_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); - } - - #[test] - fn ko_resolution_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(); - - game.team_by_id(y_team.id).cryp_by_id(y_cryp.id).unwrap().red_damage.set(u64::max_value()); - - 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::Attack).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.resolve_phase_start(); - - // resolution should have been prevented by KO - // println!("{:#?}", game); - assert!(!game.team_by_id(y_team.id).cryps[0].is_stunned()); - assert!(game.phase == Phase::Finish); - } - - #[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, 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(); - game.add_target(y_team.id, y_cryp.id, x_stun_id).unwrap(); - - game.resolve_phase_start(); - - // should auto progress back to skill phase - assert!(game.phase == Phase::Skill); - - - // 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(y_team.id).cryps[0].skill_on_cd(Skill::Empower).is_some()); - 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 - let _x_block_id = game.add_skill(x_team.id, x_cryp.id, None, Skill::Block).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.resolve_phase_start(); - - assert!(game.team_by_id(x_team.id).cryps[0].skill_on_cd(Skill::Block).is_some()); - assert!(game.team_by_id(y_team.id).cryps[0].skill_on_cd(Skill::Empower).is_none()); - } - - #[test] - fn parry_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, None, Skill::TestParry).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 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.resolve_phase_start(); - - // should not be stunned because of parry - assert!(game.team_by_id(x_team.id).cryps[0].is_stunned() == false); - } - - #[test] - fn siphon_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_siphon_id = game.add_skill(x_team.id, x_cryp.id, Some(y_team.id), Skill::TestSiphon).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_siphon_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::SiphonTick)); - } - - #[test] - fn ko_pve_test() { - let mut game = create_2v2_test_game(); - - let i_team = game.teams[0].clone(); - let x_team = game.teams[1].clone(); - - let i_cryp = i_team.cryps[0].clone(); - let j_cryp = i_team.cryps[1].clone(); - let x_cryp = x_team.cryps[0].clone(); - let y_cryp = x_team.cryps[1].clone(); - - let i_attack_id = game.add_skill(i_team.id, i_cryp.id, Some(x_team.id), Skill::TestTouch).unwrap(); - let j_attack_id = game.add_skill(i_team.id, j_cryp.id, Some(x_team.id), Skill::TestTouch).unwrap(); - let x_attack_id = game.add_skill(x_team.id, x_cryp.id, Some(i_team.id), Skill::TestTouch).unwrap(); - let y_attack_id = game.add_skill(x_team.id, y_cryp.id, Some(i_team.id), Skill::TestTouch).unwrap(); - - - assert!(game.skill_phase_finished()); - game.target_phase_start(); - - game.add_target(i_team.id, i_cryp.id, x_attack_id).unwrap(); - game.add_target(i_team.id, j_cryp.id, y_attack_id).unwrap(); - - game.add_target(x_team.id, x_cryp.id, i_attack_id).unwrap(); - game.add_target(x_team.id, y_cryp.id, j_attack_id).unwrap(); - - assert!(game.target_phase_finished()); - - game.resolve_phase_start(); - - assert!([Phase::Skill, Phase::Finish].contains(&game.phase)); - - // kill a cryp - game.team_by_id(i_team.id).cryp_by_id(i_cryp.id).unwrap().hp.reduce(u64::max_value()); - - // add some more skills - let j_attack_id = game.add_skill(i_team.id, j_cryp.id, Some(x_team.id), Skill::TestTouch).unwrap(); - let x_attack_id = game.add_skill(x_team.id, x_cryp.id, Some(i_team.id), Skill::TestTouch).unwrap(); - let y_attack_id = game.add_skill(x_team.id, y_cryp.id, Some(i_team.id), Skill::TestTouch).unwrap(); - - assert!(game.skill_phase_finished()); - game.target_phase_start(); - - assert!(game.team_by_id(i_team.id).skills_required() == 1); - assert!(game.cryp_targetable(i_team.id, i_cryp.id).is_err()); - assert!(game.cryp_targetable(i_team.id, j_cryp.id).is_ok()); - - game.add_target(i_team.id, j_cryp.id, x_attack_id).unwrap(); - game.add_target(i_team.id, j_cryp.id, y_attack_id).unwrap(); - - game.add_target(x_team.id, x_cryp.id, j_attack_id).unwrap(); - game.add_target(x_team.id, y_cryp.id, j_attack_id).unwrap(); - - assert!(game.target_phase_finished()); - return; - } -} diff --git a/server/src/instance.rs b/server/src/instance.rs index 34dc61ca..1c67786a 100644 --- a/server/src/instance.rs +++ b/server/src/instance.rs @@ -106,7 +106,7 @@ pub fn instance_get_open(tx: &mut Transaction) -> Result { let query = " SELECT * FROM instance - AND open = true; + WHERE open = true; "; let result = tx diff --git a/server/src/player.rs b/server/src/player.rs index 1fb19389..54d6aa6b 100644 --- a/server/src/player.rs +++ b/server/src/player.rs @@ -8,10 +8,9 @@ use failure::Error; use failure::err_msg; use account::Account; -use instance::Instance; -use cryp::{Cryp}; +use cryp::{Cryp, cryp_get}; use vbox::{Vbox}; -use rpc::{PlayerStateParams}; +use rpc::{PlayerStateParams, PlayerCrypsSetParams}; #[derive(Debug,Clone,Serialize,Deserialize)] pub struct Score { @@ -106,3 +105,24 @@ pub fn player_update(tx: &mut Transaction, player: Player) -> Result Result { player_get(tx, account.id, params.instance_id) } + +pub fn player_cryps_set(params: PlayerCrypsSetParams, tx: &mut Transaction, account: &Account) -> Result { + if params.instance_id != Uuid::nil() { + return Err(err_msg("only the global team can be replaced")); + } + + if params.cryp_ids.len() != 3 { + return Err(err_msg("team size is 3")); + } + + let mut player = player_get(tx, account.id, params.instance_id)?; + + let cryps = params.cryp_ids + .iter() + .map(|id| cryp_get(tx, *id, account.id)) + .collect::, Error>>()?; + + player.cryps = cryps; + + player_update(tx, player) +} diff --git a/server/src/rpc.rs b/server/src/rpc.rs index c1ebc729..c8e8a4b0 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -21,7 +21,7 @@ use account::{Account, account_create, account_login, account_from_token, accoun use skill::{Skill}; use zone::{Zone, zone_create, zone_join, zone_close}; use spec::{Spec}; -use player::{player_state, player_create, Player}; +use player::{player_state, player_create, player_cryps_set, Player}; use instance::{instance_join}; use vbox::{vbox_accept, vbox_apply, vbox_discard, vbox_combine, vbox_drop}; @@ -91,7 +91,10 @@ impl Rpc { return response; }, - Err(_e) => Err(err_msg("invalid message")), + Err(e) => { + println!("{:?}", e); + Err(err_msg("invalid message")) + }, } } @@ -114,7 +117,7 @@ impl Rpc { return Ok(game_response); } - fn game_pve(data: Vec, tx: &mut Transaction, account: Account, client: &mut WebSocket) -> Result { + fn game_pve(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; let game_response = RpcResponse { @@ -122,11 +125,6 @@ impl Rpc { params: RpcResult::GameState(game_pve(msg.params, tx, &account)?) }; - Rpc::send_msg(client, RpcResponse { - method: "account_cryps".to_string(), - params: RpcResult::CrypList(account_cryps(tx, &account)?) - })?; - return Ok(game_response); } @@ -272,7 +270,6 @@ impl Rpc { return Ok(response); } - fn player_state(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; @@ -284,6 +281,17 @@ impl Rpc { return Ok(response); } + fn player_cryps_set(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let response = RpcResponse { + method: "player_state".to_string(), + params: RpcResult::PlayerState(player_cryps_set(msg.params, tx, &account)?) + }; + + return Ok(response); + } + fn player_vbox_accept(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; @@ -362,7 +370,6 @@ pub enum RpcResult { Account(Account), CrypList(Vec), GameState(Game), - GameJoinableList(Vec), ZoneState(Zone), ZoneClose(()), @@ -540,6 +547,18 @@ pub struct PlayerStateParams { pub instance_id: Uuid, } +#[derive(Debug,Clone,Serialize,Deserialize)] +struct PlayerCrypsSetMsg { + method: String, + params: PlayerCrypsSetParams, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct PlayerCrypsSetParams { + pub instance_id: Uuid, + pub cryp_ids: Vec, +} + #[derive(Debug,Clone,Serialize,Deserialize)] struct VboxAcceptMsg { method: String,