mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
parent
94f193fbe0
commit
ce95680df9
@ -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,
|
||||||
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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};
|
||||||
|
@ -77,22 +77,23 @@ 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 {
|
||||||
amount: change.amount,
|
amount: change.amount,
|
||||||
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>) {
|
||||||
|
@ -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},
|
||||||
|
@ -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>,
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user