Merge branch 'sam/armor-fixes' into 'master'

Armor fixes

See merge request veloren/veloren!1222
This commit is contained in:
Imbris 2020-07-25 23:57:04 +00:00
commit 9156dbadb8
19 changed files with 181 additions and 85 deletions

View File

@ -5,7 +5,7 @@ Item(
( (
kind: Chest(CultistBlue), kind: Chest(CultistBlue),
stats: ( stats: (
protection: Normal(5.0), protection: Normal(30.0),
), ),
) )
), ),

View File

@ -5,7 +5,7 @@ Item(
( (
kind: Chest(CultistPurple), kind: Chest(CultistPurple),
stats: ( stats: (
protection: Normal(5.0), protection: Normal(30.0),
), ),
) )
), ),

View File

@ -5,7 +5,7 @@ Item(
( (
kind: Foot(Cultist), kind: Foot(Cultist),
stats: ( stats: (
protection: Normal(1.0), protection: Normal(6.0),
), ),
) )
), ),

View File

@ -5,7 +5,7 @@ Item(
( (
kind: Hand(CultistBlue), kind: Hand(CultistBlue),
stats: ( stats: (
protection: Normal(2.0), protection: Normal(12.0),
), ),
) )
), ),

View File

@ -5,7 +5,7 @@ Item(
( (
kind: Hand(CultistPurple), kind: Hand(CultistPurple),
stats: ( stats: (
protection: Normal(2.0), protection: Normal(12.0),
), ),
) )
), ),

View File

@ -5,7 +5,7 @@ Item(
( (
kind: Pants(CultistBlue), kind: Pants(CultistBlue),
stats: ( stats: (
protection: Normal(3.0), protection: Normal(24.0),
), ),
) )
), ),

View File

@ -5,7 +5,7 @@ Item(
( (
kind: Pants(CultistPurple), kind: Pants(CultistPurple),
stats: ( stats: (
protection: Normal(3.0), protection: Normal(24.0),
), ),
) )
), ),

View File

@ -5,7 +5,7 @@ Item(
( (
kind: Shoulder(CultistBlue), kind: Shoulder(CultistBlue),
stats: ( stats: (
protection: Normal(3.0), protection: Normal(18.0),
), ),
) )
), ),

View File

@ -5,7 +5,7 @@ Item(
( (
kind: Shoulder(CultistPurple), kind: Shoulder(CultistPurple),
stats: ( stats: (
protection: Normal(3.0), protection: Normal(18.0),
), ),
) )
), ),

77
common/src/comp/damage.rs Normal file
View File

@ -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;
}
},
_ => {},
}
}
}

View File

