Skill point gains are displayed in hud.

level up message visuals and functionality

Handles simultaneous skill point gains in UI.
This commit is contained in:
Sam 2021-01-04 14:29:15 -05:00
parent cb3e44a811
commit 4b52574750
12 changed files with 159 additions and 30 deletions

BIN
assets/voxygen/element/buttons/slot_skilltree.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -100,6 +100,7 @@
"common.weapons.bow": "Bogen", "common.weapons.bow": "Bogen",
"common.weapons.hammer": "Hammer", "common.weapons.hammer": "Hammer",
"common.weapons.sceptre": "Druiden Szepter", "common.weapons.sceptre": "Druiden Szepter",
"common.weapons.general": "Kampf",
/// End Common section /// End Common section
@ -205,6 +206,7 @@ https://veloren.net/account/.
// SCT outputs // SCT outputs
"hud.sct.experience": "{amount} Erf", "hud.sct.experience": "{amount} Erf",
"hud.sct.block": "GEBLOCKT", "hud.sct.block": "GEBLOCKT",
"hud.rank_up": "Neuer Rang",
/// Respawn message /// Respawn message
"hud.press_key_to_respawn": r#"Drückt {key} um am letzten Lagerfeuer wiederbelebt zu werden."#, "hud.press_key_to_respawn": r#"Drückt {key} um am letzten Lagerfeuer wiederbelebt zu werden."#,

View File

@ -62,6 +62,7 @@ Is the client up to date?"#,
"common.weapons.staff": "Staff", "common.weapons.staff": "Staff",
"common.weapons.bow": "Bow", "common.weapons.bow": "Bow",
"common.weapons.hammer": "Hammer", "common.weapons.hammer": "Hammer",
"common.weapons.general": "General Combat",
"common.weapons.sceptre": "Healing Sceptre", "common.weapons.sceptre": "Healing Sceptre",
"common.rand_appearance": "Random appearance and name", "common.rand_appearance": "Random appearance and name",
}, },

View File

@ -6,6 +6,7 @@
// SCT outputs // SCT outputs
"hud.sct.experience": "{amount} Exp", "hud.sct.experience": "{amount} Exp",
"hud.sct.block": "BLOCKED", "hud.sct.block": "BLOCKED",
"hud.rank_up": "New Rank",
}, },

View File

@ -544,11 +544,8 @@ impl Skill {
/// Returns the cost in skill points of unlocking a particular skill /// Returns the cost in skill points of unlocking a particular skill
pub fn skill_cost(self, level: Level) -> u16 { pub fn skill_cost(self, level: Level) -> u16 {
use Skill::*; // TODO: Better balance the costs later
match self { level.unwrap_or(1)
General(GeneralSkill::HealthIncrease) => 1,
_ => level.unwrap_or(1),
}
} }
/// Returns the maximum level a skill can reach, returns None if the skill /// Returns the maximum level a skill can reach, returns None if the skill

View File

@ -33,6 +33,11 @@ pub enum Outcome {
uid: Uid, uid: Uid,
exp: i32, exp: i32,
}, },
SkillPointGain {
uid: Uid,
skill_tree: comp::skills::SkillGroupType,
total_points: u16,
},
} }
impl Outcome { impl Outcome {
@ -43,6 +48,7 @@ impl Outcome {
Outcome::LevelUp { pos } => Some(*pos), Outcome::LevelUp { pos } => Some(*pos),
Outcome::Beam { pos, .. } => Some(*pos), Outcome::Beam { pos, .. } => Some(*pos),
Outcome::ExpChange { .. } => None, Outcome::ExpChange { .. } => None,
Outcome::SkillPointGain { .. } => None,
} }
} }
} }

View File

