Addressed comments, health and damage floaters are now separated

Enemy healing probably needs to be discussed as it doesn't show up as of now and the way crits are handled also needs to be discussed
This commit is contained in:
socksonme 2022-01-23 12:41:57 +02:00 committed by Socksonme
parent 94f193fbe0
commit ce95680df9
8 changed files with 193 additions and 181 deletions

View File

@ -11,7 +11,7 @@ use crate::{
Player, Poise, PoiseChange, SkillSet, Stats, Player, Poise, PoiseChange, SkillSet, Stats,
}, },
event::ServerEvent, event::ServerEvent,
outcome::{DamageInfo, Outcome}, outcome::Outcome,
states::utils::StageSection, states::utils::StageSection,
uid::{Uid, UidAllocator}, uid::{Uid, UidAllocator},
util::Dir, util::Dir,
@ -240,7 +240,6 @@ impl Attack {
accumulated_damage += applied_damage; accumulated_damage += applied_damage;
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,
@ -258,7 +257,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, crit: Some(is_crit),
}; };
emit(ServerEvent::HealthChange { emit(ServerEvent::HealthChange {
entity: target.entity, entity: target.entity,

View File

@ -24,7 +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) /// Whether or not the health change was caused by a crit (None if it
/// couldn't have been a crit)
pub crit: Option<bool>, pub crit: Option<bool>,
} }

View File

@ -1,4 +1,4 @@
use crate::{comp, uid::Uid, combat::DamageContributor}; use crate::{combat::DamageContributor, comp, uid::Uid};
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};

View File

@ -77,11 +77,11 @@ 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"); if let (Some(pos), Some(uid)) = (
dbg!(change); ecs.read_storage::<Pos>().get(entity),
// TODO: This will currently fuck up with healing ecs.read_storage::<Uid>().get(entity),
if let (Some(pos), Some(uid)) = (ecs.read_storage::<Pos>().get(entity), ecs.read_storage::<Uid>().get(entity)) { ) {
dbg!(change.amount); if change.amount.abs() > Health::HEALTH_EPSILON {
outcomes.push(Outcome::Damage { outcomes.push(Outcome::Damage {
pos: pos.0, pos: pos.0,
info: DamageInfo { info: DamageInfo {
@ -89,11 +89,12 @@ pub fn handle_health_change(server: &Server, entity: EcsEntity, change: HealthCh
crit: change.crit, crit: change.crit,
by: change.by, by: change.by,
target: *uid, target: *uid,
} },
}); });
} }
} }
} }
}
pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3<f32>) { pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3<f32>) {
let ecs = &server.state.ecs(); let ecs = &server.state.ecs();

View File

@ -95,8 +95,8 @@ use common::{
item::{ItemDefinitionId, ItemKind, ToolKind}, item::{ItemDefinitionId, ItemKind, ToolKind},
object, object,
poise::PoiseState, poise::PoiseState,
quadruped_low, quadruped_medium, quadruped_small, Body, CharacterAbilityType, quadruped_low, quadruped_medium, quadruped_small, Body, CharacterAbilityType, Health,
InventoryUpdateEvent, UtteranceKind, Health, InventoryUpdateEvent, UtteranceKind,
}, },
outcome::Outcome, outcome::Outcome,
terrain::{BlockKind, TerrainChunk}, terrain::{BlockKind, TerrainChunk},

View File

