battle damage dealing

This commit is contained in:
ntr 2018-10-19 21:05:43 +11:00
parent faa5d87f6f
commit bfd0208785
5 changed files with 389 additions and 155 deletions

View File

@ -44,6 +44,10 @@ teams
1v1 2v2 3v3
skill order defined by cryp/skill speed
counter -> dmg <-> heal
physical, magic, pure dmg?
elemental?

View File

@ -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<Roll>,
target_cryp_id: Option<Uuid>,
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<Cryp>,
skills: Vec<Skill>,
targets: Vec<Skill>,
abilities: Vec<Ability>,
incoming: Vec<Ability>,
}
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<plr>)
// battle.can_start() -> enough_teams() -> false
// battle.add_team(vec<mob>)
// 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) {
@ -166,27 +370,30 @@ mod tests {
let x = Cryp::new()
.named(&"pronounced \"creeep\"".to_string())
.level(8)
.learn(Skill::Stoney)
.create();
let x_id = x.id;
let y = Cryp::new()
.named(&"lemongrass tea".to_string())
.level(8)
.create();
let y_id = y.id;
let mut battle = Battle::new();
battle
.set_team_num(2)
.set_team_size(1);
let x_id = Uuid::new_v4();
let mut x_team = Team::new(x_id);
let mut x_team = Team::new();
let x_team_id = x_team.id;
x_team
.set_cryps(vec![x]);
let y_id = Uuid::new_v4();
let mut y_team = Team::new(y_id);
let mut y_team = Team::new();
let y_team_id = y_team.id;
y_team
.set_cryps(vec![y]);
@ -196,7 +403,26 @@ mod tests {
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;
}
}

View File

@ -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();

View File

@ -10,12 +10,13 @@ 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,
}
@ -24,53 +25,53 @@ pub enum StatKind {
pub struct Roll {
pub base: u64,
pub result: u64,
pub kind: StatKind,
pub kind: Stat,
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct Stat {
pub struct CrypStat {
pub value: u64,
pub kind: StatKind,
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<String>) -> Roll {
let mut rng = thread_rng();
let base: u64 = rng.gen();
// fn roll(&self, c: &Cryp, log: &mut Vec<String>) -> 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<String>,
}
@ -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<Skill>,
// pub skills: Vec<Skill>,
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<Cryp, Error> {
#[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);

View File

@ -22,7 +22,7 @@ mod cryp;
mod battle;
mod net;
mod combat;
mod skill;
// mod skill;
mod rpc;
mod account;
mod item;