diff --git a/assets/common/items/armor/chest/cultist_chest_blue.ron b/assets/common/items/armor/chest/cultist_chest_blue.ron index ddb8dcac4c..8b1a34da39 100644 --- a/assets/common/items/armor/chest/cultist_chest_blue.ron +++ b/assets/common/items/armor/chest/cultist_chest_blue.ron @@ -5,7 +5,7 @@ Item( ( kind: Chest(CultistBlue), stats: ( - protection: Normal(5.0), + protection: Normal(30.0), ), ) ), diff --git a/assets/common/items/armor/chest/cultist_chest_purple.ron b/assets/common/items/armor/chest/cultist_chest_purple.ron index d043e49512..e35a727d6b 100644 --- a/assets/common/items/armor/chest/cultist_chest_purple.ron +++ b/assets/common/items/armor/chest/cultist_chest_purple.ron @@ -5,7 +5,7 @@ Item( ( kind: Chest(CultistPurple), stats: ( - protection: Normal(5.0), + protection: Normal(30.0), ), ) ), diff --git a/assets/common/items/armor/foot/cultist_boots.ron b/assets/common/items/armor/foot/cultist_boots.ron index d24453912e..2f9453db96 100644 --- a/assets/common/items/armor/foot/cultist_boots.ron +++ b/assets/common/items/armor/foot/cultist_boots.ron @@ -5,7 +5,7 @@ Item( ( kind: Foot(Cultist), stats: ( - protection: Normal(1.0), + protection: Normal(6.0), ), ) ), diff --git a/assets/common/items/armor/hand/cultist_hands_blue.ron b/assets/common/items/armor/hand/cultist_hands_blue.ron index 32875de0cd..c4c88cd131 100644 --- a/assets/common/items/armor/hand/cultist_hands_blue.ron +++ b/assets/common/items/armor/hand/cultist_hands_blue.ron @@ -5,7 +5,7 @@ Item( ( kind: Hand(CultistBlue), stats: ( - protection: Normal(2.0), + protection: Normal(12.0), ), ) ), diff --git a/assets/common/items/armor/hand/cultist_hands_purple.ron b/assets/common/items/armor/hand/cultist_hands_purple.ron index 3df26460d4..46e1f672d7 100644 --- a/assets/common/items/armor/hand/cultist_hands_purple.ron +++ b/assets/common/items/armor/hand/cultist_hands_purple.ron @@ -5,7 +5,7 @@ Item( ( kind: Hand(CultistPurple), stats: ( - protection: Normal(2.0), + protection: Normal(12.0), ), ) ), diff --git a/assets/common/items/armor/pants/cultist_legs_blue.ron b/assets/common/items/armor/pants/cultist_legs_blue.ron index 33fe4c2826..aabf7b54a6 100644 --- a/assets/common/items/armor/pants/cultist_legs_blue.ron +++ b/assets/common/items/armor/pants/cultist_legs_blue.ron @@ -5,7 +5,7 @@ Item( ( kind: Pants(CultistBlue), stats: ( - protection: Normal(3.0), + protection: Normal(24.0), ), ) ), diff --git a/assets/common/items/armor/pants/cultist_legs_purple.ron b/assets/common/items/armor/pants/cultist_legs_purple.ron index a6e7cdb7a5..a2609e55c9 100644 --- a/assets/common/items/armor/pants/cultist_legs_purple.ron +++ b/assets/common/items/armor/pants/cultist_legs_purple.ron @@ -5,7 +5,7 @@ Item( ( kind: Pants(CultistPurple), stats: ( - protection: Normal(3.0), + protection: Normal(24.0), ), ) ), diff --git a/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron b/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron index a7df2169aa..aa1125a7ac 100644 --- a/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron +++ b/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron @@ -5,7 +5,7 @@ Item( ( kind: Shoulder(CultistBlue), stats: ( - protection: Normal(3.0), + protection: Normal(18.0), ), ) ), diff --git a/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron b/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron index 3d01973bff..52953b9bdb 100644 --- a/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron +++ b/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron @@ -5,7 +5,7 @@ Item( ( kind: Shoulder(CultistPurple), stats: ( - protection: Normal(3.0), + protection: Normal(18.0), ), ) ), diff --git a/common/src/comp/damage.rs b/common/src/comp/damage.rs new file mode 100644 index 0000000000..e2e6449975 --- /dev/null +++ b/common/src/comp/damage.rs @@ -0,0 +1,77 @@ +use crate::comp::Loadout; +use serde::{Deserialize, Serialize}; + +pub const BLOCK_EFFICIENCY: f32 = 0.9; + +pub struct Damage { + pub healthchange: f32, + pub source: DamageSource, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum DamageSource { + Melee, + Healing, + Projectile, + Explosion, + Falling, +} + +impl Damage { + pub fn modify_damage(&mut self, block: bool, loadout: &Loadout) { + match self.source { + DamageSource::Melee => { + // Critical hit + if rand::random() { + self.healthchange *= 1.2; + } + // Block + if block { + self.healthchange *= 1.0 - BLOCK_EFFICIENCY + } + // Armor + self.healthchange *= 1.0 - loadout.get_damage_reduction(); + + // Min damage + if self.healthchange > -1.0 { + self.healthchange = -1.0; + } + }, + DamageSource::Projectile => { + // Critical hit + if rand::random() { + self.healthchange *= 1.2; + } + // Block + if block { + self.healthchange *= 1.0 - BLOCK_EFFICIENCY + } + // Armor + self.healthchange *= 1.0 - loadout.get_damage_reduction(); + + // Min damage + if self.healthchange > -1.0 { + self.healthchange = -1.0; + } + }, + DamageSource::Explosion => { + // Critical hit + if rand::random() { + self.healthchange *= 1.2; + } + // Block + if block { + self.healthchange *= 1.0 - BLOCK_EFFICIENCY + } + // Armor + self.healthchange *= 1.0 - loadout.get_damage_reduction(); + + // Min damage + if self.healthchange > -1.0 { + self.healthchange = -1.0; + } + }, + _ => {}, + } + } +} diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 4409551306..3c92e7c32b 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -2,8 +2,7 @@ // version in voxygen\src\meta.rs in order to reset save files to being empty use crate::comp::{ - body::object, projectile, Body, CharacterAbility, Gravity, HealthChange, HealthSource, - LightEmitter, Projectile, + body::object, projectile, Body, CharacterAbility, Gravity, LightEmitter, Projectile, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -262,11 +261,7 @@ impl Tool { projectile: Projectile { hit_solid: vec![projectile::Effect::Stick], hit_entity: vec![ - projectile::Effect::Damage(HealthChange { - // TODO: This should not be fixed (?) - amount: -3, - cause: HealthSource::Projectile { owner: None }, - }), + projectile::Effect::Damage(-3), projectile::Effect::Knockback(10.0), projectile::Effect::RewardEnergy(100), projectile::Effect::Vanish, @@ -286,11 +281,7 @@ impl Tool { projectile: Projectile { hit_solid: vec![projectile::Effect::Stick], hit_entity: vec![ - projectile::Effect::Damage(HealthChange { - // TODO: This should not be fixed (?) - amount: -9, - cause: HealthSource::Projectile { owner: None }, - }), + projectile::Effect::Damage(-9), projectile::Effect::Knockback(15.0), projectile::Effect::RewardEnergy(50), projectile::Effect::Vanish, @@ -336,11 +327,7 @@ impl Tool { projectile: Projectile { hit_solid: vec![projectile::Effect::Vanish], hit_entity: vec![ - projectile::Effect::Damage(HealthChange { - // TODO: This should not be fixed (?) - amount: -3, - cause: HealthSource::Projectile { owner: None }, - }), + projectile::Effect::Damage(-3), projectile::Effect::RewardEnergy(150), projectile::Effect::Vanish, ], diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 8ab32575f6..b99b575450 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -5,6 +5,7 @@ mod body; mod character_state; mod chat; mod controller; +mod damage; mod energy; mod inputs; mod inventory; @@ -32,6 +33,7 @@ pub use controller::{ Climb, ControlAction, ControlEvent, Controller, ControllerInputs, Input, InventoryManip, MountState, Mounting, }; +pub use damage::{Damage, DamageSource}; pub use energy::{Energy, EnergySource}; pub use inputs::CanBuild; pub use inventory::{ diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index d029075850..8346b7cf52 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -1,4 +1,4 @@ -use crate::{comp, sync::Uid}; +use crate::sync::Uid; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; @@ -6,7 +6,7 @@ use std::time::Duration; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Effect { - Damage(comp::HealthChange), + Damage(i32), Knockback(f32), RewardEnergy(u32), Explode { power: f32 }, @@ -25,21 +25,6 @@ pub struct Projectile { pub owner: Option, } -impl Projectile { - pub fn set_owner(&mut self, new_owner: Uid) { - self.owner = Some(new_owner); - for e in self.hit_solid.iter_mut().chain(self.hit_entity.iter_mut()) { - if let Effect::Damage(comp::HealthChange { - cause: comp::HealthSource::Projectile { owner, .. }, - .. - }) = e - { - *owner = Some(new_owner); - } - } - } -} - impl Component for Projectile { type Storage = FlaggedStorage>; } diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index 5b1f7cf0d7..b41bd6c208 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -54,7 +54,7 @@ impl CharacterBehavior for Data { } else if !self.exhausted { // Fire let mut projectile = self.projectile.clone(); - projectile.set_owner(*data.uid); + projectile.owner = Some(*data.uid); update.server_events.push_front(ServerEvent::Shoot { entity: data.entity, dir: data.inputs.look_dir, diff --git a/common/src/sys/combat.rs b/common/src/sys/combat.rs index f11714b23b..d585171b0d 100644 --- a/common/src/sys/combat.rs +++ b/common/src/sys/combat.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ - Alignment, Attacking, Body, CharacterState, HealthChange, HealthSource, Loadout, Ori, Pos, - Scale, Stats, + Alignment, Attacking, Body, CharacterState, Damage, DamageSource, HealthChange, + HealthSource, Loadout, Ori, Pos, Scale, Stats, }, event::{EventBus, LocalEvent, ServerEvent}, sync::Uid, @@ -112,7 +112,15 @@ impl<'a> System<'a> for Sys { && ori2.angle_between(pos_b2 - pos2) < attack.max_angle + (rad_b / pos2.distance(pos_b2)).atan() { // Weapon gives base damage - let mut healthchange = attack.base_healthchange as f32; + let source = if attack.base_healthchange > 0 { + DamageSource::Healing + } else { + DamageSource::Melee + }; + let mut damage = Damage { + healthchange: attack.base_healthchange as f32, + source, + }; let mut knockback = attack.knockback; // TODO: remove this, either it will remain unused or be used as a temporary @@ -124,40 +132,30 @@ impl<'a> System<'a> for Sys { // TODO: remove this when there is a better way to deal with alignment // Don't heal NPCs - if (healthchange > 0.0 && alignment_b_maybe + if (damage.healthchange > 0.0 && alignment_b_maybe .map(|a| !a.is_friendly_to_players()) .unwrap_or(true)) // Don't hurt pets - || (healthchange < 0.0 && alignment_b_maybe + || (damage.healthchange < 0.0 && alignment_b_maybe .map(|b| Alignment::Owned(*uid).passive_towards(*b)) .unwrap_or(false)) { - healthchange = 0.0; + damage.healthchange = 0.0; knockback = 0.0; } - if rand::random() { - healthchange *= 1.2; - } + let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false) + && ori_b.0.angle_between(pos.0 - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0; - // Block - if character_b.map(|c_b| c_b.is_block()).unwrap_or(false) - && ori_b.0.angle_between(pos.0 - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0 - { - healthchange *= 1.0 - BLOCK_EFFICIENCY - } - - // Armor if let Some(loadout) = loadouts.get(b) { - let damage_reduction = loadout.get_damage_reduction(); - healthchange *= 1.0 - damage_reduction; + damage.modify_damage(block, loadout); } - if healthchange != 0.0 { + if damage.healthchange != 0.0 { server_emitter.emit(ServerEvent::Damage { uid: *uid_b, change: HealthChange { - amount: healthchange as i32, + amount: damage.healthchange as i32, cause: HealthSource::Attack { by: *uid }, }, }); @@ -165,7 +163,7 @@ impl<'a> System<'a> for Sys { if knockback != 0.0 { local_emitter.emit(LocalEvent::ApplyForce { entity: b, - force: attack.knockback + force: knockback * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5), }); } diff --git a/common/src/sys/projectile.rs b/common/src/sys/projectile.rs index 306b0bab10..ad2e4a1023 100644 --- a/common/src/sys/projectile.rs +++ b/common/src/sys/projectile.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ - projectile, Alignment, Energy, EnergySource, HealthSource, Ori, PhysicsState, Pos, - Projectile, Vel, + projectile, Alignment, Damage, DamageSource, Energy, EnergySource, HealthChange, + HealthSource, Loadout, Ori, PhysicsState, Pos, Projectile, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, state::DeltaTime, @@ -29,6 +29,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Projectile>, WriteStorage<'a, Energy>, ReadStorage<'a, Alignment>, + ReadStorage<'a, Loadout>, ); fn run( @@ -46,6 +47,7 @@ impl<'a> System<'a> for Sys { mut projectiles, mut energies, alignments, + loadouts, ): Self::SystemData, ) { let mut local_emitter = local_bus.emitter(); @@ -84,8 +86,19 @@ impl<'a> System<'a> for Sys { else if let Some(other) = physics.touch_entity { for effect in projectile.hit_entity.drain(..) { match effect { - projectile::Effect::Damage(change) => { + projectile::Effect::Damage(healthchange) => { let owner_uid = projectile.owner.unwrap(); + let mut damage = Damage { + healthchange: healthchange as f32, + source: DamageSource::Projectile, + }; + if let Some(entity) = + uid_allocator.retrieve_entity_internal(other.into()) + { + if let Some(loadout) = loadouts.get(entity) { + damage.modify_damage(false, loadout); + } + } // Hacky: remove this when groups get implemented let passive = uid_allocator .retrieve_entity_internal(other.into()) @@ -96,7 +109,13 @@ impl<'a> System<'a> for Sys { }) .unwrap_or(false); if other != projectile.owner.unwrap() && !passive { - server_emitter.emit(ServerEvent::Damage { uid: other, change }); + server_emitter.emit(ServerEvent::Damage { + uid: other, + change: HealthChange { + amount: damage.healthchange as i32, + cause: HealthSource::Attack { by: owner_uid }, + }, + }); } }, projectile::Effect::Knockback(knockback) => { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 454f8f95e9..521acfbed1 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1,11 +1,14 @@ use crate::{client::Client, Server, SpawnPoint, StateExt}; use common::{ assets, - comp::{self, item::lottery::Lottery, object, Body, HealthChange, HealthSource, Player, Stats}, + comp::{ + self, item::lottery::Lottery, object, Body, Damage, DamageSource, HealthChange, + HealthSource, Player, Stats, + }, msg::{PlayerListUpdate, ServerMsg}, state::BlockChange, sync::{Uid, WorldSyncExt}, - sys::combat::{BLOCK_ANGLE, BLOCK_EFFICIENCY}, + sys::combat::BLOCK_ANGLE, terrain::{Block, TerrainGrid}, vol::{ReadVol, Vox}, }; @@ -159,9 +162,16 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3) let state = &server.state; if vel.z <= -30.0 { if let Some(stats) = state.ecs().write_storage::().get_mut(entity) { - let falldmg = vel.z.powi(2) as i32 / 20 - 40; + let falldmg = vel.z.powi(2) / 20.0 - 40.0; + let mut damage = Damage { + healthchange: -falldmg, + source: DamageSource::Falling, + }; + if let Some(loadout) = state.ecs().read_storage::().get(entity) { + damage.modify_damage(false, loadout); + } stats.health.change_by(comp::HealthChange { - amount: -falldmg, + amount: damage.healthchange as i32, cause: comp::HealthSource::World, }); } @@ -211,11 +221,12 @@ pub fn handle_explosion(server: &Server, pos: Vec3, power: f32, owner: Opti // Go through all other entities let hit_range = 3.0 * power; let ecs = &server.state.ecs(); - for (pos_b, ori_b, character_b, stats_b) in ( + for (pos_b, ori_b, character_b, stats_b, loadout_b) in ( &ecs.read_storage::(), &ecs.read_storage::(), ecs.read_storage::().maybe(), &mut ecs.write_storage::(), + ecs.read_storage::().maybe(), ) .join() { @@ -227,21 +238,22 @@ pub fn handle_explosion(server: &Server, pos: Vec3, power: f32, owner: Opti && distance_squared < hit_range.powi(2) { // Weapon gives base damage - let mut dmg = ((1.0 - distance_squared / hit_range.powi(2)) * power * 10.0) as u32; + let dmg = (1.0 - distance_squared / hit_range.powi(2)) * power * 10.0; - if rand::random() { - dmg += 1; - } + let mut damage = Damage { + healthchange: -dmg, + source: DamageSource::Explosion, + }; - // Block - if character_b.map(|c_b| c_b.is_block()).unwrap_or(false) - && ori_b.0.angle_between(pos - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0 - { - dmg = (dmg as f32 * (1.0 - BLOCK_EFFICIENCY)) as u32 + let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false) + && ori_b.0.angle_between(pos - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0; + + if let Some(loadout) = loadout_b { + damage.modify_damage(block, loadout); } stats_b.health.change_by(HealthChange { - amount: -(dmg as i32), + amount: damage.healthchange as i32, cause: HealthSource::Projectile { owner }, }); } diff --git a/server/src/migrations/2020-07-24-191205_fix_projectile_stats/down.sql b/server/src/migrations/2020-07-24-191205_fix_projectile_stats/down.sql new file mode 100644 index 0000000000..291a97c5ce --- /dev/null +++ b/server/src/migrations/2020-07-24-191205_fix_projectile_stats/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/server/src/migrations/2020-07-24-191205_fix_projectile_stats/up.sql b/server/src/migrations/2020-07-24-191205_fix_projectile_stats/up.sql new file mode 100644 index 0000000000..2bf12adfb9 --- /dev/null +++ b/server/src/migrations/2020-07-24-191205_fix_projectile_stats/up.sql @@ -0,0 +1,15 @@ +-- This migration adjusts projectiles in accordance with the changes made in MR 1222 +UPDATE + loadout +SET + items = json_replace( + items, + '$.active_item.ability1.BasicRanged.projectile.hit_entity[0]', + json('{"Damage": -3}'), + '$.active_item.ability2.BasicRanged.projectile.hit_entity[0]', + json('{"Damage": -3}'), + '$.second_item.ability1.BasicRanged.projectile.hit_entity[0]', + json('{"Damage": -3}'), + '$.second_item.ability2.BasicRanged.projectile.hit_entity[0]', + json('{"Damage": -3}') + ); \ No newline at end of file