mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'imbris/random-fixes' into 'master'
Random fixes Closes #697 See merge request veloren/veloren!1391
This commit is contained in:
commit
43398afa0c
@ -176,6 +176,7 @@ impl Tool {
|
|||||||
],
|
],
|
||||||
time_left: Duration::from_secs(15),
|
time_left: Duration::from_secs(15),
|
||||||
owner: None,
|
owner: None,
|
||||||
|
ignore_group: true,
|
||||||
},
|
},
|
||||||
projectile_body: Body::Object(object::Body::Arrow),
|
projectile_body: Body::Object(object::Body::Arrow),
|
||||||
projectile_light: None,
|
projectile_light: None,
|
||||||
@ -274,6 +275,7 @@ impl Tool {
|
|||||||
],
|
],
|
||||||
time_left: Duration::from_secs(20),
|
time_left: Duration::from_secs(20),
|
||||||
owner: None,
|
owner: None,
|
||||||
|
ignore_group: true,
|
||||||
},
|
},
|
||||||
projectile_body: Body::Object(object::Body::BoltFire),
|
projectile_body: Body::Object(object::Body::BoltFire),
|
||||||
projectile_light: Some(LightEmitter {
|
projectile_light: Some(LightEmitter {
|
||||||
@ -303,6 +305,7 @@ impl Tool {
|
|||||||
],
|
],
|
||||||
time_left: Duration::from_secs(20),
|
time_left: Duration::from_secs(20),
|
||||||
owner: None,
|
owner: None,
|
||||||
|
ignore_group: true,
|
||||||
},
|
},
|
||||||
projectile_body: Body::Object(object::Body::BoltFireBig),
|
projectile_body: Body::Object(object::Body::BoltFireBig),
|
||||||
projectile_light: Some(LightEmitter {
|
projectile_light: Some(LightEmitter {
|
||||||
@ -350,6 +353,7 @@ impl Tool {
|
|||||||
],
|
],
|
||||||
time_left: Duration::from_secs(10),
|
time_left: Duration::from_secs(10),
|
||||||
owner: None,
|
owner: None,
|
||||||
|
ignore_group: false,
|
||||||
},
|
},
|
||||||
projectile_body: Body::Object(object::Body::ArrowSnake),
|
projectile_body: Body::Object(object::Body::ArrowSnake),
|
||||||
projectile_light: Some(LightEmitter {
|
projectile_light: Some(LightEmitter {
|
||||||
|
@ -23,6 +23,9 @@ pub struct Projectile {
|
|||||||
/// Time left until the projectile will despawn
|
/// Time left until the projectile will despawn
|
||||||
pub time_left: Duration,
|
pub time_left: Duration,
|
||||||
pub owner: Option<Uid>,
|
pub owner: Option<Uid>,
|
||||||
|
/// Whether projectile collides with entities in the same group as its
|
||||||
|
/// owner
|
||||||
|
pub ignore_group: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Projectile {
|
impl Component for Projectile {
|
||||||
|
@ -136,6 +136,7 @@ impl CharacterBehavior for Data {
|
|||||||
],
|
],
|
||||||
time_left: Duration::from_secs(15),
|
time_left: Duration::from_secs(15),
|
||||||
owner: None,
|
owner: None,
|
||||||
|
ignore_group: true,
|
||||||
};
|
};
|
||||||
projectile.owner = Some(*data.uid);
|
projectile.owner = Some(*data.uid);
|
||||||
update.server_events.push_front(ServerEvent::Shoot {
|
update.server_events.push_front(ServerEvent::Shoot {
|
||||||
|
@ -158,6 +158,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
// Group to ignore collisions with
|
// Group to ignore collisions with
|
||||||
let ignore_group = projectile
|
let ignore_group = projectile
|
||||||
|
.filter(|p| p.ignore_group)
|
||||||
.and_then(|p| p.owner)
|
.and_then(|p| p.owner)
|
||||||
.and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into()))
|
.and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into()))
|
||||||
.and_then(|e| groups.get(e));
|
.and_then(|e| groups.get(e));
|
||||||
@ -185,7 +186,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
if ignore_group.is_some() && ignore_group == group {
|
if entity == entity_other || (ignore_group.is_some() && ignore_group == group) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,7 +238,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if diff.magnitude_squared() > 0.0 {
|
if diff.magnitude_squared() > 0.0 {
|
||||||
let force = 40.0 * (collision_dist - diff.magnitude()) * mass_other
|
let force = 400.0 * (collision_dist - diff.magnitude()) * mass_other
|
||||||
/ (mass + mass_other);
|
/ (mass + mass_other);
|
||||||
|
|
||||||
vel_delta += Vec3::from(diff.normalized()) * force * step_delta;
|
vel_delta += Vec3::from(diff.normalized()) * force * step_delta;
|
||||||
@ -617,11 +618,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
.map(|block_aabb| block_aabb.max.z - pos.0.z);
|
.map(|block_aabb| block_aabb.max.z - pos.0.z);
|
||||||
},
|
},
|
||||||
Collider::Point => {
|
Collider::Point => {
|
||||||
let (dist, block) = terrain.ray(pos.0, pos.0 + pos_delta).ignore_error().cast();
|
let (dist, block) = terrain.ray(pos.0, pos.0 + pos_delta)
|
||||||
|
.until(|vox| !vox.is_air() && !vox.is_fluid())
|
||||||
|
.ignore_error().cast();
|
||||||
|
|
||||||
pos.0 += pos_delta.try_normalized().unwrap_or(Vec3::zero()) * dist;
|
pos.0 += pos_delta.try_normalized().unwrap_or(Vec3::zero()) * dist;
|
||||||
|
|
||||||
// Can't fair since we do ignore_error above
|
// Can't fail since we do ignore_error above
|
||||||
if block.unwrap().is_some() {
|
if block.unwrap().is_some() {
|
||||||
let block_center = pos.0.map(|e| e.floor()) + 0.5;
|
let block_center = pos.0.map(|e| e.floor()) + 0.5;
|
||||||
let block_rpos = (pos.0 - block_center)
|
let block_rpos = (pos.0 - block_center)
|
||||||
@ -649,6 +652,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
physics_state.in_fluid = terrain.get(pos.0.map(|e| e.floor() as i32))
|
||||||
|
.ok()
|
||||||
|
.and_then(|vox| vox.is_fluid().then_some(1.0));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +134,8 @@ const NAMETAG_RANGE: f32 = 40.0;
|
|||||||
const NAMETAG_DMG_TIME: f32 = 60.0;
|
const NAMETAG_DMG_TIME: f32 = 60.0;
|
||||||
/// Range damaged triggered nametags can be seen
|
/// Range damaged triggered nametags can be seen
|
||||||
const NAMETAG_DMG_RANGE: f32 = 120.0;
|
const NAMETAG_DMG_RANGE: f32 = 120.0;
|
||||||
|
/// Range to display speech-bubbles at
|
||||||
|
const SPEECH_BUBBLE_RANGE: f32 = NAMETAG_RANGE;
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
struct Ids {
|
struct Ids {
|
||||||
@ -566,7 +568,6 @@ pub struct Hud {
|
|||||||
tab_complete: Option<String>,
|
tab_complete: Option<String>,
|
||||||
pulse: f32,
|
pulse: f32,
|
||||||
velocity: f32,
|
velocity: f32,
|
||||||
fps: f32,
|
|
||||||
voxygen_i18n: std::sync::Arc<VoxygenLocalization>,
|
voxygen_i18n: std::sync::Arc<VoxygenLocalization>,
|
||||||
slot_manager: slots::SlotManager,
|
slot_manager: slots::SlotManager,
|
||||||
hotbar: hotbar::State,
|
hotbar: hotbar::State,
|
||||||
@ -662,7 +663,6 @@ impl Hud {
|
|||||||
force_chat_cursor: None,
|
force_chat_cursor: None,
|
||||||
tab_complete: None,
|
tab_complete: None,
|
||||||
pulse: 0.0,
|
pulse: 0.0,
|
||||||
fps: 0.0,
|
|
||||||
velocity: 0.0,
|
velocity: 0.0,
|
||||||
voxygen_i18n,
|
voxygen_i18n,
|
||||||
slot_manager,
|
slot_manager,
|
||||||
@ -695,9 +695,7 @@ impl Hud {
|
|||||||
// pulse time for pulsating elements
|
// pulse time for pulsating elements
|
||||||
self.pulse = self.pulse + dt.as_secs_f32();
|
self.pulse = self.pulse + dt.as_secs_f32();
|
||||||
// FPS
|
// FPS
|
||||||
// TODO Get actual FPS from session.rs instead of TPS from the client as they
|
let fps = global_state.clock.get_tps();
|
||||||
// may be different in the future
|
|
||||||
self.fps = 1.0 / client.state().get_delta_time();
|
|
||||||
let version = format!(
|
let version = format!(
|
||||||
"{}-{}",
|
"{}-{}",
|
||||||
env!("CARGO_PKG_VERSION"),
|
env!("CARGO_PKG_VERSION"),
|
||||||
@ -712,7 +710,6 @@ impl Hud {
|
|||||||
let hp_floater_lists = ecs.read_storage::<vcomp::HpFloaterList>();
|
let hp_floater_lists = ecs.read_storage::<vcomp::HpFloaterList>();
|
||||||
let uids = ecs.read_storage::<common::sync::Uid>();
|
let uids = ecs.read_storage::<common::sync::Uid>();
|
||||||
let interpolated = ecs.read_storage::<vcomp::Interpolated>();
|
let interpolated = ecs.read_storage::<vcomp::Interpolated>();
|
||||||
let players = ecs.read_storage::<comp::Player>();
|
|
||||||
let scales = ecs.read_storage::<comp::Scale>();
|
let scales = ecs.read_storage::<comp::Scale>();
|
||||||
let bodies = ecs.read_storage::<comp::Body>();
|
let bodies = ecs.read_storage::<comp::Body>();
|
||||||
let items = ecs.read_storage::<comp::Item>();
|
let items = ecs.read_storage::<comp::Item>();
|
||||||
@ -1080,93 +1077,79 @@ impl Hud {
|
|||||||
.set(overitem_id, ui_widgets);
|
.set(overitem_id, ui_widgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let speech_bubbles = &self.speech_bubbles;
|
||||||
|
|
||||||
// Render overhead name tags and health bars
|
// Render overhead name tags and health bars
|
||||||
for (pos, name, stats, energy, height_offset, hpfl, uid, in_group) in (
|
for (pos, info, bubble, stats, height_offset, hpfl, in_group) in (
|
||||||
&entities,
|
&entities,
|
||||||
&pos,
|
&pos,
|
||||||
interpolated.maybe(),
|
interpolated.maybe(),
|
||||||
&stats,
|
&stats,
|
||||||
energy.maybe(),
|
energy.maybe(),
|
||||||
players.maybe(),
|
|
||||||
scales.maybe(),
|
scales.maybe(),
|
||||||
&bodies,
|
&bodies,
|
||||||
&hp_floater_lists,
|
&hp_floater_lists,
|
||||||
&uids,
|
&uids,
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.map(|(a, b, c, d, e, f, g, h, i, uid)| {
|
.filter(|t| {
|
||||||
(
|
let stats = t.3;
|
||||||
a,
|
let entity = t.0;
|
||||||
b,
|
entity != me && !stats.is_dead
|
||||||
c,
|
|
||||||
d,
|
|
||||||
e,
|
|
||||||
f,
|
|
||||||
g,
|
|
||||||
h,
|
|
||||||
i,
|
|
||||||
uid,
|
|
||||||
client.group_members().contains_key(uid),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.filter(|(entity, pos, _, stats, _, _, _, _, hpfl, _, in_group)| {
|
.filter_map(
|
||||||
*entity != me && !stats.is_dead
|
|(entity, pos, interpolated, stats, energy, scale, body, hpfl, uid)| {
|
||||||
&& (stats.health.current() != stats.health.maximum()
|
// Use interpolated position if available
|
||||||
|| info.target_entity.map_or(false, |e| e == *entity)
|
let pos = interpolated.map_or(pos.0, |i| i.pos);
|
||||||
|| info.selected_entity.map_or(false, |s| s.0 == *entity)
|
let in_group = client.group_members().contains_key(uid);
|
||||||
|| *in_group
|
let dist_sqr = pos.distance_squared(player_pos);
|
||||||
)
|
// Determine whether to display nametag and healthbar based on whether the
|
||||||
// Don't show outside a certain range
|
// entity has been damaged, is targeted/selected, or is in your group
|
||||||
&& pos.0.distance_squared(player_pos)
|
// Note: even if this passes the healthbar can be hidden in some cases if it
|
||||||
< (if *in_group
|
// is at maximum
|
||||||
{
|
let display_overhead_info =
|
||||||
NAMETAG_GROUP_RANGE
|
(info.target_entity.map_or(false, |e| e == entity)
|
||||||
} else if hpfl
|
|| info.selected_entity.map_or(false, |s| s.0 == entity)
|
||||||
.time_since_last_dmg_by_me
|
|| overhead::show_healthbar(stats)
|
||||||
.map_or(false, |t| t < NAMETAG_DMG_TIME)
|
|| in_group)
|
||||||
{
|
&& dist_sqr
|
||||||
NAMETAG_DMG_RANGE
|
< (if in_group {
|
||||||
} else {
|
NAMETAG_GROUP_RANGE
|
||||||
NAMETAG_RANGE
|
} else if hpfl
|
||||||
})
|
.time_since_last_dmg_by_me
|
||||||
.powi(2)
|
.map_or(false, |t| t < NAMETAG_DMG_TIME)
|
||||||
})
|
{
|
||||||
.map(
|
NAMETAG_DMG_RANGE
|
||||||
|(
|
} else {
|
||||||
_,
|
NAMETAG_RANGE
|
||||||
pos,
|
})
|
||||||
interpolated,
|
.powi(2);
|
||||||
stats,
|
|
||||||
energy,
|
let info = display_overhead_info.then(|| overhead::Info {
|
||||||
player,
|
name: &stats.name,
|
||||||
scale,
|
|
||||||
body,
|
|
||||||
hpfl,
|
|
||||||
uid,
|
|
||||||
in_group,
|
|
||||||
)| {
|
|
||||||
// TODO: This is temporary
|
|
||||||
// If the player used the default character name display their name instead
|
|
||||||
let name = if stats.name == "Character Name" {
|
|
||||||
player.map_or(&stats.name, |p| &p.alias)
|
|
||||||
} else {
|
|
||||||
&stats.name
|
|
||||||
};
|
|
||||||
(
|
|
||||||
interpolated.map_or(pos.0, |i| i.pos),
|
|
||||||
name,
|
|
||||||
stats,
|
stats,
|
||||||
energy,
|
energy,
|
||||||
body.height() * scale.map_or(1.0, |s| s.0) + 0.5,
|
});
|
||||||
hpfl,
|
let bubble = if dist_sqr < SPEECH_BUBBLE_RANGE.powi(2) {
|
||||||
uid,
|
speech_bubbles.get(uid)
|
||||||
in_group,
|
} else {
|
||||||
)
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
(info.is_some() || bubble.is_some()).then(|| {
|
||||||
|
(
|
||||||
|
pos,
|
||||||
|
info,
|
||||||
|
bubble,
|
||||||
|
stats,
|
||||||
|
body.height() * scale.map_or(1.0, |s| s.0) + 0.5,
|
||||||
|
hpfl,
|
||||||
|
in_group,
|
||||||
|
)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
let bubble = self.speech_bubbles.get(uid);
|
|
||||||
|
|
||||||
let overhead_id = overhead_walker.next(
|
let overhead_id = overhead_walker.next(
|
||||||
&mut self.ids.overheads,
|
&mut self.ids.overheads,
|
||||||
&mut ui_widgets.widget_id_generator(),
|
&mut ui_widgets.widget_id_generator(),
|
||||||
@ -1178,10 +1161,8 @@ impl Hud {
|
|||||||
|
|
||||||
// Speech bubble, name, level, and hp bars
|
// Speech bubble, name, level, and hp bars
|
||||||
overhead::Overhead::new(
|
overhead::Overhead::new(
|
||||||
&name,
|
info,
|
||||||
bubble,
|
bubble,
|
||||||
stats,
|
|
||||||
energy,
|
|
||||||
own_level,
|
own_level,
|
||||||
in_group,
|
in_group,
|
||||||
&global_state.settings.gameplay,
|
&global_state.settings.gameplay,
|
||||||
@ -1872,7 +1853,7 @@ impl Hud {
|
|||||||
&self.imgs,
|
&self.imgs,
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
&self.voxygen_i18n,
|
&self.voxygen_i18n,
|
||||||
self.fps,
|
fps as f32,
|
||||||
)
|
)
|
||||||
.set(self.ids.settings_window, ui_widgets)
|
.set(self.ids.settings_window, ui_widgets)
|
||||||
{
|
{
|
||||||
|
@ -47,14 +47,22 @@ widget_ids! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Info<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
pub stats: &'a Stats,
|
||||||
|
pub energy: Option<&'a Energy>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines whether to show the healthbar
|
||||||
|
pub fn show_healthbar(stats: &Stats) -> bool { stats.health.current() != stats.health.maximum() }
|
||||||
|
|
||||||
/// ui widget containing everything that goes over a character's head
|
/// ui widget containing everything that goes over a character's head
|
||||||
/// (Speech bubble, Name, Level, HP/energy bars, etc.)
|
/// (Speech bubble, Name, Level, HP/energy bars, etc.)
|
||||||
#[derive(WidgetCommon)]
|
#[derive(WidgetCommon)]
|
||||||
pub struct Overhead<'a> {
|
pub struct Overhead<'a> {
|
||||||
name: &'a str,
|
info: Option<Info<'a>>,
|
||||||
bubble: Option<&'a SpeechBubble>,
|
bubble: Option<&'a SpeechBubble>,
|
||||||
stats: &'a Stats,
|
|
||||||
energy: Option<&'a Energy>,
|
|
||||||
own_level: u32,
|
own_level: u32,
|
||||||
in_group: bool,
|
in_group: bool,
|
||||||
settings: &'a GameplaySettings,
|
settings: &'a GameplaySettings,
|
||||||
@ -70,10 +78,8 @@ pub struct Overhead<'a> {
|
|||||||
impl<'a> Overhead<'a> {
|
impl<'a> Overhead<'a> {
|
||||||
#[allow(clippy::too_many_arguments)] // TODO: Pending review in #587
|
#[allow(clippy::too_many_arguments)] // TODO: Pending review in #587
|
||||||
pub fn new(
|
pub fn new(
|
||||||
name: &'a str,
|
info: Option<Info<'a>>,
|
||||||
bubble: Option<&'a SpeechBubble>,
|
bubble: Option<&'a SpeechBubble>,
|
||||||
stats: &'a Stats,
|
|
||||||
energy: Option<&'a Energy>,
|
|
||||||
own_level: u32,
|
own_level: u32,
|
||||||
in_group: bool,
|
in_group: bool,
|
||||||
settings: &'a GameplaySettings,
|
settings: &'a GameplaySettings,
|
||||||
@ -83,10 +89,8 @@ impl<'a> Overhead<'a> {
|
|||||||
fonts: &'a ConrodVoxygenFonts,
|
fonts: &'a ConrodVoxygenFonts,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
info,
|
||||||
bubble,
|
bubble,
|
||||||
stats,
|
|
||||||
energy,
|
|
||||||
own_level,
|
own_level,
|
||||||
in_group,
|
in_group,
|
||||||
settings,
|
settings,
|
||||||
@ -120,14 +124,13 @@ impl<'a> Ingameable for Overhead<'a> {
|
|||||||
// - 2 Text::new for speech bubble
|
// - 2 Text::new for speech bubble
|
||||||
// - 1 Image::new for icon
|
// - 1 Image::new for icon
|
||||||
// - 10 Image::new for speech bubble (9-slice + tail)
|
// - 10 Image::new for speech bubble (9-slice + tail)
|
||||||
2 + if self.bubble.is_some() { 13 } else { 0 }
|
self.info.map_or(0, |info| {
|
||||||
+ if f64::from(self.stats.health.current()) / f64::from(self.stats.health.maximum())
|
2 + if show_healthbar(info.stats) {
|
||||||
< 1.0
|
5 + if info.energy.is_some() { 1 } else { 0 }
|
||||||
{
|
|
||||||
5 + if self.energy.is_some() { 1 } else { 0 }
|
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
}) + if self.bubble.is_some() { 13 } else { 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,62 +145,179 @@ impl<'a> Widget for Overhead<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unused_unit)] // TODO: Pending review in #587
|
fn style(&self) -> Self::Style {}
|
||||||
fn style(&self) -> Self::Style { () }
|
|
||||||
|
|
||||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||||
let widget::UpdateArgs { id, state, ui, .. } = args;
|
let widget::UpdateArgs { id, state, ui, .. } = args;
|
||||||
const BARSIZE: f64 = 2.0; // Scaling
|
const BARSIZE: f64 = 2.0; // Scaling
|
||||||
const MANA_BAR_HEIGHT: f64 = BARSIZE * 1.5;
|
const MANA_BAR_HEIGHT: f64 = BARSIZE * 1.5;
|
||||||
const MANA_BAR_Y: f64 = MANA_BAR_HEIGHT / 2.0;
|
const MANA_BAR_Y: f64 = MANA_BAR_HEIGHT / 2.0;
|
||||||
// Used to set healthbar colours based on hp_percentage
|
if let Some(Info {
|
||||||
let hp_percentage =
|
name,
|
||||||
self.stats.health.current() as f64 / self.stats.health.maximum() as f64 * 100.0;
|
stats,
|
||||||
// Compare levels to decide if a skull is shown
|
energy,
|
||||||
let level_comp = self.stats.level.level() as i64 - self.own_level as i64;
|
}) = self.info
|
||||||
let health_current = (self.stats.health.current() / 10) as f64;
|
{
|
||||||
let health_max = (self.stats.health.maximum() / 10) as f64;
|
// Used to set healthbar colours based on hp_percentage
|
||||||
let name_y = if (health_current - health_max).abs() < 1e-6 {
|
let hp_percentage =
|
||||||
MANA_BAR_Y + 20.0
|
stats.health.current() as f64 / stats.health.maximum() as f64 * 100.0;
|
||||||
} else if level_comp > 9 && !self.in_group {
|
// Compare levels to decide if a skull is shown
|
||||||
MANA_BAR_Y + 38.0
|
let level_comp = stats.level.level() as i64 - self.own_level as i64;
|
||||||
} else {
|
let health_current = (stats.health.current() / 10) as f64;
|
||||||
MANA_BAR_Y + 32.0
|
let health_max = (stats.health.maximum() / 10) as f64;
|
||||||
};
|
let name_y = if (health_current - health_max).abs() < 1e-6 {
|
||||||
let font_size = if hp_percentage.abs() > 99.9 { 24 } else { 20 };
|
MANA_BAR_Y + 20.0
|
||||||
// Show K for numbers above 10^3 and truncate them
|
} else if level_comp > 9 && !self.in_group {
|
||||||
// Show M for numbers above 10^6 and truncate them
|
MANA_BAR_Y + 38.0
|
||||||
let health_cur_txt = match health_current as u32 {
|
|
||||||
0..=999 => format!("{:.0}", health_current.max(1.0)),
|
|
||||||
1000..=999999 => format!("{:.0}K", (health_current / 1000.0).max(1.0)),
|
|
||||||
_ => format!("{:.0}M", (health_current as f64 / 1.0e6).max(1.0)),
|
|
||||||
};
|
|
||||||
let health_max_txt = match health_max as u32 {
|
|
||||||
0..=999 => format!("{:.0}", health_max.max(1.0)),
|
|
||||||
1000..=999999 => format!("{:.0}K", (health_max / 1000.0).max(1.0)),
|
|
||||||
_ => format!("{:.0}M", (health_max as f64 / 1.0e6).max(1.0)),
|
|
||||||
};
|
|
||||||
// Name
|
|
||||||
Text::new(&self.name)
|
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
|
||||||
.font_size(font_size)
|
|
||||||
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
|
||||||
.x_y(-1.0, name_y)
|
|
||||||
.parent(id)
|
|
||||||
.set(state.ids.name_bg, ui);
|
|
||||||
Text::new(&self.name)
|
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
|
||||||
.font_size(font_size)
|
|
||||||
.color(if self.in_group {
|
|
||||||
GROUP_MEMBER
|
|
||||||
/*} else if targets player { //TODO: Add a way to see if the entity is trying to attack the player, their pet(s) or a member of their group and recolour their nametag accordingly
|
|
||||||
DEFAULT_NPC*/
|
|
||||||
} else {
|
} else {
|
||||||
DEFAULT_NPC
|
MANA_BAR_Y + 32.0
|
||||||
})
|
};
|
||||||
.x_y(0.0, name_y + 1.0)
|
let font_size = if hp_percentage.abs() > 99.9 { 24 } else { 20 };
|
||||||
.parent(id)
|
// Show K for numbers above 10^3 and truncate them
|
||||||
.set(state.ids.name, ui);
|
// Show M for numbers above 10^6 and truncate them
|
||||||
|
let health_cur_txt = match health_current as u32 {
|
||||||
|
0..=999 => format!("{:.0}", health_current.max(1.0)),
|
||||||
|
1000..=999999 => format!("{:.0}K", (health_current / 1000.0).max(1.0)),
|
||||||
|
_ => format!("{:.0}M", (health_current as f64 / 1.0e6).max(1.0)),
|
||||||
|
};
|
||||||
|
let health_max_txt = match health_max as u32 {
|
||||||
|
0..=999 => format!("{:.0}", health_max.max(1.0)),
|
||||||
|
1000..=999999 => format!("{:.0}K", (health_max / 1000.0).max(1.0)),
|
||||||
|
_ => format!("{:.0}M", (health_max as f64 / 1.0e6).max(1.0)),
|
||||||
|
};
|
||||||
|
// Name
|
||||||
|
Text::new(name)
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.font_size(font_size)
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||||
|
.x_y(-1.0, name_y)
|
||||||
|
.parent(id)
|
||||||
|
.set(state.ids.name_bg, ui);
|
||||||
|
Text::new(name)
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.font_size(font_size)
|
||||||
|
.color(if self.in_group {
|
||||||
|
GROUP_MEMBER
|
||||||
|
/*} else if targets player { //TODO: Add a way to see if the entity is trying to attack the player, their pet(s) or a member of their group and recolour their nametag accordingly
|
||||||
|
DEFAULT_NPC*/
|
||||||
|
} else {
|
||||||
|
DEFAULT_NPC
|
||||||
|
})
|
||||||
|
.x_y(0.0, name_y + 1.0)
|
||||||
|
.parent(id)
|
||||||
|
.set(state.ids.name, ui);
|
||||||
|
|
||||||
|
if show_healthbar(stats) {
|
||||||
|
// Show HP Bar
|
||||||
|
let hp_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 1.0; //Animation timer
|
||||||
|
let crit_hp_color: Color = Color::Rgba(0.79, 0.19, 0.17, hp_ani);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
Image::new(self.imgs.enemy_health_bg)
|
||||||
|
.w_h(84.0 * BARSIZE, 10.0 * BARSIZE)
|
||||||
|
.x_y(0.0, MANA_BAR_Y + 6.5) //-25.5)
|
||||||
|
.color(Some(Color::Rgba(0.1, 0.1, 0.1, 0.8)))
|
||||||
|
.parent(id)
|
||||||
|
.set(state.ids.health_bar_bg, ui);
|
||||||
|
|
||||||
|
// % HP Filling
|
||||||
|
Image::new(self.imgs.enemy_bar)
|
||||||
|
.w_h(73.0 * (hp_percentage / 100.0) * BARSIZE, 6.0 * BARSIZE)
|
||||||
|
.x_y(
|
||||||
|
(4.5 + (hp_percentage / 100.0 * 36.45 - 36.45)) * BARSIZE,
|
||||||
|
MANA_BAR_Y + 7.5,
|
||||||
|
)
|
||||||
|
.color(Some(if hp_percentage <= 25.0 {
|
||||||
|
crit_hp_color
|
||||||
|
} else if hp_percentage <= 50.0 {
|
||||||
|
LOW_HP_COLOR
|
||||||
|
} else {
|
||||||
|
HP_COLOR
|
||||||
|
}))
|
||||||
|
.parent(id)
|
||||||
|
.set(state.ids.health_bar, ui);
|
||||||
|
let mut txt = format!("{}/{}", health_cur_txt, health_max_txt);
|
||||||
|
if stats.is_dead {
|
||||||
|
txt = self.voxygen_i18n.get("hud.group.dead").to_string()
|
||||||
|
};
|
||||||
|
Text::new(&txt)
|
||||||
|
.mid_top_with_margin_on(state.ids.health_bar_bg, 2.0)
|
||||||
|
.font_size(10)
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.parent(id)
|
||||||
|
.set(state.ids.health_txt, ui);
|
||||||
|
|
||||||
|
// % Mana Filling
|
||||||
|
if let Some(energy) = energy {
|
||||||
|
let energy_factor = energy.current() as f64 / energy.maximum() as f64;
|
||||||
|
|
||||||
|
Rectangle::fill_with(
|
||||||
|
[72.0 * energy_factor * BARSIZE, MANA_BAR_HEIGHT],
|
||||||
|
MANA_COLOR,
|
||||||
|
)
|
||||||
|
.x_y(
|
||||||
|
((3.5 + (energy_factor * 36.5)) - 36.45) * BARSIZE,
|
||||||
|
MANA_BAR_Y, //-32.0,
|
||||||
|
)
|
||||||
|
.parent(id)
|
||||||
|
.set(state.ids.mana_bar, ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foreground
|
||||||
|
Image::new(self.imgs.enemy_health)
|
||||||
|
.w_h(84.0 * BARSIZE, 10.0 * BARSIZE)
|
||||||
|
.x_y(0.0, MANA_BAR_Y + 6.5) //-25.5)
|
||||||
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.99)))
|
||||||
|
.parent(id)
|
||||||
|
.set(state.ids.health_bar_fg, ui);
|
||||||
|
|
||||||
|
// Level
|
||||||
|
const LOW: Color = Color::Rgba(0.54, 0.81, 0.94, 0.4);
|
||||||
|
const HIGH: Color = Color::Rgba(1.0, 0.0, 0.0, 1.0);
|
||||||
|
const EQUAL: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
||||||
|
// Change visuals of the level display depending on the player level/opponent
|
||||||
|
// level
|
||||||
|
let level_comp = stats.level.level() as i64 - self.own_level as i64;
|
||||||
|
// + 10 level above player -> skull
|
||||||
|
// + 5-10 levels above player -> high
|
||||||
|
// -5 - +5 levels around player level -> equal
|
||||||
|
// - 5 levels below player -> low
|
||||||
|
if level_comp > 9 && !self.in_group {
|
||||||
|
let skull_ani = ((self.pulse * 0.7/* speed factor */).cos() * 0.5 + 0.5) * 10.0; //Animation timer
|
||||||
|
Image::new(if skull_ani as i32 == 1 && rand::random::<f32>() < 0.9 {
|
||||||
|
self.imgs.skull_2
|
||||||
|
} else {
|
||||||
|
self.imgs.skull
|
||||||
|
})
|
||||||
|
.w_h(18.0 * BARSIZE, 18.0 * BARSIZE)
|
||||||
|
.x_y(-39.0 * BARSIZE, MANA_BAR_Y + 7.0)
|
||||||
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||||
|
.parent(id)
|
||||||
|
.set(state.ids.level_skull, ui);
|
||||||
|
} else {
|
||||||
|
let fnt_size = match stats.level.level() {
|
||||||
|
0..=9 => 15,
|
||||||
|
10..=99 => 12,
|
||||||
|
100..=999 => 9,
|
||||||
|
_ => 2,
|
||||||
|
};
|
||||||
|
Text::new(&format!("{}", stats.level.level()))
|
||||||
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
.font_size(fnt_size)
|
||||||
|
.color(if level_comp > 4 {
|
||||||
|
HIGH
|
||||||
|
} else if level_comp < -5 {
|
||||||
|
LOW
|
||||||
|
} else {
|
||||||
|
EQUAL
|
||||||
|
})
|
||||||
|
.x_y(-37.0 * BARSIZE, MANA_BAR_Y + 9.0)
|
||||||
|
.parent(id)
|
||||||
|
.set(state.ids.level, ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Speech bubble
|
// Speech bubble
|
||||||
if let Some(bubble) = self.bubble {
|
if let Some(bubble) = self.bubble {
|
||||||
@ -347,119 +467,6 @@ impl<'a> Widget for Overhead<'a> {
|
|||||||
// .parent(id)
|
// .parent(id)
|
||||||
.set(state.ids.speech_bubble_icon, ui);
|
.set(state.ids.speech_bubble_icon, ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
if hp_percentage < 100.0 {
|
|
||||||
// Show HP Bar
|
|
||||||
let hp_percentage =
|
|
||||||
self.stats.health.current() as f64 / self.stats.health.maximum() as f64 * 100.0;
|
|
||||||
let hp_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 1.0; //Animation timer
|
|
||||||
let crit_hp_color: Color = Color::Rgba(0.79, 0.19, 0.17, hp_ani);
|
|
||||||
|
|
||||||
// Background
|
|
||||||
Image::new(self.imgs.enemy_health_bg)
|
|
||||||
.w_h(84.0 * BARSIZE, 10.0 * BARSIZE)
|
|
||||||
.x_y(0.0, MANA_BAR_Y + 6.5) //-25.5)
|
|
||||||
.color(Some(Color::Rgba(0.1, 0.1, 0.1, 0.8)))
|
|
||||||
.parent(id)
|
|
||||||
.set(state.ids.health_bar_bg, ui);
|
|
||||||
|
|
||||||
// % HP Filling
|
|
||||||
Image::new(self.imgs.enemy_bar)
|
|
||||||
.w_h(73.0 * (hp_percentage / 100.0) * BARSIZE, 6.0 * BARSIZE)
|
|
||||||
.x_y(
|
|
||||||
(4.5 + (hp_percentage / 100.0 * 36.45 - 36.45)) * BARSIZE,
|
|
||||||
MANA_BAR_Y + 7.5,
|
|
||||||
)
|
|
||||||
.color(Some(if hp_percentage <= 25.0 {
|
|
||||||
crit_hp_color
|
|
||||||
} else if hp_percentage <= 50.0 {
|
|
||||||
LOW_HP_COLOR
|
|
||||||
} else {
|
|
||||||
HP_COLOR
|
|
||||||
}))
|
|
||||||
.parent(id)
|
|
||||||
.set(state.ids.health_bar, ui);
|
|
||||||
let mut txt = format!("{}/{}", health_cur_txt, health_max_txt);
|
|
||||||
if self.stats.is_dead {
|
|
||||||
txt = self.voxygen_i18n.get("hud.group.dead").to_string()
|
|
||||||
};
|
|
||||||
Text::new(&txt)
|
|
||||||
.mid_top_with_margin_on(state.ids.health_bar_bg, 2.0)
|
|
||||||
.font_size(10)
|
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
|
||||||
.color(TEXT_COLOR)
|
|
||||||
.parent(id)
|
|
||||||
.set(state.ids.health_txt, ui);
|
|
||||||
|
|
||||||
// % Mana Filling
|
|
||||||
if let Some(energy) = self.energy {
|
|
||||||
let energy_factor = energy.current() as f64 / energy.maximum() as f64;
|
|
||||||
|
|
||||||
Rectangle::fill_with(
|
|
||||||
[72.0 * energy_factor * BARSIZE, MANA_BAR_HEIGHT],
|
|
||||||
MANA_COLOR,
|
|
||||||
)
|
|
||||||
.x_y(
|
|
||||||
((3.5 + (energy_factor * 36.5)) - 36.45) * BARSIZE,
|
|
||||||
MANA_BAR_Y, //-32.0,
|
|
||||||
)
|
|
||||||
.parent(id)
|
|
||||||
.set(state.ids.mana_bar, ui);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Foreground
|
|
||||||
Image::new(self.imgs.enemy_health)
|
|
||||||
.w_h(84.0 * BARSIZE, 10.0 * BARSIZE)
|
|
||||||
.x_y(0.0, MANA_BAR_Y + 6.5) //-25.5)
|
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.99)))
|
|
||||||
.parent(id)
|
|
||||||
.set(state.ids.health_bar_fg, ui);
|
|
||||||
|
|
||||||
// Level
|
|
||||||
const LOW: Color = Color::Rgba(0.54, 0.81, 0.94, 0.4);
|
|
||||||
const HIGH: Color = Color::Rgba(1.0, 0.0, 0.0, 1.0);
|
|
||||||
const EQUAL: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
|
||||||
// Change visuals of the level display depending on the player level/opponent
|
|
||||||
// level
|
|
||||||
let level_comp = self.stats.level.level() as i64 - self.own_level as i64;
|
|
||||||
// + 10 level above player -> skull
|
|
||||||
// + 5-10 levels above player -> high
|
|
||||||
// -5 - +5 levels around player level -> equal
|
|
||||||
// - 5 levels below player -> low
|
|
||||||
if level_comp > 9 && !self.in_group {
|
|
||||||
let skull_ani = ((self.pulse * 0.7/* speed factor */).cos() * 0.5 + 0.5) * 10.0; //Animation timer
|
|
||||||
Image::new(if skull_ani as i32 == 1 && rand::random::<f32>() < 0.9 {
|
|
||||||
self.imgs.skull_2
|
|
||||||
} else {
|
|
||||||
self.imgs.skull
|
|
||||||
})
|
|
||||||
.w_h(18.0 * BARSIZE, 18.0 * BARSIZE)
|
|
||||||
.x_y(-39.0 * BARSIZE, MANA_BAR_Y + 7.0)
|
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
|
||||||
.parent(id)
|
|
||||||
.set(state.ids.level_skull, ui);
|
|
||||||
} else {
|
|
||||||
let fnt_size = match self.stats.level.level() {
|
|
||||||
0..=9 => 15,
|
|
||||||
10..=99 => 12,
|
|
||||||
100..=999 => 9,
|
|
||||||
_ => 2,
|
|
||||||
};
|
|
||||||
Text::new(&format!("{}", self.stats.level.level()))
|
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
|
||||||
.font_size(fnt_size)
|
|
||||||
.color(if level_comp > 4 {
|
|
||||||
HIGH
|
|
||||||
} else if level_comp < -5 {
|
|
||||||
LOW
|
|
||||||
} else {
|
|
||||||
EQUAL
|
|
||||||
})
|
|
||||||
.x_y(-37.0 * BARSIZE, MANA_BAR_Y + 9.0)
|
|
||||||
.parent(id)
|
|
||||||
.set(state.ids.level, ui);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user