From 94f193fbe071e046f723658a42934e866a8939aa Mon Sep 17 00:00:00 2001 From: socksonme Date: Sat, 22 Jan 2022 22:15:12 +0200 Subject: [PATCH] Damage outcomes are now emitted with health change events Being healed and damaged at the same time still leads to issues - needs to be sorted out --- common/src/combat.rs | 22 ++++++--------- common/src/comp/health.rs | 10 +++++++ common/src/outcome.rs | 6 ++-- common/systems/src/buff.rs | 1 + server/src/cmd.rs | 1 + server/src/events/entity_manipulation.rs | 23 ++++++++++++++- voxygen/src/audio/sfx/mod.rs | 10 ++++--- voxygen/src/hud/mod.rs | 36 +++++++++++++++--------- 8 files changed, 75 insertions(+), 34 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index b86ceed36a..fbee94c1ac 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -239,20 +239,8 @@ impl Attack { let applied_damage = -change.amount; accumulated_damage += applied_damage; - // Could also check this when handling outcomes and display 0.0 damage - // differently? - if applied_damage != 0.0 { - emit_outcome(Outcome::Damage { - pos: target.pos, - info: DamageInfo { - target: target.uid, - by: attacker.map(|a| a.uid), - amount: change.amount, - crit: is_crit, - }, - }); - } if change.amount.abs() > Health::HEALTH_EPSILON { + emit(ServerEvent::HealthChange { entity: target.entity, change, @@ -270,6 +258,7 @@ impl Attack { by: attacker.map(|x| x.into()), cause: Some(damage.damage.source), time, + crit: None, }; emit(ServerEvent::HealthChange { entity: target.entity, @@ -367,6 +356,7 @@ impl Attack { by: attacker.map(|a| a.into()), cause: None, time, + crit: None, }; if change.amount.abs() > Health::HEALTH_EPSILON { emit(ServerEvent::HealthChange { @@ -399,6 +389,7 @@ impl Attack { by: attacker.map(|a| a.into()), cause: None, time, + crit: None, }; if change.amount.abs() > Health::HEALTH_EPSILON { emit(ServerEvent::HealthChange { @@ -510,6 +501,7 @@ impl Attack { by: attacker.map(|a| a.into()), cause: None, time, + crit: None, }; if change.amount.abs() > Health::HEALTH_EPSILON { emit(ServerEvent::HealthChange { @@ -542,6 +534,7 @@ impl Attack { by: attacker.map(|a| a.into()), cause: None, time, + crit: None, }; if change.amount.abs() > Health::HEALTH_EPSILON { emit(ServerEvent::HealthChange { @@ -854,6 +847,7 @@ impl Damage { by: damage_contributor, cause: Some(self.source), time, + crit: Some(is_crit), } }, DamageSource::Falling => { @@ -866,6 +860,7 @@ impl Damage { by: None, cause: Some(self.source), time, + crit: None, } }, DamageSource::Buff(_) | DamageSource::Other => HealthChange { @@ -873,6 +868,7 @@ impl Damage { by: None, cause: Some(self.source), time, + crit: None, }, } } diff --git a/common/src/comp/health.rs b/common/src/comp/health.rs index ac771637e0..eae25e9715 100644 --- a/common/src/comp/health.rs +++ b/common/src/comp/health.rs @@ -24,6 +24,8 @@ pub struct HealthChange { pub cause: Option, /// The time that the health change occurred at pub time: Time, + /// Whether or not the health change was caused by a crit (None if it couldn't have been a crit) + pub crit: Option, } impl HealthChange { @@ -132,6 +134,7 @@ impl Health { amount: 0.0, by: None, cause: None, + crit: None, time: Time(0.0), }, is_dead: false, @@ -210,6 +213,7 @@ impl Health { amount: 0.0, by: None, cause: None, + crit: None, time: Time(0.0), }, is_dead: false, @@ -244,6 +248,7 @@ mod tests { time: Time(123.0), by: Some(damage_contrib), cause: None, + crit: None, }; health.change_by(health_change); @@ -269,6 +274,7 @@ mod tests { time: Time(123.0), by: Some(damage_contrib), cause: None, + crit: None, }; health.change_by(health_change); @@ -288,6 +294,7 @@ mod tests { time: Time(123.0), by: Some(damage_contrib), cause: None, + crit: None, }; health.change_by(health_change); health.change_by(health_change); @@ -313,6 +320,7 @@ mod tests { time: Time(10.0), by: Some(damage_contrib1), cause: None, + crit: None, }; health.change_by(health_change); @@ -322,6 +330,7 @@ mod tests { time: Time(100.0), by: Some(damage_contrib2), cause: None, + crit: None, }; health.change_by(health_change); @@ -335,6 +344,7 @@ mod tests { time: Time(620.0), by: Some(damage_contrib2), cause: None, + crit: None, }; health.change_by(health_change); diff --git a/common/src/outcome.rs b/common/src/outcome.rs index e5c9f2a98f..3627727427 100644 --- a/common/src/outcome.rs +++ b/common/src/outcome.rs @@ -1,4 +1,4 @@ -use crate::{comp, uid::Uid}; +use crate::{comp, uid::Uid, combat::DamageContributor}; use comp::{beam, item::Reagent, poise::PoiseState, skillset::SkillGroupKind, UtteranceKind}; use hashbrown::HashSet; use serde::{Deserialize, Serialize}; @@ -7,9 +7,9 @@ use vek::*; #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct DamageInfo { pub amount: f32, - pub crit: bool, + pub crit: Option, pub target: Uid, - pub by: Option, + pub by: Option, } /// An outcome represents the final result of an instantaneous event. It implies diff --git a/common/systems/src/buff.rs b/common/systems/src/buff.rs index e1d94d57c3..596cef3946 100644 --- a/common/systems/src/buff.rs +++ b/common/systems/src/buff.rs @@ -274,6 +274,7 @@ impl<'a> System<'a> for Sys { by: damage_contributor, cause, time: *read_data.time, + crit: None, }, }); *accumulated = 0.0; diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 4b5edf8778..afb23db986 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1056,6 +1056,7 @@ fn handle_health( amount: hp - health.current(), by: None, cause: None, + crit: None, time: *time, }; health.change_by(change); diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 7ff49a5e79..58bfea2eff 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -23,7 +23,7 @@ use common::{ Player, Poise, Pos, SkillSet, Stats, }, event::{EventBus, ServerEvent}, - outcome::Outcome, + outcome::{DamageInfo, Outcome}, resources::Time, rtsim::RtSimEntity, terrain::{Block, BlockKind, TerrainGrid}, @@ -66,6 +66,7 @@ pub fn handle_poise(server: &Server, entity: EcsEntity, change: comp::PoiseChang pub fn handle_health_change(server: &Server, entity: EcsEntity, change: HealthChange) { let ecs = &server.state.ecs(); + let mut outcomes = ecs.write_resource::>(); if let Some(mut health) = ecs.write_storage::().get_mut(entity) { health.change_by(change); } @@ -76,6 +77,21 @@ pub fn handle_health_change(server: &Server, entity: EcsEntity, change: HealthCh if let Some(agent) = ecs.write_storage::().get_mut(entity) { agent.inbox.push_front(AgentEvent::Hurt); } + dbg!("hit"); + dbg!(change); + // TODO: This will currently fuck up with healing + if let (Some(pos), Some(uid)) = (ecs.read_storage::().get(entity), ecs.read_storage::().get(entity)) { + dbg!(change.amount); + outcomes.push(Outcome::Damage{ + pos: pos.0, + info: DamageInfo { + amount: change.amount, + crit: change.crit, + by: change.by, + target: *uid, + } + }); + } } } @@ -576,6 +592,11 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3) let change = damage.calculate_health_change(damage_reduction, None, false, 0.0, 1.0, *time); health.change_by(change); + let server_eventbus = ecs.read_resource::>(); + server_eventbus.emit_now(ServerEvent::HealthChange { + entity, + change, + }); } // Handle poise change if let Some(mut poise) = ecs.write_storage::().get_mut(entity) { diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index e0f7abb266..6053e71ffc 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -96,7 +96,7 @@ use common::{ object, poise::PoiseState, quadruped_low, quadruped_medium, quadruped_small, Body, CharacterAbilityType, - InventoryUpdateEvent, UtteranceKind, + InventoryUpdateEvent, UtteranceKind, Health, }, outcome::Outcome, terrain::{BlockKind, TerrainChunk}, @@ -514,9 +514,11 @@ impl SfxMgr { false, ); }, - Outcome::Damage { pos, .. } => { - let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Damage); - audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); + Outcome::Damage { pos, info, .. } => { + if info.amount < Health::HEALTH_EPSILON { + let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Damage); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); + } }, Outcome::Death { pos, .. } => { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Death); diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index a91ade3f0f..98858fa256 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1437,7 +1437,8 @@ impl Hud { .last() .expect("There must be at least one floater") .info - .crit; + .crit + .map_or(false, |c| c); // Increase font size based on fraction of maximum health // "flashes" by having a larger size in the first 100ms let font_size = 30 @@ -1496,13 +1497,14 @@ impl Hud { ); let max_hp_frac = floater.info.amount.abs() as f32 / health.maximum() as f32; + let crit = floater.info.crit.map_or(false, |c| c); // Increase font size based on fraction of maximum health // "flashes" by having a larger size in the first 100ms // TODO: example let font_size = 30 + ((max_hp_frac * 10.0) as u32) * 3 - * if floater.info.crit { 2 } else { 1 } + * if crit { 2 } else { 1 } + if floater.timer < 0.1 { // TODO: Maybe change font size wrt crits here? FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32) @@ -1547,7 +1549,7 @@ impl Hud { .font_id(self.fonts.cyri.conrod_id) .color(if floater.info.amount < 0.0 { // TODO: example - if floater.info.crit { + if crit { Color::Rgba(1.0, 0.9, 0.1, hp_fade) } else { Color::Rgba(1.0, 0.1, 0.0, hp_fade) @@ -1569,7 +1571,7 @@ impl Hud { .font_id(self.fonts.cyri.conrod_id) .color(if floater.info.amount < 0.0 { // TODO: example - if floater.info.crit { + if crit { Color::Rgba(1.0, 0.9, 0.1, hp_fade) } else { Color::Rgba(1.0, 0.1, 0.0, hp_fade) @@ -2203,8 +2205,7 @@ impl Hud { let floaters = &hpfl.floaters; // Colors - // TODO: Add for crits as well? - // Maybe decrease increase blue for lighter colour? + // TODO: The crit colors and their names are pretty bad as it stands const WHITE: Rgb = Rgb::new(1.0, 0.9, 0.8); const LIGHT_OR: Rgb = Rgb::new(1.0, 0.925, 0.749); const LIGHT_MED_OR: Rgb = Rgb::new(1.0, 0.85, 0.498); @@ -2268,7 +2269,8 @@ impl Hud { .last() .expect("There must be at least one floater") .info - .crit; + .crit + .map_or(false, |c| c); // Increase font size based on fraction of maximum health // "flashes" by having a larger size in the first 100ms let font_size = 30 @@ -2337,18 +2339,19 @@ impl Hud { // Calculate total change let max_hp_frac = floater.info.amount.abs() as f32 / health.map_or(1.0, |h| h.maximum() as f32); + let crit = floater.info.crit.map_or(false, |c| c); // Increase font size based on fraction of maximum health // "flashes" by having a larger size in the first 100ms let font_size = 30 + ((max_hp_frac * 10.0) as u32) * 3 - * if floater.info.crit { 2 } else { 1 } + * if crit { 2 } else { 1 } + if floater.timer < 0.1 { FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32) } else { 0 }; - let font_col = font_col(font_size, floater.info.crit); + let font_col = font_col(font_size, crit); // Timer sets the widget offset let y = (floater.timer as f64 / crate::ecs::sys::floater::HP_SHOWTIME as f64 @@ -4537,24 +4540,31 @@ impl Hud { if let Some(entity) = ecs.entity_from_uid(info.target.0) { if let Some(floater_list) = hp_floater_lists.get_mut(entity) { + let hit_me = my_uid.map_or(false, |&uid| info.target == uid); if match info.by { Some(by) => { - let by_me = my_uid.map_or(false, |&uid| by == uid); + let by_me = my_uid.map_or(false, |&uid| by.uid() == uid); // If the attack was by me also reset this timer if by_me { floater_list.time_since_last_dmg_by_me = Some(0.0); } - my_uid.map_or(false, |&uid| info.target == uid) || by_me + hit_me || by_me + }, + None => { + hit_me }, - None => false, } { + // TODO: This will currently fuck up with healing let last_floater = floater_list.floaters.last_mut(); match last_floater { Some(f) if f.timer < floater::HP_ACCUMULATETIME => { //TODO: Add "jumping" animation on floater when it changes its // value f.info.amount += info.amount; - f.info.crit = info.crit; + // Only change the crit value if it's not None + if info.crit.is_some() { + f.info.crit = info.crit; + } }, _ => { floater_list.floaters.push(HpFloater {