From e546a1d1a90702356eb72c281c99135db4888758 Mon Sep 17 00:00:00 2001 From: voxal Date: Fri, 31 Dec 2021 13:42:01 -0800 Subject: [PATCH 1/7] Allow players to see own speech bubbles. --- assets/voxygen/i18n/en/hud/settings.ron | 3 +- voxygen/src/hud/mod.rs | 8 +- voxygen/src/hud/overhead.rs | 742 ++++++++++--------- voxygen/src/hud/settings_window/interface.rs | 32 +- voxygen/src/session/settings_change.rs | 4 + voxygen/src/settings/interface.rs | 2 + 6 files changed, 420 insertions(+), 371 deletions(-) diff --git a/assets/voxygen/i18n/en/hud/settings.ron b/assets/voxygen/i18n/en/hud/settings.ron index 7f8f21ee63..e106f1efb1 100644 --- a/assets/voxygen/i18n/en/hud/settings.ron +++ b/assets/voxygen/i18n/en/hud/settings.ron @@ -30,6 +30,7 @@ "hud.settings.incoming_damage": "Incoming Damage", "hud.settings.cumulated_incoming_damage": "Cumulated Incoming Damage", "hud.settings.speech_bubble": "Speech Bubble", + "hud.settings.speech_bubble_self": "Show Own Speech Bubbles", "hud.settings.speech_bubble_dark_mode": "Speech Bubble Dark Mode", "hud.settings.speech_bubble_icon": "Speech Bubble Icon", "hud.settings.energybar_numbers": "Energybar Numbers", @@ -38,7 +39,7 @@ "hud.settings.percentages": "Percentages", "hud.settings.chat": "Chat", "hud.settings.background_opacity": "Background Opacity", - "hud.settings.chat_character_name": "Character Names in chat", + "hud.settings.chat_character_name": "Character Names in Chat", "hud.settings.loading_tips": "Loading Screen Tips", "hud.settings.reset_interface": "Reset to Defaults", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 97e077b6f1..9fc4eee078 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1777,7 +1777,7 @@ impl Hud { let speech_bubbles = &self.speech_bubbles; // Render overhead name tags and health bars - for (pos, info, bubble, _, _, health, _, height_offset, hpfl, in_group) in ( + for (pos, info, bubble, _, _, health, _, height_offset, hpfl, in_group, is_me) in ( &entities, &pos, interpolated.maybe(), @@ -1797,8 +1797,7 @@ impl Hud { .join() .filter(|t| { let health = t.5; - let entity = t.0; - entity != me && !health.map_or(false, |h| h.is_dead) + !health.map_or(false, |h| h.is_dead) }) .filter_map( |( @@ -1821,6 +1820,7 @@ impl Hud { // Use interpolated position if available let pos = interpolated.map_or(pos.0, |i| i.pos); let in_group = client.group_members().contains_key(uid); + let is_me = entity == me; // TODO: once the site2 rework lands and merchants have dedicated stalls or // buildings, they no longer need to be emphasized via the higher overhead // text radius relative to other NPCs @@ -1884,6 +1884,7 @@ impl Hud { body.height() * scale.map_or(1.0, |s| s.0) + 0.5, hpfl, in_group, + is_me, ) }) }, @@ -1903,6 +1904,7 @@ impl Hud { info, bubble, in_group, + is_me, &global_state.settings.interface, self.pulse, i18n, diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index d3e3cd8a1e..f8d501b26b 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -81,6 +81,7 @@ pub struct Overhead<'a> { info: Option>, bubble: Option<&'a SpeechBubble>, in_group: bool, + is_me: bool, settings: &'a InterfaceSettings, pulse: f32, i18n: &'a Localization, @@ -96,6 +97,7 @@ impl<'a> Overhead<'a> { info: Option>, bubble: Option<&'a SpeechBubble>, in_group: bool, + is_me: bool, settings: &'a InterfaceSettings, pulse: f32, i18n: &'a Localization, @@ -106,6 +108,7 @@ impl<'a> Overhead<'a> { info, bubble, in_group, + is_me, settings, pulse, i18n, @@ -179,115 +182,119 @@ impl<'a> Widget for Overhead<'a> { const BARSIZE: f64 = 2.0; // Scaling const MANA_BAR_HEIGHT: f64 = BARSIZE * 1.5; const MANA_BAR_Y: f64 = MANA_BAR_HEIGHT / 2.0; - if let Some(Info { - name, - health, - buffs, - energy, - combat_rating, - }) = self.info - { - // Used to set healthbar colours based on hp_percentage - let hp_percentage = health.map_or(100.0, |h| { - f64::from(h.current() / h.base_max().max(h.maximum()) * 100.0) - }); - // Compare levels to decide if a skull is shown - let health_current = health.map_or(1.0, |h| f64::from(h.current())); - let health_max = health.map_or(1.0, |h| f64::from(h.maximum())); - let name_y = if (health_current - health_max).abs() < 1e-6 { - MANA_BAR_Y + 20.0 - } else { - MANA_BAR_Y + 32.0 - }; - 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 - 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)), - }; - // Buffs - // Alignment - let buff_count = buffs.kinds.len().min(11); - Rectangle::fill_with([168.0, 100.0], color::TRANSPARENT) - .x_y(-1.0, name_y + 60.0) - .parent(id) - .set(state.ids.buffs_align, ui); + // Only render info if not me + if !self.is_me { + if let Some(Info { + name, + health, + buffs, + energy, + combat_rating, + }) = self.info + { + // Used to set healthbar colours based on hp_percentage + let hp_percentage = health.map_or(100.0, |h| { + f64::from(h.current() / h.base_max().max(h.maximum()) * 100.0) + }); + // Compare levels to decide if a skull is shown + let health_current = health.map_or(1.0, |h| f64::from(h.current())); + let health_max = health.map_or(1.0, |h| f64::from(h.maximum())); + let name_y = if (health_current - health_max).abs() < 1e-6 { + MANA_BAR_Y + 20.0 + } else { + MANA_BAR_Y + 32.0 + }; + 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 + 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)), + }; + // Buffs + // Alignment + let buff_count = buffs.kinds.len().min(11); + Rectangle::fill_with([168.0, 100.0], color::TRANSPARENT) + .x_y(-1.0, name_y + 60.0) + .parent(id) + .set(state.ids.buffs_align, ui); - 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() < buff_count { - state.update(|state| state.ids.buff_timers.resize(buff_count, gen)); - }; + 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() < 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 - if self.bubble.is_none() { - state - .ids - .buffs - .iter() - .copied() - .zip(state.ids.buff_timers.iter().copied()) - .zip(buffs.iter_active().map(get_buff_info)) - .enumerate() - .for_each(|(i, ((id, timer_id), buff))| { - // Limit displayed buffs - let max_duration = buff.data.duration; - let current_duration = buff.dur; - let duration_percentage = current_duration.map_or(1000.0, |cur| { - max_duration.map_or(1000.0, |max| { - cur.as_secs_f32() / max.as_secs_f32() * 1000.0 + 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 + if self.bubble.is_none() { + state + .ids + .buffs + .iter() + .copied() + .zip(state.ids.buff_timers.iter().copied()) + .zip(buffs.iter_active().map(get_buff_info)) + .enumerate() + .for_each(|(i, ((id, timer_id), buff))| { + // Limit displayed buffs + let max_duration = buff.data.duration; + let current_duration = buff.dur; + let duration_percentage = current_duration.map_or(1000.0, |cur| { + max_duration.map_or(1000.0, |max| { + cur.as_secs_f32() / max.as_secs_f32() * 1000.0 + }) + }) as u32; // Percentage to determine which frame of the timer overlay is displayed + let buff_img = get_buff_image(buff.kind, self.imgs); + 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 + .map_or(false, |cur| cur.as_secs_f32() < 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, }) - }) as u32; // Percentage to determine which frame of the timer overlay is displayed - let buff_img = get_buff_image(buff.kind, self.imgs); - 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.map_or(false, |cur| cur.as_secs_f32() < 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(id) - .set(timer_id, ui); - }); - } - // Name - Text::new(name) + .w_h(20.0, 20.0) + .middle_of(id) + .set(timer_id, ui); + }); + } + // Name + Text::new(name) //Text::new(&format!("{} [{:?}]", name, combat_rating)) // <- Uncomment to debug combat ratings .font_id(self.fonts.cyri.conrod_id) .font_size(font_size) @@ -295,7 +302,7 @@ impl<'a> Widget for Overhead<'a> { .x_y(-1.0, name_y) .parent(id) .set(state.ids.name_bg, ui); - Text::new(name) + Text::new(name) //Text::new(&format!("{} [{:?}]", name, combat_rating)) // <- Uncomment to debug combat ratings .font_id(self.fonts.cyri.conrod_id) .font_size(font_size) @@ -310,292 +317,297 @@ impl<'a> Widget for Overhead<'a> { .parent(id) .set(state.ids.name, ui); - match health { - Some(health) - if should_show_healthbar(health) || decayed_health_displayed(health) => - { - // 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.93, 0.59, 0.03, hp_ani); - let decayed_health = f64::from(1.0 - health.maximum() / health.base_max()); - // Background - Image::new(if self.in_group {self.imgs.health_bar_group_bg} else {self.imgs.enemy_health_bg}) + match health { + Some(health) + if should_show_healthbar(health) || decayed_health_displayed(health) => + { + // 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.93, 0.59, 0.03, hp_ani); + let decayed_health = f64::from(1.0 - health.maximum() / health.base_max()); + // Background + Image::new(if self.in_group {self.imgs.health_bar_group_bg} else {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 - let size_factor = (hp_percentage / 100.0) * BARSIZE; - let w = if self.in_group { - 82.0 * size_factor - } else { - 73.0 * size_factor - }; - let h = 6.0 * BARSIZE; - let x = if self.in_group { - (0.0 + (hp_percentage / 100.0 * 41.0 - 41.0)) * BARSIZE - } else { - (4.5 + (hp_percentage / 100.0 * 36.45 - 36.45)) * BARSIZE - }; - Image::new(self.imgs.enemy_bar) - .w_h(w, h) - .x_y(x, MANA_BAR_Y + 8.0) - .color(if self.in_group { - // Different HP bar colors only for group members - Some(match hp_percentage { - x if (0.0..25.0).contains(&x) => crit_hp_color, - x if (25.0..50.0).contains(&x) => LOW_HP_COLOR, - _ => HP_COLOR, - }) - } else { - Some(ENEMY_HP_COLOR) - }) - .parent(id) - .set(state.ids.health_bar, ui); - - if decayed_health > 0.0 { - let x_decayed = if self.in_group { - (0.0 - (decayed_health * 41.0 - 41.0)) * BARSIZE - } else { - (4.5 - (decayed_health * 36.45 - 36.45)) * BARSIZE - }; - - let decay_bar_len = decayed_health - * if self.in_group { - 82.0 * BARSIZE - } else { - 73.0 * BARSIZE - }; - Image::new(self.imgs.enemy_bar) - .w_h(decay_bar_len, h) - .x_y(x_decayed, MANA_BAR_Y + 8.0) - .color(Some(QUALITY_EPIC)) - .parent(id) - .set(state.ids.decay_bar, ui); - } - let mut txt = format!("{}/{}", health_cur_txt, health_max_txt); - if health.is_dead { - txt = self.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 = f64::from(energy.current() / energy.maximum()); - let size_factor = energy_factor * BARSIZE; + // % HP Filling + let size_factor = (hp_percentage / 100.0) * BARSIZE; let w = if self.in_group { - 80.0 * size_factor + 82.0 * size_factor } else { - 72.0 * size_factor + 73.0 * size_factor }; + let h = 6.0 * BARSIZE; let x = if self.in_group { - ((0.0 + (energy_factor * 40.0)) - 40.0) * BARSIZE + (0.0 + (hp_percentage / 100.0 * 41.0 - 41.0)) * BARSIZE } else { - ((3.5 + (energy_factor * 36.5)) - 36.45) * BARSIZE + (4.5 + (hp_percentage / 100.0 * 36.45 - 36.45)) * BARSIZE }; - Rectangle::fill_with([w, MANA_BAR_HEIGHT], STAMINA_COLOR) - .x_y( - x, MANA_BAR_Y, //-32.0, - ) + Image::new(self.imgs.enemy_bar) + .w_h(w, h) + .x_y(x, MANA_BAR_Y + 8.0) + .color(if self.in_group { + // Different HP bar colors only for group members + Some(match hp_percentage { + x if (0.0..25.0).contains(&x) => crit_hp_color, + x if (25.0..50.0).contains(&x) => LOW_HP_COLOR, + _ => HP_COLOR, + }) + } else { + Some(ENEMY_HP_COLOR) + }) .parent(id) - .set(state.ids.mana_bar, ui); - } + .set(state.ids.health_bar, ui); - // Foreground - Image::new(if self.in_group {self.imgs.health_bar_group} else {self.imgs.enemy_health}) + if decayed_health > 0.0 { + let x_decayed = if self.in_group { + (0.0 - (decayed_health * 41.0 - 41.0)) * BARSIZE + } else { + (4.5 - (decayed_health * 36.45 - 36.45)) * BARSIZE + }; + + let decay_bar_len = decayed_health + * if self.in_group { + 82.0 * BARSIZE + } else { + 73.0 * BARSIZE + }; + Image::new(self.imgs.enemy_bar) + .w_h(decay_bar_len, h) + .x_y(x_decayed, MANA_BAR_Y + 8.0) + .color(Some(QUALITY_EPIC)) + .parent(id) + .set(state.ids.decay_bar, ui); + } + let mut txt = format!("{}/{}", health_cur_txt, health_max_txt); + if health.is_dead { + txt = self.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 = f64::from(energy.current() / energy.maximum()); + let size_factor = energy_factor * BARSIZE; + let w = if self.in_group { + 80.0 * size_factor + } else { + 72.0 * size_factor + }; + let x = if self.in_group { + ((0.0 + (energy_factor * 40.0)) - 40.0) * BARSIZE + } else { + ((3.5 + (energy_factor * 36.5)) - 36.45) * BARSIZE + }; + Rectangle::fill_with([w, MANA_BAR_HEIGHT], STAMINA_COLOR) + .x_y( + x, MANA_BAR_Y, //-32.0, + ) + .parent(id) + .set(state.ids.mana_bar, ui); + } + + // Foreground + Image::new(if self.in_group {self.imgs.health_bar_group} else {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); - let indicator_col = cr_color(combat_rating); - let artifact_diffculty = 122.0; + let indicator_col = cr_color(combat_rating); + let artifact_diffculty = 122.0; - if combat_rating > artifact_diffculty && !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::() < 0.9 { - self.imgs.skull_2 + if combat_rating > artifact_diffculty && !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::() < 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 { - 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 { - Image::new(if self.in_group { - self.imgs.nothing - } else { - self.imgs.combat_rating_ico - }) - .w_h(7.0 * BARSIZE, 7.0 * BARSIZE) - .x_y(-37.0 * BARSIZE, MANA_BAR_Y + 6.0) - .color(Some(indicator_col)) - .parent(id) - .set(state.ids.level, ui); - } - }, - _ => {}, + Image::new(if self.in_group { + self.imgs.nothing + } else { + self.imgs.combat_rating_ico + }) + .w_h(7.0 * BARSIZE, 7.0 * BARSIZE) + .x_y(-37.0 * BARSIZE, MANA_BAR_Y + 6.0) + .color(Some(indicator_col)) + .parent(id) + .set(state.ids.level, ui); + } + }, + _ => {}, + } } } // Speech bubble - if let Some(bubble) = self.bubble { - let dark_mode = self.settings.speech_bubble_dark_mode; - let localizer = |s: &str, i| -> String { self.i18n.get_variation(s, i).to_string() }; - let bubble_contents: String = bubble.message(localizer); - let (text_color, shadow_color) = bubble_color(bubble, dark_mode); - let mut text = Text::new(&bubble_contents) - .color(text_color) - .font_id(self.fonts.cyri.conrod_id) - .font_size(18) - .up_from(state.ids.name, 26.0) - .x_align_to(state.ids.name, Align::Middle) - .parent(id); - - if let Some(w) = text.get_w(ui) { - if w > MAX_BUBBLE_WIDTH { - text = text.w(MAX_BUBBLE_WIDTH); + // Only render if setting + if !self.is_me || (self.is_me && self.settings.speech_bubble_self) { + if let Some(bubble) = self.bubble { + let dark_mode = self.settings.speech_bubble_dark_mode; + let localizer = |s: &str, i| -> String { self.i18n.get_variation(s, i).to_string() }; + let bubble_contents: String = bubble.message(localizer); + let (text_color, shadow_color) = bubble_color(bubble, dark_mode); + let mut text = Text::new(&bubble_contents) + .color(text_color) + .font_id(self.fonts.cyri.conrod_id) + .font_size(18) + .up_from(state.ids.name, 26.0) + .x_align_to(state.ids.name, Align::Middle) + .parent(id); + + if let Some(w) = text.get_w(ui) { + if w > MAX_BUBBLE_WIDTH { + text = text.w(MAX_BUBBLE_WIDTH); + } } - } - Image::new(if dark_mode { - self.imgs.dark_bubble_top_left - } else { - self.imgs.speech_bubble_top_left - }) - .w_h(16.0, 16.0) - .top_left_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_top_left, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_top - } else { - self.imgs.speech_bubble_top - }) - .h(16.0) - .padded_w_of(state.ids.speech_bubble_text, -4.0) - .mid_top_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_top, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_top_right - } else { - self.imgs.speech_bubble_top_right - }) - .w_h(16.0, 16.0) - .top_right_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_top_right, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_left - } else { - self.imgs.speech_bubble_left - }) - .w(16.0) - .padded_h_of(state.ids.speech_bubble_text, -4.0) - .mid_left_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_left, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_mid - } else { - self.imgs.speech_bubble_mid - }) - .padded_wh_of(state.ids.speech_bubble_text, -4.0) - .top_left_with_margin_on(state.ids.speech_bubble_text, -4.0) - .parent(id) - .set(state.ids.speech_bubble_mid, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_right - } else { - self.imgs.speech_bubble_right - }) - .w(16.0) - .padded_h_of(state.ids.speech_bubble_text, -4.0) - .mid_right_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_right, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_bottom_left - } else { - self.imgs.speech_bubble_bottom_left - }) - .w_h(16.0, 16.0) - .bottom_left_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_bottom_left, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_bottom - } else { - self.imgs.speech_bubble_bottom - }) - .h(16.0) - .padded_w_of(state.ids.speech_bubble_text, -4.0) - .mid_bottom_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_bottom, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_bottom_right - } else { - self.imgs.speech_bubble_bottom_right - }) - .w_h(16.0, 16.0) - .bottom_right_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_bottom_right, ui); - let tail = Image::new(if dark_mode { - self.imgs.dark_bubble_tail - } else { - self.imgs.speech_bubble_tail - }) - .parent(id) - .mid_bottom_with_margin_on(state.ids.speech_bubble_text, -32.0); - - if dark_mode { - tail.w_h(22.0, 13.0) - } else { - tail.w_h(22.0, 28.0) - } - .set(state.ids.speech_bubble_tail, ui); - - let mut text_shadow = Text::new(&bubble_contents) - .color(shadow_color) - .font_id(self.fonts.cyri.conrod_id) - .font_size(18) - .x_relative_to(state.ids.speech_bubble_text, 1.0) - .y_relative_to(state.ids.speech_bubble_text, -1.0) - .parent(id); - // Move text to front (conrod depth is lowest first; not a z-index) - text.depth(text_shadow.get_depth() - 1.0) - .set(state.ids.speech_bubble_text, ui); - if let Some(w) = text_shadow.get_w(ui) { - if w > MAX_BUBBLE_WIDTH { - text_shadow = text_shadow.w(MAX_BUBBLE_WIDTH); - } - } - text_shadow.set(state.ids.speech_bubble_shadow, ui); - let icon = if self.settings.speech_bubble_icon { - bubble_icon(bubble, self.imgs) - } else { - self.imgs.nothing - }; - Image::new(icon) + Image::new(if dark_mode { + self.imgs.dark_bubble_top_left + } else { + self.imgs.speech_bubble_top_left + }) .w_h(16.0, 16.0) - .top_left_with_margin_on(state.ids.speech_bubble_text, -16.0) - // TODO: Figure out whether this should be parented. - // .parent(id) - .set(state.ids.speech_bubble_icon, ui); + .top_left_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_top_left, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_top + } else { + self.imgs.speech_bubble_top + }) + .h(16.0) + .padded_w_of(state.ids.speech_bubble_text, -4.0) + .mid_top_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_top, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_top_right + } else { + self.imgs.speech_bubble_top_right + }) + .w_h(16.0, 16.0) + .top_right_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_top_right, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_left + } else { + self.imgs.speech_bubble_left + }) + .w(16.0) + .padded_h_of(state.ids.speech_bubble_text, -4.0) + .mid_left_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_left, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_mid + } else { + self.imgs.speech_bubble_mid + }) + .padded_wh_of(state.ids.speech_bubble_text, -4.0) + .top_left_with_margin_on(state.ids.speech_bubble_text, -4.0) + .parent(id) + .set(state.ids.speech_bubble_mid, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_right + } else { + self.imgs.speech_bubble_right + }) + .w(16.0) + .padded_h_of(state.ids.speech_bubble_text, -4.0) + .mid_right_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_right, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_bottom_left + } else { + self.imgs.speech_bubble_bottom_left + }) + .w_h(16.0, 16.0) + .bottom_left_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_bottom_left, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_bottom + } else { + self.imgs.speech_bubble_bottom + }) + .h(16.0) + .padded_w_of(state.ids.speech_bubble_text, -4.0) + .mid_bottom_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_bottom, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_bottom_right + } else { + self.imgs.speech_bubble_bottom_right + }) + .w_h(16.0, 16.0) + .bottom_right_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_bottom_right, ui); + let tail = Image::new(if dark_mode { + self.imgs.dark_bubble_tail + } else { + self.imgs.speech_bubble_tail + }) + .parent(id) + .mid_bottom_with_margin_on(state.ids.speech_bubble_text, -32.0); + + if dark_mode { + tail.w_h(22.0, 13.0) + } else { + tail.w_h(22.0, 28.0) + } + .set(state.ids.speech_bubble_tail, ui); + + let mut text_shadow = Text::new(&bubble_contents) + .color(shadow_color) + .font_id(self.fonts.cyri.conrod_id) + .font_size(18) + .x_relative_to(state.ids.speech_bubble_text, 1.0) + .y_relative_to(state.ids.speech_bubble_text, -1.0) + .parent(id); + // Move text to front (conrod depth is lowest first; not a z-index) + text.depth(text_shadow.get_depth() - 1.0) + .set(state.ids.speech_bubble_text, ui); + if let Some(w) = text_shadow.get_w(ui) { + if w > MAX_BUBBLE_WIDTH { + text_shadow = text_shadow.w(MAX_BUBBLE_WIDTH); + } + } + text_shadow.set(state.ids.speech_bubble_shadow, ui); + let icon = if self.settings.speech_bubble_icon { + bubble_icon(bubble, self.imgs) + } else { + self.imgs.nothing + }; + Image::new(icon) + .w_h(16.0, 16.0) + .top_left_with_margin_on(state.ids.speech_bubble_text, -16.0) + // TODO: Figure out whether this should be parented. + // .parent(id) + .set(state.ids.speech_bubble_icon, ui); + } } + } } diff --git a/voxygen/src/hud/settings_window/interface.rs b/voxygen/src/hud/settings_window/interface.rs index 0aee62f2f1..ff868cc3f2 100644 --- a/voxygen/src/hud/settings_window/interface.rs +++ b/voxygen/src/hud/settings_window/interface.rs @@ -88,6 +88,8 @@ widget_ids! { sct_batch_inc_radio, // speech_bubble_text, + speech_bubble_self_text, + speech_bubble_self_button, speech_bubble_dark_mode_text, speech_bubble_dark_mode_button, speech_bubble_icon_text, @@ -846,7 +848,7 @@ impl<'a> Widget for Interface<'a> { .set(state.ids.sct_batch_inc_text, ui); } - // Speech bubble dark mode + // Speech bubbles Text::new(self.localized_strings.get("hud.settings.speech_bubble")) .down_from( if self.global_state.settings.interface.sct { @@ -862,12 +864,38 @@ impl<'a> Widget for Interface<'a> { .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) .set(state.ids.speech_bubble_text, ui); + + // Show own speech bubbles + let speech_bubble_self = ToggleButton::new( + self.global_state.settings.interface.speech_bubble_self, + self.imgs.checkbox, + self.imgs.checkbox_checked, + ) + .down_from(state.ids.speech_bubble_text, 10.0) + .w_h(18.0, 18.0) + .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) + .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) + .set(state.ids.speech_bubble_self_button, ui); + if self.global_state.settings.interface.speech_bubble_self != speech_bubble_self { + events.push(SpeechBubbleSelf(speech_bubble_self)); + } + Text::new( + self.localized_strings + .get("hud.settings.speech_bubble_self"), + ) + .right_from(state.ids.speech_bubble_self_button, 10.0) + .font_size(self.fonts.cyri.scale(15)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.speech_bubble_self_text, ui); + + // Speech bubble dark mode let speech_bubble_dark_mode = ToggleButton::new( self.global_state.settings.interface.speech_bubble_dark_mode, self.imgs.checkbox, self.imgs.checkbox_checked, ) - .down_from(state.ids.speech_bubble_text, 10.0) + .down_from(state.ids.speech_bubble_self_button, 10.0) .w_h(18.0, 18.0) .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) diff --git a/voxygen/src/session/settings_change.rs b/voxygen/src/session/settings_change.rs index e2d9e3dbf8..cbe0814e6a 100644 --- a/voxygen/src/session/settings_change.rs +++ b/voxygen/src/session/settings_change.rs @@ -93,6 +93,7 @@ pub enum Interface { Sct(bool), SctPlayerBatch(bool), SctDamageBatch(bool), + SpeechBubbleSelf(bool), SpeechBubbleDarkMode(bool), SpeechBubbleIcon(bool), ToggleHelp(bool), @@ -436,6 +437,9 @@ impl SettingsChange { Interface::SctDamageBatch(sct_damage_batch) => { settings.interface.sct_damage_batch = sct_damage_batch; }, + Interface::SpeechBubbleSelf(sbdm) => { + settings.interface.speech_bubble_self = sbdm; + }, Interface::SpeechBubbleDarkMode(sbdm) => { settings.interface.speech_bubble_dark_mode = sbdm; }, diff --git a/voxygen/src/settings/interface.rs b/voxygen/src/settings/interface.rs index 7618ba0f46..cb48e1d9af 100644 --- a/voxygen/src/settings/interface.rs +++ b/voxygen/src/settings/interface.rs @@ -16,6 +16,7 @@ pub struct InterfaceSettings { pub sct: bool, pub sct_player_batch: bool, pub sct_damage_batch: bool, + pub speech_bubble_self: bool, pub speech_bubble_dark_mode: bool, pub speech_bubble_icon: bool, pub crosshair_opacity: f32, @@ -54,6 +55,7 @@ impl Default for InterfaceSettings { sct: true, sct_player_batch: false, sct_damage_batch: false, + speech_bubble_self: false, speech_bubble_dark_mode: false, speech_bubble_icon: true, crosshair_opacity: 0.6, From 0b4446ad813042c806579d0bf0dcfdf925fb6cde Mon Sep 17 00:00:00 2001 From: voxal Date: Fri, 31 Dec 2021 13:46:25 -0800 Subject: [PATCH 2/7] Add changes to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8175eb29b..ed6c41557d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added a setting to always show health and energy bars - Added a crafting station icon to the crafting menu sidebar for items that could be crafted at a crafting station - Added a setting to disable the hotkey hints +- Added a setting to see own speech bubbles - Added a credits screen in the main menu which shows attributions for assets - Shrubs, a system for spawning smaller tree-like plants into the world. - Waterfalls From 41e7e25f1634e6b248e8f1e3f9765f528ba9ed56 Mon Sep 17 00:00:00 2001 From: voxal Date: Fri, 31 Dec 2021 14:06:22 -0800 Subject: [PATCH 3/7] Fix clippy::nonminimal-bool --- voxygen/src/hud/overhead.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index f8d501b26b..1f6e36c7a3 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -458,8 +458,8 @@ impl<'a> Widget for Overhead<'a> { } } // Speech bubble - // Only render if setting - if !self.is_me || (self.is_me && self.settings.speech_bubble_self) { + // Only render if setting or other players + if !self.is_me || self.settings.speech_bubble_self { if let Some(bubble) = self.bubble { let dark_mode = self.settings.speech_bubble_dark_mode; let localizer = |s: &str, i| -> String { self.i18n.get_variation(s, i).to_string() }; From 471e75d8465ba449b0179fcfda46007554d22cf4 Mon Sep 17 00:00:00 2001 From: voxal Date: Sat, 1 Jan 2022 10:26:37 -0800 Subject: [PATCH 4/7] cargo fmt --- voxygen/src/hud/overhead.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index 1f6e36c7a3..397e01d97e 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -459,10 +459,11 @@ impl<'a> Widget for Overhead<'a> { } // Speech bubble // Only render if setting or other players - if !self.is_me || self.settings.speech_bubble_self { + if !self.is_me || self.settings.speech_bubble_self { if let Some(bubble) = self.bubble { let dark_mode = self.settings.speech_bubble_dark_mode; - let localizer = |s: &str, i| -> String { self.i18n.get_variation(s, i).to_string() }; + let localizer = + |s: &str, i| -> String { self.i18n.get_variation(s, i).to_string() }; let bubble_contents: String = bubble.message(localizer); let (text_color, shadow_color) = bubble_color(bubble, dark_mode); let mut text = Text::new(&bubble_contents) @@ -472,7 +473,7 @@ impl<'a> Widget for Overhead<'a> { .up_from(state.ids.name, 26.0) .x_align_to(state.ids.name, Align::Middle) .parent(id); - + if let Some(w) = text.get_w(ui) { if w > MAX_BUBBLE_WIDTH { text = text.w(MAX_BUBBLE_WIDTH); @@ -570,14 +571,14 @@ impl<'a> Widget for Overhead<'a> { }) .parent(id) .mid_bottom_with_margin_on(state.ids.speech_bubble_text, -32.0); - + if dark_mode { tail.w_h(22.0, 13.0) } else { tail.w_h(22.0, 28.0) } .set(state.ids.speech_bubble_tail, ui); - + let mut text_shadow = Text::new(&bubble_contents) .color(shadow_color) .font_id(self.fonts.cyri.conrod_id) @@ -607,7 +608,6 @@ impl<'a> Widget for Overhead<'a> { .set(state.ids.speech_bubble_icon, ui); } } - } } From 39e3d5c163202639b67cc24c8d94b05ed84dac63 Mon Sep 17 00:00:00 2001 From: voxal Date: Sat, 1 Jan 2022 12:54:16 -0800 Subject: [PATCH 5/7] Move changelog entry to bottom --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed6c41557d..1177557408 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added a setting to always show health and energy bars - Added a crafting station icon to the crafting menu sidebar for items that could be crafted at a crafting station - Added a setting to disable the hotkey hints -- Added a setting to see own speech bubbles - Added a credits screen in the main menu which shows attributions for assets - Shrubs, a system for spawning smaller tree-like plants into the world. - Waterfalls @@ -30,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Nightly linux Aarch64 builds are now produced (distribution via airshipper will follow soon) - Worldgen wildlife density modifier in features.ron - Rivers now make ambient sounds (again) +- Added a setting to see own speech bubbles ### Changed From e75f569014806398f71bc0fe6aaec0dfa5dbf6a0 Mon Sep 17 00:00:00 2001 From: voxal Date: Sat, 1 Jan 2022 20:21:17 -0800 Subject: [PATCH 6/7] Refactor to avoid changing overhead struct --- voxygen/src/hud/mod.rs | 11 +- voxygen/src/hud/overhead.rs | 728 ++++++++++++++++++------------------ 2 files changed, 365 insertions(+), 374 deletions(-) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 9fc4eee078..c05c328a5f 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1777,7 +1777,7 @@ impl Hud { let speech_bubbles = &self.speech_bubbles; // Render overhead name tags and health bars - for (pos, info, bubble, _, _, health, _, height_offset, hpfl, in_group, is_me) in ( + for (pos, info, bubble, _, _, health, _, height_offset, hpfl, in_group) in ( &entities, &pos, interpolated.maybe(), @@ -1835,7 +1835,8 @@ impl Hud { || info.selected_entity.map_or(false, |s| s.0 == entity) || health.map_or(true, overhead::should_show_healthbar) || in_group - || is_merchant) + || is_merchant + || !is_me) && dist_sqr < (if in_group { NAMETAG_GROUP_RANGE @@ -1866,7 +1867,9 @@ impl Hud { 0.0 }, }); - let bubble = if dist_sqr < SPEECH_BUBBLE_RANGE.powi(2) { + let bubble = if (dist_sqr < SPEECH_BUBBLE_RANGE.powi(2) && !is_me) + || (is_me && global_state.settings.interface.speech_bubble_self) + { speech_bubbles.get(uid) } else { None @@ -1884,7 +1887,6 @@ impl Hud { body.height() * scale.map_or(1.0, |s| s.0) + 0.5, hpfl, in_group, - is_me, ) }) }, @@ -1904,7 +1906,6 @@ impl Hud { info, bubble, in_group, - is_me, &global_state.settings.interface, self.pulse, i18n, diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index 397e01d97e..3cb42953ef 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -81,7 +81,6 @@ pub struct Overhead<'a> { info: Option>, bubble: Option<&'a SpeechBubble>, in_group: bool, - is_me: bool, settings: &'a InterfaceSettings, pulse: f32, i18n: &'a Localization, @@ -97,7 +96,6 @@ impl<'a> Overhead<'a> { info: Option>, bubble: Option<&'a SpeechBubble>, in_group: bool, - is_me: bool, settings: &'a InterfaceSettings, pulse: f32, i18n: &'a Localization, @@ -108,7 +106,6 @@ impl<'a> Overhead<'a> { info, bubble, in_group, - is_me, settings, pulse, i18n, @@ -183,118 +180,115 @@ impl<'a> Widget for Overhead<'a> { const MANA_BAR_HEIGHT: f64 = BARSIZE * 1.5; const MANA_BAR_Y: f64 = MANA_BAR_HEIGHT / 2.0; // Only render info if not me - if !self.is_me { - if let Some(Info { - name, - health, - buffs, - energy, - combat_rating, - }) = self.info - { - // Used to set healthbar colours based on hp_percentage - let hp_percentage = health.map_or(100.0, |h| { - f64::from(h.current() / h.base_max().max(h.maximum()) * 100.0) - }); - // Compare levels to decide if a skull is shown - let health_current = health.map_or(1.0, |h| f64::from(h.current())); - let health_max = health.map_or(1.0, |h| f64::from(h.maximum())); - let name_y = if (health_current - health_max).abs() < 1e-6 { - MANA_BAR_Y + 20.0 - } else { - MANA_BAR_Y + 32.0 - }; - 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 - 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)), - }; - // Buffs - // Alignment - let buff_count = buffs.kinds.len().min(11); - Rectangle::fill_with([168.0, 100.0], color::TRANSPARENT) - .x_y(-1.0, name_y + 60.0) - .parent(id) - .set(state.ids.buffs_align, ui); + if let Some(Info { + name, + health, + buffs, + energy, + combat_rating, + }) = self.info + { + // Used to set healthbar colours based on hp_percentage + let hp_percentage = health.map_or(100.0, |h| { + f64::from(h.current() / h.base_max().max(h.maximum()) * 100.0) + }); + // Compare levels to decide if a skull is shown + let health_current = health.map_or(1.0, |h| f64::from(h.current())); + let health_max = health.map_or(1.0, |h| f64::from(h.maximum())); + let name_y = if (health_current - health_max).abs() < 1e-6 { + MANA_BAR_Y + 20.0 + } else { + MANA_BAR_Y + 32.0 + }; + 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 + 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)), + }; + // Buffs + // Alignment + let buff_count = buffs.kinds.len().min(11); + Rectangle::fill_with([168.0, 100.0], color::TRANSPARENT) + .x_y(-1.0, name_y + 60.0) + .parent(id) + .set(state.ids.buffs_align, ui); - 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() < buff_count { - state.update(|state| state.ids.buff_timers.resize(buff_count, gen)); - }; + 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() < 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 - if self.bubble.is_none() { - state - .ids - .buffs - .iter() - .copied() - .zip(state.ids.buff_timers.iter().copied()) - .zip(buffs.iter_active().map(get_buff_info)) - .enumerate() - .for_each(|(i, ((id, timer_id), buff))| { - // Limit displayed buffs - let max_duration = buff.data.duration; - let current_duration = buff.dur; - let duration_percentage = current_duration.map_or(1000.0, |cur| { - max_duration.map_or(1000.0, |max| { - cur.as_secs_f32() / max.as_secs_f32() * 1000.0 - }) - }) as u32; // Percentage to determine which frame of the timer overlay is displayed - let buff_img = get_buff_image(buff.kind, self.imgs); - 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 - .map_or(false, |cur| cur.as_secs_f32() < 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, + 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 + if self.bubble.is_none() { + state + .ids + .buffs + .iter() + .copied() + .zip(state.ids.buff_timers.iter().copied()) + .zip(buffs.iter_active().map(get_buff_info)) + .enumerate() + .for_each(|(i, ((id, timer_id), buff))| { + // Limit displayed buffs + let max_duration = buff.data.duration; + let current_duration = buff.dur; + let duration_percentage = current_duration.map_or(1000.0, |cur| { + max_duration.map_or(1000.0, |max| { + cur.as_secs_f32() / max.as_secs_f32() * 1000.0 }) - .w_h(20.0, 20.0) - .middle_of(id) - .set(timer_id, ui); - }); - } - // Name - Text::new(name) + }) as u32; // Percentage to determine which frame of the timer overlay is displayed + let buff_img = get_buff_image(buff.kind, self.imgs); + 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.map_or(false, |cur| cur.as_secs_f32() < 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(id) + .set(timer_id, ui); + }); + } + // Name + Text::new(name) //Text::new(&format!("{} [{:?}]", name, combat_rating)) // <- Uncomment to debug combat ratings .font_id(self.fonts.cyri.conrod_id) .font_size(font_size) @@ -302,7 +296,7 @@ impl<'a> Widget for Overhead<'a> { .x_y(-1.0, name_y) .parent(id) .set(state.ids.name_bg, ui); - Text::new(name) + Text::new(name) //Text::new(&format!("{} [{:?}]", name, combat_rating)) // <- Uncomment to debug combat ratings .font_id(self.fonts.cyri.conrod_id) .font_size(font_size) @@ -317,296 +311,292 @@ impl<'a> Widget for Overhead<'a> { .parent(id) .set(state.ids.name, ui); - match health { - Some(health) - if should_show_healthbar(health) || decayed_health_displayed(health) => - { - // 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.93, 0.59, 0.03, hp_ani); - let decayed_health = f64::from(1.0 - health.maximum() / health.base_max()); - // Background - Image::new(if self.in_group {self.imgs.health_bar_group_bg} else {self.imgs.enemy_health_bg}) + match health { + Some(health) + if should_show_healthbar(health) || decayed_health_displayed(health) => + { + // 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.93, 0.59, 0.03, hp_ani); + let decayed_health = f64::from(1.0 - health.maximum() / health.base_max()); + // Background + Image::new(if self.in_group {self.imgs.health_bar_group_bg} else {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 - let size_factor = (hp_percentage / 100.0) * BARSIZE; - let w = if self.in_group { - 82.0 * size_factor - } else { - 73.0 * size_factor - }; - let h = 6.0 * BARSIZE; - let x = if self.in_group { - (0.0 + (hp_percentage / 100.0 * 41.0 - 41.0)) * BARSIZE - } else { - (4.5 + (hp_percentage / 100.0 * 36.45 - 36.45)) * BARSIZE - }; - Image::new(self.imgs.enemy_bar) - .w_h(w, h) - .x_y(x, MANA_BAR_Y + 8.0) - .color(if self.in_group { - // Different HP bar colors only for group members - Some(match hp_percentage { - x if (0.0..25.0).contains(&x) => crit_hp_color, - x if (25.0..50.0).contains(&x) => LOW_HP_COLOR, - _ => HP_COLOR, - }) - } else { - Some(ENEMY_HP_COLOR) + // % HP Filling + let size_factor = (hp_percentage / 100.0) * BARSIZE; + let w = if self.in_group { + 82.0 * size_factor + } else { + 73.0 * size_factor + }; + let h = 6.0 * BARSIZE; + let x = if self.in_group { + (0.0 + (hp_percentage / 100.0 * 41.0 - 41.0)) * BARSIZE + } else { + (4.5 + (hp_percentage / 100.0 * 36.45 - 36.45)) * BARSIZE + }; + Image::new(self.imgs.enemy_bar) + .w_h(w, h) + .x_y(x, MANA_BAR_Y + 8.0) + .color(if self.in_group { + // Different HP bar colors only for group members + Some(match hp_percentage { + x if (0.0..25.0).contains(&x) => crit_hp_color, + x if (25.0..50.0).contains(&x) => LOW_HP_COLOR, + _ => HP_COLOR, }) - .parent(id) - .set(state.ids.health_bar, ui); + } else { + Some(ENEMY_HP_COLOR) + }) + .parent(id) + .set(state.ids.health_bar, ui); - if decayed_health > 0.0 { - let x_decayed = if self.in_group { - (0.0 - (decayed_health * 41.0 - 41.0)) * BARSIZE - } else { - (4.5 - (decayed_health * 36.45 - 36.45)) * BARSIZE - }; - - let decay_bar_len = decayed_health - * if self.in_group { - 82.0 * BARSIZE - } else { - 73.0 * BARSIZE - }; - Image::new(self.imgs.enemy_bar) - .w_h(decay_bar_len, h) - .x_y(x_decayed, MANA_BAR_Y + 8.0) - .color(Some(QUALITY_EPIC)) - .parent(id) - .set(state.ids.decay_bar, ui); - } - let mut txt = format!("{}/{}", health_cur_txt, health_max_txt); - if health.is_dead { - txt = self.i18n.get("hud.group.dead").to_string() + if decayed_health > 0.0 { + let x_decayed = if self.in_group { + (0.0 - (decayed_health * 41.0 - 41.0)) * BARSIZE + } else { + (4.5 - (decayed_health * 36.45 - 36.45)) * BARSIZE }; - 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) + + let decay_bar_len = decayed_health + * if self.in_group { + 82.0 * BARSIZE + } else { + 73.0 * BARSIZE + }; + Image::new(self.imgs.enemy_bar) + .w_h(decay_bar_len, h) + .x_y(x_decayed, MANA_BAR_Y + 8.0) + .color(Some(QUALITY_EPIC)) .parent(id) - .set(state.ids.health_txt, ui); + .set(state.ids.decay_bar, ui); + } + let mut txt = format!("{}/{}", health_cur_txt, health_max_txt); + if health.is_dead { + txt = self.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 = f64::from(energy.current() / energy.maximum()); - let size_factor = energy_factor * BARSIZE; - let w = if self.in_group { - 80.0 * size_factor - } else { - 72.0 * size_factor - }; - let x = if self.in_group { - ((0.0 + (energy_factor * 40.0)) - 40.0) * BARSIZE - } else { - ((3.5 + (energy_factor * 36.5)) - 36.45) * BARSIZE - }; - Rectangle::fill_with([w, MANA_BAR_HEIGHT], STAMINA_COLOR) - .x_y( - x, MANA_BAR_Y, //-32.0, - ) - .parent(id) - .set(state.ids.mana_bar, ui); - } + // % Mana Filling + if let Some(energy) = energy { + let energy_factor = f64::from(energy.current() / energy.maximum()); + let size_factor = energy_factor * BARSIZE; + let w = if self.in_group { + 80.0 * size_factor + } else { + 72.0 * size_factor + }; + let x = if self.in_group { + ((0.0 + (energy_factor * 40.0)) - 40.0) * BARSIZE + } else { + ((3.5 + (energy_factor * 36.5)) - 36.45) * BARSIZE + }; + Rectangle::fill_with([w, MANA_BAR_HEIGHT], STAMINA_COLOR) + .x_y( + x, MANA_BAR_Y, //-32.0, + ) + .parent(id) + .set(state.ids.mana_bar, ui); + } - // Foreground - Image::new(if self.in_group {self.imgs.health_bar_group} else {self.imgs.enemy_health}) + // Foreground + Image::new(if self.in_group {self.imgs.health_bar_group} else {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); - let indicator_col = cr_color(combat_rating); - let artifact_diffculty = 122.0; + let indicator_col = cr_color(combat_rating); + let artifact_diffculty = 122.0; - if combat_rating > artifact_diffculty && !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::() < 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); + if combat_rating > artifact_diffculty && !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::() < 0.9 { + self.imgs.skull_2 } else { - Image::new(if self.in_group { - self.imgs.nothing - } else { - self.imgs.combat_rating_ico - }) - .w_h(7.0 * BARSIZE, 7.0 * BARSIZE) - .x_y(-37.0 * BARSIZE, MANA_BAR_Y + 6.0) - .color(Some(indicator_col)) - .parent(id) - .set(state.ids.level, ui); - } - }, - _ => {}, - } + 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 { + Image::new(if self.in_group { + self.imgs.nothing + } else { + self.imgs.combat_rating_ico + }) + .w_h(7.0 * BARSIZE, 7.0 * BARSIZE) + .x_y(-37.0 * BARSIZE, MANA_BAR_Y + 6.0) + .color(Some(indicator_col)) + .parent(id) + .set(state.ids.level, ui); + } + }, + _ => {}, } } // Speech bubble // Only render if setting or other players - if !self.is_me || self.settings.speech_bubble_self { - if let Some(bubble) = self.bubble { - let dark_mode = self.settings.speech_bubble_dark_mode; - let localizer = - |s: &str, i| -> String { self.i18n.get_variation(s, i).to_string() }; - let bubble_contents: String = bubble.message(localizer); - let (text_color, shadow_color) = bubble_color(bubble, dark_mode); - let mut text = Text::new(&bubble_contents) - .color(text_color) - .font_id(self.fonts.cyri.conrod_id) - .font_size(18) - .up_from(state.ids.name, 26.0) - .x_align_to(state.ids.name, Align::Middle) - .parent(id); + if let Some(bubble) = self.bubble { + let dark_mode = self.settings.speech_bubble_dark_mode; + let localizer = |s: &str, i| -> String { self.i18n.get_variation(s, i).to_string() }; + let bubble_contents: String = bubble.message(localizer); + let (text_color, shadow_color) = bubble_color(bubble, dark_mode); + let mut text = Text::new(&bubble_contents) + .color(text_color) + .font_id(self.fonts.cyri.conrod_id) + .font_size(18) + .up_from(state.ids.name, 26.0) + .x_align_to(state.ids.name, Align::Middle) + .parent(id); - if let Some(w) = text.get_w(ui) { - if w > MAX_BUBBLE_WIDTH { - text = text.w(MAX_BUBBLE_WIDTH); - } + if let Some(w) = text.get_w(ui) { + if w > MAX_BUBBLE_WIDTH { + text = text.w(MAX_BUBBLE_WIDTH); } - Image::new(if dark_mode { - self.imgs.dark_bubble_top_left - } else { - self.imgs.speech_bubble_top_left - }) - .w_h(16.0, 16.0) - .top_left_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_top_left, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_top - } else { - self.imgs.speech_bubble_top - }) - .h(16.0) - .padded_w_of(state.ids.speech_bubble_text, -4.0) - .mid_top_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_top, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_top_right - } else { - self.imgs.speech_bubble_top_right - }) - .w_h(16.0, 16.0) - .top_right_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_top_right, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_left - } else { - self.imgs.speech_bubble_left - }) - .w(16.0) - .padded_h_of(state.ids.speech_bubble_text, -4.0) - .mid_left_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_left, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_mid - } else { - self.imgs.speech_bubble_mid - }) - .padded_wh_of(state.ids.speech_bubble_text, -4.0) - .top_left_with_margin_on(state.ids.speech_bubble_text, -4.0) - .parent(id) - .set(state.ids.speech_bubble_mid, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_right - } else { - self.imgs.speech_bubble_right - }) - .w(16.0) - .padded_h_of(state.ids.speech_bubble_text, -4.0) - .mid_right_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_right, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_bottom_left - } else { - self.imgs.speech_bubble_bottom_left - }) - .w_h(16.0, 16.0) - .bottom_left_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_bottom_left, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_bottom - } else { - self.imgs.speech_bubble_bottom - }) - .h(16.0) - .padded_w_of(state.ids.speech_bubble_text, -4.0) - .mid_bottom_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_bottom, ui); - Image::new(if dark_mode { - self.imgs.dark_bubble_bottom_right - } else { - self.imgs.speech_bubble_bottom_right - }) - .w_h(16.0, 16.0) - .bottom_right_with_margin_on(state.ids.speech_bubble_text, -20.0) - .parent(id) - .set(state.ids.speech_bubble_bottom_right, ui); - let tail = Image::new(if dark_mode { - self.imgs.dark_bubble_tail - } else { - self.imgs.speech_bubble_tail - }) - .parent(id) - .mid_bottom_with_margin_on(state.ids.speech_bubble_text, -32.0); + } + Image::new(if dark_mode { + self.imgs.dark_bubble_top_left + } else { + self.imgs.speech_bubble_top_left + }) + .w_h(16.0, 16.0) + .top_left_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_top_left, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_top + } else { + self.imgs.speech_bubble_top + }) + .h(16.0) + .padded_w_of(state.ids.speech_bubble_text, -4.0) + .mid_top_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_top, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_top_right + } else { + self.imgs.speech_bubble_top_right + }) + .w_h(16.0, 16.0) + .top_right_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_top_right, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_left + } else { + self.imgs.speech_bubble_left + }) + .w(16.0) + .padded_h_of(state.ids.speech_bubble_text, -4.0) + .mid_left_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_left, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_mid + } else { + self.imgs.speech_bubble_mid + }) + .padded_wh_of(state.ids.speech_bubble_text, -4.0) + .top_left_with_margin_on(state.ids.speech_bubble_text, -4.0) + .parent(id) + .set(state.ids.speech_bubble_mid, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_right + } else { + self.imgs.speech_bubble_right + }) + .w(16.0) + .padded_h_of(state.ids.speech_bubble_text, -4.0) + .mid_right_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_right, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_bottom_left + } else { + self.imgs.speech_bubble_bottom_left + }) + .w_h(16.0, 16.0) + .bottom_left_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_bottom_left, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_bottom + } else { + self.imgs.speech_bubble_bottom + }) + .h(16.0) + .padded_w_of(state.ids.speech_bubble_text, -4.0) + .mid_bottom_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_bottom, ui); + Image::new(if dark_mode { + self.imgs.dark_bubble_bottom_right + } else { + self.imgs.speech_bubble_bottom_right + }) + .w_h(16.0, 16.0) + .bottom_right_with_margin_on(state.ids.speech_bubble_text, -20.0) + .parent(id) + .set(state.ids.speech_bubble_bottom_right, ui); + let tail = Image::new(if dark_mode { + self.imgs.dark_bubble_tail + } else { + self.imgs.speech_bubble_tail + }) + .parent(id) + .mid_bottom_with_margin_on(state.ids.speech_bubble_text, -32.0); - if dark_mode { - tail.w_h(22.0, 13.0) - } else { - tail.w_h(22.0, 28.0) - } - .set(state.ids.speech_bubble_tail, ui); + if dark_mode { + tail.w_h(22.0, 13.0) + } else { + tail.w_h(22.0, 28.0) + } + .set(state.ids.speech_bubble_tail, ui); - let mut text_shadow = Text::new(&bubble_contents) - .color(shadow_color) - .font_id(self.fonts.cyri.conrod_id) - .font_size(18) - .x_relative_to(state.ids.speech_bubble_text, 1.0) - .y_relative_to(state.ids.speech_bubble_text, -1.0) - .parent(id); - // Move text to front (conrod depth is lowest first; not a z-index) - text.depth(text_shadow.get_depth() - 1.0) - .set(state.ids.speech_bubble_text, ui); - if let Some(w) = text_shadow.get_w(ui) { - if w > MAX_BUBBLE_WIDTH { - text_shadow = text_shadow.w(MAX_BUBBLE_WIDTH); - } + let mut text_shadow = Text::new(&bubble_contents) + .color(shadow_color) + .font_id(self.fonts.cyri.conrod_id) + .font_size(18) + .x_relative_to(state.ids.speech_bubble_text, 1.0) + .y_relative_to(state.ids.speech_bubble_text, -1.0) + .parent(id); + // Move text to front (conrod depth is lowest first; not a z-index) + text.depth(text_shadow.get_depth() - 1.0) + .set(state.ids.speech_bubble_text, ui); + if let Some(w) = text_shadow.get_w(ui) { + if w > MAX_BUBBLE_WIDTH { + text_shadow = text_shadow.w(MAX_BUBBLE_WIDTH); } - text_shadow.set(state.ids.speech_bubble_shadow, ui); - let icon = if self.settings.speech_bubble_icon { - bubble_icon(bubble, self.imgs) - } else { - self.imgs.nothing - }; - Image::new(icon) + } + text_shadow.set(state.ids.speech_bubble_shadow, ui); + let icon = if self.settings.speech_bubble_icon { + bubble_icon(bubble, self.imgs) + } else { + self.imgs.nothing + }; + Image::new(icon) .w_h(16.0, 16.0) .top_left_with_margin_on(state.ids.speech_bubble_text, -16.0) // TODO: Figure out whether this should be parented. // .parent(id) .set(state.ids.speech_bubble_icon, ui); - } } } } From 7d07598370661830ab5c7dd75bf472ef67effb81 Mon Sep 17 00:00:00 2001 From: voxal Date: Sun, 2 Jan 2022 15:24:11 -0800 Subject: [PATCH 7/7] Move comments and turn on by default --- voxygen/src/hud/mod.rs | 1 + voxygen/src/hud/overhead.rs | 2 -- voxygen/src/settings/interface.rs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index c05c328a5f..a7bab73efb 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1867,6 +1867,7 @@ impl Hud { 0.0 }, }); + // Only render bubble if nearby or if its me and setting is on let bubble = if (dist_sqr < SPEECH_BUBBLE_RANGE.powi(2) && !is_me) || (is_me && global_state.settings.interface.speech_bubble_self) { diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index 3cb42953ef..1c5c05db0f 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -179,7 +179,6 @@ impl<'a> Widget for Overhead<'a> { const BARSIZE: f64 = 2.0; // Scaling const MANA_BAR_HEIGHT: f64 = BARSIZE * 1.5; const MANA_BAR_Y: f64 = MANA_BAR_HEIGHT / 2.0; - // Only render info if not me if let Some(Info { name, health, @@ -451,7 +450,6 @@ impl<'a> Widget for Overhead<'a> { } } // Speech bubble - // Only render if setting or other players if let Some(bubble) = self.bubble { let dark_mode = self.settings.speech_bubble_dark_mode; let localizer = |s: &str, i| -> String { self.i18n.get_variation(s, i).to_string() }; diff --git a/voxygen/src/settings/interface.rs b/voxygen/src/settings/interface.rs index cb48e1d9af..494ea651c8 100644 --- a/voxygen/src/settings/interface.rs +++ b/voxygen/src/settings/interface.rs @@ -55,7 +55,7 @@ impl Default for InterfaceSettings { sct: true, sct_player_batch: false, sct_damage_batch: false, - speech_bubble_self: false, + speech_bubble_self: true, speech_bubble_dark_mode: false, speech_bubble_icon: true, crosshair_opacity: 0.6,