diff --git a/assets/common/items/debug/admin.ron b/assets/common/items/debug/admin.ron index 3eeed622c8..27126699d7 100644 --- a/assets/common/items/debug/admin.ron +++ b/assets/common/items/debug/admin.ron @@ -7,6 +7,10 @@ ItemDef( stats: ( protection: Invincible, poise_resilience: Invincible, + energy_max: 9000, + energy_recovery: 9.0, + crit_chance: 1000.0, + stealth: 1000.0, ), ) ), diff --git a/common/src/combat.rs b/common/src/combat.rs index 4755f3d170..8405025725 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -56,6 +56,7 @@ pub struct AttackerInfo<'a> { pub uid: Uid, pub energy: Option<&'a Energy>, pub combo: Option<&'a Combo>, + pub inventory: Option<&'a Inventory>, } #[cfg(not(target_arch = "wasm32"))] @@ -213,11 +214,13 @@ impl Attack { } }, CombatEffect::EnergyReward(ec) => { - if let Some(attacker_entity) = attacker.map(|a| a.entity) { + if let Some(attacker) = attacker { emit(ServerEvent::EnergyChange { - entity: attacker_entity, + entity: attacker.entity, change: EnergyChange { - amount: *ec as i32, + amount: (*ec + * Energy::compute_energy_reward_mod(attacker.inventory)) + as i32, source: EnergySource::HitEnemy, }, }); @@ -348,11 +351,13 @@ impl Attack { } }, CombatEffect::EnergyReward(ec) => { - if let Some(attacker_entity) = attacker.map(|a| a.entity) { + if let Some(attacker) = attacker { emit(ServerEvent::EnergyChange { - entity: attacker_entity, + entity: attacker.entity, change: EnergyChange { - amount: ec as i32, + amount: (ec + * Energy::compute_energy_reward_mod(attacker.inventory)) + as i32, source: EnergySource::HitEnemy, }, }); diff --git a/common/src/comp/energy.rs b/common/src/comp/energy.rs index e540cb6ad5..a10546e038 100644 --- a/common/src/comp/energy.rs +++ b/common/src/comp/energy.rs @@ -1,4 +1,4 @@ -use crate::comp::Body; +use crate::comp::{self, Body, Inventory}; use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage}; use specs_idvs::IdvStorage; @@ -120,6 +120,24 @@ impl Energy { self.base_max = amount; self.current = self.current.min(self.maximum); } + + /// Computes the energy reward modifer from worn armor + pub fn compute_energy_reward_mod(inventory: Option<&Inventory>) -> f32 { + use comp::item::ItemKind; + // Starts with a value of 1.0 when summing the stats from each armor piece, and + // defaults to a value of 1.0 if no inventory is equipped + inventory.map_or(1.0, |inv| { + inv.equipped_items() + .filter_map(|item| { + if let ItemKind::Armor(armor) = &item.kind() { + Some(armor.get_energy_recovery()) + } else { + None + } + }) + .fold(1.0, |a, b| a + b) + }) + } } pub struct EnergyChange { diff --git a/common/src/comp/inventory/item/armor.rs b/common/src/comp/inventory/item/armor.rs index 19b0f42fd4..2d6ffe18b7 100644 --- a/common/src/comp/inventory/item/armor.rs +++ b/common/src/comp/inventory/item/armor.rs @@ -28,23 +28,52 @@ impl Armor { #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct Stats { + #[serde(default)] protection: Protection, + #[serde(default)] poise_resilience: Protection, + #[serde(default)] + energy_max: u32, + #[serde(default)] + energy_recovery: f32, + #[serde(default)] + crit_chance: f32, + #[serde(default)] + stealth: f32, } impl Stats { // DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING // Added for csv import of stats - pub fn new(protection: Protection, poise_resilience: Protection) -> Self { + pub fn new( + protection: Protection, + poise_resilience: Protection, + energy_max: u32, + energy_recovery: f32, + crit_chance: f32, + stealth: f32, + ) -> Self { Self { protection, poise_resilience, + energy_max, + energy_recovery, + crit_chance, + stealth, } } pub fn get_protection(&self) -> Protection { self.protection } pub fn get_poise_resilience(&self) -> Protection { self.poise_resilience } + + pub fn get_energy_max(&self) -> u32 { self.energy_max } + + pub fn get_energy_recovery(&self) -> f32 { self.energy_recovery } + + pub fn get_crit_chance(&self) -> f32 { self.crit_chance } + + pub fn get_stealth(&self) -> f32 { self.stealth } } impl Sub for Stats { @@ -54,6 +83,10 @@ impl Sub for Stats { Self { protection: self.protection - other.protection, poise_resilience: self.poise_resilience - other.poise_resilience, + energy_max: self.energy_max.saturating_sub(other.energy_max), + energy_recovery: self.energy_recovery - other.energy_recovery, + crit_chance: self.crit_chance - other.crit_chance, + stealth: self.stealth - other.stealth, } } } @@ -64,6 +97,10 @@ pub enum Protection { Normal(f32), } +impl Default for Protection { + fn default() -> Self { Self::Normal(0.0) } +} + impl Sub for Protection { type Output = Self; @@ -96,20 +133,20 @@ pub struct Armor { } impl Armor { - pub fn new(kind: ArmorKind, protection: Protection, poise_resilience: Protection) -> Self { - Self { - kind, - stats: Stats { - protection, - poise_resilience, - }, - } - } + pub fn new(kind: ArmorKind, stats: Stats) -> Self { Self { kind, stats } } pub fn get_protection(&self) -> Protection { self.stats.protection } pub fn get_poise_resilience(&self) -> Protection { self.stats.poise_resilience } + pub fn get_energy_max(&self) -> u32 { self.stats.energy_max } + + pub fn get_energy_recovery(&self) -> f32 { self.stats.energy_recovery } + + pub fn get_crit_chance(&self) -> f32 { self.stats.crit_chance } + + pub fn get_stealth(&self) -> f32 { self.stats.stealth } + #[cfg(test)] pub fn test_armor( kind: ArmorKind, diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 50f62365d5..a9ba8dedd2 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -64,6 +64,8 @@ pub struct Stats { pub power: f32, pub poise_strength: f32, pub speed: f32, + // Done for testing purposes, properly remove stat from weapons later + #[serde(skip)] pub crit_chance: f32, pub crit_mult: f32, } diff --git a/common/systems/src/beam.rs b/common/systems/src/beam.rs index 440f1d8550..c3373a3533 100644 --- a/common/systems/src/beam.rs +++ b/common/systems/src/beam.rs @@ -198,6 +198,7 @@ impl<'a> System<'a> for Sys { uid, energy: read_data.energies.get(entity), combo: read_data.combos.get(entity), + inventory: read_data.inventories.get(entity), }); let target_info = TargetInfo { diff --git a/common/systems/src/melee.rs b/common/systems/src/melee.rs index 78d77a3e93..561b9a4b5b 100644 --- a/common/systems/src/melee.rs +++ b/common/systems/src/melee.rs @@ -146,6 +146,7 @@ impl<'a> System<'a> for Sys { uid: *uid, energy: read_data.energies.get(attacker), combo: read_data.combos.get(attacker), + inventory: read_data.inventories.get(attacker), }); let target_info = TargetInfo { diff --git a/common/systems/src/projectile.rs b/common/systems/src/projectile.rs index 443a8b84ba..a1bab8cd96 100644 --- a/common/systems/src/projectile.rs +++ b/common/systems/src/projectile.rs @@ -135,6 +135,7 @@ impl<'a> System<'a> for Sys { uid, energy: read_data.energies.get(entity), combo: read_data.combos.get(entity), + inventory: read_data.inventories.get(entity), } }); diff --git a/common/systems/src/shockwave.rs b/common/systems/src/shockwave.rs index 2b5c8fc812..d1d4fbb575 100644 --- a/common/systems/src/shockwave.rs +++ b/common/systems/src/shockwave.rs @@ -194,6 +194,7 @@ impl<'a> System<'a> for Sys { uid, energy: read_data.energies.get(entity), combo: read_data.combos.get(entity), + inventory: read_data.inventories.get(entity), }); let target_info = TargetInfo { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 5e6ed52000..0f07c4cfec 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -705,25 +705,18 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o RadiusEffect::Attack(attack) => { let energies = &ecs.read_storage::(); let combos = &ecs.read_storage::(); + let inventories = &ecs.read_storage::(); for ( entity_b, pos_b, health_b, - ( - body_b_maybe, - inventory_b_maybe, - stats_b_maybe, - ori_b_maybe, - char_state_b_maybe, - uid_b, - ), + (body_b_maybe, stats_b_maybe, ori_b_maybe, char_state_b_maybe, uid_b), ) in ( &ecs.entities(), &ecs.read_storage::(), &ecs.read_storage::(), ( ecs.read_storage::().maybe(), - ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), @@ -767,12 +760,13 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o uid, energy: energies.get(entity), combo: combos.get(entity), + inventory: inventories.get(entity), }); let target_info = combat::TargetInfo { entity: entity_b, uid: *uid_b, - inventory: inventory_b_maybe, + inventory: inventories.get(entity_b), stats: stats_b_maybe, health: Some(health_b), pos: pos_b.0,