@ -2,8 +2,7 @@
// version in voxygen\src\meta.rs in order to reset save files to being empty // version in voxygen\src\meta.rs in order to reset save files to being empty
use crate::comp::{ use crate::comp::{
body::object, projectile, Body, CharacterAbility, Gravity, HealthChange, HealthSource, body::object, projectile, Body, CharacterAbility, Gravity, LightEmitter, Projectile,
LightEmitter, Projectile,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::time::Duration; use std::time::Duration;
@ -262,11 +261,7 @@ impl Tool {
projectile: Projectile { projectile: Projectile {
hit_solid: vec![projectile::Effect::Stick], hit_solid: vec![projectile::Effect::Stick],
hit_entity: vec![ hit_entity: vec![
projectile::Effect::Damage(HealthChange { projectile::Effect::Damage(-3),
// TODO: This should not be fixed (?)
amount: -3,
cause: HealthSource::Projectile { owner: None },
}),
projectile::Effect::Knockback(10.0), projectile::Effect::Knockback(10.0),
projectile::Effect::RewardEnergy(100), projectile::Effect::RewardEnergy(100),
projectile::Effect::Vanish, projectile::Effect::Vanish,
@ -286,11 +281,7 @@ impl Tool {
projectile: Projectile { projectile: Projectile {
hit_solid: vec![projectile::Effect::Stick], hit_solid: vec![projectile::Effect::Stick],
hit_entity: vec![ hit_entity: vec![
projectile::Effect::Damage(HealthChange { projectile::Effect::Damage(-9),
// TODO: This should not be fixed (?)
amount: -9,
cause: HealthSource::Projectile { owner: None },
}),
projectile::Effect::Knockback(15.0), projectile::Effect::Knockback(15.0),
projectile::Effect::RewardEnergy(50), projectile::Effect::RewardEnergy(50),
projectile::Effect::Vanish, projectile::Effect::Vanish,
@ -336,11 +327,7 @@ impl Tool {
projectile: Projectile { projectile: Projectile {
hit_solid: vec![projectile::Effect::Vanish], hit_solid: vec![projectile::Effect::Vanish],
hit_entity: vec![ hit_entity: vec![
projectile::Effect::Damage(HealthChange { projectile::Effect::Damage(-3),
// TODO: This should not be fixed (?)
amount: -3,
cause: HealthSource::Projectile { owner: None },
}),
projectile::Effect::RewardEnergy(150), projectile::Effect::RewardEnergy(150),
projectile::Effect::Vanish, projectile::Effect::Vanish,
], ],

View File

@ -5,6 +5,7 @@ mod body;
mod character_state; mod character_state;
mod chat; mod chat;
mod controller; mod controller;
mod damage;
mod energy; mod energy;
mod inputs; mod inputs;
mod inventory; mod inventory;
@ -32,6 +33,7 @@ pub use controller::{
Climb, ControlAction, ControlEvent, Controller, ControllerInputs, Input, InventoryManip, Climb, ControlAction, ControlEvent, Controller, ControllerInputs, Input, InventoryManip,
MountState, Mounting, MountState, Mounting,
}; };
pub use damage::{Damage, DamageSource};
pub use energy::{Energy, EnergySource}; pub use energy::{Energy, EnergySource};
pub use inputs::CanBuild; pub use inputs::CanBuild;
pub use inventory::{ pub use inventory::{

View File

@ -1,4 +1,4 @@
use crate::{comp, sync::Uid}; use crate::sync::Uid;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use specs::{Component, FlaggedStorage}; use specs::{Component, FlaggedStorage};
use specs_idvs::IdvStorage; use specs_idvs::IdvStorage;
@ -6,7 +6,7 @@ use std::time::Duration;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Effect { pub enum Effect {
Damage(comp::HealthChange), Damage(i32),
Knockback(f32), Knockback(f32),
RewardEnergy(u32), RewardEnergy(u32),
Explode { power: f32 }, Explode { power: f32 },
@ -25,21 +25,6 @@ pub struct Projectile {
pub owner: Option<Uid>, pub owner: Option<Uid>,
} }
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 { impl Component for Projectile {
type Storage = FlaggedStorage<Self, IdvStorage<Self>>; type Storage = FlaggedStorage<Self, IdvStorage<Self>>;
} }

View File

@ -54,7 +54,7 @@ impl CharacterBehavior for Data {
} else if !self.exhausted { } else if !self.exhausted {
// Fire // Fire
let mut projectile = self.projectile.clone(); let mut projectile = self.projectile.clone();
projectile.set_owner(*data.uid); projectile.owner = Some(*data.uid);
update.server_events.push_front(ServerEvent::Shoot { update.server_events.push_front(ServerEvent::Shoot {
entity: data.entity, entity: data.entity,
dir: data.inputs.look_dir, dir: data.inputs.look_dir,

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
comp::{ comp::{
Alignment, Attacking, Body, CharacterState, HealthChange, HealthSource, Loadout, Ori, Pos, Alignment, Attacking, Body, CharacterState, Damage, DamageSource, HealthChange,
Scale, Stats, HealthSource, Loadout, Ori, Pos, Scale, Stats,
}, },
event::{EventBus, LocalEvent, ServerEvent}, event::{EventBus, LocalEvent, ServerEvent},
sync::Uid, 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() && ori2.angle_between(pos_b2 - pos2) < attack.max_angle + (rad_b / pos2.distance(pos_b2)).atan()
{ {
// Weapon gives base damage // 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; let mut knockback = attack.knockback;
// TODO: remove this, either it will remain unused or be used as a temporary // 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 // TODO: remove this when there is a better way to deal with alignment
// Don't heal NPCs // 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()) .map(|a| !a.is_friendly_to_players())
.unwrap_or(true)) .unwrap_or(true))
// Don't hurt pets // 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)) .map(|b| Alignment::Owned(*uid).passive_towards(*b))
.unwrap_or(false)) .unwrap_or(false))
{ {
healthchange = 0.0; damage.healthchange = 0.0;
knockback = 0.0; knockback = 0.0;
} }
if rand::random() { let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false)
healthchange *= 1.2; && 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) { if let Some(loadout) = loadouts.get(b) {
let damage_reduction = loadout.get_damage_reduction(); damage.modify_damage(block, loadout);
healthchange *= 1.0 - damage_reduction;
} }
if healthchange != 0.0 { if damage.healthchange != 0.0 {
server_emitter.emit(ServerEvent::Damage { server_emitter.emit(ServerEvent::Damage {
uid: *uid_b, uid: *uid_b,
change: HealthChange { change: HealthChange {
amount: healthchange as i32, amount: damage.healthchange as i32,
cause: HealthSource::Attack { by: *uid }, cause: HealthSource::Attack { by: *uid },
}, },
}); });
@ -165,7 +163,7 @@ impl<'a> System<'a> for Sys {
if knockback != 0.0 { if knockback != 0.0 {
local_emitter.emit(LocalEvent::ApplyForce { local_emitter.emit(LocalEvent::ApplyForce {
entity: b, entity: b,
force: attack.knockback force: knockback
* *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5), * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5),
}); });
} }

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
comp::{ comp::{
projectile, Alignment, Energy, EnergySource, HealthSource, Ori, PhysicsState, Pos, projectile, Alignment, Damage, DamageSource, Energy, EnergySource, HealthChange,
Projectile, Vel, HealthSource, Loadout, Ori, PhysicsState, Pos, Projectile, Vel,
}, },
event::{EventBus, LocalEvent, ServerEvent}, event::{EventBus, LocalEvent, ServerEvent},
state::DeltaTime, state::DeltaTime,
@ -29,6 +29,7 @@ impl<'a> System<'a> for Sys {
WriteStorage<'a, Projectile>, WriteStorage<'a, Projectile>,
WriteStorage<'a, Energy>, WriteStorage<'a, Energy>,
ReadStorage<'a, Alignment>, ReadStorage<'a, Alignment>,
ReadStorage<'a, Loadout>,
); );
fn run( fn run(
@ -46,6 +47,7 @@ impl<'a> System<'a> for Sys {
mut projectiles, mut projectiles,
mut energies, mut energies,
alignments, alignments,
loadouts,
): Self::SystemData, ): Self::SystemData,
) { ) {
let mut local_emitter = local_bus.emitter(); 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 { else if let Some(other) = physics.touch_entity {
for effect in projectile.hit_entity.drain(..) { for effect in projectile.hit_entity.drain(..) {
match effect { match effect {
projectile::Effect::Damage(change) => { projectile::Effect::Damage(healthchange) => {
let owner_uid = projectile.owner.unwrap(); 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 // Hacky: remove this when groups get implemented
let passive = uid_allocator let passive = uid_allocator
.retrieve_entity_internal(other.into()) .retrieve_entity_internal(other.into())
@ -96,7 +109,13 @@ impl<'a> System<'a> for Sys {
}) })
.unwrap_or(false); .unwrap_or(false);
if other != projectile.owner.unwrap() && !passive { 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) => { projectile::Effect::Knockback(knockback) => {

View File

@ -1,11 +1,14 @@
use crate::{client::Client, Server, SpawnPoint, StateExt}; use crate::{client::Client, Server, SpawnPoint, StateExt};
use common::{ use common::{
assets, 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}, msg::{PlayerListUpdate, ServerMsg},
state::BlockChange, state::BlockChange,
sync::{Uid, WorldSyncExt}, sync::{Uid, WorldSyncExt},
sys::combat::{BLOCK_ANGLE, BLOCK_EFFICIENCY}, sys::combat::BLOCK_ANGLE,
terrain::{Block, TerrainGrid}, terrain::{Block, TerrainGrid},
vol::{ReadVol, Vox}, vol::{ReadVol, Vox},
}; };
@ -159,9 +162,16 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
let state = &server.state; let state = &server.state;
if vel.z <= -30.0 { if vel.z <= -30.0 {
if let Some(stats) = state.ecs().write_storage::<comp::Stats>().get_mut(entity) { if let Some(stats) = state.ecs().write_storage::<comp::Stats>().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::<comp::Loadout>().get(entity) {
damage.modify_damage(false, loadout);
}
stats.health.change_by(comp::HealthChange { stats.health.change_by(comp::HealthChange {
amount: -falldmg, amount: damage.healthchange as i32,
cause: comp::HealthSource::World, cause: comp::HealthSource::World,
}); });
} }
@ -211,11 +221,12 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, power: f32, owner: Opti
// Go through all other entities // Go through all other entities
let hit_range = 3.0 * power; let hit_range = 3.0 * power;
let ecs = &server.state.ecs(); 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::<comp::Pos>(), &ecs.read_storage::<comp::Pos>(),
&ecs.read_storage::<comp::Ori>(), &ecs.read_storage::<comp::Ori>(),
ecs.read_storage::<comp::CharacterState>().maybe(), ecs.read_storage::<comp::CharacterState>().maybe(),
&mut ecs.write_storage::<comp::Stats>(), &mut ecs.write_storage::<comp::Stats>(),
ecs.read_storage::<comp::Loadout>().maybe(),
) )
.join() .join()
{ {
@ -227,21 +238,22 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, power: f32, owner: Opti
&& distance_squared < hit_range.powi(2) && distance_squared < hit_range.powi(2)
{ {
// Weapon gives base damage // 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() { let mut damage = Damage {
dmg += 1; healthchange: -dmg,
} source: DamageSource::Explosion,
};
// Block let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false)
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;
&& ori_b.0.angle_between(pos - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0
{ if let Some(loadout) = loadout_b {
dmg = (dmg as f32 * (1.0 - BLOCK_EFFICIENCY)) as u32 damage.modify_damage(block, loadout);
} }
stats_b.health.change_by(HealthChange { stats_b.health.change_by(HealthChange {
amount: -(dmg as i32), amount: damage.healthchange as i32,
cause: HealthSource::Projectile { owner }, cause: HealthSource::Projectile { owner },
}); });
} }

View File

@ -0,0 +1 @@
-- This file should undo anything in `up.sql`

View File

@ -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}')
);