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
This commit is contained in:
socksonme 2022-01-22 22:15:12 +02:00 committed by Socksonme
parent 634a3095d6
commit 94f193fbe0
8 changed files with 75 additions and 34 deletions

View File

@ -239,20 +239,8 @@ impl Attack {
let applied_damage = -change.amount; let applied_damage = -change.amount;
accumulated_damage += applied_damage; 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 { if change.amount.abs() > Health::HEALTH_EPSILON {
emit(ServerEvent::HealthChange { emit(ServerEvent::HealthChange {
entity: target.entity, entity: target.entity,
change, change,
@ -270,6 +258,7 @@ impl Attack {
by: attacker.map(|x| x.into()), by: attacker.map(|x| x.into()),
cause: Some(damage.damage.source), cause: Some(damage.damage.source),
time, time,
crit: None,
}; };
emit(ServerEvent::HealthChange { emit(ServerEvent::HealthChange {
entity: target.entity, entity: target.entity,
@ -367,6 +356,7 @@ impl Attack {
by: attacker.map(|a| a.into()), by: attacker.map(|a| a.into()),
cause: None, cause: None,
time, time,
crit: None,
}; };
if change.amount.abs() > Health::HEALTH_EPSILON { if change.amount.abs() > Health::HEALTH_EPSILON {
emit(ServerEvent::HealthChange { emit(ServerEvent::HealthChange {
@ -399,6 +389,7 @@ impl Attack {
by: attacker.map(|a| a.into()), by: attacker.map(|a| a.into()),
cause: None, cause: None,
time, time,
crit: None,
}; };
if change.amount.abs() > Health::HEALTH_EPSILON { if change.amount.abs() > Health::HEALTH_EPSILON {
emit(ServerEvent::HealthChange { emit(ServerEvent::HealthChange {
@ -510,6 +501,7 @@ impl Attack {
by: attacker.map(|a| a.into()), by: attacker.map(|a| a.into()),
cause: None, cause: None,
time, time,
crit: None,
}; };
if change.amount.abs() > Health::HEALTH_EPSILON { if change.amount.abs() > Health::HEALTH_EPSILON {
emit(ServerEvent::HealthChange { emit(ServerEvent::HealthChange {
@ -542,6 +534,7 @@ impl Attack {
by: attacker.map(|a| a.into()), by: attacker.map(|a| a.into()),
cause: None, cause: None,
time, time,
crit: None,
}; };
if change.amount.abs() > Health::HEALTH_EPSILON { if change.amount.abs() > Health::HEALTH_EPSILON {
emit(ServerEvent::HealthChange { emit(ServerEvent::HealthChange {
@ -854,6 +847,7 @@ impl Damage {
by: damage_contributor, by: damage_contributor,
cause: Some(self.source), cause: Some(self.source),
time, time,
crit: Some(is_crit),
} }
}, },
DamageSource::Falling => { DamageSource::Falling => {
@ -866,6 +860,7 @@ impl Damage {
by: None, by: None,
cause: Some(self.source), cause: Some(self.source),
time, time,
crit: None,
} }
}, },
DamageSource::Buff(_) | DamageSource::Other => HealthChange { DamageSource::Buff(_) | DamageSource::Other => HealthChange {
@ -873,6 +868,7 @@ impl Damage {
by: None, by: None,
cause: Some(self.source), cause: Some(self.source),
time, time,
crit: None,
}, },
} }
} }

View File

@ -24,6 +24,8 @@ pub struct HealthChange {
pub cause: Option<DamageSource>, pub cause: Option<DamageSource>,
/// The time that the health change occurred at /// The time that the health change occurred at
pub time: Time, 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<bool>,
} }
impl HealthChange { impl HealthChange {
@ -132,6 +134,7 @@ impl Health {
amount: 0.0, amount: 0.0,
by: None, by: None,
cause: None, cause: None,
crit: None,
time: Time(0.0), time: Time(0.0),
}, },
is_dead: false, is_dead: false,
@ -210,6 +213,7 @@ impl Health {
amount: 0.0, amount: 0.0,
by: None, by: None,
cause: None, cause: None,
crit: None,
time: Time(0.0), time: Time(0.0),
}, },
is_dead: false, is_dead: false,
@ -244,6 +248,7 @@ mod tests {
time: Time(123.0), time: Time(123.0),
by: Some(damage_contrib), by: Some(damage_contrib),
cause: None, cause: None,
crit: None,
}; };
health.change_by(health_change); health.change_by(health_change);
@ -269,6 +274,7 @@ mod tests {
time: Time(123.0), time: Time(123.0),
by: Some(damage_contrib), by: Some(damage_contrib),
cause: None, cause: None,
crit: None,
}; };
health.change_by(health_change); health.change_by(health_change);
@ -288,6 +294,7 @@ mod tests {
time: Time(123.0), time: Time(123.0),
by: Some(damage_contrib), by: Some(damage_contrib),
cause: None, cause: None,
crit: None,
}; };
health.change_by(health_change); health.change_by(health_change);
health.change_by(health_change); health.change_by(health_change);
@ -313,6 +320,7 @@ mod tests {
time: Time(10.0), time: Time(10.0),
by: Some(damage_contrib1), by: Some(damage_contrib1),
cause: None, cause: None,
crit: None,
}; };
health.change_by(health_change); health.change_by(health_change);
@ -322,6 +330,7 @@ mod tests {
time: Time(100.0), time: Time(100.0),
by: Some(damage_contrib2), by: Some(damage_contrib2),
cause: None, cause: None,
crit: None,
}; };
health.change_by(health_change); health.change_by(health_change);
@ -335,6 +344,7 @@ mod tests {
time: Time(620.0), time: Time(620.0),
by: Some(damage_contrib2), by: Some(damage_contrib2),
cause: None, cause: None,
crit: None,
}; };
health.change_by(health_change); health.change_by(health_change);

View File

@ -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 comp::{beam, item::Reagent, poise::PoiseState, skillset::SkillGroupKind, UtteranceKind};
use hashbrown::HashSet; use hashbrown::HashSet;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -7,9 +7,9 @@ use vek::*;
#[derive(Debug, Clone, Copy, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct DamageInfo { pub struct DamageInfo {
pub amount: f32, pub amount: f32,
pub crit: bool, pub crit: Option<bool>,
pub target: Uid, pub target: Uid,
pub by: Option<Uid>, pub by: Option<DamageContributor>,
} }
/// An outcome represents the final result of an instantaneous event. It implies /// An outcome represents the final result of an instantaneous event. It implies

View File

@ -274,6 +274,7 @@ impl<'a> System<'a> for Sys {
by: damage_contributor, by: damage_contributor,
cause, cause,
time: *read_data.time, time: *read_data.time,
crit: None,
}, },
}); });
*accumulated = 0.0; *accumulated = 0.0;

View File

@ -1056,6 +1056,7 @@ fn handle_health(
amount: hp - health.current(), amount: hp - health.current(),
by: None, by: None,
cause: None, cause: None,
crit: None,
time: *time, time: *time,
}; };
health.change_by(change); health.change_by(change);

View File

@ -23,7 +23,7 @@ use common::{
Player, Poise, Pos, SkillSet, Stats, Player, Poise, Pos, SkillSet, Stats,
}, },
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
outcome::Outcome, outcome::{DamageInfo, Outcome},
resources::Time, resources::Time,
rtsim::RtSimEntity, rtsim::RtSimEntity,
terrain::{Block, BlockKind, TerrainGrid}, 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) { pub fn handle_health_change(server: &Server, entity: EcsEntity, change: HealthChange) {
let ecs = &server.state.ecs(); let ecs = &server.state.ecs();
let mut outcomes = ecs.write_resource::<Vec<Outcome>>();
if let Some(mut health) = ecs.write_storage::<Health>().get_mut(entity) { if let Some(mut health) = ecs.write_storage::<Health>().get_mut(entity) {
health.change_by(change); 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::<Agent>().get_mut(entity) { if let Some(agent) = ecs.write_storage::<Agent>().get_mut(entity) {
agent.inbox.push_front(AgentEvent::Hurt); 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::<Pos>().get(entity), ecs.read_storage::<Uid>().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<f32>)
let change = let change =
damage.calculate_health_change(damage_reduction, None, false, 0.0, 1.0, *time); damage.calculate_health_change(damage_reduction, None, false, 0.0, 1.0, *time);
health.change_by(change); health.change_by(change);
let server_eventbus = ecs.read_resource::<EventBus<ServerEvent>>();
server_eventbus.emit_now(ServerEvent::HealthChange {
entity,
change,
});
} }
// Handle poise change // Handle poise change
if let Some(mut poise) = ecs.write_storage::<comp::Poise>().get_mut(entity) { if let Some(mut poise) = ecs.write_storage::<comp::Poise>().get_mut(entity) {

View File

@ -96,7 +96,7 @@ use common::{
object, object,
poise::PoiseState, poise::PoiseState,
quadruped_low, quadruped_medium, quadruped_small, Body, CharacterAbilityType, quadruped_low, quadruped_medium, quadruped_small, Body, CharacterAbilityType,
InventoryUpdateEvent, UtteranceKind, InventoryUpdateEvent, UtteranceKind, Health,
}, },
outcome::Outcome, outcome::Outcome,
terrain::{BlockKind, TerrainChunk}, terrain::{BlockKind, TerrainChunk},
@ -514,9 +514,11 @@ impl SfxMgr {
false, false,
); );
}, },
Outcome::Damage { pos, .. } => { Outcome::Damage { pos, info, .. } => {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Damage); if info.amount < Health::HEALTH_EPSILON {
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Damage);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false);
}
}, },
Outcome::Death { pos, .. } => { Outcome::Death { pos, .. } => {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Death); let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Death);

View File

@ -1437,7 +1437,8 @@ impl Hud {
.last() .last()
.expect("There must be at least one floater") .expect("There must be at least one floater")
.info .info
.crit; .crit
.map_or(false, |c| c);
// Increase font size based on fraction of maximum health // Increase font size based on fraction of maximum health
// "flashes" by having a larger size in the first 100ms // "flashes" by having a larger size in the first 100ms
let font_size = 30 let font_size = 30
@ -1496,13 +1497,14 @@ impl Hud {
); );
let max_hp_frac = let max_hp_frac =
floater.info.amount.abs() as f32 / health.maximum() as f32; 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 // Increase font size based on fraction of maximum health
// "flashes" by having a larger size in the first 100ms // "flashes" by having a larger size in the first 100ms
// TODO: example // TODO: example
let font_size = 30 let font_size = 30
+ ((max_hp_frac * 10.0) as u32) + ((max_hp_frac * 10.0) as u32)
* 3 * 3
* if floater.info.crit { 2 } else { 1 } * if crit { 2 } else { 1 }
+ if floater.timer < 0.1 { + if floater.timer < 0.1 {
// TODO: Maybe change font size wrt crits here? // TODO: Maybe change font size wrt crits here?
FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32) 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) .font_id(self.fonts.cyri.conrod_id)
.color(if floater.info.amount < 0.0 { .color(if floater.info.amount < 0.0 {
// TODO: example // TODO: example
if floater.info.crit { if crit {
Color::Rgba(1.0, 0.9, 0.1, hp_fade) Color::Rgba(1.0, 0.9, 0.1, hp_fade)
} else { } else {
Color::Rgba(1.0, 0.1, 0.0, hp_fade) Color::Rgba(1.0, 0.1, 0.0, hp_fade)
@ -1569,7 +1571,7 @@ impl Hud {
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.color(if floater.info.amount < 0.0 { .color(if floater.info.amount < 0.0 {
// TODO: example // TODO: example
if floater.info.crit { if crit {
Color::Rgba(1.0, 0.9, 0.1, hp_fade) Color::Rgba(1.0, 0.9, 0.1, hp_fade)
} else { } else {
Color::Rgba(1.0, 0.1, 0.0, hp_fade) Color::Rgba(1.0, 0.1, 0.0, hp_fade)
@ -2203,8 +2205,7 @@ impl Hud {
let floaters = &hpfl.floaters; let floaters = &hpfl.floaters;
// Colors // Colors
// TODO: Add for crits as well? // TODO: The crit colors and their names are pretty bad as it stands
// Maybe decrease increase blue for lighter colour?
const WHITE: Rgb<f32> = Rgb::new(1.0, 0.9, 0.8); const WHITE: Rgb<f32> = Rgb::new(1.0, 0.9, 0.8);
const LIGHT_OR: Rgb<f32> = Rgb::new(1.0, 0.925, 0.749); const LIGHT_OR: Rgb<f32> = Rgb::new(1.0, 0.925, 0.749);
const LIGHT_MED_OR: Rgb<f32> = Rgb::new(1.0, 0.85, 0.498); const LIGHT_MED_OR: Rgb<f32> = Rgb::new(1.0, 0.85, 0.498);
@ -2268,7 +2269,8 @@ impl Hud {
.last() .last()
.expect("There must be at least one floater") .expect("There must be at least one floater")
.info .info
.crit; .crit
.map_or(false, |c| c);
// Increase font size based on fraction of maximum health // Increase font size based on fraction of maximum health
// "flashes" by having a larger size in the first 100ms // "flashes" by having a larger size in the first 100ms
let font_size = 30 let font_size = 30
@ -2337,18 +2339,19 @@ impl Hud {
// Calculate total change // Calculate total change
let max_hp_frac = floater.info.amount.abs() as f32 let max_hp_frac = floater.info.amount.abs() as f32
/ health.map_or(1.0, |h| h.maximum() 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 // Increase font size based on fraction of maximum health
// "flashes" by having a larger size in the first 100ms // "flashes" by having a larger size in the first 100ms
let font_size = 30 let font_size = 30
+ ((max_hp_frac * 10.0) as u32) + ((max_hp_frac * 10.0) as u32)
* 3 * 3
* if floater.info.crit { 2 } else { 1 } * if crit { 2 } else { 1 }
+ if floater.timer < 0.1 { + if floater.timer < 0.1 {
FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32) FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32)
} else { } else {
0 0
}; };
let font_col = font_col(font_size, floater.info.crit); let font_col = font_col(font_size, crit);
// Timer sets the widget offset // Timer sets the widget offset
let y = (floater.timer as f64 let y = (floater.timer as f64
/ crate::ecs::sys::floater::HP_SHOWTIME 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(entity) = ecs.entity_from_uid(info.target.0) {
if let Some(floater_list) = hp_floater_lists.get_mut(entity) { 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 { if match info.by {
Some(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 the attack was by me also reset this timer
if by_me { if by_me {
floater_list.time_since_last_dmg_by_me = Some(0.0); 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(); let last_floater = floater_list.floaters.last_mut();
match last_floater { match last_floater {
Some(f) if f.timer < floater::HP_ACCUMULATETIME => { Some(f) if f.timer < floater::HP_ACCUMULATETIME => {
//TODO: Add "jumping" animation on floater when it changes its //TODO: Add "jumping" animation on floater when it changes its
// value // value
f.info.amount += info.amount; 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 { floater_list.floaters.push(HpFloater {