@ -5,11 +5,13 @@ use common::{
}, },
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
metrics::SysMetrics, metrics::SysMetrics,
outcome::Outcome,
resources::DeltaTime, resources::DeltaTime,
span, span,
uid::Uid,
}; };
use hashbrown::HashSet; use hashbrown::HashSet;
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage}; use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage};
const ENERGY_REGEN_ACCEL: f32 = 10.0; const ENERGY_REGEN_ACCEL: f32 = 10.0;
@ -26,6 +28,8 @@ impl<'a> System<'a> for Sys {
WriteStorage<'a, Stats>, WriteStorage<'a, Stats>,
WriteStorage<'a, Health>, WriteStorage<'a, Health>,
WriteStorage<'a, Energy>, WriteStorage<'a, Energy>,
ReadStorage<'a, Uid>,
Write<'a, Vec<Outcome>>,
); );
fn run( fn run(
@ -39,6 +43,8 @@ impl<'a> System<'a> for Sys {
mut stats, mut stats,
mut healths, mut healths,
mut energies, mut energies,
uids,
mut outcomes,
): Self::SystemData, ): Self::SystemData,
) { ) {
let start_time = std::time::Instant::now(); let start_time = std::time::Instant::now();
@ -53,8 +59,9 @@ impl<'a> System<'a> for Sys {
healths.set_event_emission(true); healths.set_event_emission(true);
// Update stats // Update stats
for (entity, mut stats, mut health) in ( for (entity, uid, mut stats, mut health) in (
&entities, &entities,
&uids,
&mut stats.restrict_mut(), &mut stats.restrict_mut(),
&mut healths.restrict_mut(), &mut healths.restrict_mut(),
) )
@ -91,6 +98,11 @@ impl<'a> System<'a> for Sys {
stat.skill_set stat.skill_set
.change_experience(skill_group, -(skill_group.skill_point_cost() as i32)); .change_experience(skill_group, -(skill_group.skill_point_cost() as i32));
stat.skill_set.add_skill_points(skill_group, 1); stat.skill_set.add_skill_points(skill_group, 1);
outcomes.push(Outcome::SkillPointGain {
uid: *uid,
skill_tree: skill_group,
total_points: stat.skill_set.get_earned_sp(skill_group),
});
} }
} }
} }

View File

@ -358,7 +358,7 @@ impl SfxMgr {
audio.play_sfx(file_ref, *pos, None); audio.play_sfx(file_ref, *pos, None);
} }
}, },
Outcome::ExpChange { .. } => {}, _ => {},
} }
} }

View File

