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),
stats: (
protection: Normal(5.0),
protection: Normal(30.0),
),
)
),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ Item(
(
kind: Shoulder(CultistPurple),
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
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,
],

View File

@ -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::{

View File

@ -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<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 {
type Storage = FlaggedStorage<Self, IdvStorage<Self>>;
}

View File

@ -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,

View File

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

View File

@ -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) => {

View File

@ -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<f32>)
let state = &server.state;
if vel.z <= -30.0 {
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 {
amount: -falldmg,
amount: damage.healthchange as i32,
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
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::<comp::Pos>(),
&ecs.read_storage::<comp::Ori>(),
ecs.read_storage::<comp::CharacterState>().maybe(),
&mut ecs.write_storage::<comp::Stats>(),
ecs.read_storage::<comp::Loadout>().maybe(),
)
.join()
{
@ -227,21 +238,22 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, 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 },
});
}

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