diff --git a/CHANGELOG.md b/CHANGELOG.md index c81a1137d1..d2a842d4df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bombs - Training dummy items - Added spin attack for axe +- Creature specific stats ### Changed diff --git a/common/src/comp/stats.rs b/common/src/comp/stats.rs index 3ddf2fc108..98392efbd9 100644 --- a/common/src/comp/stats.rs +++ b/common/src/comp/stats.rs @@ -131,6 +131,81 @@ pub struct Stats { pub fitness: u32, pub willpower: u32, pub is_dead: bool, + pub body_type: Body, +} + +impl Body { + pub fn base_health(&self) -> u32 { + match self { + Body::Humanoid(_) => 52, + Body::QuadrupedSmall(_) => 44, + Body::QuadrupedMedium(_) => 72, + Body::BirdMedium(_) => 36, + Body::FishMedium(_) => 32, + Body::Dragon(_) => 256, + Body::BirdSmall(_) => 24, + Body::FishSmall(_) => 20, + Body::BipedLarge(_) => 144, + Body::Object(_) => 100, + Body::Golem(_) => 168, + Body::Critter(_) => 32, + Body::QuadrupedLow(_) => 64, + } + } + + pub fn base_health_increase(&self) -> u32 { + match self { + Body::Humanoid(_) => 5, + Body::QuadrupedSmall(_) => 4, + Body::QuadrupedMedium(_) => 7, + Body::BirdMedium(_) => 4, + Body::FishMedium(_) => 3, + Body::Dragon(_) => 26, + Body::BirdSmall(_) => 2, + Body::FishSmall(_) => 2, + Body::BipedLarge(_) => 14, + Body::Object(_) => 0, + Body::Golem(_) => 17, + Body::Critter(_) => 3, + Body::QuadrupedLow(_) => 6, + } + } + + pub fn base_exp(&self) -> u32 { + match self { + Body::Humanoid(_) => 15, + Body::QuadrupedSmall(_) => 12, + Body::QuadrupedMedium(_) => 28, + Body::BirdMedium(_) => 10, + Body::FishMedium(_) => 8, + Body::Dragon(_) => 160, + Body::BirdSmall(_) => 5, + Body::FishSmall(_) => 4, + Body::BipedLarge(_) => 75, + Body::Object(_) => 0, + Body::Golem(_) => 75, + Body::Critter(_) => 8, + Body::QuadrupedLow(_) => 24, + } + } + + pub fn base_exp_increase(&self) -> u32 { + match self { + Body::Humanoid(_) => 3, + Body::QuadrupedSmall(_) => 2, + Body::QuadrupedMedium(_) => 6, + Body::BirdMedium(_) => 2, + Body::FishMedium(_) => 2, + Body::Dragon(_) => 32, + Body::BirdSmall(_) => 1, + Body::FishSmall(_) => 1, + Body::BipedLarge(_) => 15, + Body::Object(_) => 0, + Body::Golem(_) => 15, + Body::Critter(_) => 2, + Body::QuadrupedLow(_) => 5, + } + } } impl Stats { @@ -143,7 +218,10 @@ impl Stats { } // TODO: Delete this once stat points will be a thing - pub fn update_max_hp(&mut self) { self.health.set_maximum(52 + 3 * self.level.amount); } + pub fn update_max_hp(&mut self, body: Body) { + self.health + .set_maximum(body.base_health() + body.base_health_increase() * self.level.amount); + } } impl Stats { @@ -187,9 +265,10 @@ impl Stats { fitness, willpower, is_dead: false, + body_type: body, }; - stats.update_max_hp(); + stats.update_max_hp(body); stats .health .set_to(stats.health.maximum(), HealthSource::Revive); diff --git a/common/src/loadout_builder.rs b/common/src/loadout_builder.rs index 48df447299..1ed2512b0a 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/loadout_builder.rs @@ -2,7 +2,7 @@ use crate::{ assets, comp::{ item::{Item, ItemKind}, - CharacterAbility, ItemConfig, Loadout, + Body, CharacterAbility, ItemConfig, Loadout, }, }; use std::time::Duration; @@ -24,6 +24,44 @@ use std::time::Duration; /// ``` pub struct LoadoutBuilder(Loadout); +impl Body { + pub fn base_dmg(&self) -> u32 { + match self { + Body::Humanoid(_) => 8, + Body::QuadrupedSmall(_) => 7, + Body::QuadrupedMedium(_) => 10, + Body::BirdMedium(_) => 6, + Body::FishMedium(_) => 5, + Body::Dragon(_) => 75, + Body::BirdSmall(_) => 4, + Body::FishSmall(_) => 3, + Body::BipedLarge(_) => 30, + Body::Object(_) => 0, + Body::Golem(_) => 30, + Body::Critter(_) => 6, + Body::QuadrupedLow(_) => 9, + } + } + + pub fn base_range(&self) -> f32 { + match self { + Body::Humanoid(_) => 5.0, + Body::QuadrupedSmall(_) => 4.5, + Body::QuadrupedMedium(_) => 5.5, + Body::BirdMedium(_) => 3.5, + Body::FishMedium(_) => 3.5, + Body::Dragon(_) => 12.5, + Body::BirdSmall(_) => 3.0, + Body::FishSmall(_) => 3.0, + Body::BipedLarge(_) => 10.0, + Body::Object(_) => 3.0, + Body::Golem(_) => 7.5, + Body::Critter(_) => 3.0, + Body::QuadrupedLow(_) => 4.5, + } + } +} + impl LoadoutBuilder { #[allow(clippy::new_without_default)] // TODO: Pending review in #587 pub fn new() -> Self { @@ -63,7 +101,7 @@ impl LoadoutBuilder { } /// Default animal configuration - pub fn animal() -> Self { + pub fn animal(body: Body) -> Self { Self(Loadout { active_item: Some(ItemConfig { item: assets::load_expect_cloned("common.items.weapons.empty"), @@ -71,8 +109,8 @@ impl LoadoutBuilder { energy_cost: 10, buildup_duration: Duration::from_millis(600), recover_duration: Duration::from_millis(100), - base_healthchange: -6, - range: 5.0, + base_healthchange: -(body.base_dmg() as i32), + range: body.base_range(), max_angle: 80.0, }), ability2: None, diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index 8f7d57b100..f6621cd34c 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -22,7 +22,7 @@ impl<'a> System<'a> for Sys { fn run( &mut self, - (entities, dt, server_event_bus, character_states, mut stats,mut energies): Self::SystemData, + (entities, dt, server_event_bus, character_states, mut stats, mut energies): Self::SystemData, ) { let mut server_event_emitter = server_event_bus.emitter(); @@ -62,7 +62,7 @@ impl<'a> System<'a> for Sys { server_event_emitter.emit(ServerEvent::LevelUp(entity, stat.level.level())); } - stat.update_max_hp(); + stat.update_max_hp(stat.body_type); stat.health .set_to(stat.health.maximum(), HealthSource::LevelUp); } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 3f6bac7169..2667cee3ac 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -544,7 +544,7 @@ fn handle_spawn( .create_npc( pos, comp::Stats::new(get_npc_name(id).into(), body), - LoadoutBuilder::animal().build(), + LoadoutBuilder::animal(body).build(), body, ) .with(comp::Vel(vel)) @@ -1568,7 +1568,7 @@ fn handle_set_level( { stats.level.set_level(lvl); - stats.update_max_hp(); + stats.update_max_hp(stats.body_type); stats .health .set_to(stats.health.maximum(), comp::HealthSource::LevelUp); diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 423b707753..454f8f95e9 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -63,9 +63,12 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc if let Some(attacker_stats) = stats.get_mut(attacker) { // TODO: Discuss whether we should give EXP by Player // Killing or not. - attacker_stats - .exp - .change_by((entity_stats.level.level() * 10) as i64); + attacker_stats.exp.change_by( + (entity_stats.body_type.base_exp() + + entity_stats.level.level() + * entity_stats.body_type.base_exp_increase()) + as i64, + ); } }); } diff --git a/server/src/persistence/models.rs b/server/src/persistence/models.rs index 1d7e3e7043..b5ad85b438 100644 --- a/server/src/persistence/models.rs +++ b/server/src/persistence/models.rs @@ -106,7 +106,7 @@ impl From> for comp::Stats { base_stats.exp.set_current(data.stats.exp as u32); - base_stats.update_max_hp(); + base_stats.update_max_hp(base_stats.body_type); base_stats .health .set_to(base_stats.health.maximum(), comp::HealthSource::Revive); diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 5a3b9c6a36..4c2e9e976f 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -215,7 +215,7 @@ impl<'a> System<'a> for Sys { head: None, tabard: None, }, - _ => LoadoutBuilder::animal().build(), + _ => LoadoutBuilder::animal(entity.body).build(), }; let mut scale = entity.scale; @@ -296,7 +296,7 @@ impl<'a> System<'a> for Sys { scale = 2.0 + rand::random::(); } - stats.update_max_hp(); + stats.update_max_hp(stats.body_type); stats .health