diff --git a/common/src/comp/energy.rs b/common/src/comp/energy.rs index bb7d9374cb..908dd7835f 100644 --- a/common/src/comp/energy.rs +++ b/common/src/comp/energy.rs @@ -1,3 +1,4 @@ +use crate::comp::Body; use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage}; use specs_idvs::IdvStorage; @@ -29,10 +30,19 @@ pub enum StatChangeError { } impl Energy { - pub fn new(amount: u32) -> Energy { + pub fn new(body: Body, level: u16) -> Energy { + let mut energy = Energy::empty(); + + energy.update_max_energy(Some(body), level); + energy.set_to(energy.maximum(), EnergySource::Revive); + + energy + } + + pub fn empty() -> Self { Energy { - current: amount, - maximum: amount, + current: 0, + maximum: 0, regen_rate: 0.0, last_change: None, } @@ -75,6 +85,12 @@ impl Energy { self.maximum = amount; self.current = self.current.min(self.maximum); } + + pub fn update_max_energy(&mut self, body: Option, level: u16) { + if let Some(body) = body { + self.set_maximum(body.base_energy() + 50 * level as u32); + } + } } pub struct EnergyChange { diff --git a/common/src/comp/health.rs b/common/src/comp/health.rs index 43f4a1407d..541736c280 100644 --- a/common/src/comp/health.rs +++ b/common/src/comp/health.rs @@ -39,7 +39,7 @@ pub struct Health { } impl Health { - pub fn new(body: Body, level: u32) -> Self { + pub fn new(body: Body, level: u16) -> Self { let mut health = Health::empty(); health.update_max_hp(Some(body), level); @@ -103,10 +103,10 @@ impl Health { } // TODO: Delete this once stat points will be a thing - pub fn update_max_hp(&mut self, body: Option, level: u32) { + pub fn update_max_hp(&mut self, body: Option, level: u16) { if let Some(body) = body { - self.set_base_max(body.base_health() + body.base_health_increase() * level); - self.set_maximum(body.base_health() + body.base_health_increase() * level); + self.set_base_max(body.base_health() + body.base_health_increase() * level as u32); + self.set_maximum(body.base_health() + body.base_health_increase() * level as u32); } } diff --git a/common/sys/src/stats.rs b/common/sys/src/stats.rs index 9b284545c1..b6feb7df45 100644 --- a/common/sys/src/stats.rs +++ b/common/sys/src/stats.rs @@ -113,7 +113,7 @@ impl<'a> System<'a> for Sys { .copied() .flatten() .unwrap_or(0); - health.update_max_hp(Some(stat.body_type), health_level.into()); + health.update_max_hp(Some(stat.body_type), health_level); let mut stat = stats.get_mut_unchecked(); stat.skill_set.modify_health = false; } @@ -126,9 +126,8 @@ impl<'a> System<'a> for Sys { .get(&Skill::General(GeneralSkill::EnergyIncrease)) .copied() .flatten() - .unwrap_or(0) as u32; - let energy_max = stat.body_type.base_energy() + 50 * energy_level; - energy.set_maximum(energy_max); + .unwrap_or(0); + energy.update_max_energy(Some(stat.body_type), energy_level); let mut stat = stats.get_mut_unchecked(); stat.skill_set.modify_energy = false; } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 31ea0832d5..722d652180 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -251,14 +251,18 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc } } let num_pools = xp_pools.len() as f32; - let exp = (exp / num_pools).ceil() as i32; for pool in xp_pools.drain() { - stats.skill_set.change_experience(pool, exp); + stats + .skill_set + .change_experience(pool, (exp / num_pools).ceil() as i32); } state .ecs() .write_resource::>() - .push(Outcome::ExpChange { uid: *uid, exp }); + .push(Outcome::ExpChange { + uid: *uid, + exp: exp as i32, + }); } }); } @@ -294,16 +298,17 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc } } let num_pools = xp_pools.len() as f32; - let exp = (exp_reward / num_pools).ceil() as i32; for pool in xp_pools.drain() { - attacker_stats.skill_set.change_experience(pool, exp); + attacker_stats + .skill_set + .change_experience(pool, (exp_reward / num_pools).ceil() as i32); } state .ecs() .write_resource::>() .push(Outcome::ExpChange { uid: *attacker_uid, - exp, + exp: exp_reward as i32, }); } })(); diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 06ea7b657f..5f624eb527 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -4,8 +4,11 @@ use crate::{ }; use common::{ character::CharacterId, - comp, - comp::Inventory, + comp::{ + self, + skills::{GeneralSkill, Skill}, + Inventory, + }, effect::Effect, uid::{Uid, UidAllocator}, util::Dir, @@ -139,7 +142,7 @@ impl StateExt for State { .with(stats) .with(health) .with(comp::Alignment::Npc) - .with(comp::Energy::new(body.base_energy())) + .with(comp::Energy::new(body, 0)) .with(comp::Gravity(1.0)) .with(comp::CharacterState::default()) .with(inventory) @@ -219,7 +222,6 @@ impl StateExt for State { fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId) { let spawn_point = self.ecs().read_resource::().0; - self.write_component(entity, comp::Energy::new(1000)); self.write_component(entity, comp::Controller::default()); self.write_component(entity, comp::Pos(spawn_point)); self.write_component(entity, comp::Vel(Vec3::zero())); @@ -269,10 +271,24 @@ impl StateExt for State { z_max: body.height(), }); self.write_component(entity, body); - self.write_component( - entity, - comp::Health::new(stats.body_type, 0), //Placeholder 0 + let (health_level, energy_level) = ( + stats + .skill_set + .skills + .get(&Skill::General(GeneralSkill::HealthIncrease)) + .copied() + .flatten() + .unwrap_or(0), + stats + .skill_set + .skills + .get(&Skill::General(GeneralSkill::EnergyIncrease)) + .copied() + .flatten() + .unwrap_or(0), ); + self.write_component(entity, comp::Health::new(stats.body_type, health_level)); + self.write_component(entity, comp::Energy::new(stats.body_type, energy_level)); self.write_component(entity, stats); self.write_component(entity, inventory); self.write_component( diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index ee4491fd47..d4a1be2397 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, XP_COLOR, HP_COLOR, CRITICAL_HP_COLOR, + Show, CRITICAL_HP_COLOR, HP_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, XP_COLOR, }; use crate::{ i18n::Localization, @@ -1148,20 +1148,29 @@ impl<'a> Widget for Diary<'a> { let skill = Skill::Sword(DCost); let prereqs_met = tweak!(true); let suff_pts = tweak!(false); - let label_txt = &format!( "{}/{}", + let label_txt = &format!( + "{}/{}", skills.get(&skill).copied().map_or(0, |l| l.unwrap_or(1)), - skill.get_max_level().unwrap_or(1)); + skill.get_max_level().unwrap_or(1) + ); if Button::image(self.imgs.sword_whirlwind) .w_h(tweak!(74.0), tweak!(74.0)) .middle_of(state.skills_top_r[2]) - .label(if prereqs_met {&label_txt} else {""} - ) + .label(if prereqs_met { &label_txt } else { "" }) .label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0))) .label_x(conrod_core::position::Relative::Scalar(tweak!(32.0))) - .label_color(if suff_pts {HP_COLOR} else {CRITICAL_HP_COLOR}) + .label_color(if suff_pts { + HP_COLOR + } else { + CRITICAL_HP_COLOR + }) .label_font_size(self.fonts.cyri.scale(tweak!(16))) .label_font_id(self.fonts.cyri.conrod_id) - .image_color(if prereqs_met {TEXT_COLOR} else {Color::Rgba(0.41, 0.41, 0.41, tweak!(0.7))}) + .image_color(if prereqs_met { + TEXT_COLOR + } else { + Color::Rgba(0.41, 0.41, 0.41, tweak!(0.7)) + }) .with_tooltip( self.tooltip_manager, "Dash Cost", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index ae5ba75a6e..4f2e1f56d0 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -77,6 +77,7 @@ use conrod_core::{ widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, }; use hashbrown::HashMap; +use rand::Rng; use specs::{Join, WorldExt}; use std::{ collections::VecDeque, @@ -285,6 +286,13 @@ pub struct BuffInfo { dur: Option, } +pub struct ExpFloater { + pub owner: Uid, + pub exp_change: i32, + pub timer: f32, + pub rand_offset: (f32, f32), +} + pub struct DebugInfo { pub tps: f64, pub frame_time: Duration, @@ -681,6 +689,7 @@ pub struct Hud { hotbar: hotbar::State, events: Vec, crosshair_opacity: f32, + exp_floaters: Vec, } impl Hud { @@ -779,6 +788,7 @@ impl Hud { hotbar: hotbar_state, events: Vec::new(), crosshair_opacity: 0.0, + exp_floaters: Vec::new(), } } @@ -1060,26 +1070,10 @@ impl Hud { } } // EXP Numbers + self.exp_floaters.retain(|f| f.timer > 0_f32); if let Some(uid) = uids.get(me) { - for exp in ecs - .read_resource::>() - .iter() - .filter( - |o| matches!(o, Outcome::ExpChange { uid: uid_, .. } if uid == uid_ ), - ) - .filter_map(|o| { - if let Outcome::ExpChange { exp, .. } = o { - Some(exp) - } else { - None - } - }) - { - println!("{}", exp); + for floater in self.exp_floaters.iter_mut().filter(|f| f.owner == *uid) { let number_speed = 50.0; // Number Speed for Single EXP - let timer = 100.0; // Fake number - let rand1 = 0.5; // Fake number - let rand2 = -0.1; // Fake number let player_sct_bg_id = player_sct_bg_id_walker.next( &mut self.ids.player_sct_bgs, &mut ui_widgets.widget_id_generator(), @@ -1091,34 +1085,35 @@ impl Hud { // 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 as f32 / 300.0).min(1.0) * 50.0) as u32 - + if timer < 0.1 { - FLASH_MAX * (((1.0 - timer / 0.1) * 10.0) as u32) + + ((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 = 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 + 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", exp)) + 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 * rand1 as f64 - 0.25), - ui_widgets.win_h * (0.15 * rand2 as f64) + y - 3.0, + ui_widgets.win_w * (0.5 * floater.rand_offset.0 as f64 - 0.25), + ui_widgets.win_h * (0.15 * floater.rand_offset.1 as f64) + y - 3.0, ) .set(player_sct_bg_id, ui_widgets); - Text::new(&format!("{} Exp", exp)) + 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 * rand1 as f64 - 0.25), - ui_widgets.win_h * (0.15 * rand2 as f64) + y, + ui_widgets.win_w * (0.5 * floater.rand_offset.0 as f64 - 0.25), + ui_widgets.win_h * (0.15 * floater.rand_offset.1 as f64) + y, ) .set(player_sct_id, ui_widgets); + floater.timer -= dt.as_secs_f32(); } } } @@ -2778,6 +2773,18 @@ impl Hud { pub fn free_look(&mut self, free_look: bool) { self.show.free_look = free_look; } pub fn auto_walk(&mut self, auto_walk: bool) { self.show.auto_walk = auto_walk; } + + pub fn handle_outcome(&mut self, outcome: &Outcome) { + match outcome { + Outcome::ExpChange { uid, exp } => self.exp_floaters.push(ExpFloater { + owner: *uid, + exp_change: *exp, + timer: 4.0, + rand_offset: rand::thread_rng().gen::<(f32, f32)>(), + }), + _ => {}, + } + } } // Get item qualities of equipped items and assign a tooltip title/frame color pub fn get_quality_col(item: &I) -> Color { diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index a99111c5c6..b6c2240686 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -1250,6 +1250,7 @@ impl PlayState for SessionState { for outcome in outcomes { self.scene .handle_outcome(&outcome, &scene_data, &mut global_state.audio); + self.hud.handle_outcome(&outcome); } } }