@ -16,9 +16,7 @@ pub struct HpFloater {
pub struct HpFloaterList { pub struct HpFloaterList {
// Order oldest to newest // Order oldest to newest
pub floaters: Vec<HpFloater>, pub floaters: Vec<HpFloater>,
// Keep from spawning more floaters from same hp change
// Note: this can't detect a change if equivalent healing and damage take place simultaneously
pub last_hp: f32,
// The time since you last damaged this entity // The time since you last damaged this entity
// Used to display nametags outside normal range if this time is below a certain value // Used to display nametags outside normal range if this time is below a certain value
pub time_since_last_dmg_by_me: Option<f32>, pub time_since_last_dmg_by_me: Option<f32>,

View File

@ -34,18 +34,18 @@ impl<'a> System<'a> for Sys {
) { ) {
// Add hp floater lists to all entities with health and a position // Add hp floater lists to all entities with health and a position
// Note: necessary in order to know last_hp // Note: necessary in order to know last_hp
for (entity, last_hp) in (&entities, &healths, &pos, !&hp_floater_lists) for entity in (&entities, &healths, &pos, !&hp_floater_lists)
.join() .join()
.map(|(e, h, _, _)| (e, h.current())) .map(|(e, _, _, _)| e)
.collect::<Vec<_>>() .collect::<Vec<_>>()
{ {
let _ = hp_floater_lists.insert(entity, HpFloaterList { let _ = hp_floater_lists.insert(entity, HpFloaterList {
floaters: Vec::new(), floaters: Vec::new(),
last_hp,
time_since_last_dmg_by_me: None, time_since_last_dmg_by_me: None,
}); });
} }
// TODO: avoid join??
for hp_floater_list in (&mut hp_floater_lists).join() { for hp_floater_list in (&mut hp_floater_lists).join() {
// Increment timer for time since last damaged by me // Increment timer for time since last damaged by me
hp_floater_list hp_floater_list
@ -74,9 +74,7 @@ impl<'a> System<'a> for Sys {
for ( for (
entity, entity,
HpFloaterList { HpFloaterList {
ref mut floaters, ref mut floaters, ..
ref last_hp,
..
}, },
) in (&entities, &mut hp_floater_lists).join() ) in (&entities, &mut hp_floater_lists).join()
{ {
@ -92,7 +90,7 @@ impl<'a> System<'a> for Sys {
} else { } else {
MY_HP_SHOWTIME MY_HP_SHOWTIME
} }
|| last_hp.abs() < Health::HEALTH_EPSILON //|| last_hp.abs() < Health::HEALTH_EPSILON;
}) { }) {
floaters.clear(); floaters.clear();
} }

View File

@ -91,7 +91,7 @@ use common::{
loot_owner::LootOwnerKind, loot_owner::LootOwnerKind,
pet::is_mountable, pet::is_mountable,
skillset::{skills::Skill, SkillGroupKind}, skillset::{skills::Skill, SkillGroupKind},
BuffData, BuffKind, Item, MapMarkerChange, BuffData, BuffKind, Health, Item, MapMarkerChange,
}, },
consts::MAX_PICKUP_RANGE, consts::MAX_PICKUP_RANGE,
link::Is, link::Is,
@ -1422,23 +1422,30 @@ impl Hud {
&mut ui_widgets.widget_id_generator(), &mut ui_widgets.widget_id_generator(),
); );
// Because only the Damage HpFloaters are batched
// and would result in the wrong timers/crit info being used
let damage_floaters: Vec<&HpFloater> =
floaters.iter().filter(|f| f.info.amount < 0.0).collect();
if !damage_floaters.is_empty() {
// Calculate total change // Calculate total change
// Ignores healing // Ignores healing
let hp_damage: f32 = floaters.iter().map(|f| f.info.amount.min(0.0)).sum(); let hp_damage: f32 =
damage_floaters.iter().map(|fl| fl.info.amount).sum();
// .fold(0.0, |acc, f| f.info.amount.min(0.0) + acc); // .fold(0.0, |acc, f| f.info.amount.min(0.0) + acc);
let hp_dmg_rounded_abs = hp_damage.round().abs() as u32; let hp_dmg_rounded_abs = hp_damage.round().abs() as u32;
let max_hp_frac = hp_damage.abs() as f32 / health.maximum() as f32; let max_hp_frac = hp_damage.abs() / health.maximum();
let timer = floaters
let timer = damage_floaters
.last() .last()
.expect("There must be at least one floater") .expect("There must be at least one floater")
.timer; .timer;
let crit = floaters let crit = damage_floaters
.last() .last()
.expect("There must be at least one floater") .and_then(|f| f.info.crit)
.info .unwrap_or(false);
.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
@ -1477,6 +1484,7 @@ impl Hud {
}) })
.mid_bottom_with_margin_on(ui_widgets.window, 300.0 + y) .mid_bottom_with_margin_on(ui_widgets.window, 300.0 + y)
.set(player_sct_id, ui_widgets); .set(player_sct_id, ui_widgets);
}
}; };
for floater in floaters { for floater in floaters {
// Healing always single numbers so just skip damage when in batch mode // Healing always single numbers so just skip damage when in batch mode
@ -1495,16 +1503,13 @@ impl Hud {
&mut self.ids.player_scts, &mut self.ids.player_scts,
&mut ui_widgets.widget_id_generator(), &mut ui_widgets.widget_id_generator(),
); );
let max_hp_frac = let max_hp_frac = floater.info.amount.abs() / health.maximum();
floater.info.amount.abs() as f32 / health.maximum() as f32;
let crit = floater.info.crit.map_or(false, |c| c); 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 * if crit { 2 } else { 1 }
* 3
* 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)
@ -1618,7 +1623,7 @@ impl Hud {
//let fade = ((4.0 - floater.timer as f32) * 0.25) + 0.2; // Timer sets //let fade = ((4.0 - floater.timer as f32) * 0.25) + 0.2; // Timer sets
// text transparency // text transparency
let fade = if floater.timer < 1.0 { let fade = if floater.timer < 1.0 {
floater.timer as f32 floater.timer
} else { } else {
1.0 1.0
}; };
@ -1680,9 +1685,9 @@ impl Hud {
.find(|d| d.owner == *uid) .find(|d| d.owner == *uid)
{ {
let fade = if display.timer < 3.0 { let fade = if display.timer < 3.0 {
display.timer as f32 * 0.33 display.timer * 0.33
} else if display.timer < 2.0 { } else if display.timer < 2.0 {
display.timer as f32 * 0.33 * 0.1 display.timer * 0.33 * 0.1
} else { } else {
1.0 1.0
}; };
@ -1794,7 +1799,7 @@ impl Hud {
let y = floater.timer as f64 * number_speed; // Timer sets the widget offset let y = floater.timer as f64 * number_speed; // Timer sets the widget offset
// text transparency // text transparency
let fade = if floater.timer < 0.25 { let fade = if floater.timer < 0.25 {
floater.timer as f32 / 0.25 floater.timer / 0.25
} else { } else {
1.0 1.0
}; };
@ -2244,33 +2249,33 @@ impl Hud {
} }
}; };
// TODO: Might have to change something here as well
if global_state.settings.interface.sct_damage_batch { if global_state.settings.interface.sct_damage_batch {
let number_speed = 50.0; // Damage number speed let number_speed = 50.0; // Damage number speed
let sct_id = sct_walker let sct_id = sct_walker
.next(&mut self.ids.scts, &mut ui_widgets.widget_id_generator()); .next(&mut self.ids.scts, &mut ui_widgets.widget_id_generator());
let sct_bg_id = sct_bg_walker let sct_bg_id = sct_bg_walker
.next(&mut self.ids.sct_bgs, &mut ui_widgets.widget_id_generator()); .next(&mut self.ids.sct_bgs, &mut ui_widgets.widget_id_generator());
let damage_floaters: Vec<&HpFloater> =
floaters.iter().filter(|f| f.info.amount < 0.0).collect();
if !damage_floaters.is_empty() {
// Calculate total change // Calculate total change
// Ignores healing // Ignores healing
let hp_damage = floaters.iter().fold(0.0, |acc, f| { let hp_damage: f32 =
if f.info.amount < 0.0 { damage_floaters.iter().map(|fl| fl.info.amount).sum();
acc + f.info.amount
} else {
acc
}
});
let hp_dmg_rounded_abs = hp_damage.round().abs(); let hp_dmg_rounded_abs = hp_damage.round().abs();
let max_hp_frac = hp_damage.abs() / health.map_or(1.0, |h| h.maximum()); let max_hp_frac = hp_damage.abs() / health.map_or(1.0, |h| h.maximum());
let timer = floaters let timer = damage_floaters
.last() .last()
.expect("There must be at least one floater") .expect("There must be at least one floater")
.timer; .timer;
let crit = floaters let crit = damage_floaters
.last() .last()
.expect("There must be at least one floater") .and_then(|f| f.info.crit)
.info .unwrap_or(false);
.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
@ -2286,7 +2291,9 @@ impl Hud {
* number_speed) * number_speed)
+ 100.0; + 100.0;
// Timer sets text transparency // Timer sets text transparency
let fade = ((crate::ecs::sys::floater::HP_SHOWTIME - timer) * 0.25) + 0.2; let fade =
((crate::ecs::sys::floater::HP_SHOWTIME - timer) * 0.25) + 0.2;
// TODO: Can the Healing variant even be reached as healing is ignored?
if hp_damage.abs() < 1.0 { if hp_damage.abs() < 1.0 {
// Damage and heal below 10/10 are shown as decimals // Damage and heal below 10/10 are shown as decimals
Text::new(&format!("{}", hp_damage.abs())) Text::new(&format!("{}", hp_damage.abs()))
@ -2329,6 +2336,7 @@ impl Hud {
.position_ingame(ingame_pos) .position_ingame(ingame_pos)
.set(sct_id, ui_widgets); .set(sct_id, ui_widgets);
}; };
}
} else { } else {
for floater in floaters { for floater in floaters {
let number_speed = 250.0; // Single Numbers Speed let number_speed = 250.0; // Single Numbers Speed
@ -2337,15 +2345,13 @@ impl Hud {
let sct_bg_id = sct_bg_walker let sct_bg_id = sct_bg_walker
.next(&mut self.ids.sct_bgs, &mut ui_widgets.widget_id_generator()); .next(&mut self.ids.sct_bgs, &mut ui_widgets.widget_id_generator());
// Calculate total change // Calculate total change
let max_hp_frac = floater.info.amount.abs() as f32 let max_hp_frac =
/ health.map_or(1.0, |h| h.maximum() as f32); floater.info.amount.abs() / health.map_or(1.0, |h| h.maximum());
let crit = floater.info.crit.map_or(false, |c| c); 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 * if crit { 2 } else { 1 }
* 3
* 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 {
@ -4550,12 +4556,21 @@ impl Hud {
} }
hit_me || by_me hit_me || by_me
}, },
None => { None => hit_me,
hit_me
},
} { } {
// TODO: This will currently fuck up with healing let last_floater = if info.amount < Health::HEALTH_EPSILON {
let last_floater = floater_list.floaters.last_mut(); floater_list
.floaters
.iter_mut()
.rev()
.find(|f| f.info.amount < Health::HEALTH_EPSILON)
} else {
floater_list
.floaters
.iter_mut()
.rev()
.find(|f| f.info.amount > Health::HEALTH_EPSILON)
};
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