diff --git a/server/WORKLOG.md b/server/WORKLOG.md index 47d80a42..858c5adc 100755 --- a/server/WORKLOG.md +++ b/server/WORKLOG.md @@ -44,6 +44,10 @@ teams 1v1 2v2 3v3 +skill order defined by cryp/skill speed + +counter -> dmg <-> heal + physical, magic, pure dmg? elemental? diff --git a/server/src/battle.rs b/server/src/battle.rs index 5c7e6c8d..8e8499a5 100755 --- a/server/src/battle.rs +++ b/server/src/battle.rs @@ -1,30 +1,107 @@ use uuid::Uuid; -// use rand::prelude::*; +use rand::prelude::*; -use cryp::Cryp; -use skill::Skill; +use cryp::{Cryp, CrypStat, Stat}; -#[derive(Debug,Clone,Serialize,Deserialize)] +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +pub struct Roll { + pub base: u64, + pub result: u64, +} + +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +pub enum AbilityKind { + Attack, + Block, + Heal, +} + +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +pub struct Ability { + id: Uuid, + kind: AbilityKind, + cryp_id: Uuid, + roll: Option, + target_cryp_id: Option, + target_team_id: Uuid, +} + +impl Ability { + pub fn new(cryp_id: Uuid, target_team_id: Uuid, kind: AbilityKind) -> Ability { + return Ability { + id: Uuid::new_v4(), + cryp_id, + target_cryp_id: None, + target_team_id, + roll: None, + kind, + }; + } + + fn roll(&self, c: &Cryp) -> Roll { + let mut rng = thread_rng(); + let base: u64 = rng.gen(); + + let stat = match self.kind { + AbilityKind::Attack => &c.str, + AbilityKind::Block => &c.str, + AbilityKind::Heal => &c.int, + }; + + let mut roll = Roll { base, result: base }; + + // // apply skills + // roll = c.skills.iter().fold(roll, |roll, s| s.apply(roll)); + + // finally combine with stat + // log.push(format!("{:064b} <- finalised", roll.result)); + roll.result = roll.result & stat.value; + + // log.push(format!("{:064b} & <- attribute roll", self.value)); + println!("{:064b} = {:?}", roll.result, roll.result); + // log.push(format!("")); + + return roll; + } + + pub fn resolve(&mut self, cryp: &mut Cryp, target: &mut Cryp) -> &mut Ability { + let roll = self.roll(&cryp); + + println!("{:?} gettin clapped for {:?}", target.name, roll.result); + target.hp.reduce(roll.result); + self + } + + pub fn set_target(&mut self, cryp_id: Uuid) -> &mut Ability { + self.target_cryp_id = Some(cryp_id); + self + } +} + +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Phase { - Skill, + Start, + Ability, Target, + Damage, + Finish, } #[derive(Debug,Clone,Serialize,Deserialize)] pub struct Team { - player: Uuid, + id: Uuid, cryps: Vec, - skills: Vec, - targets: Vec, + abilities: Vec, + incoming: Vec, } impl Team { - pub fn new(player: Uuid) -> Team { + pub fn new() -> Team { return Team { - player, + id: Uuid::new_v4(), cryps: vec![], - skills: vec![], - targets: vec![], + abilities: vec![], + incoming: vec![], }; } @@ -32,6 +109,17 @@ impl 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) + } + + pub fn ability_by_id(&mut self, id: Uuid) -> &mut Ability { + match self.incoming.iter_mut().find(|a| a.id == id) { + Some(a) => a, + None => panic!("abiltity not in battle"), + } + } } #[derive(Debug,Clone,Serialize,Deserialize)] @@ -51,7 +139,7 @@ impl Battle { team_num: 0, teams: vec![], is_pve: true, - phase: Phase::Skill, + phase: Phase::Start, log: vec![], }; } @@ -74,42 +162,165 @@ impl Battle { self } - pub fn start(&mut self) -> &mut Battle { - self - } - - pub fn add_skill(&mut self, player: Uuid, skill: Skill) -> &mut Battle { - // check if cryp actually has this skill - self.team_by_player(player).skills.push(skill); - self - } - - pub fn team_by_player(&mut self, player: Uuid) -> &mut Team { - match self.teams.iter_mut().find(|t| t.player == player) { + // handle missing team properly + pub 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!("player not in battle {:?}", player), + None => panic!("id not in battle {:?}", id), } } - pub fn can_start(&mut self) -> bool { + pub 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 battle"); + } + + fn replace_cryp(&mut self, updated: Cryp) -> &mut Battle { + 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; + } + } + self + } + + pub fn can_start(&self) -> bool { self.teams.len() == self.team_num } - // battle.new() - // battle.set_team_size(1) - // battle.add_team(vec) - // battle.can_start() -> enough_teams() -> false - // battle.add_team(vec) - // battle.can_start() -> enough_teams() -> true - // battle.start() -> start_move_phase() -> is_pve() ? mob_moves() - // battle.skill_phase() -> send_state() - // battle.add_skill(team_id, cryp_id, skill) - // battle.skill_phase_finished() -> enough_moves() - // battle.targets_phase() -> is_pve() ? mob_targets() - // battle.select_target(skill, target) - // battle.targets_phase_finished() -> enough_targets() - // battle.assign_dmg() - // battle.is_finished() + pub fn start(&mut self) -> &mut Battle { + self.ability_phase_start(); + self + } + + pub fn ability_phase_start(&mut self) -> &mut Battle { + if ![Phase::Start, Phase::Damage].contains(&self.phase) { + panic!("battle not in damage or start phase"); + } + + self.phase = Phase::Ability; + for team in self.teams.iter_mut() { + team.abilities.clear(); + team.incoming.clear(); + } + self + } + + // abilities can target any team, but we have to check if the caller is the owner of the cryp + // and that the cryp has the ability they are trying to add + pub fn add_ability(&mut self, team_id: Uuid, cryp_id: Uuid, target_team_id: Uuid, kind: AbilityKind) -> Uuid { + let team = self.team_by_id(team_id); + match team.cryp_by_id(cryp_id) { + Some(c) => c, + None => panic!("cryp not in team"), + }; + + let ability = Ability::new(cryp_id, target_team_id, kind); + team.abilities.push(ability); + + return ability.id; + } + + pub fn ability_phase_finished(&self) -> bool { + self.teams.iter().all(|t| t.abilities.len() == self.team_size) + } + + // move all abilities into their target team's targets list + pub fn targets_phase_start(&mut self) -> &mut Battle { + if self.phase != Phase::Ability { + panic!("battle not in ability phase"); + } + + 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 ability in team.abilities.iter_mut() { + let target_team = self.team_by_id(ability.target_team_id); + target_team.incoming.push(*ability); + } + } + + self + } + + // targets can only be added by the owner of the team + pub fn add_target(&mut self, team_id: Uuid, cryp_id: Uuid, ability_id: Uuid) -> &mut Ability { + // 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 => panic!("cryp not in team"), + }; + + // set the target + let ability = team.ability_by_id(ability_id); + ability.set_target(cryp_id) + } + + pub fn target_phase_finished(&self) -> bool { + self.teams.iter().all(|t| t.incoming.iter().all(|i| i.target_cryp_id.is_some())) + } + + // requires no input + // just do it + pub fn damage_phase_start(&mut self) -> &mut Battle { + if self.phase != Phase::Target { + panic!("battle not in target phase"); + } + + self.phase = Phase::Damage; + + self.resolve_abilities(); + + if self.is_finished() { + self.phase = Phase::Finish; + return self + } + + self.ability_phase_start(); + + self + } + + fn resolve_abilities(&mut self) -> &mut Battle { + if self.phase != Phase::Damage { + panic!("battle not in damage phase"); + } + + // go through every team + // roll the incoming ability + // resolve + // check if battle is over + + // 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.target_cryp_id.unwrap()).clone(); + let mut target_cryp = self.cryp_by_id(incoming.target_cryp_id.unwrap()).clone(); + incoming.resolve(&mut cryp, &mut target_cryp); + self.replace_cryp(target_cryp); + } + } + self + } + + pub fn is_finished(&self) -> bool { + self.teams.iter().any(|t| t.cryps.iter().all(|c| c.is_ko())) + } // pub fn next(&mut self) -> &mut Battle { // if self.finished() { @@ -128,13 +339,6 @@ impl Battle { // self // } - pub fn skill_phase_finished(&self) -> bool { - self.teams.iter().all(|t| t.skills.len() == self.team_size) - } - - pub fn finished(&self) -> bool { - self.teams.iter().any(|t| t.cryps.iter().all(|c| c.is_ko())) - } // pub fn cryp_by_id(&self, id: Uuid) -> &Cryp { // match self.cryps().iter().find(|c| c.id == id) { @@ -158,45 +362,67 @@ impl Battle { #[cfg(test)] mod tests { - use battle::*; - use cryp::*; + use battle::*; + use cryp::*; - #[test] - fn battle_test() { - let x = Cryp::new() - .named(&"pronounced \"creeep\"".to_string()) - .level(8) - .learn(Skill::Stoney) - .create(); + #[test] + fn battle_test() { + let x = Cryp::new() + .named(&"pronounced \"creeep\"".to_string()) + .level(8) + .create(); - let y = Cryp::new() - .named(&"lemongrass tea".to_string()) - .level(8) - .create(); + let x_id = x.id; - let mut battle = Battle::new(); + let y = Cryp::new() + .named(&"lemongrass tea".to_string()) + .level(8) + .create(); - battle - .set_team_num(2) - .set_team_size(1); + let y_id = y.id; - let x_id = Uuid::new_v4(); - let mut x_team = Team::new(x_id); - x_team - .set_cryps(vec![x]); + let mut battle = Battle::new(); - let y_id = Uuid::new_v4(); - let mut y_team = Team::new(y_id); - y_team - .set_cryps(vec![y]); + battle + .set_team_num(2) + .set_team_size(1); - battle - .add_team(x_team) - .add_team(y_team); + let mut x_team = Team::new(); + let x_team_id = x_team.id; + x_team + .set_cryps(vec![x]); - assert!(battle.can_start()); + let mut y_team = Team::new(); + let y_team_id = y_team.id; + y_team + .set_cryps(vec![y]); - return; - } + battle + .add_team(x_team) + .add_team(y_team); + assert!(battle.can_start()); + + battle.start(); + + let x_attack_id = battle.add_ability(x_team_id, x_id, y_team_id, AbilityKind::Attack); + let y_attack_id = battle.add_ability(y_team_id, y_id, x_team_id, AbilityKind::Attack); + + assert!(battle.ability_phase_finished()); + + battle.targets_phase_start(); + + println!("{:?}", battle); + + battle.add_target(x_team_id, x_id, y_attack_id); + battle.add_target(y_team_id, y_id, x_attack_id); + + assert!(battle.target_phase_finished()); + + battle.damage_phase_start(); + + assert!([Phase::Ability, Phase::Finish].contains(&battle.phase)); + + return; + } } diff --git a/server/src/combat.rs b/server/src/combat.rs index 779574e5..03d2dd18 100755 --- a/server/src/combat.rs +++ b/server/src/combat.rs @@ -11,7 +11,7 @@ use rpc::{CombatPveParams}; use cryp::{Cryp, cryp_write}; use battle::Battle; -use skill::Skill; +// use skill::Skill; fn generate_mob(plr: &Cryp) -> Cryp { let mut rng = thread_rng(); diff --git a/server/src/cryp.rs b/server/src/cryp.rs index aa4dce55..3e442d96 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -10,67 +10,68 @@ use failure::err_msg; use net::Db; use account::Account; use rpc::{CrypSpawnParams}; -use skill::{Skill}; +// use skill::{Skill}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub enum StatKind { - Dmg, - Def, +pub enum Stat { + Str, + Agi, + Int, Hp, Stam, } #[derive(Debug,Clone)] pub struct Roll { - pub base: u64, - pub result: u64, - pub kind: StatKind, + pub base: u64, + pub result: u64, + pub kind: Stat, } #[derive(Debug,Clone,Serialize,Deserialize)] -pub struct Stat { - pub value: u64, - pub kind: StatKind, +pub struct CrypStat { + pub value: u64, + pub stat: Stat, } -impl Stat { - fn set(&mut self, v: u64) -> &Stat { +impl CrypStat { + fn set(&mut self, v: u64) -> &CrypStat { self.value = v; self } - fn roll(&self, c: &Cryp, log: &mut Vec) -> Roll { - let mut rng = thread_rng(); - let base: u64 = rng.gen(); + // fn roll(&self, c: &Cryp, log: &mut Vec) -> Roll { + // let mut rng = thread_rng(); + // let base: u64 = rng.gen(); - let mut roll = Roll { kind: self.kind, base, result: base }; + // let mut roll = Roll { kind: self.kind, base, result: base }; - log.push(format!("{:?}", self.kind)); - log.push(format!("{:064b} <- base roll", base)); + // log.push(format!("{:?}", self.kind)); + // log.push(format!("{:064b} <- base roll", base)); - // apply skills - roll = c.skills.iter().fold(roll, |roll, s| s.apply(roll)); + // // apply skills + // roll = c.skills.iter().fold(roll, |roll, s| s.apply(roll)); - // finally combine with stat - log.push(format!("{:064b} <- finalised", roll.result)); - roll.result = roll.result & self.value; + // // finally combine with CrypStat + // log.push(format!("{:064b} <- finalised", roll.result)); + // roll.result = roll.result & self.value; - log.push(format!("{:064b} & <- attribute roll", self.value)); - log.push(format!("{:064b} = {:?}", roll.result, roll.result)); - log.push(format!("")); + // log.push(format!("{:064b} & <- attribute roll", self.value)); + // log.push(format!("{:064b} = {:?}", roll.result, roll.result)); + // log.push(format!("")); - return roll; - } + // return roll; + // } - fn reduce(&mut self, dmg: u64) -> &mut Stat { + pub fn reduce(&mut self, dmg: u64) -> &mut CrypStat { self.value = self.value.saturating_sub(dmg); self } } pub struct Turn { - pub dmg: Roll, - pub def: Roll, + pub str: Roll, + pub agi: Roll, pub log: Vec, } @@ -78,13 +79,14 @@ pub struct Turn { pub struct Cryp { pub id: Uuid, pub account: Uuid, - pub dmg: Stat, - pub def: Stat, - pub stam: Stat, - pub hp: Stat, + pub str: CrypStat, + pub agi: CrypStat, + pub int: CrypStat, + pub stam: CrypStat, + pub hp: CrypStat, pub xp: u64, pub lvl: u8, - pub skills: Vec, + // pub skills: Vec, pub name: String, } @@ -99,13 +101,14 @@ impl Cryp { return Cryp { id, account: id, - dmg: Stat { value: 0, kind: StatKind::Dmg }, - def: Stat { value: 0, kind: StatKind::Def }, - stam: Stat { value: 0, kind: StatKind::Stam }, - hp: Stat { value: 0, kind: StatKind::Hp }, + str: CrypStat { value: 0, stat: Stat::Str }, + agi: CrypStat { value: 0, stat: Stat::Agi }, + int: CrypStat { value: 0, stat: Stat::Int }, + stam: CrypStat { value: 0, stat: Stat::Stam }, + hp: CrypStat { value: 0, stat: Stat::Hp }, lvl: 0, xp: 0, - skills: vec![], + // skills: vec![], name: String::new() }; } @@ -126,10 +129,10 @@ impl Cryp { self } - pub fn learn(mut self, s: Skill) -> Cryp { - self.skills.push(s); - self - } + // pub fn learn(mut self, s: Skill) -> Cryp { + // self.skills.push(s); + // self + // } pub fn add_xp(mut self) -> Cryp { self.xp = self.xp.saturating_add(1); @@ -144,14 +147,14 @@ impl Cryp { self.create() } - pub fn turn(&self) -> Turn { - let mut log = vec![format!("{:?}'s turn:", self.name)]; + // pub fn turn(&self) -> Turn { + // let mut log = vec![format!("{:?}'s turn:", self.name)]; - let dmg = self.dmg.roll(self, &mut log); - let def = self.def.roll(self, &mut log); + // let str = self.str.roll(self, &mut log); + // let agi = self.agi.roll(self, &mut log); - return Turn { dmg, def, log } - } + // return Turn { str, agi, log } + // } pub fn create(mut self) -> Cryp { let mut rng = thread_rng(); @@ -162,36 +165,37 @@ impl Cryp { self.xp = max; - self.dmg.set(rng.gen_range(1, max)); - self.def.set(rng.gen_range(1, max)); + self.str.set(rng.gen_range(1, max)); + self.agi.set(rng.gen_range(1, max)); + self.int.set(rng.gen_range(1, max)); self.stam.set(rng.gen_range(1, max)); self.hp.set(self.stam.value); self } - pub fn assign_dmg(&mut self, opp: &Cryp, plr_t: &mut Turn, opp_t: &Turn) -> &mut Cryp { - // let final_dmg = opp_t.dmg.result.saturating_sub(plr_t.def.result); - // let blocked = opp_t.dmg.result.saturating_sub(final_dmg); + // pub fn assign_str(&mut self, opp: &Cryp, plr_t: &mut Turn, opp_t: &Turn) -> &mut Cryp { + // // let final_str = opp_t.str.result.saturating_sub(plr_t.agi.result); + // // let blocked = opp_t.str.result.saturating_sub(final_str); - let final_dmg = opp_t.dmg.result & !plr_t.def.result; - let blocked = opp_t.dmg.result & plr_t.def.result; + // let final_str = opp_t.str.result & !plr_t.agi.result; + // let blocked = opp_t.str.result & plr_t.agi.result; - plr_t.log.push(format!("{:064b} <- attacking roll {:?}", opp_t.dmg.result, opp_t.dmg.result)); - // plr_t.log.push(format!("{:064b} <- blocking roll {:?}", plr_t.def.result, plr_t.def.result)); - plr_t.log.push(format!("{:064b} <- final dmg {:?} ({:?} blocked)", final_dmg, final_dmg, blocked)); + // plr_t.log.push(format!("{:064b} <- attacking roll {:?}", opp_t.str.result, opp_t.str.result)); + // // plr_t.log.push(format!("{:064b} <- blocking roll {:?}", plr_t.agi.result, plr_t.agi.result)); + // plr_t.log.push(format!("{:064b} <- final str {:?} ({:?} blocked)", final_str, final_str, blocked)); - self.hp.reduce(final_dmg); + // self.hp.reduce(final_str); - plr_t.log.push(format!("{:?} deals {:?} dmg to {:?} ({:?} blocked / {:?} hp remaining)" - ,opp.name - ,final_dmg - ,self.name - ,blocked - ,self.hp.value)); + // plr_t.log.push(format!("{:?} deals {:?} str to {:?} ({:?} blocked / {:?} hp remaining)" + // ,opp.name + // ,final_str + // ,self.name + // ,blocked + // ,self.hp.value)); - plr_t.log.push(format!("")); - self - } + // plr_t.log.push(format!("")); + // self + // } pub fn is_ko(&self) -> bool { self.hp.value == 0 @@ -270,14 +274,14 @@ pub fn cryp_write(cryp: Cryp, tx: &mut Transaction) -> Result { #[cfg(test)] mod tests { use cryp::*; - use skill::*; + // use skill::*; #[test] fn create_cryp_test() { let max_level = Cryp::new() .named(&"hatchling".to_string()) .level(64) - .learn(Skill::Stoney) + // .learn(Skill::Stoney) .create(); assert_eq!(max_level.lvl, 64); diff --git a/server/src/main.rs b/server/src/main.rs index 2c709cae..eb5373b7 100755 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -22,7 +22,7 @@ mod cryp; mod battle; mod net; mod combat; -mod skill; +// mod skill; mod rpc; mod account; mod item;