specs with values
This commit is contained in:
parent
17d71b2258
commit
61cbef6999
@ -34,6 +34,11 @@ strangle
|
||||
## NOW
|
||||
* check zone completion
|
||||
* recalc evasion rating
|
||||
* serialize modified stats
|
||||
* items add specs
|
||||
* add effects to spec
|
||||
* remove spec from cryp
|
||||
* spec limits per category
|
||||
|
||||
## SOON
|
||||
* clean up categories
|
||||
|
||||
@ -10,7 +10,7 @@ use failure::err_msg;
|
||||
use account::Account;
|
||||
use rpc::{CrypSpawnParams, CrypLearnParams, CrypForgetParams};
|
||||
use skill::{Skill, Cooldown, Effect, Cast, Category, Immunity, Disable, ResolutionResult};
|
||||
use spec::{Spec};
|
||||
use spec::{Spec, SpecLevel};
|
||||
use game::{Log};
|
||||
|
||||
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
||||
@ -59,43 +59,61 @@ pub enum Stat {
|
||||
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
||||
pub struct CrypStat {
|
||||
base: u64,
|
||||
value: u64,
|
||||
pub stat: Stat,
|
||||
}
|
||||
|
||||
impl CrypStat {
|
||||
pub fn set(&mut self, v: u64) -> &CrypStat {
|
||||
pub fn set(&mut self, v: u64, specs: &CrypSpecs) -> &mut CrypStat {
|
||||
self.base = v;
|
||||
self.recalculate(specs)
|
||||
}
|
||||
|
||||
pub fn recalculate(&mut self, specs: &CrypSpecs) -> &mut CrypStat {
|
||||
let specs = specs.affects(self.stat);
|
||||
// applied with fold because it can be zeroed or multiplied
|
||||
// but still needs access to the base amount
|
||||
let value = specs.iter().fold(self.base, |acc, s| s.apply(acc, self.base));
|
||||
self.value = value;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn reduce(&mut self, amt: u64) -> &mut CrypStat {
|
||||
self.base = self.base.saturating_sub(amt);
|
||||
self.value = self.value.saturating_sub(amt);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn increase(&mut self, amt: u64) -> &mut CrypStat {
|
||||
self.base = self.base.saturating_add(amt);
|
||||
self.value = self.value.saturating_add(amt);
|
||||
self
|
||||
}
|
||||
|
||||
fn modified(&self, specs: &CrypSpecs) -> u64 {
|
||||
let specs = specs.affects(self.stat);
|
||||
// applied with fold because it can be zeroed or multiplied
|
||||
// but still needs access to the base amount
|
||||
let modified = specs.iter().fold(self.base, |acc, s| s.apply(acc, self.base));
|
||||
return modified;
|
||||
pub fn force(&mut self, v: u64) -> &mut CrypStat {
|
||||
self.base = v;
|
||||
self.value = v;
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||
struct CrypSpecs {
|
||||
pub struct CrypSpecs {
|
||||
common: Vec<Spec>,
|
||||
uncommon: Vec<Spec>,
|
||||
rare: Vec<Spec>,
|
||||
}
|
||||
|
||||
impl CrypSpecs {
|
||||
fn new() -> CrypSpecs {
|
||||
CrypSpecs {
|
||||
common: vec![],
|
||||
uncommon: vec![],
|
||||
rare: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn affects(&self, stat: Stat) -> Vec<Spec> {
|
||||
[&self.common, &self.uncommon, &self.rare]
|
||||
.iter()
|
||||
@ -130,6 +148,7 @@ pub struct Cryp {
|
||||
pub lvl: u8,
|
||||
pub skills: Vec<CrypSkill>,
|
||||
pub effects: Vec<CrypEffect>,
|
||||
pub specs: CrypSpecs,
|
||||
pub name: String,
|
||||
pub ko_logged: bool,
|
||||
}
|
||||
@ -145,18 +164,19 @@ impl Cryp {
|
||||
return Cryp {
|
||||
id,
|
||||
account: id,
|
||||
phys_dmg: CrypStat { base: 0, stat: Stat::PhysicalDamage },
|
||||
spell_dmg: CrypStat { base: 0, stat: Stat::SpellDamage },
|
||||
speed: CrypStat { base: 0, stat: Stat::Speed },
|
||||
stamina: CrypStat { base: 0, stat: Stat::Stamina },
|
||||
hp: CrypStat { base: 0, stat: Stat::Hp },
|
||||
armour: CrypStat { base: 0, stat: Stat::Armour },
|
||||
spell_shield: CrypStat { base: 0, stat: Stat::SpellShield },
|
||||
evasion: CrypStat { base: 0, stat: Stat::Evasion },
|
||||
phys_dmg: CrypStat { base: 0, value: 0, stat: Stat::PhysicalDamage },
|
||||
spell_dmg: CrypStat { base: 0, value: 0, stat: Stat::SpellDamage },
|
||||
speed: CrypStat { base: 0, value: 0, stat: Stat::Speed },
|
||||
stamina: CrypStat { base: 0, value: 0, stat: Stat::Stamina },
|
||||
hp: CrypStat { base: 0, value: 0, stat: Stat::Hp },
|
||||
armour: CrypStat { base: 0, value: 0, stat: Stat::Armour },
|
||||
spell_shield: CrypStat { base: 0, value: 0, stat: Stat::SpellShield },
|
||||
evasion: CrypStat { base: 0, value: 0, stat: Stat::Evasion },
|
||||
lvl: 0,
|
||||
xp: 0,
|
||||
skills: vec![],
|
||||
effects: vec![],
|
||||
specs: CrypSpecs::new(),
|
||||
name: String::new(),
|
||||
ko_logged: false,
|
||||
};
|
||||
@ -215,19 +235,19 @@ impl Cryp {
|
||||
let stat_max = 256u64.saturating_mul(self.lvl.into());
|
||||
|
||||
let evasion_min = 1u64;
|
||||
let evasion_max = 25;
|
||||
let evasion_max = 5;
|
||||
|
||||
match stat {
|
||||
Stat::PhysicalDamage => self.phys_dmg.set(rng.gen_range(stat_min, stat_max)),
|
||||
Stat::SpellDamage => self.spell_dmg.set(rng.gen_range(stat_min, stat_max)),
|
||||
Stat::Speed => self.speed.set(rng.gen_range(stat_min, stat_max)),
|
||||
Stat::PhysicalDamage => self.phys_dmg.set(rng.gen_range(stat_min, stat_max), &self.specs),
|
||||
Stat::SpellDamage => self.spell_dmg.set(rng.gen_range(stat_min, stat_max), &self.specs),
|
||||
Stat::Speed => self.speed.set(rng.gen_range(stat_min, stat_max), &self.specs),
|
||||
Stat::Stamina => {
|
||||
self.stamina.set(rng.gen_range(stam_min, stam_max));
|
||||
self.hp.set(self.stamina.base)
|
||||
self.stamina.set(rng.gen_range(stam_min, stam_max), &self.specs);
|
||||
self.hp.set(self.stamina.base, &self.specs)
|
||||
},
|
||||
Stat::SpellShield => self.spell_shield.set(rng.gen_range(stat_min, stat_max)),
|
||||
Stat::Armour => self.armour.set(rng.gen_range(stat_min, stat_max)),
|
||||
Stat::Evasion => self.evasion.set(rng.gen_range(evasion_min, evasion_max)),
|
||||
Stat::SpellShield => self.spell_shield.set(rng.gen_range(stat_min, stat_max), &self.specs),
|
||||
Stat::Armour => self.armour.set(rng.gen_range(stat_min, stat_max), &self.specs),
|
||||
Stat::Evasion => self.evasion.set(rng.gen_range(evasion_min, evasion_max), &self.specs),
|
||||
_ => panic!("{:?} not a rollable stat", stat),
|
||||
};
|
||||
|
||||
@ -250,8 +270,54 @@ impl Cryp {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn spec_apply(&mut self, spec: Spec) -> Result<&mut Cryp, Error> {
|
||||
let max_common = 20;
|
||||
let max_uncommon = 10;
|
||||
let max_rare = 5;
|
||||
|
||||
match spec.level {
|
||||
SpecLevel::Common => {
|
||||
if self.specs.common.len() >= max_common {
|
||||
return Err(format_err!("cryp at maximum common specalisations ({:})", max_common))
|
||||
}
|
||||
|
||||
self.specs.common.push(spec);
|
||||
},
|
||||
SpecLevel::Uncommon => {
|
||||
if self.specs.uncommon.len() >= max_uncommon {
|
||||
return Err(format_err!("cryp at maximum uncommon specalisations ({:})", max_uncommon))
|
||||
}
|
||||
|
||||
self.specs.uncommon.push(spec);
|
||||
},
|
||||
SpecLevel::Rare => {
|
||||
if self.specs.rare.len() >= max_rare {
|
||||
return Err(format_err!("cryp at maximum rare specalisations ({:})", max_rare))
|
||||
}
|
||||
|
||||
// check dupes
|
||||
self.specs.rare.push(spec);
|
||||
},
|
||||
};
|
||||
|
||||
return Ok(self.recalculate_stats());
|
||||
}
|
||||
|
||||
fn recalculate_stats(&mut self) -> &mut Cryp {
|
||||
self.stamina.recalculate(&self.specs);
|
||||
self.hp.recalculate(&self.specs);
|
||||
self.phys_dmg.recalculate(&self.specs);
|
||||
self.spell_dmg.recalculate(&self.specs);
|
||||
self.evasion.recalculate(&self.specs);
|
||||
self.armour.recalculate(&self.specs);
|
||||
self.spell_shield.recalculate(&self.specs);
|
||||
self.speed.recalculate(&self.specs);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_ko(&self) -> bool {
|
||||
self.hp.base == 0
|
||||
self.hp.value == 0
|
||||
}
|
||||
|
||||
pub fn immune(&self, skill: Skill) -> Immunity {
|
||||
@ -383,7 +449,7 @@ impl Cryp {
|
||||
.map(|cryp_effect| cryp_effect.effect)
|
||||
.collect::<Vec<Effect>>();
|
||||
|
||||
let modified_phys_dmg = phys_dmg_mods.iter().fold(self.phys_dmg.base, |acc, m| m.apply(acc));
|
||||
let modified_phys_dmg = phys_dmg_mods.iter().fold(self.phys_dmg.value, |acc, m| m.apply(acc));
|
||||
return modified_phys_dmg;
|
||||
}
|
||||
|
||||
@ -393,7 +459,7 @@ impl Cryp {
|
||||
.map(|cryp_effect| cryp_effect.effect)
|
||||
.collect::<Vec<Effect>>();
|
||||
|
||||
let modified_spell_dmg = spell_dmg_mods.iter().fold(self.spell_dmg.base, |acc, m| m.apply(acc));
|
||||
let modified_spell_dmg = spell_dmg_mods.iter().fold(self.spell_dmg.value, |acc, m| m.apply(acc));
|
||||
return modified_spell_dmg;
|
||||
}
|
||||
|
||||
@ -407,16 +473,16 @@ impl Cryp {
|
||||
.map(|cryp_effect| cryp_effect.effect)
|
||||
.collect::<Vec<Effect>>();
|
||||
|
||||
let modified_speed = speed_mods.iter().fold(self.speed.base, |acc, m| m.apply(acc));
|
||||
let modified_speed = speed_mods.iter().fold(self.speed.value, |acc, m| m.apply(acc));
|
||||
return modified_speed;
|
||||
}
|
||||
|
||||
pub fn hp(&self) -> u64 {
|
||||
self.hp.base
|
||||
self.hp.value
|
||||
}
|
||||
|
||||
pub fn stamina(&self) -> u64 {
|
||||
self.stamina.base
|
||||
self.stamina.value
|
||||
}
|
||||
|
||||
pub fn heal(&mut self, skill: Skill, amount: u64) -> ResolutionResult {
|
||||
@ -450,7 +516,7 @@ impl Cryp {
|
||||
let healing = new_hp - current_hp;
|
||||
let overhealing = amount - healing;
|
||||
|
||||
self.hp.set(new_hp);
|
||||
self.hp.increase(healing);
|
||||
|
||||
return ResolutionResult::Healing {
|
||||
amount: healing,
|
||||
@ -486,7 +552,7 @@ impl Cryp {
|
||||
// eg 50 armour 25 dmg -> 0 remainder 25 mitigation
|
||||
// 50 armour 100 dmg -> 50 remainder 50 mitigation
|
||||
// 50 armour 5 dmg -> 0 remainder 5 mitigation
|
||||
let remainder = modified_dmg.saturating_sub(self.armour.base);
|
||||
let remainder = modified_dmg.saturating_sub(self.armour.value);
|
||||
let mitigation = modified_dmg.saturating_sub(remainder);
|
||||
|
||||
// reduce armour by mitigation amount
|
||||
@ -524,7 +590,7 @@ impl Cryp {
|
||||
// println!("{:?}", spell_dmg_mods);
|
||||
|
||||
let modified_dmg = spell_dmg_mods.iter().fold(amount, |acc, m| m.apply(acc));
|
||||
let remainder = modified_dmg.saturating_sub(self.armour.base);
|
||||
let remainder = modified_dmg.saturating_sub(self.armour.value);
|
||||
let mitigation = modified_dmg.saturating_sub(remainder);
|
||||
|
||||
self.armour.reduce(mitigation);
|
||||
@ -550,6 +616,7 @@ impl Cryp {
|
||||
};
|
||||
|
||||
if !immune {
|
||||
// println!("{:?} {:?} adding effect", self.name, effect.effect);
|
||||
self.effects.push(effect);
|
||||
}
|
||||
|
||||
@ -557,13 +624,13 @@ impl Cryp {
|
||||
}
|
||||
|
||||
pub fn evade(&self, skill: Skill) -> Option<ResolutionResult> {
|
||||
if self.evasion.base == 0 {
|
||||
if self.evasion.value == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut rng = thread_rng();
|
||||
let hp_pct = (self.hp.base * 100) / self.stamina.base;
|
||||
let evasion_rating = (self.evasion.base * hp_pct) / 100;
|
||||
let hp_pct = (self.hp.value * 100) / self.stamina.value;
|
||||
let evasion_rating = (self.evasion.value * hp_pct) / 100;
|
||||
let roll = rng.gen_range(0, 100);
|
||||
println!("{:} < {:?}", roll, evasion_rating);
|
||||
|
||||
|
||||
@ -1014,12 +1014,12 @@ mod tests {
|
||||
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().phys_dmg.set(u64::max_value());
|
||||
game.team_by_id(y_team.id).cryp_by_id(y_cryp.id).unwrap().speed.set(u64::max_value());
|
||||
game.team_by_id(y_team.id).cryp_by_id(y_cryp.id).unwrap().phys_dmg.force(u64::max_value());
|
||||
game.team_by_id(y_team.id).cryp_by_id(y_cryp.id).unwrap().speed.force(u64::max_value());
|
||||
|
||||
// just in case
|
||||
// remove all mitigation
|
||||
game.team_by_id(x_team.id).cryp_by_id(x_cryp.id).unwrap().armour.set(0);
|
||||
game.team_by_id(x_team.id).cryp_by_id(x_cryp.id).unwrap().armour.force(0);
|
||||
|
||||
let _x_stun_id = game.add_skill(x_team.id, x_cryp.id, Some(y_cryp.id), Skill::TestStun).unwrap();
|
||||
game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::Attack).unwrap();
|
||||
|
||||
@ -13,6 +13,7 @@ use account::Account;
|
||||
use rpc::{ItemUseParams};
|
||||
use cryp::{Stat, cryp_get, cryp_write};
|
||||
use game::{GameMode};
|
||||
use spec::{Spec, SpecType};
|
||||
|
||||
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
||||
pub enum ItemAction {
|
||||
@ -23,6 +24,8 @@ pub enum ItemAction {
|
||||
RerollArmour,
|
||||
RerollSpellShield,
|
||||
RerollEvasion,
|
||||
SpecPhysDmg5,
|
||||
SpecSpellDmg5,
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
||||
@ -52,16 +55,20 @@ impl Item {
|
||||
ItemAction::RerollArmour => reroll(self, tx, target, Stat::Armour),
|
||||
ItemAction::RerollSpellShield => reroll(self, tx, target, Stat::SpellShield),
|
||||
ItemAction::RerollEvasion => reroll(self, tx, target, Stat::Evasion),
|
||||
|
||||
ItemAction::SpecPhysDmg5 => spec_apply(self, tx, target, SpecType::PhysDamage5),
|
||||
ItemAction::SpecSpellDmg5 => spec_apply(self, tx, target, SpecType::SpellDamage5),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fn revive(item: &mut Item, tx: &mut Transaction, target: Uuid) -> Result<(), Error> {
|
||||
// let mut cryp = cryp_get(tx, target, item.account)?;
|
||||
// cryp.rez();
|
||||
// cryp_write(cryp, tx)?;
|
||||
// return Ok(());
|
||||
// }
|
||||
fn spec_apply(item: &mut Item, tx: &mut Transaction, target: Uuid, spec_type: SpecType) -> Result<(), Error> {
|
||||
let mut cryp = cryp_get(tx, target, item.account)?;
|
||||
let spec = Spec::new(spec_type);
|
||||
cryp.spec_apply(spec)?;
|
||||
cryp_write(cryp, tx)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn reroll(item: &mut Item, tx: &mut Transaction, target: Uuid, stat: Stat) -> Result<(), Error> {
|
||||
let mut cryp = cryp_get(tx, target, item.account)?;
|
||||
|
||||
@ -260,7 +260,7 @@ impl Effect {
|
||||
|
||||
pub fn duration(&self) -> u8 {
|
||||
match self {
|
||||
Effect::Stun => 1,
|
||||
Effect::Stun => 2,
|
||||
Effect::Block => 1,
|
||||
Effect::Parry => 1,
|
||||
|
||||
@ -1051,8 +1051,8 @@ mod tests {
|
||||
.create();
|
||||
|
||||
// ensure it doesn't have 0 pd
|
||||
x.phys_dmg.set(100);
|
||||
y.hp.set(500);
|
||||
x.phys_dmg.force(100);
|
||||
y.hp.force(500);
|
||||
|
||||
block(&mut y.clone(), &mut y, Resolution::new(Skill::Block));
|
||||
assert!(y.effects.iter().any(|e| e.effect == Effect::Block));
|
||||
@ -1078,11 +1078,11 @@ mod tests {
|
||||
.create();
|
||||
|
||||
// ensure it doesn't have 0 sd
|
||||
x.spell_dmg.set(50);
|
||||
x.spell_dmg.force(50);
|
||||
|
||||
// remove all mitigation
|
||||
y.armour.set(0);
|
||||
y.spell_shield.set(0);
|
||||
y.armour.force(0);
|
||||
y.spell_shield.force(0);
|
||||
|
||||
y.deal_phys_dmg(Skill::Attack, 5);
|
||||
let prev_hp = y.hp();
|
||||
@ -1118,7 +1118,7 @@ mod tests {
|
||||
.level(8)
|
||||
.create();
|
||||
|
||||
x.spell_dmg.set(50);
|
||||
x.spell_dmg.force(50);
|
||||
|
||||
amplify(&mut x.clone(), &mut x, Resolution::new(Skill::Amplify));
|
||||
assert!(x.effects.iter().any(|e| e.effect == Effect::Amplify));
|
||||
|
||||
54
server/src/spec.rs
Normal file
54
server/src/spec.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use cryp::{Stat};
|
||||
|
||||
#[derive(Debug,Copy,Clone,Serialize,Deserialize)]
|
||||
pub enum SpecLevel {
|
||||
Common,
|
||||
Uncommon,
|
||||
Rare,
|
||||
}
|
||||
|
||||
#[derive(Debug,Copy,Clone,Serialize,Deserialize)]
|
||||
pub struct Spec {
|
||||
pub affects: Stat,
|
||||
pub spec: SpecType,
|
||||
pub level: SpecLevel,
|
||||
}
|
||||
|
||||
impl Spec {
|
||||
pub fn new(spec_type: SpecType) -> Spec {
|
||||
Spec {
|
||||
affects: spec_type.affects(),
|
||||
level: spec_type.level(),
|
||||
spec: spec_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply(&self, modified: u64, base: u64) -> u64 {
|
||||
match self.spec {
|
||||
SpecType::PhysDamage5 => modified + ( base * 105 / 100),
|
||||
SpecType::SpellDamage5 => modified + ( base * 105 / 100),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,Copy,Clone,Serialize,Deserialize)]
|
||||
pub enum SpecType {
|
||||
PhysDamage5,
|
||||
SpellDamage5,
|
||||
}
|
||||
|
||||
impl SpecType {
|
||||
fn affects(&self) -> Stat {
|
||||
match *self {
|
||||
SpecType::PhysDamage5 => Stat::PhysicalDamage,
|
||||
SpecType::SpellDamage5 => Stat::SpellDamage,
|
||||
}
|
||||
}
|
||||
|
||||
fn level(&self) -> SpecLevel {
|
||||
match *self {
|
||||
SpecType::PhysDamage5 => SpecLevel::Common,
|
||||
SpecType::SpellDamage5 => SpecLevel::Common,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -412,6 +412,6 @@ mod tests {
|
||||
fn zone_joinable_test() {
|
||||
let graph = create_zone_graph();
|
||||
assert!(node_joinable(&graph, NodeIndex::from(1)).is_ok());
|
||||
assert!(node_joinable(&graph, NodeIndex::from(2)).is_err());
|
||||
assert!(node_joinable(&graph, NodeIndex::from(graph.node_count() as u32 - 1)).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user