Fix group tooltips, make ui buff code more efficient, avoid crashing on characters button press

This commit is contained in:
Imbris 2020-10-17 18:41:59 -04:00 committed by Sam
parent 8fa398954d
commit be1767a5af
4 changed files with 170 additions and 160 deletions

View File

@ -9,7 +9,6 @@ use crate::{
GlobalState, GlobalState,
}; };
use crate::hud::BuffInfo;
use common::comp::{BuffId, Buffs}; use common::comp::{BuffId, Buffs};
use conrod_core::{ use conrod_core::{
color, color,
@ -131,55 +130,51 @@ impl<'a> Widget for BuffsBar<'a> {
.set(state.ids.buffs_align, ui); .set(state.ids.buffs_align, ui);
// Buffs and Debuffs // Buffs and Debuffs
// Create two vecs to display buffs and debuffs separately let (buff_count, debuff_count) = buffs.active_buffs.iter().map(get_buff_info).fold(
let mut buffs_vec = Vec::<BuffInfo>::new(); (0, 0),
let mut debuffs_vec = Vec::<BuffInfo>::new(); |(buff_count, debuff_count), info| {
for buff in buffs.active_buffs.clone() { if info.is_buff {
let info = get_buff_info(buff); (buff_count + 1, debuff_count)
if info.is_buff { } else {
buffs_vec.push(info); (buff_count, debuff_count + 1)
} else { }
debuffs_vec.push(info); },
} );
} // Limit displayed buffs
if state.ids.buffs.len() < buffs_vec.len() { let buff_count = buff_count.min(22);
state.update(|state| { let debuff_count = debuff_count.min(22);
state
.ids let gen = &mut ui.widget_id_generator();
.buffs if state.ids.buffs.len() < buff_count {
.resize(buffs_vec.len(), &mut ui.widget_id_generator()) state.update(|state| state.ids.buffs.resize(buff_count, gen));
});
}; };
if state.ids.debuffs.len() < debuffs_vec.len() { if state.ids.debuffs.len() < debuff_count {
state.update(|state| { state.update(|state| state.ids.debuffs.resize(debuff_count, gen));
state
.ids
.debuffs
.resize(debuffs_vec.len(), &mut ui.widget_id_generator())
});
}; };
if state.ids.buff_timers.len() < buffs_vec.len() { if state.ids.buff_timers.len() < buff_count {
state.update(|state| { state.update(|state| state.ids.buff_timers.resize(buff_count, gen));
state
.ids
.buff_timers
.resize(buffs_vec.len(), &mut ui.widget_id_generator())
});
}; };
if state.ids.debuff_timers.len() < debuffs_vec.len() { if state.ids.debuff_timers.len() < debuff_count {
state.update(|state| { state.update(|state| state.ids.debuff_timers.resize(debuff_count, gen));
state
.ids
.debuff_timers
.resize(debuffs_vec.len(), &mut ui.widget_id_generator())
});
}; };
let pulsating_col = Color::Rgba(1.0, 1.0, 1.0, buff_ani); let pulsating_col = Color::Rgba(1.0, 1.0, 1.0, buff_ani);
let norm_col = Color::Rgba(1.0, 1.0, 1.0, 1.0); let norm_col = Color::Rgba(1.0, 1.0, 1.0, 1.0);
// Create Buff Widgets // Create Buff Widgets
for (i, buff) in buffs_vec.iter().enumerate() { state
if i < 22 { .ids
// Limit displayed buffs .buffs
.iter()
.copied()
.zip(state.ids.buff_timers.iter().copied())
.zip(
buffs
.active_buffs
.iter()
.map(get_buff_info)
.filter(|info| info.is_buff),
)
.enumerate()
.for_each(|(i, ((id, timer_id), buff))| {
let max_duration = match buff.id { let max_duration = match buff.id {
BuffId::Regeneration { duration, .. } => duration.unwrap().as_secs_f32(), BuffId::Regeneration { duration, .. } => duration.unwrap().as_secs_f32(),
_ => 10.0, _ => 10.0,
@ -205,7 +200,7 @@ impl<'a> Widget for BuffsBar<'a> {
} else { } else {
Some(norm_col) Some(norm_col)
}) })
.set(state.ids.buffs[i], ui); .set(id, ui);
// Create Buff tooltip // Create Buff tooltip
let title = match buff.id { let title = match buff.id {
BuffId::Regeneration { .. } => { BuffId::Regeneration { .. } => {
@ -239,7 +234,7 @@ impl<'a> Widget for BuffsBar<'a> {
_ => self.imgs.nothing, _ => self.imgs.nothing,
}) })
.w_h(20.0, 20.0) .w_h(20.0, 20.0)
.middle_of(state.ids.buffs[i]) .middle_of(id)
.with_tooltip( .with_tooltip(
self.tooltip_manager, self.tooltip_manager,
title, title,
@ -247,18 +242,28 @@ impl<'a> Widget for BuffsBar<'a> {
&buffs_tooltip, &buffs_tooltip,
BUFF_COLOR, BUFF_COLOR,
) )
.set(state.ids.buff_timers[i], ui) .set(timer_id, ui)
.was_clicked() .was_clicked()
{ {
event.push(Event::RemoveBuff(buff.id)); event.push(Event::RemoveBuff(buff.id));
}; };
}; });
}
// Create Debuff Widgets // Create Debuff Widgets
for (i, debuff) in debuffs_vec.iter().enumerate() { state
if i < 22 { .ids
// Limit displayed buffs .debuffs
.iter()
.copied()
.zip(state.ids.debuff_timers.iter().copied())
.zip(
buffs
.active_buffs
.iter()
.map(get_buff_info)
.filter(|info| !info.is_buff),
)
.enumerate()
.for_each(|(i, ((id, timer_id), debuff))| {
let max_duration = match debuff.id { let max_duration = match debuff.id {
BuffId::Bleeding { duration, .. } => { BuffId::Bleeding { duration, .. } => {
duration.unwrap_or(Duration::from_secs(60)).as_secs_f32() duration.unwrap_or(Duration::from_secs(60)).as_secs_f32()
@ -292,7 +297,7 @@ impl<'a> Widget for BuffsBar<'a> {
} else { } else {
Some(norm_col) Some(norm_col)
}) })
.set(state.ids.debuffs[i], ui); .set(id, ui);
// Create Debuff tooltip // Create Debuff tooltip
let title = match debuff.id { let title = match debuff.id {
BuffId::Bleeding { .. } => { BuffId::Bleeding { .. } => {
@ -324,7 +329,7 @@ impl<'a> Widget for BuffsBar<'a> {
_ => self.imgs.nothing, _ => self.imgs.nothing,
}) })
.w_h(20.0, 20.0) .w_h(20.0, 20.0)
.middle_of(state.ids.debuffs[i]) .middle_of(id)
.with_tooltip( .with_tooltip(
self.tooltip_manager, self.tooltip_manager,
title, title,
@ -332,10 +337,10 @@ impl<'a> Widget for BuffsBar<'a> {
&buffs_tooltip, &buffs_tooltip,
DEBUFF_COLOR, DEBUFF_COLOR,
) )
.set(state.ids.debuff_timers[i], ui); .set(timer_id, ui);
}; });
}
} }
if let BuffPosition::Map = buff_position { if let BuffPosition::Map = buff_position {
// Alignment // Alignment
Rectangle::fill_with([tweak!(300.0), tweak!(280.0)], color::RED) Rectangle::fill_with([tweak!(300.0), tweak!(280.0)], color::RED)

View File

@ -5,7 +5,7 @@ use super::{
}; };
use crate::{ use crate::{
hud::{get_buff_info, BuffInfo}, hud::get_buff_info,
i18n::VoxygenLocalization, i18n::VoxygenLocalization,
settings::Settings, settings::Settings,
ui::{fonts::ConrodVoxygenFonts, ImageFrame, Tooltip, TooltipManager, Tooltipable}, ui::{fonts::ConrodVoxygenFonts, ImageFrame, Tooltip, TooltipManager, Tooltipable},
@ -75,7 +75,6 @@ pub struct Group<'a> {
localized_strings: &'a std::sync::Arc<VoxygenLocalization>, localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
pulse: f32, pulse: f32,
global_state: &'a GlobalState, global_state: &'a GlobalState,
buffs: &'a Buffs,
tooltip_manager: &'a mut TooltipManager, tooltip_manager: &'a mut TooltipManager,
#[conrod(common_builder)] #[conrod(common_builder)]
@ -94,7 +93,6 @@ impl<'a> Group<'a> {
localized_strings: &'a std::sync::Arc<VoxygenLocalization>, localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
pulse: f32, pulse: f32,
global_state: &'a GlobalState, global_state: &'a GlobalState,
buffs: &'a Buffs,
tooltip_manager: &'a mut TooltipManager, tooltip_manager: &'a mut TooltipManager,
) -> Self { ) -> Self {
Self { Self {
@ -107,7 +105,6 @@ impl<'a> Group<'a> {
localized_strings, localized_strings,
pulse, pulse,
global_state, global_state,
buffs,
tooltip_manager, tooltip_manager,
common: widget::CommonBuilder::default(), common: widget::CommonBuilder::default(),
} }
@ -145,7 +142,6 @@ impl<'a> Widget for Group<'a> {
let widget::UpdateArgs { state, ui, .. } = args; let widget::UpdateArgs { state, ui, .. } = args;
let mut events = Vec::new(); let mut events = Vec::new();
let localized_strings = self.localized_strings; let localized_strings = self.localized_strings;
//let buffs = self.buffs;
let buff_ani = ((self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8) + 0.5; //Animation timer let buff_ani = ((self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8) + 0.5; //Animation timer
let buffs_tooltip = Tooltip::new({ let buffs_tooltip = Tooltip::new({
// Edge images [t, b, r, l] // Edge images [t, b, r, l]
@ -333,6 +329,8 @@ impl<'a> Widget for Group<'a> {
.ecs() .ecs()
.read_resource::<common::sync::UidAllocator>(); .read_resource::<common::sync::UidAllocator>();
// Keep track of the total number of widget ids we are using for buffs
let mut total_buff_count = 0;
for (i, &uid) in group_members.iter().copied().enumerate() { for (i, &uid) in group_members.iter().copied().enumerate() {
self.show.group = true; self.show.group = true;
let entity = uid_allocator.retrieve_entity_internal(uid.into()); let entity = uid_allocator.retrieve_entity_internal(uid.into());
@ -447,27 +445,29 @@ impl<'a> Widget for Group<'a> {
.set(state.ids.member_stam[i], ui); .set(state.ids.member_stam[i], ui);
} }
if let Some(buffs) = buffs { if let Some(buffs) = buffs {
let mut buffs_vec = Vec::<BuffInfo>::new(); // Limit displayed buffs to 11
for buff in buffs.active_buffs.clone() { let buff_count = buffs.active_buffs.len().min(11);
let info = get_buff_info(buff); total_buff_count += buff_count;
buffs_vec.push(info); let gen = &mut ui.widget_id_generator();
if state.ids.buffs.len() < total_buff_count {
state.update(|state| state.ids.buffs.resize(total_buff_count, gen));
}
if state.ids.buff_timers.len() < total_buff_count {
state.update(|state| {
state.ids.buff_timers.resize(total_buff_count, gen)
});
} }
state.update(|state| {
state.ids.buffs.resize(
state.ids.buffs.len() + buffs_vec.len(),
&mut ui.widget_id_generator(),
)
});
state.update(|state| {
state.ids.buff_timers.resize(
state.ids.buff_timers.len() + buffs_vec.len(),
&mut ui.widget_id_generator(),
)
});
// Create Buff Widgets // Create Buff Widgets
for (x, buff) in buffs_vec.iter().enumerate() { let mut prev_id = None;
if x < 11 { state
// Limit displayed buffs .ids
.buffs
.iter()
.copied()
.zip(state.ids.buff_timers.iter().copied())
.skip(total_buff_count - buff_count)
.zip(buffs.active_buffs.iter().map(get_buff_info))
.for_each(|((id, timer_id), buff)| {
let max_duration = match buff.id { let max_duration = match buff.id {
BuffId::Regeneration { duration, .. } => { BuffId::Regeneration { duration, .. } => {
duration.unwrap().as_secs_f32() duration.unwrap().as_secs_f32()
@ -485,22 +485,23 @@ impl<'a> Widget for Group<'a> {
BuffId::Cursed { .. } => self.imgs.debuff_skull_0, BuffId::Cursed { .. } => self.imgs.debuff_skull_0,
}; };
let buff_widget = Image::new(buff_img).w_h(20.0, 20.0); let buff_widget = Image::new(buff_img).w_h(20.0, 20.0);
let buff_widget = if x == 0 { let buff_widget = if let Some(id) = prev_id {
buff_widget.right_from(id, 1.0)
} else {
buff_widget.bottom_left_with_margins_on( buff_widget.bottom_left_with_margins_on(
state.ids.member_panels_frame[i], state.ids.member_panels_frame[i],
-21.0, -21.0,
1.0, 1.0,
) )
} else {
buff_widget.right_from(state.ids.buffs[state.ids.buffs.len() - buffs_vec.len() + x - 1/*x - 1*/], 1.0)
}; };
prev_id = Some(id);
buff_widget buff_widget
.color(if current_duration < 10.0 { .color(if current_duration < 10.0 {
Some(pulsating_col) Some(pulsating_col)
} else { } else {
Some(norm_col) Some(norm_col)
}) })
.set(state.ids.buffs[state.ids.buffs.len() - buffs_vec.len() + x/*x*/], ui); .set(id, ui);
// Create Buff tooltip // Create Buff tooltip
let title = match buff.id { let title = match buff.id {
BuffId::Regeneration { .. } => { BuffId::Regeneration { .. } => {
@ -538,17 +539,20 @@ impl<'a> Widget for Group<'a> {
_ => self.imgs.nothing, _ => self.imgs.nothing,
}) })
.w_h(20.0, 20.0) .w_h(20.0, 20.0)
.middle_of(state.ids.buffs[state.ids.buffs.len() - buffs_vec.len() + x/*x*/]) .middle_of(id)
.with_tooltip( .with_tooltip(
self.tooltip_manager, self.tooltip_manager,
title, title,
&desc, &desc,
&buffs_tooltip, &buffs_tooltip,
if buff.is_buff {BUFF_COLOR} else {DEBUFF_COLOR}, if buff.is_buff {
BUFF_COLOR
} else {
DEBUFF_COLOR
},
) )
.set(state.ids.buff_timers[state.ids.buffs.len() - buffs_vec.len() + x/*x*/], ui); .set(timer_id, ui);
}; });
}
} else { } else {
// Values N.A. // Values N.A.
Text::new(&stats.name.to_string()) Text::new(&stats.name.to_string())

View File

@ -1803,7 +1803,6 @@ impl Hud {
} }
} }
// Group Window // Group Window
let buffs = buffs.get(client.entity()).unwrap();
for event in Group::new( for event in Group::new(
&mut self.show, &mut self.show,
client, client,
@ -1814,7 +1813,6 @@ impl Hud {
&self.voxygen_i18n, &self.voxygen_i18n,
self.pulse, self.pulse,
&global_state, &global_state,
&buffs,
tooltip_manager, tooltip_manager,
) )
.set(self.ids.group_window, ui_widgets) .set(self.ids.group_window, ui_widgets)
@ -2728,7 +2726,7 @@ pub fn get_quality_col<I: ItemDesc>(item: &I) -> Color {
} }
} }
// Get info about applied buffs // Get info about applied buffs
fn get_buff_info(buff: comp::Buff) -> BuffInfo { fn get_buff_info(buff: &comp::Buff) -> BuffInfo {
BuffInfo { BuffInfo {
id: buff.id, id: buff.id,
is_buff: buff is_buff: buff

View File

@ -3,7 +3,7 @@ use super::{
REGION_COLOR, SAY_COLOR, STAMINA_COLOR, TELL_COLOR, TEXT_BG, TEXT_COLOR, REGION_COLOR, SAY_COLOR, STAMINA_COLOR, TELL_COLOR, TEXT_BG, TEXT_COLOR,
}; };
use crate::{ use crate::{
hud::{get_buff_info, BuffInfo}, hud::get_buff_info,
i18n::VoxygenLocalization, i18n::VoxygenLocalization,
settings::GameplaySettings, settings::GameplaySettings,
ui::{fonts::ConrodVoxygenFonts, Ingameable}, ui::{fonts::ConrodVoxygenFonts, Ingameable},
@ -135,14 +135,18 @@ impl<'a> Ingameable for Overhead<'a> {
// - 1 Rect::new for mana // - 1 Rect::new for mana
// If there are Buffs // If there are Buffs
// - 1 Alignment Rectangle // - 1 Alignment Rectangle
// - 10 + 10 Buffs and Timer Overlays // - 10 + 10 Buffs and Timer Overlays (only if there is no speech bubble)
// If there's a speech bubble // If there's a speech bubble
// - 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)
self.info.map_or(0, |info| { self.info.map_or(0, |info| {
2 + 1 2 + 1
+ info.buffs.active_buffs.len().min(10) * 2 + if self.bubble.is_none() {
info.buffs.active_buffs.len().min(10) * 2
} else {
0
}
+ if show_healthbar(info.stats) { + if show_healthbar(info.stats) {
5 + if info.energy.is_some() { 1 } else { 0 } 5 + if info.energy.is_some() { 1 } else { 0 }
} else { } else {
@ -191,11 +195,6 @@ impl<'a> Widget for Overhead<'a> {
} else { } else {
MANA_BAR_Y + 32.0 MANA_BAR_Y + 32.0
}; };
let mut buffs_vec = Vec::<BuffInfo>::new();
for buff in buffs.active_buffs.clone() {
let info = get_buff_info(buff);
buffs_vec.push(info);
}
let font_size = if hp_percentage.abs() > 99.9 { 24 } else { 20 }; let font_size = if hp_percentage.abs() > 99.9 { 24 } else { 20 };
// Show K for numbers above 10^3 and truncate them // Show K for numbers above 10^3 and truncate them
// Show M for numbers above 10^6 and truncate them // Show M for numbers above 10^6 and truncate them
@ -211,76 +210,80 @@ impl<'a> Widget for Overhead<'a> {
}; };
// Buffs // Buffs
// Alignment // Alignment
let buff_count = buffs.active_buffs.len().min(11);
Rectangle::fill_with([tweak!(168.0), tweak!(100.0)], color::TRANSPARENT) Rectangle::fill_with([tweak!(168.0), tweak!(100.0)], color::TRANSPARENT)
.x_y(-1.0, name_y + tweak!(60.0)) .x_y(-1.0, name_y + tweak!(60.0))
.parent(id) .parent(id)
.set(state.ids.buffs_align, ui); .set(state.ids.buffs_align, ui);
if state.ids.buffs.len() < buffs_vec.len() {
state.update(|state| { let gen = &mut ui.widget_id_generator();
state if state.ids.buffs.len() < buff_count {
.ids state.update(|state| state.ids.buffs.resize(buff_count, gen));
.buffs
.resize(buffs_vec.len(), &mut ui.widget_id_generator())
});
}; };
if state.ids.buff_timers.len() < buffs_vec.len() { if state.ids.buff_timers.len() < buff_count {
state.update(|state| { state.update(|state| state.ids.buff_timers.resize(buff_count, gen));
state
.ids
.buff_timers
.resize(buffs_vec.len(), &mut ui.widget_id_generator())
});
}; };
let buff_ani = ((self.pulse * 4.0).cos() * 0.5 + 0.8) + 0.5; //Animation timer let buff_ani = ((self.pulse * 4.0).cos() * 0.5 + 0.8) + 0.5; //Animation timer
let pulsating_col = Color::Rgba(1.0, 1.0, 1.0, buff_ani); let pulsating_col = Color::Rgba(1.0, 1.0, 1.0, buff_ani);
let norm_col = Color::Rgba(1.0, 1.0, 1.0, 1.0); let norm_col = Color::Rgba(1.0, 1.0, 1.0, 1.0);
// Create Buff Widgets // Create Buff Widgets
for (i, buff) in buffs_vec.iter().enumerate() { if self.bubble.is_none() {
if i < 11 && self.bubble.is_none() { state
// Limit displayed buffs .ids
let max_duration = match buff.id { .buffs
BuffId::Regeneration { duration, .. } => duration.unwrap().as_secs_f32(), .iter()
_ => 10.0, .copied()
}; .zip(state.ids.buff_timers.iter().copied())
let current_duration = buff.dur; .zip(buffs.active_buffs.iter().map(get_buff_info))
let duration_percentage = (current_duration / max_duration * 1000.0) as u32; // Percentage to determine which frame of the timer overlay is displayed .enumerate()
let buff_img = match buff.id { .for_each(|(i, ((id, timer_id), buff))| {
BuffId::Regeneration { .. } => self.imgs.buff_plus_0, // Limit displayed buffs
BuffId::Bleeding { .. } => self.imgs.debuff_bleed_0, let max_duration = match buff.id {
BuffId::Cursed { .. } => self.imgs.debuff_skull_0, BuffId::Regeneration { duration, .. } => {
}; duration.unwrap().as_secs_f32()
let buff_widget = Image::new(buff_img).w_h(20.0, 20.0); },
// Sort buffs into rows of 5 slots _ => 10.0,
let x = i % 5; };
let y = i / 5; let current_duration = buff.dur;
let buff_widget = buff_widget.bottom_left_with_margins_on( let duration_percentage = (current_duration / max_duration * 1000.0) as u32; // Percentage to determine which frame of the timer overlay is displayed
state.ids.buffs_align, let buff_img = match buff.id {
0.0 + y as f64 * (21.0), BuffId::Regeneration { .. } => self.imgs.buff_plus_0,
0.0 + x as f64 * (21.0), BuffId::Bleeding { .. } => self.imgs.debuff_bleed_0,
); BuffId::Cursed { .. } => self.imgs.debuff_skull_0,
buff_widget };
.color(if current_duration < 10.0 { let buff_widget = Image::new(buff_img).w_h(20.0, 20.0);
Some(pulsating_col) // Sort buffs into rows of 5 slots
} else { let x = i % 5;
Some(norm_col) let y = i / 5;
}) let buff_widget = buff_widget.bottom_left_with_margins_on(
.set(state.ids.buffs[i], ui); state.ids.buffs_align,
0.0 + y as f64 * (21.0),
0.0 + x as f64 * (21.0),
);
buff_widget
.color(if current_duration < 10.0 {
Some(pulsating_col)
} else {
Some(norm_col)
})
.set(id, ui);
Image::new(match duration_percentage as u64 { Image::new(match duration_percentage as u64 {
875..=1000 => self.imgs.nothing, // 8/8 875..=1000 => self.imgs.nothing, // 8/8
750..=874 => self.imgs.buff_0, // 7/8 750..=874 => self.imgs.buff_0, // 7/8
625..=749 => self.imgs.buff_1, // 6/8 625..=749 => self.imgs.buff_1, // 6/8
500..=624 => self.imgs.buff_2, // 5/8 500..=624 => self.imgs.buff_2, // 5/8
375..=499 => self.imgs.buff_3, // 4/8 375..=499 => self.imgs.buff_3, // 4/8
250..=374 => self.imgs.buff_4, //3/8 250..=374 => self.imgs.buff_4, //3/8
125..=249 => self.imgs.buff_5, // 2/8 125..=249 => self.imgs.buff_5, // 2/8
0..=124 => self.imgs.buff_6, // 1/8 0..=124 => self.imgs.buff_6, // 1/8
_ => self.imgs.nothing, _ => self.imgs.nothing,
}) })
.w_h(20.0, 20.0) .w_h(20.0, 20.0)
.middle_of(state.ids.buffs[i]) .middle_of(id)
.set(state.ids.buff_timers[i], ui); .set(timer_id, ui);
}; });
} }
// Name // Name
Text::new(name) Text::new(name)