From 48bd921d0a36fddfb05e5b9f157dfad288581eae Mon Sep 17 00:00:00 2001 From: Monty Marz Date: Sat, 2 Jan 2021 05:11:30 +0100 Subject: [PATCH] EXP bar and available points UI fix overhead UI Add WIP overhead difficulty indicator readd commented out exp counter for later use Wired skill information into UI. --- .../element/icons/indicator_bubble.png | 3 + .../voxygen/element/misc_bg/diary_exp_bg.png | 3 + .../element/misc_bg/diary_exp_frame.png | 3 + assets/voxygen/item_image_manifest.ron | 6 +- assets/voxygen/voxel/weapon/shield/wood-0.vox | 2 +- common/src/comp/skills.rs | 35 +++ common/sys/src/stats.rs | 5 +- .../2020-12-13-172324_skills/up.sql | 5 +- .../src/persistence/character/conversions.rs | 2 + server/src/persistence/models.rs | 1 + server/src/persistence/schema.rs | 1 + voxygen/src/hud/buttons.rs | 2 +- voxygen/src/hud/diary.rs | 252 ++++++++++++------ voxygen/src/hud/img_ids.rs | 3 + voxygen/src/hud/mod.rs | 110 +++++++- voxygen/src/hud/overhead.rs | 38 ++- 16 files changed, 370 insertions(+), 101 deletions(-) create mode 100644 assets/voxygen/element/icons/indicator_bubble.png create mode 100644 assets/voxygen/element/misc_bg/diary_exp_bg.png create mode 100644 assets/voxygen/element/misc_bg/diary_exp_frame.png diff --git a/assets/voxygen/element/icons/indicator_bubble.png b/assets/voxygen/element/icons/indicator_bubble.png new file mode 100644 index 0000000000..bd776e5831 --- /dev/null +++ b/assets/voxygen/element/icons/indicator_bubble.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c010347d67e5e5ceb0d759e93db1e67e2736767e50f2de3e6070c5fea6594f33 +size 1612 diff --git a/assets/voxygen/element/misc_bg/diary_exp_bg.png b/assets/voxygen/element/misc_bg/diary_exp_bg.png new file mode 100644 index 0000000000..6c4bea3df9 --- /dev/null +++ b/assets/voxygen/element/misc_bg/diary_exp_bg.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11863757e3829d22c0acb332baa826b855556d18fda91261ac7520b8098f30de +size 2461 diff --git a/assets/voxygen/element/misc_bg/diary_exp_frame.png b/assets/voxygen/element/misc_bg/diary_exp_frame.png new file mode 100644 index 0000000000..2f67dd3e61 --- /dev/null +++ b/assets/voxygen/element/misc_bg/diary_exp_frame.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:809adea64863330f3e45d75660b253704c06b6e70b2c4e578ff1a79a15d153f5 +size 3068 diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index 6d6af8b410..c42bd103aa 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -37,12 +37,12 @@ (0.0, 0.0, 0.0), (90.0, 90.0, 0.0), 1.0, ), Tool("example_general_combat_left"): VoxTrans( - "voxel.weapon.shield.wood-0", - (0.0, 0.0, 0.0), (90.0, 90.0, 0.0), 1.0, + "voxel.weapon.sword.long_2h_saurok", + (0.0, 0.0, 0.0), (85.0, -90.0, -40.0), 1.0, ), Tool("example_general_combat_right"): VoxTrans( "voxel.weapon.sword.long_2h_saurok", - (0.0, 0.0, 0.0), (90.0, 90.0, 0.0), 1.0, + (0.0, 0.0, 0.0), (125.0, 90.0, 80.0), 1.0, ), // Bows Tool("common.items.weapons.bow.starter_bow"): VoxTrans( diff --git a/assets/voxygen/voxel/weapon/shield/wood-0.vox b/assets/voxygen/voxel/weapon/shield/wood-0.vox index ba507aeccd..88bd00b44d 100644 --- a/assets/voxygen/voxel/weapon/shield/wood-0.vox +++ b/assets/voxygen/voxel/weapon/shield/wood-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c138ca5bed89a98478b4ce5d25304c016ebceb5c1da725ef1832281d5ff460b +oid sha256:8d40d735aaa7938921f545cafd09ef9f29fe4178e62f29b26e76bd81b088d1d1 size 1512 diff --git a/common/src/comp/skills.rs b/common/src/comp/skills.rs index 2b25e0f57d..7c9fd23956 100644 --- a/common/src/comp/skills.rs +++ b/common/src/comp/skills.rs @@ -220,6 +220,11 @@ pub enum SkillGroupType { Weapon(ToolKind), } +impl SkillGroupType { + /// Gets the cost in experience of earning a skill point + pub fn skill_point_cost(self) -> u16 { 300 } +} + /// A group of skills that have been unlocked by a player. Each skill group has /// independent exp and skill points which are used to unlock skills in that /// skill group. @@ -228,6 +233,7 @@ pub struct SkillGroup { pub skill_group_type: SkillGroupType, pub exp: u16, pub available_sp: u16, + pub earned_sp: u16, } impl SkillGroup { @@ -236,6 +242,7 @@ impl SkillGroup { skill_group_type, exp: 0, available_sp: 0, + earned_sp: 0, } } } @@ -433,6 +440,7 @@ impl SkillSet { .find(|x| x.skill_group_type == skill_group_type) { skill_group.available_sp += number_of_skill_points; + skill_group.earned_sp += number_of_skill_points; } else { warn!("Tried to add skill points to a skill group that player does not have"); } @@ -467,6 +475,33 @@ impl SkillSet { self.skills.contains_key(s) && self.skills.get(s).map_or(false, |l_b| l_b >= l) }) } + + /// Gets the available points for a particular skill group + pub fn get_available_sp(&self, skill_group: SkillGroupType) -> u16 { + let mut skill_groups = self + .skill_groups + .iter() + .filter(|s_g| s_g.skill_group_type == skill_group); + skill_groups.next().map_or(0, |s_g| s_g.available_sp) + } + + /// Gets the total earned points for a particular skill group + pub fn get_earned_sp(&self, skill_group: SkillGroupType) -> u16 { + let mut skill_groups = self + .skill_groups + .iter() + .filter(|s_g| s_g.skill_group_type == skill_group); + skill_groups.next().map_or(0, |s_g| s_g.earned_sp) + } + + /// Gets the available experience for a particular skill group + pub fn get_experience(&self, skill_group: SkillGroupType) -> u16 { + let mut skill_groups = self + .skill_groups + .iter() + .filter(|s_g| s_g.skill_group_type == skill_group); + skill_groups.next().map_or(0, |s_g| s_g.exp) + } } impl Skill { diff --git a/common/sys/src/stats.rs b/common/sys/src/stats.rs index 45e0b016dd..9b284545c1 100644 --- a/common/sys/src/stats.rs +++ b/common/sys/src/stats.rs @@ -79,7 +79,7 @@ impl<'a> System<'a> for Sys { let stat = stats.get_unchecked(); { for skill_group in stat.skill_set.skill_groups.iter() { - if skill_group.exp >= 300 { + if skill_group.exp >= skill_group.skill_group_type.skill_point_cost() { skills_to_level.insert(skill_group.skill_group_type); } } @@ -88,7 +88,8 @@ impl<'a> System<'a> for Sys { if !skills_to_level.is_empty() { let mut stat = stats.get_mut_unchecked(); for skill_group in skills_to_level.drain() { - stat.skill_set.change_experience(skill_group, -300); + stat.skill_set + .change_experience(skill_group, -(skill_group.skill_point_cost() as i32)); stat.skill_set.add_skill_points(skill_group, 1); } } diff --git a/server/src/migrations/2020-12-13-172324_skills/up.sql b/server/src/migrations/2020-12-13-172324_skills/up.sql index 5d59a897fa..f085236649 100644 --- a/server/src/migrations/2020-12-13-172324_skills/up.sql +++ b/server/src/migrations/2020-12-13-172324_skills/up.sql @@ -5,7 +5,7 @@ CREATE TABLE "_character_new" ( "character_id" INT NOT NULL, "player_uuid" TEXT NOT NULL, "alias" TEXT NOT NULL, - "waypoint" TEXT NOT NULL, + "waypoint" TEXT, PRIMARY KEY("character_id"), FOREIGN KEY("character_id") REFERENCES "body"("body_id"), FOREIGN KEY("character_id") REFERENCES "item"("item_id") @@ -37,6 +37,7 @@ CREATE TABLE skill_group ( skill_group_type TEXT NOT NULL, exp INTEGER NOT NULL, available_sp INTEGER NOT NULL, + earned_sp INTEGER NOT NULL, FOREIGN KEY(character_id) REFERENCES character(character_id), PRIMARY KEY(character_id,skill_group_type) ); @@ -52,5 +53,5 @@ CREATE TABLE skill ( -- Inserts starting skill group for everyone INSERT INTO skill_group -SELECT c.character_id, '"General"', 0, 0 +SELECT c.character_id, '"General"', 0, 0, 0 FROM character c \ No newline at end of file diff --git a/server/src/persistence/character/conversions.rs b/server/src/persistence/character/conversions.rs index 38d1b5d184..dc7c7be8be 100644 --- a/server/src/persistence/character/conversions.rs +++ b/server/src/persistence/character/conversions.rs @@ -381,6 +381,7 @@ fn convert_skill_groups_from_database(skill_groups: &[SkillGroup]) -> Vec Text, exp -> Integer, available_sp -> Integer, + earned_sp -> Integer, } } diff --git a/voxygen/src/hud/buttons.rs b/voxygen/src/hud/buttons.rs index fb0ae3bac2..c2b6795dee 100644 --- a/voxygen/src/hud/buttons.rs +++ b/voxygen/src/hud/buttons.rs @@ -335,7 +335,7 @@ impl<'a> Widget for Buttons<'a> { .press_image(self.imgs.spellbook_press) .with_tooltip( self.tooltip_manager, - &localized_strings.get("hud.spell"), + &localized_strings.get("hud.diary"), "", &button_tooltip, TEXT_COLOR, diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index 5f71781515..1f33886ed9 100644 --- a/voxygen/src/hud/diary.rs +++ b/voxygen/src/hud/diary.rs @@ -1,7 +1,7 @@ use super::{ img_ids::{Imgs, ImgsRot}, item_imgs::{ItemImgs, ItemKey::Tool}, - Show, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, + Show, QUALITY_LEGENDARY, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, XP_COLOR, }; use crate::{ i18n::Localization, @@ -29,6 +29,13 @@ widget_ids! { close, title, content_align, + exp_bar_bg, + exp_bar_frame, + exp_bar_content_align, + exp_bar_content, + exp_bar_rank, + exp_bar_txt, + available_pts_txt, weapon_imgs[], weapon_btns[], skills_top_l_align, @@ -127,7 +134,8 @@ widget_ids! { skill_sceptre_bomb_2, skill_sceptre_bomb_3, skill_sceptre_bomb_4, - general_combat_render, + general_combat_render_0, + general_combat_render_1, skill_general_stat_0, skill_general_stat_1, skill_general_tree_0, @@ -162,6 +170,7 @@ pub struct Diary<'a> { created_btns_top_r: usize, created_btns_bot_l: usize, created_btns_bot_r: usize, + hovering_exp_bar: bool, } impl<'a> Diary<'a> { @@ -191,6 +200,7 @@ impl<'a> Diary<'a> { created_btns_top_r: 0, created_btns_bot_l: 0, created_btns_bot_r: 0, + hovering_exp_bar: false, } } } @@ -204,16 +214,7 @@ impl<'a> Diary<'a> { Achievements, }*/ -pub enum SelectedSkillTree { - None, - Sword, - Hammer, - Axe, - Sceptre, - Bow, - StaffFire, - GeneralCombat, -} +pub type SelectedSkillTree = skills::SkillGroupType; const WEAPONS: [&str; 7] = [ "General Combat", @@ -227,7 +228,7 @@ const WEAPONS: [&str; 7] = [ pub enum Event { Close, - ChangeWeaponTree(SelectedSkillTree), + ChangeSkillTree(SelectedSkillTree), UnlockSkill(Skill), } @@ -346,31 +347,35 @@ impl<'a> Widget for Diary<'a> { // Weapon icons if Button::image(match i.1 { "General Combat" => match sel_tab { - SelectedSkillTree::GeneralCombat => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::General => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border, }, "Sword" => match sel_tab { - SelectedSkillTree::Sword => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Sword) => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border, }, "Hammer" => match sel_tab { - SelectedSkillTree::Hammer => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Hammer) => { + self.imgs.wpn_icon_border_pressed + }, _ => self.imgs.wpn_icon_border, }, "Axe" => match sel_tab { - SelectedSkillTree::Axe => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Axe) => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border, }, "Sceptre" => match sel_tab { - SelectedSkillTree::Sceptre => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Sceptre) => { + self.imgs.wpn_icon_border_pressed + }, _ => self.imgs.wpn_icon_border, }, "Bow" => match sel_tab { - SelectedSkillTree::Bow => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Bow) => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border, }, "Fire Staff" => match sel_tab { - SelectedSkillTree::StaffFire => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Staff) => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border, }, _ => self.imgs.wpn_icon_border, @@ -378,62 +383,70 @@ impl<'a> Widget for Diary<'a> { .w_h(tweak!(50.0), tweak!(50.0)) .hover_image(match i.1 { "General Combat" => match sel_tab { - SelectedSkillTree::GeneralCombat => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::General => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border_mo, }, "Sword" => match sel_tab { - SelectedSkillTree::Sword => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Sword) => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border_mo, }, "Hammer" => match sel_tab { - SelectedSkillTree::Hammer => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Hammer) => { + self.imgs.wpn_icon_border_pressed + }, _ => self.imgs.wpn_icon_border_mo, }, "Axe" => match sel_tab { - SelectedSkillTree::Axe => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Axe) => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border_mo, }, "Sceptre" => match sel_tab { - SelectedSkillTree::Sceptre => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Sceptre) => { + self.imgs.wpn_icon_border_pressed + }, _ => self.imgs.wpn_icon_border_mo, }, "Bow" => match sel_tab { - SelectedSkillTree::Bow => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Bow) => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border_mo, }, "Fire Staff" => match sel_tab { - SelectedSkillTree::StaffFire => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Staff) => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border_mo, }, _ => self.imgs.wpn_icon_border, }) .press_image(match i.1 { "General Combat" => match sel_tab { - SelectedSkillTree::GeneralCombat => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::General => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border_press, }, "Sword" => match sel_tab { - SelectedSkillTree::Sword => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Sword) => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border_press, }, "Hammer" => match sel_tab { - SelectedSkillTree::Hammer => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Hammer) => { + self.imgs.wpn_icon_border_pressed + }, _ => self.imgs.wpn_icon_border_press, }, "Axe" => match sel_tab { - SelectedSkillTree::Axe => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Axe) => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border_press, }, "Sceptre" => match sel_tab { - SelectedSkillTree::Sceptre => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Sceptre) => { + self.imgs.wpn_icon_border_pressed + }, _ => self.imgs.wpn_icon_border_press, }, "Bow" => match sel_tab { - SelectedSkillTree::Bow => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Bow) => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border_press, }, "Fire Staff" => match sel_tab { - SelectedSkillTree::StaffFire => self.imgs.wpn_icon_border_pressed, + SelectedSkillTree::Weapon(ToolKind::Staff) => self.imgs.wpn_icon_border_pressed, _ => self.imgs.wpn_icon_border_press, }, _ => self.imgs.wpn_icon_border, @@ -444,21 +457,84 @@ impl<'a> Widget for Diary<'a> { { match i.1 { "General Combat" => { - events.push(Event::ChangeWeaponTree(SelectedSkillTree::GeneralCombat)) + events.push(Event::ChangeSkillTree(SelectedSkillTree::General)) }, - "Sword" => events.push(Event::ChangeWeaponTree(SelectedSkillTree::Sword)), - "Hammer" => events.push(Event::ChangeWeaponTree(SelectedSkillTree::Hammer)), - "Axe" => events.push(Event::ChangeWeaponTree(SelectedSkillTree::Axe)), - "Sceptre" => events.push(Event::ChangeWeaponTree(SelectedSkillTree::Sceptre)), - "Bow" => events.push(Event::ChangeWeaponTree(SelectedSkillTree::Bow)), - "Fire Staff" => { - events.push(Event::ChangeWeaponTree(SelectedSkillTree::StaffFire)) - }, - _ => events.push(Event::ChangeWeaponTree(SelectedSkillTree::None)), + "Sword" => events.push(Event::ChangeSkillTree(SelectedSkillTree::Weapon( + ToolKind::Sword, + ))), + "Hammer" => events.push(Event::ChangeSkillTree(SelectedSkillTree::Weapon( + ToolKind::Hammer, + ))), + "Axe" => events.push(Event::ChangeSkillTree(SelectedSkillTree::Weapon( + ToolKind::Axe, + ))), + "Sceptre" => events.push(Event::ChangeSkillTree(SelectedSkillTree::Weapon( + ToolKind::Sceptre, + ))), + "Bow" => events.push(Event::ChangeSkillTree(SelectedSkillTree::Weapon( + ToolKind::Bow, + ))), + "Fire Staff" => events.push(Event::ChangeSkillTree(SelectedSkillTree::Weapon( + ToolKind::Staff, + ))), + _ => events.push(Event::Close), } } } - + // Exp Bars and Rank Display + let current_exp = self.stats.skill_set.get_experience(*sel_tab) as f64; + let max_exp = sel_tab.skill_point_cost() as f64; + let exp_percentage = current_exp / max_exp; + let rank = self.stats.skill_set.get_earned_sp(*sel_tab); + let rank_txt = format!("{}", rank); + let exp_txt = format!("{}/{}", current_exp, max_exp); + let available_pts = self.stats.skill_set.get_available_sp(*sel_tab); + let available_pts_txt = format!("{} SP available!", available_pts); + Image::new(self.imgs.diary_exp_bg) + .w_h(480.0, 76.0) + .mid_bottom_with_margin_on(state.content_align, tweak!(10.0)) + .set(state.exp_bar_bg, ui); + Rectangle::fill_with([400.0, 40.0], color::TRANSPARENT) + .top_left_with_margins_on(state.exp_bar_bg, 32.0, 40.0) + .set(state.exp_bar_content_align, ui); + Image::new(self.imgs.bar_content) + .w_h(400.0 * exp_percentage, 40.0) + .top_left_with_margins_on(state.exp_bar_content_align, 0.0, 0.0) + .color(Some(XP_COLOR)) + .set(state.exp_bar_content, ui); + Image::new(self.imgs.diary_exp_frame) + .w_h(480.0, 76.0) + .color(Some(UI_HIGHLIGHT_0)) + .middle_of(state.exp_bar_bg) + .set(state.exp_bar_frame, ui); + // Show EXP bar text on hover + self.hovering_exp_bar = ui + .widget_input(state.exp_bar_frame) + .mouse() + .map_or(false, |m| m.is_over()); + if self.hovering_exp_bar { + Text::new(&exp_txt) + .mid_top_with_margin_on(state.exp_bar_frame, tweak!(47.0)) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(tweak!(14))) + .color(TEXT_COLOR) + .graphics_for(state.exp_bar_frame) + .set(state.exp_bar_txt, ui); + } + Text::new(&rank_txt) + .mid_top_with_margin_on(state.exp_bar_frame, tweak!(5.0)) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(tweak!(28))) + .color(TEXT_COLOR) + .set(state.exp_bar_rank, ui); + if available_pts > 0 { + Text::new(&available_pts_txt) + .mid_top_with_margin_on(state.content_align, tweak!(10.0)) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(tweak!(28))) + .color(QUALITY_LEGENDARY) + .set(state.available_pts_txt, ui); + } // Skill Trees // Alignment Placing let x = tweak!(200.0); @@ -479,37 +555,37 @@ impl<'a> Widget for Diary<'a> { // Number of skills per rectangle per weapon, start counting at 0 // Maximum of 9 skills/8 indices let skills_top_l = match sel_tab { - SelectedSkillTree::GeneralCombat => 2, - SelectedSkillTree::Sword => 4, - SelectedSkillTree::Axe => 4, - SelectedSkillTree::Hammer => 4, - SelectedSkillTree::Bow => 2, - SelectedSkillTree::StaffFire => 4, - SelectedSkillTree::Sceptre => 6, + SelectedSkillTree::General => 2, + SelectedSkillTree::Weapon(ToolKind::Sword) => 4, + SelectedSkillTree::Weapon(ToolKind::Axe) => 4, + SelectedSkillTree::Weapon(ToolKind::Hammer) => 4, + SelectedSkillTree::Weapon(ToolKind::Bow) => 2, + SelectedSkillTree::Weapon(ToolKind::Staff) => 4, + SelectedSkillTree::Weapon(ToolKind::Sceptre) => 6, _ => 0, }; let skills_top_r = match sel_tab { - SelectedSkillTree::GeneralCombat => 6, - SelectedSkillTree::Sword => 6, - SelectedSkillTree::Axe => 5, - SelectedSkillTree::Hammer => 4, - SelectedSkillTree::Bow => 6, - SelectedSkillTree::StaffFire => 4, - SelectedSkillTree::Sceptre => 5, + SelectedSkillTree::General => 6, + SelectedSkillTree::Weapon(ToolKind::Sword) => 6, + SelectedSkillTree::Weapon(ToolKind::Axe) => 5, + SelectedSkillTree::Weapon(ToolKind::Hammer) => 4, + SelectedSkillTree::Weapon(ToolKind::Bow) => 6, + SelectedSkillTree::Weapon(ToolKind::Staff) => 4, + SelectedSkillTree::Weapon(ToolKind::Sceptre) => 5, _ => 0, }; let skills_bot_l = match sel_tab { - SelectedSkillTree::GeneralCombat => 4, - SelectedSkillTree::Sword => 5, - SelectedSkillTree::Axe => 5, - SelectedSkillTree::Hammer => 6, - SelectedSkillTree::Bow => 5, - SelectedSkillTree::StaffFire => 5, + SelectedSkillTree::General => 4, + SelectedSkillTree::Weapon(ToolKind::Sword) => 5, + SelectedSkillTree::Weapon(ToolKind::Axe) => 5, + SelectedSkillTree::Weapon(ToolKind::Hammer) => 6, + SelectedSkillTree::Weapon(ToolKind::Bow) => 5, + SelectedSkillTree::Weapon(ToolKind::Staff) => 5, _ => 0, }; let skills_bot_r = match sel_tab { - SelectedSkillTree::Sword => 1, - SelectedSkillTree::Bow => 1, + SelectedSkillTree::Weapon(ToolKind::Sword) => 1, + SelectedSkillTree::Weapon(ToolKind::Bow) => 1, _ => 0, }; // Update widget id array len @@ -614,16 +690,26 @@ impl<'a> Widget for Diary<'a> { let art_size = [tweak!(320.0), tweak!(320.0)]; let skills = &self.stats.skill_set.skills; match sel_tab { - SelectedSkillTree::GeneralCombat => { + SelectedSkillTree::General => { use skills::{GeneralSkill::*, RollSkill::*, SkillGroupType::*}; use ToolKind::*; // General Combat - Image::new(self.imgs.not_found) - .wh(art_size) - .middle_of(state.content_align) - .graphics_for(state.content_align) - .color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0)))) - .set(state.general_combat_render, ui); + Image::new( + self.item_imgs + .img_id_or_not_found_img(Tool("example_general_combat_left".to_string())), + ) + .wh(art_size) + .middle_of(state.content_align) + .color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0)))) + .set(state.general_combat_render_0, ui); + Image::new( + self.item_imgs + .img_id_or_not_found_img(Tool("example_general_combat_right".to_string())), + ) + .wh(art_size) + .middle_of(state.general_combat_render_0) + .color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0)))) + .set(state.general_combat_render_1, ui); // Top Left skills // 5 1 6 // 3 0 4 @@ -943,7 +1029,7 @@ impl<'a> Widget for Diary<'a> { events.push(Event::UnlockSkill(skill)); }; }, - SelectedSkillTree::Sword => { + SelectedSkillTree::Weapon(ToolKind::Sword) => { use skills::SwordSkill::*; // Sword Image::new( @@ -952,7 +1038,6 @@ impl<'a> Widget for Diary<'a> { ) .wh(art_size) .middle_of(state.content_align) - .graphics_for(state.content_align) .color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0)))) .set(state.sword_render, ui); // Top Left skills @@ -1379,7 +1464,7 @@ impl<'a> Widget for Diary<'a> { events.push(Event::UnlockSkill(skill)); }; }, - SelectedSkillTree::Axe => { + SelectedSkillTree::Weapon(ToolKind::Axe) => { use skills::AxeSkill::*; // Axe Image::new( @@ -1388,7 +1473,6 @@ impl<'a> Widget for Diary<'a> { ) .wh(art_size) .middle_of(state.content_align) - .graphics_for(state.content_align) .color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0)))) .set(state.axe_render, ui); // Top Left skills @@ -1762,7 +1846,7 @@ impl<'a> Widget for Diary<'a> { events.push(Event::UnlockSkill(skill)); }; }, - SelectedSkillTree::Hammer => { + SelectedSkillTree::Weapon(ToolKind::Hammer) => { use skills::HammerSkill::*; // Hammer Image::new( @@ -1771,7 +1855,6 @@ impl<'a> Widget for Diary<'a> { ) .wh(art_size) .middle_of(state.content_align) - .graphics_for(state.content_align) .color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0)))) .set(state.hammer_render, ui); // Top Left skills @@ -2145,7 +2228,7 @@ impl<'a> Widget for Diary<'a> { events.push(Event::UnlockSkill(skill)); }; }, - SelectedSkillTree::Bow => { + SelectedSkillTree::Weapon(ToolKind::Bow) => { use skills::BowSkill::*; // Bow Image::new( @@ -2154,7 +2237,6 @@ impl<'a> Widget for Diary<'a> { ) .wh(art_size) .middle_of(state.content_align) - .graphics_for(state.content_align) .set(state.bow_render, ui); // Top Left skills // 5 1 6 @@ -2528,7 +2610,7 @@ impl<'a> Widget for Diary<'a> { events.push(Event::UnlockSkill(skill)); }; }, - SelectedSkillTree::StaffFire => { + SelectedSkillTree::Weapon(ToolKind::Staff) => { use skills::StaffSkill::*; // Staff Image::new( @@ -2537,7 +2619,6 @@ impl<'a> Widget for Diary<'a> { ) .wh(art_size) .middle_of(state.content_align) - .graphics_for(state.content_align) .color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0)))) .set(state.staff_render, ui); // Top Left skills @@ -2885,7 +2966,7 @@ impl<'a> Widget for Diary<'a> { events.push(Event::UnlockSkill(skill)); }; }, - SelectedSkillTree::Sceptre => { + SelectedSkillTree::Weapon(ToolKind::Sceptre) => { use skills::SceptreSkill::*; // Sceptre Image::new( @@ -2894,7 +2975,6 @@ impl<'a> Widget for Diary<'a> { ) .wh(art_size) .middle_of(state.content_align) - .graphics_for(state.content_align) .color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0)))) .set(state.sceptre_render, ui); // Top Left skills diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index ad05bad1d3..6332fce296 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -65,6 +65,8 @@ image_ids! { // Diary Window diary_bg: "voxygen.element.misc_bg.diary_bg", diary_frame: "voxygen.element.misc_bg.diary_frame", + diary_exp_bg: "voxygen.element.misc_bg.diary_exp_bg", + diary_exp_frame: "voxygen.element.misc_bg.diary_exp_frame", // Skill Trees sceptre: "voxygen.element.icons.sceptre", @@ -184,6 +186,7 @@ image_ids! { // Other Icons/Art skull: "voxygen.element.icons.skull", skull_2: "voxygen.element.icons.skull_2", + indicator_bubble: "voxygen.element.icons.indicator_bubble", fireplace: "voxygen.element.misc_bg.fireplace", // Crosshair diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index fddcccf5f9..f16877075c 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -99,6 +99,7 @@ const LOW_HP_COLOR: Color = Color::Rgba(0.93, 0.59, 0.03, 1.0); const CRITICAL_HP_COLOR: Color = Color::Rgba(0.79, 0.19, 0.17, 1.0); const STAMINA_COLOR: Color = Color::Rgba(0.29, 0.62, 0.75, 0.9); const ENEMY_HP_COLOR: Color = Color::Rgba(0.93, 0.1, 0.29, 1.0); +const XP_COLOR: Color = Color::Rgba(0.59, 0.41, 0.67, 1.0); //const TRANSPARENT: Color = Color::Rgba(0.0, 0.0, 0.0, 0.0); //const FOCUS_COLOR: Color = Color::Rgba(1.0, 0.56, 0.04, 1.0); //const RAGE_COLOR: Color = Color::Rgba(0.5, 0.04, 0.13, 1.0); @@ -756,7 +757,7 @@ impl Hud { group_menu: false, mini_map: true, settings_tab: SettingsTab::Interface, - skilltreetab: SelectedSkillTree::GeneralCombat, + skilltreetab: SelectedSkillTree::General, social_tab: SocialTab::Online, want_grab: true, ingame: true, @@ -1057,6 +1058,111 @@ impl Hud { } } } + // EXP Numbers + /*if let (Some(floaters), Some(stats)) = ( + Some(&*ecs.read_resource::()) + .map(|l| &l.floaters) + .filter(|f| !f.is_empty()), + stats.get(me), + ) { + // TODO replace with setting + let batched_sct = false; + if batched_sct { + let number_speed = 50.0; // Number Speed for Cumulated EXP + let player_sct_bg_id = player_sct_bg_id_walker.next( + &mut self.ids.player_sct_bgs, + &mut ui_widgets.widget_id_generator(), + ); + let player_sct_id = player_sct_id_walker.next( + &mut self.ids.player_scts, + &mut ui_widgets.widget_id_generator(), + ); + // Sum xp change + let exp_change = floaters.iter().fold(0, |acc, f| f.exp_change + acc); + // Can't fail since we filtered out empty lists above + let (timer, rand) = floaters + .last() + .map(|f| (f.timer, f.rand)) + .expect("Impossible"); + // Increase font size based on fraction of maximum health + // "flashes" by having a larger size in the first 100ms + let font_size_xp = 30 + + ((exp_change.abs() as f32 / stats.exp.maximum() as f32).min(1.0) + * 50.0) as u32 + + if timer < 0.1 { + FLASH_MAX * (((1.0 - timer / 0.1) * 10.0) as u32) + } else { + 0 + }; + + let y = timer as f64 * number_speed; // Timer sets the widget offset + let fade = ((4.0 - timer as f32) * 0.25) + 0.2; // Timer sets text transparency + + Text::new(&format!("{} Exp", exp_change)) + .font_size(font_size_xp) + .font_id(self.fonts.cyri.conrod_id) + .color(Color::Rgba(0.0, 0.0, 0.0, fade)) + .x_y( + ui_widgets.win_w * (0.5 * rand.0 as f64 - 0.25), + ui_widgets.win_h * (0.15 * rand.1 as f64) + y - 3.0, + ) + .set(player_sct_bg_id, ui_widgets); + Text::new(&format!("{} Exp", exp_change)) + .font_size(font_size_xp) + .font_id(self.fonts.cyri.conrod_id) + .color(Color::Rgba(0.59, 0.41, 0.67, fade)) + .x_y( + ui_widgets.win_w * (0.5 * rand.0 as f64 - 0.25), + ui_widgets.win_h * (0.15 * rand.1 as f64) + y, + ) + .set(player_sct_id, ui_widgets); + } else { + for floater in floaters { + let number_speed = 50.0; // Number Speed for Single EXP + let player_sct_bg_id = player_sct_bg_id_walker.next( + &mut self.ids.player_sct_bgs, + &mut ui_widgets.widget_id_generator(), + ); + let player_sct_id = player_sct_id_walker.next( + &mut self.ids.player_scts, + &mut ui_widgets.widget_id_generator(), + ); + // Increase font size based on fraction of maximum health + // "flashes" by having a larger size in the first 100ms + let font_size_xp = 30 + + ((floater.exp_change.abs() as f32 / stats.exp.maximum() as f32) + .min(1.0) + * 50.0) as u32 + + if floater.timer < 0.1 { + FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32) + } else { + 0 + }; + + let y = floater.timer as f64 * number_speed; // Timer sets the widget offset + let fade = ((4.0 - floater.timer as f32) * 0.25) + 0.2; // Timer sets text transparency + + Text::new(&format!("{} Exp", floater.exp_change)) + .font_size(font_size_xp) + .font_id(self.fonts.cyri.conrod_id) + .color(Color::Rgba(0.0, 0.0, 0.0, fade)) + .x_y( + ui_widgets.win_w * (0.5 * floater.rand.0 as f64 - 0.25), + ui_widgets.win_h * (0.15 * floater.rand.1 as f64) + y - 3.0, + ) + .set(player_sct_bg_id, ui_widgets); + Text::new(&format!("{} Exp", floater.exp_change)) + .font_size(font_size_xp) + .font_id(self.fonts.cyri.conrod_id) + .color(Color::Rgba(0.59, 0.41, 0.67, fade)) + .x_y( + ui_widgets.win_w * (0.5 * floater.rand.0 as f64 - 0.25), + ui_widgets.win_h * (0.15 * floater.rand.1 as f64) + y, + ) + .set(player_sct_id, ui_widgets); + } + } + }*/ } // Pop speech bubbles @@ -2222,7 +2328,7 @@ impl Hud { self.show.want_grab = true; self.force_ungrab = false; }, - diary::Event::ChangeWeaponTree(tree_sel) => { + diary::Event::ChangeSkillTree(tree_sel) => { self.show.open_skill_tree(tree_sel) }, diary::Event::UnlockSkill(skill) => events.push(Event::UnlockSkill(skill)), diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index c8af2c4970..ddb636f858 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -1,6 +1,8 @@ use super::{ - img_ids::Imgs, DEFAULT_NPC, ENEMY_HP_COLOR, FACTION_COLOR, GROUP_COLOR, GROUP_MEMBER, HP_COLOR, - LOW_HP_COLOR, REGION_COLOR, SAY_COLOR, STAMINA_COLOR, TELL_COLOR, TEXT_BG, TEXT_COLOR, + img_ids::Imgs, DEFAULT_NPC, FACTION_COLOR, GROUP_COLOR, GROUP_MEMBER, HP_COLOR, LOW_HP_COLOR, + QUALITY_ARTIFACT, QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LEGENDARY, + QUALITY_LOW, QUALITY_MODERATE, REGION_COLOR, SAY_COLOR, STAMINA_COLOR, TELL_COLOR, TEXT_BG, + TEXT_COLOR, XP_COLOR, ENEMY_HP_COLOR }; use crate::{ hud::get_buff_info, @@ -17,6 +19,8 @@ use conrod_core::{ }; const MAX_BUBBLE_WIDTH: f64 = 250.0; +use inline_tweak::*; + widget_ids! { struct Ids { // Speech bubble @@ -119,7 +123,8 @@ impl<'a> Ingameable for Overhead<'a> { // - 2 Text::new for name // // If HP Info is shown: - // - 1 for level: either Text or Image + // - 1 for level: either Text or Image <-- Not used currently, will be replaced + // by something else // - 3 for HP + fg + bg // - 1 for HP text // - If there's mana @@ -365,9 +370,34 @@ impl<'a> Widget for Overhead<'a> { .color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.99))) .parent(id) .set(state.ids.health_bar_fg, ui); + + // TODO: Add strength comparison here, this is just an example + // Factors to take into account: + // Maximum HP + // Protection + // Mainhand Weapon DPS + // "Boss Factor" (?) + // For players: Highest skilltree rank + + let indicator_col = match health_max as u32 { + 0..=50 => QUALITY_LOW, + 51..=100 => QUALITY_COMMON, + 101..=150 => QUALITY_MODERATE, + 151..=200 => QUALITY_HIGH, + 201..=250 => QUALITY_EPIC, + 251..=300 => QUALITY_LEGENDARY, + 301..=350 => QUALITY_ARTIFACT, + 351..=9999 => QUALITY_DEBUG, + _ => XP_COLOR, + }; + Image::new(self.imgs.indicator_bubble) + .w_h(5.0 * BARSIZE, 5.0 * BARSIZE) + .x_y(tweak!(-37.0) * BARSIZE, MANA_BAR_Y + tweak!(7.5)) + .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;