@ -69,6 +69,7 @@ image_ids! {
diary_exp_frame: "voxygen.element.misc_bg.diary_exp_frame", diary_exp_frame: "voxygen.element.misc_bg.diary_exp_frame",
// Skill Trees // Skill Trees
slot_skills: "voxygen.element.buttons.slot_skilltree",
swords_crossed: "voxygen.element.icons.swords_crossed", swords_crossed: "voxygen.element.icons.swords_crossed",
sceptre: "voxygen.element.icons.sceptre", sceptre: "voxygen.element.icons.sceptre",
sword: "voxygen.element.icons.sword", sword: "voxygen.element.icons.sword",
@ -179,7 +180,6 @@ image_ids! {
// Skillbar // Skillbar
level_up: "voxygen.element.misc_bg.level_up", level_up: "voxygen.element.misc_bg.level_up",
level_down:"voxygen.element.misc_bg.level_down",
bar_content: "voxygen.element.skillbar.bar_content", bar_content: "voxygen.element.skillbar.bar_content",
skillbar_bg: "voxygen.element.skillbar.bg", skillbar_bg: "voxygen.element.skillbar.bg",
skillbar_frame: "voxygen.element.skillbar.frame", skillbar_frame: "voxygen.element.skillbar.frame",

View File

@ -56,11 +56,12 @@ use crate::{
GlobalState, GlobalState,
}; };
use client::Client; use client::Client;
use common::{ use common::{
comp, comp,
comp::{ comp::{
item::{ItemDesc, Quality}, item::{tool::ToolKind, ItemDesc, Quality},
skills::Skill, skills::{Skill, SkillGroupType},
BuffKind, BuffKind,
}, },
outcome::Outcome, outcome::Outcome,
@ -77,6 +78,7 @@ use conrod_core::{
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
}; };
use hashbrown::HashMap; use hashbrown::HashMap;
use inline_tweak::*;
use rand::Rng; use rand::Rng;
use specs::{Join, WorldExt}; use specs::{Join, WorldExt};
use std::{ use std::{
@ -177,6 +179,13 @@ widget_ids! {
// SCT // SCT
player_scts[], player_scts[],
player_sct_bgs[], player_sct_bgs[],
player_rank_up,
player_rank_up_txt_number,
player_rank_up_txt_0,
player_rank_up_txt_0_bg,
player_rank_up_txt_1,
player_rank_up_txt_1_bg,
player_rank_up_icon,
sct_exp_bgs[], sct_exp_bgs[],
sct_exps[], sct_exps[],
sct_lvl_bg, sct_lvl_bg,
@ -293,6 +302,13 @@ pub struct ExpFloater {
pub rand_offset: (f32, f32), pub rand_offset: (f32, f32),
} }
pub struct SkillPointGain {
pub owner: Uid,
pub skill_tree: SkillGroupType,
pub total_points: u16,
pub timer: f32,
}
pub struct DebugInfo { pub struct DebugInfo {
pub tps: f64, pub tps: f64,
pub frame_time: Duration, pub frame_time: Duration,
@ -690,6 +706,7 @@ pub struct Hud {
events: Vec<Event>, events: Vec<Event>,
crosshair_opacity: f32, crosshair_opacity: f32,
exp_floaters: Vec<ExpFloater>, exp_floaters: Vec<ExpFloater>,
skill_point_displays: Vec<SkillPointGain>,
} }
impl Hud { impl Hud {
@ -789,6 +806,7 @@ impl Hud {
events: Vec::new(), events: Vec::new(),
crosshair_opacity: 0.0, crosshair_opacity: 0.0,
exp_floaters: Vec::new(), exp_floaters: Vec::new(),
skill_point_displays: Vec::new(),
} }
} }
@ -868,8 +886,7 @@ impl Hud {
.graphics_for(ui_widgets.window) .graphics_for(ui_widgets.window)
.color(Some(Color::Rgba(0.0, 0.0, 0.0, 1.0))) .color(Some(Color::Rgba(0.0, 0.0, 0.0, 1.0)))
.set(self.ids.death_bg, ui_widgets); .set(self.ids.death_bg, ui_widgets);
} } // Crosshair
// Crosshair
let show_crosshair = (info.is_aiming || info.is_first_person) && !health.is_dead; let show_crosshair = (info.is_aiming || info.is_first_person) && !health.is_dead;
self.crosshair_opacity = Lerp::lerp( self.crosshair_opacity = Lerp::lerp(
self.crosshair_opacity, self.crosshair_opacity,
@ -1084,17 +1101,16 @@ impl Hud {
); );
// Increase font size based on fraction of maximum health // Increase font size based on fraction of maximum health
// "flashes" by having a larger size in the first 100ms // "flashes" by having a larger size in the first 100ms
let font_size_xp = 30 let font_size_xp =
+ ((floater.exp_change as f32 / 300.0).min(1.0) * 50.0) as u32 30 + ((floater.exp_change as f32 / 300.0).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 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 //let fade = ((4.0 - floater.timer as f32) * 0.25) + 0.2; // Timer sets
// text transparency
let fade = if floater.timer < 1.0 {
floater.timer as f32
} else {
1.0
};
Text::new(&format!("{} Exp", floater.exp_change)) Text::new(&format!("{} Exp", floater.exp_change))
.font_size(font_size_xp) .font_size(font_size_xp)
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
@ -1116,6 +1132,90 @@ impl Hud {
floater.timer -= dt.as_secs_f32(); floater.timer -= dt.as_secs_f32();
} }
} }
// Skill points
self.skill_point_displays.retain(|d| d.timer > 0_f32);
if let Some(uid) = uids.get(me) {
if let Some(display) = self
.skill_point_displays
.iter_mut()
.find(|d| d.owner == *uid)
{
let fade = if display.timer < 1.0 {
display.timer as f32
} else {
1.0
};
// Background image
Image::new(self.imgs.level_up)
.w_h(328.0, 126.0)
.mid_top_with_margin_on(ui_widgets.window, tweak!(300.0))
.graphics_for(ui_widgets.window)
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade)))
.set(self.ids.player_rank_up, ui_widgets);
// Rank Number
let rank = display.total_points;
Text::new(&format!("{}", rank))
.font_size(tweak!(20))
.font_id(self.fonts.cyri.conrod_id)
.color(Color::Rgba(1.0, 1.0, 1.0, fade))
.mid_top_with_margin_on(self.ids.player_rank_up, tweak!(8.0))
.set(self.ids.player_rank_up_txt_number, ui_widgets);
// Static "New Rank!" text
Text::new(&i18n.get("hud.rank_up"))
.font_size(tweak!(40))
.font_id(self.fonts.cyri.conrod_id)
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
.mid_bottom_with_margin_on(self.ids.player_rank_up, tweak!(20.0))
.set(self.ids.player_rank_up_txt_0_bg, ui_widgets);
Text::new(&i18n.get("hud.rank_up"))
.font_size(tweak!(40))
.font_id(self.fonts.cyri.conrod_id)
.color(Color::Rgba(1.0, 1.0, 1.0, fade))
.bottom_left_with_margins_on(self.ids.player_rank_up_txt_0_bg, 2.0, 2.0)
.set(self.ids.player_rank_up_txt_0, ui_widgets);
// Variable skilltree text
let skill = match display.skill_tree {
General => &i18n.get("common.weapons.general"),
Weapon(ToolKind::Hammer) => &i18n.get("common.weapons.hammer"),
Weapon(ToolKind::Axe) => &i18n.get("common.weapons.axe"),
Weapon(ToolKind::Sword) => &i18n.get("common.weapons.sword"),
Weapon(ToolKind::Sceptre) => &i18n.get("common.weapons.sceptre"),
Weapon(ToolKind::Bow) => &i18n.get("common.weapons.bow"),
Weapon(ToolKind::Staff) => &i18n.get("common.weapons.staff"),
_ => "Unknown",
};
Text::new(skill)
.font_size(tweak!(20))
.font_id(self.fonts.cyri.conrod_id)
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
.mid_top_with_margin_on(self.ids.player_rank_up, tweak!(45.0))
.set(self.ids.player_rank_up_txt_1_bg, ui_widgets);
Text::new(skill)
.font_size(tweak!(20))
.font_id(self.fonts.cyri.conrod_id)
.color(Color::Rgba(1.0, 1.0, 1.0, fade))
.bottom_left_with_margins_on(self.ids.player_rank_up_txt_1_bg, 2.0, 2.0)
.set(self.ids.player_rank_up_txt_1, ui_widgets);
// Variable skilltree icon
use crate::hud::SkillGroupType::{General, Weapon};
Image::new(match display.skill_tree {
General => self.imgs.swords_crossed,
Weapon(ToolKind::Hammer) => self.imgs.hammer,
Weapon(ToolKind::Axe) => self.imgs.axe,
Weapon(ToolKind::Sword) => self.imgs.sword,
Weapon(ToolKind::Sceptre) => self.imgs.sceptre,
Weapon(ToolKind::Bow) => self.imgs.bow,
Weapon(ToolKind::Staff) => self.imgs.staff,
_ => self.imgs.swords_crossed,
})
.w_h(tweak!(20.0), tweak!(20.0))
.left_from(self.ids.player_rank_up_txt_1_bg, tweak!(5.0))
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade)))
.set(self.ids.player_rank_up_icon, ui_widgets);
display.timer -= dt.as_secs_f32();
}
}
} }
// Pop speech bubbles // Pop speech bubbles
@ -2782,6 +2882,16 @@ impl Hud {
timer: 4.0, timer: 4.0,
rand_offset: rand::thread_rng().gen::<(f32, f32)>(), rand_offset: rand::thread_rng().gen::<(f32, f32)>(),
}), }),
Outcome::SkillPointGain {
uid,
skill_tree,
total_points,
} => self.skill_point_displays.push(SkillPointGain {
owner: *uid,
skill_tree: *skill_tree,
total_points: *total_points,
timer: 5.0,
}),
_ => {}, _ => {},
} }
} }