From be1767a5afbb8cc13449a25a54f31272adf66573 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 17 Oct 2020 18:41:59 -0400 Subject: [PATCH] Fix group tooltips, make ui buff code more efficient, avoid crashing on characters button press --- voxygen/src/hud/buffs.rs | 119 ++++++++++++++++--------------- voxygen/src/hud/group.rs | 70 +++++++++--------- voxygen/src/hud/mod.rs | 4 +- voxygen/src/hud/overhead.rs | 137 ++++++++++++++++++------------------ 4 files changed, 170 insertions(+), 160 deletions(-) diff --git a/voxygen/src/hud/buffs.rs b/voxygen/src/hud/buffs.rs index 15a6552399..65688a4252 100644 --- a/voxygen/src/hud/buffs.rs +++ b/voxygen/src/hud/buffs.rs @@ -9,7 +9,6 @@ use crate::{ GlobalState, }; -use crate::hud::BuffInfo; use common::comp::{BuffId, Buffs}; use conrod_core::{ color, @@ -131,55 +130,51 @@ impl<'a> Widget for BuffsBar<'a> { .set(state.ids.buffs_align, ui); // Buffs and Debuffs - // Create two vecs to display buffs and debuffs separately - let mut buffs_vec = Vec::::new(); - let mut debuffs_vec = Vec::::new(); - for buff in buffs.active_buffs.clone() { - let info = get_buff_info(buff); - if info.is_buff { - buffs_vec.push(info); - } else { - debuffs_vec.push(info); - } - } - if state.ids.buffs.len() < buffs_vec.len() { - state.update(|state| { - state - .ids - .buffs - .resize(buffs_vec.len(), &mut ui.widget_id_generator()) - }); + let (buff_count, debuff_count) = buffs.active_buffs.iter().map(get_buff_info).fold( + (0, 0), + |(buff_count, debuff_count), info| { + if info.is_buff { + (buff_count + 1, debuff_count) + } else { + (buff_count, debuff_count + 1) + } + }, + ); + // Limit displayed buffs + let buff_count = buff_count.min(22); + let debuff_count = debuff_count.min(22); + + let gen = &mut ui.widget_id_generator(); + if state.ids.buffs.len() < buff_count { + state.update(|state| state.ids.buffs.resize(buff_count, gen)); }; - if state.ids.debuffs.len() < debuffs_vec.len() { - state.update(|state| { - state - .ids - .debuffs - .resize(debuffs_vec.len(), &mut ui.widget_id_generator()) - }); + if state.ids.debuffs.len() < debuff_count { + state.update(|state| state.ids.debuffs.resize(debuff_count, gen)); }; - if state.ids.buff_timers.len() < buffs_vec.len() { - state.update(|state| { - state - .ids - .buff_timers - .resize(buffs_vec.len(), &mut ui.widget_id_generator()) - }); + if state.ids.buff_timers.len() < buff_count { + state.update(|state| state.ids.buff_timers.resize(buff_count, gen)); }; - if state.ids.debuff_timers.len() < debuffs_vec.len() { - state.update(|state| { - state - .ids - .debuff_timers - .resize(debuffs_vec.len(), &mut ui.widget_id_generator()) - }); + if state.ids.debuff_timers.len() < debuff_count { + state.update(|state| state.ids.debuff_timers.resize(debuff_count, gen)); }; 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); // Create Buff Widgets - for (i, buff) in buffs_vec.iter().enumerate() { - if i < 22 { - // Limit displayed buffs + state + .ids + .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 { BuffId::Regeneration { duration, .. } => duration.unwrap().as_secs_f32(), _ => 10.0, @@ -205,7 +200,7 @@ impl<'a> Widget for BuffsBar<'a> { } else { Some(norm_col) }) - .set(state.ids.buffs[i], ui); + .set(id, ui); // Create Buff tooltip let title = match buff.id { BuffId::Regeneration { .. } => { @@ -239,7 +234,7 @@ impl<'a> Widget for BuffsBar<'a> { _ => self.imgs.nothing, }) .w_h(20.0, 20.0) - .middle_of(state.ids.buffs[i]) + .middle_of(id) .with_tooltip( self.tooltip_manager, title, @@ -247,18 +242,28 @@ impl<'a> Widget for BuffsBar<'a> { &buffs_tooltip, BUFF_COLOR, ) - .set(state.ids.buff_timers[i], ui) + .set(timer_id, ui) .was_clicked() { event.push(Event::RemoveBuff(buff.id)); }; - }; - } + }); // Create Debuff Widgets - for (i, debuff) in debuffs_vec.iter().enumerate() { - if i < 22 { - // Limit displayed buffs - + state + .ids + .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 { BuffId::Bleeding { duration, .. } => { duration.unwrap_or(Duration::from_secs(60)).as_secs_f32() @@ -292,7 +297,7 @@ impl<'a> Widget for BuffsBar<'a> { } else { Some(norm_col) }) - .set(state.ids.debuffs[i], ui); + .set(id, ui); // Create Debuff tooltip let title = match debuff.id { BuffId::Bleeding { .. } => { @@ -324,7 +329,7 @@ impl<'a> Widget for BuffsBar<'a> { _ => self.imgs.nothing, }) .w_h(20.0, 20.0) - .middle_of(state.ids.debuffs[i]) + .middle_of(id) .with_tooltip( self.tooltip_manager, title, @@ -332,10 +337,10 @@ impl<'a> Widget for BuffsBar<'a> { &buffs_tooltip, DEBUFF_COLOR, ) - .set(state.ids.debuff_timers[i], ui); - }; - } + .set(timer_id, ui); + }); } + if let BuffPosition::Map = buff_position { // Alignment Rectangle::fill_with([tweak!(300.0), tweak!(280.0)], color::RED) diff --git a/voxygen/src/hud/group.rs b/voxygen/src/hud/group.rs index 06503bc9d8..4c1385593c 100644 --- a/voxygen/src/hud/group.rs +++ b/voxygen/src/hud/group.rs @@ -5,7 +5,7 @@ use super::{ }; use crate::{ - hud::{get_buff_info, BuffInfo}, + hud::get_buff_info, i18n::VoxygenLocalization, settings::Settings, ui::{fonts::ConrodVoxygenFonts, ImageFrame, Tooltip, TooltipManager, Tooltipable}, @@ -75,7 +75,6 @@ pub struct Group<'a> { localized_strings: &'a std::sync::Arc, pulse: f32, global_state: &'a GlobalState, - buffs: &'a Buffs, tooltip_manager: &'a mut TooltipManager, #[conrod(common_builder)] @@ -94,7 +93,6 @@ impl<'a> Group<'a> { localized_strings: &'a std::sync::Arc, pulse: f32, global_state: &'a GlobalState, - buffs: &'a Buffs, tooltip_manager: &'a mut TooltipManager, ) -> Self { Self { @@ -107,7 +105,6 @@ impl<'a> Group<'a> { localized_strings, pulse, global_state, - buffs, tooltip_manager, common: widget::CommonBuilder::default(), } @@ -145,7 +142,6 @@ impl<'a> Widget for Group<'a> { let widget::UpdateArgs { state, ui, .. } = args; let mut events = Vec::new(); 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 buffs_tooltip = Tooltip::new({ // Edge images [t, b, r, l] @@ -333,6 +329,8 @@ impl<'a> Widget for Group<'a> { .ecs() .read_resource::(); + // 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() { self.show.group = true; 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); } if let Some(buffs) = buffs { - let mut buffs_vec = Vec::::new(); - for buff in buffs.active_buffs.clone() { - let info = get_buff_info(buff); - buffs_vec.push(info); + // Limit displayed buffs to 11 + let buff_count = buffs.active_buffs.len().min(11); + total_buff_count += buff_count; + 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 - for (x, buff) in buffs_vec.iter().enumerate() { - if x < 11 { - // Limit displayed buffs + let mut prev_id = None; + state + .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 { BuffId::Regeneration { duration, .. } => { duration.unwrap().as_secs_f32() @@ -485,22 +485,23 @@ impl<'a> Widget for Group<'a> { BuffId::Cursed { .. } => self.imgs.debuff_skull_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( state.ids.member_panels_frame[i], -21.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 .color(if current_duration < 10.0 { Some(pulsating_col) } else { Some(norm_col) }) - .set(state.ids.buffs[state.ids.buffs.len() - buffs_vec.len() + x/*x*/], ui); + .set(id, ui); // Create Buff tooltip let title = match buff.id { BuffId::Regeneration { .. } => { @@ -538,17 +539,20 @@ impl<'a> Widget for Group<'a> { _ => self.imgs.nothing, }) .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( self.tooltip_manager, title, &desc, &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 { // Values N.A. Text::new(&stats.name.to_string()) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 1576fb3ff1..ec48a034b3 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1803,7 +1803,6 @@ impl Hud { } } // Group Window - let buffs = buffs.get(client.entity()).unwrap(); for event in Group::new( &mut self.show, client, @@ -1814,7 +1813,6 @@ impl Hud { &self.voxygen_i18n, self.pulse, &global_state, - &buffs, tooltip_manager, ) .set(self.ids.group_window, ui_widgets) @@ -2728,7 +2726,7 @@ pub fn get_quality_col(item: &I) -> Color { } } // Get info about applied buffs -fn get_buff_info(buff: comp::Buff) -> BuffInfo { +fn get_buff_info(buff: &comp::Buff) -> BuffInfo { BuffInfo { id: buff.id, is_buff: buff diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index 8ee435ea1f..da8861dcc8 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -3,7 +3,7 @@ use super::{ REGION_COLOR, SAY_COLOR, STAMINA_COLOR, TELL_COLOR, TEXT_BG, TEXT_COLOR, }; use crate::{ - hud::{get_buff_info, BuffInfo}, + hud::get_buff_info, i18n::VoxygenLocalization, settings::GameplaySettings, ui::{fonts::ConrodVoxygenFonts, Ingameable}, @@ -135,14 +135,18 @@ impl<'a> Ingameable for Overhead<'a> { // - 1 Rect::new for mana // If there are Buffs // - 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 // - 2 Text::new for speech bubble // - 1 Image::new for icon // - 10 Image::new for speech bubble (9-slice + tail) self.info.map_or(0, |info| { 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) { 5 + if info.energy.is_some() { 1 } else { 0 } } else { @@ -191,11 +195,6 @@ impl<'a> Widget for Overhead<'a> { } else { MANA_BAR_Y + 32.0 }; - let mut buffs_vec = Vec::::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 }; // Show K for numbers above 10^3 and truncate them // Show M for numbers above 10^6 and truncate them @@ -211,76 +210,80 @@ impl<'a> Widget for Overhead<'a> { }; // Buffs // Alignment + let buff_count = buffs.active_buffs.len().min(11); Rectangle::fill_with([tweak!(168.0), tweak!(100.0)], color::TRANSPARENT) .x_y(-1.0, name_y + tweak!(60.0)) .parent(id) .set(state.ids.buffs_align, ui); - if state.ids.buffs.len() < buffs_vec.len() { - state.update(|state| { - state - .ids - .buffs - .resize(buffs_vec.len(), &mut ui.widget_id_generator()) - }); + + let gen = &mut ui.widget_id_generator(); + if state.ids.buffs.len() < buff_count { + state.update(|state| state.ids.buffs.resize(buff_count, gen)); }; - if state.ids.buff_timers.len() < buffs_vec.len() { - state.update(|state| { - state - .ids - .buff_timers - .resize(buffs_vec.len(), &mut ui.widget_id_generator()) - }); + if state.ids.buff_timers.len() < buff_count { + state.update(|state| state.ids.buff_timers.resize(buff_count, gen)); }; + 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 norm_col = Color::Rgba(1.0, 1.0, 1.0, 1.0); // Create Buff Widgets - for (i, buff) in buffs_vec.iter().enumerate() { - if i < 11 && self.bubble.is_none() { - // Limit displayed buffs - let max_duration = match buff.id { - BuffId::Regeneration { duration, .. } => duration.unwrap().as_secs_f32(), - _ => 10.0, - }; - let current_duration = buff.dur; - let duration_percentage = (current_duration / max_duration * 1000.0) as u32; // Percentage to determine which frame of the timer overlay is displayed - let buff_img = match buff.id { - BuffId::Regeneration { .. } => self.imgs.buff_plus_0, - BuffId::Bleeding { .. } => self.imgs.debuff_bleed_0, - BuffId::Cursed { .. } => self.imgs.debuff_skull_0, - }; - let buff_widget = Image::new(buff_img).w_h(20.0, 20.0); - // Sort buffs into rows of 5 slots - let x = i % 5; - let y = i / 5; - let buff_widget = buff_widget.bottom_left_with_margins_on( - 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(state.ids.buffs[i], ui); + if self.bubble.is_none() { + state + .ids + .buffs + .iter() + .copied() + .zip(state.ids.buff_timers.iter().copied()) + .zip(buffs.active_buffs.iter().map(get_buff_info)) + .enumerate() + .for_each(|(i, ((id, timer_id), buff))| { + // Limit displayed buffs + let max_duration = match buff.id { + BuffId::Regeneration { duration, .. } => { + duration.unwrap().as_secs_f32() + }, + _ => 10.0, + }; + let current_duration = buff.dur; + let duration_percentage = (current_duration / max_duration * 1000.0) as u32; // Percentage to determine which frame of the timer overlay is displayed + let buff_img = match buff.id { + BuffId::Regeneration { .. } => self.imgs.buff_plus_0, + BuffId::Bleeding { .. } => self.imgs.debuff_bleed_0, + BuffId::Cursed { .. } => self.imgs.debuff_skull_0, + }; + let buff_widget = Image::new(buff_img).w_h(20.0, 20.0); + // Sort buffs into rows of 5 slots + let x = i % 5; + let y = i / 5; + let buff_widget = buff_widget.bottom_left_with_margins_on( + 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 { - 875..=1000 => self.imgs.nothing, // 8/8 - 750..=874 => self.imgs.buff_0, // 7/8 - 625..=749 => self.imgs.buff_1, // 6/8 - 500..=624 => self.imgs.buff_2, // 5/8 - 375..=499 => self.imgs.buff_3, // 4/8 - 250..=374 => self.imgs.buff_4, //3/8 - 125..=249 => self.imgs.buff_5, // 2/8 - 0..=124 => self.imgs.buff_6, // 1/8 - _ => self.imgs.nothing, - }) - .w_h(20.0, 20.0) - .middle_of(state.ids.buffs[i]) - .set(state.ids.buff_timers[i], ui); - }; + Image::new(match duration_percentage as u64 { + 875..=1000 => self.imgs.nothing, // 8/8 + 750..=874 => self.imgs.buff_0, // 7/8 + 625..=749 => self.imgs.buff_1, // 6/8 + 500..=624 => self.imgs.buff_2, // 5/8 + 375..=499 => self.imgs.buff_3, // 4/8 + 250..=374 => self.imgs.buff_4, //3/8 + 125..=249 => self.imgs.buff_5, // 2/8 + 0..=124 => self.imgs.buff_6, // 1/8 + _ => self.imgs.nothing, + }) + .w_h(20.0, 20.0) + .middle_of(id) + .set(timer_id, ui); + }); } // Name Text::new(name)