Exp floaters.

You load in with correct energy and health values now.
This commit is contained in:
Sam 2021-01-04 12:16:42 -05:00
parent 30df603115
commit b6d2d48ead
8 changed files with 113 additions and 60 deletions

View File

@ -1,3 +1,4 @@
use crate::comp::Body;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use specs::{Component, DerefFlaggedStorage}; use specs::{Component, DerefFlaggedStorage};
use specs_idvs::IdvStorage; use specs_idvs::IdvStorage;
@ -29,10 +30,19 @@ pub enum StatChangeError {
} }
impl Energy { 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 { Energy {
current: amount, current: 0,
maximum: amount, maximum: 0,
regen_rate: 0.0, regen_rate: 0.0,
last_change: None, last_change: None,
} }
@ -75,6 +85,12 @@ impl Energy {
self.maximum = amount; self.maximum = amount;
self.current = self.current.min(self.maximum); self.current = self.current.min(self.maximum);
} }
pub fn update_max_energy(&mut self, body: Option<Body>, level: u16) {
if let Some(body) = body {
self.set_maximum(body.base_energy() + 50 * level as u32);
}
}
} }
pub struct EnergyChange { pub struct EnergyChange {

View File

@ -39,7 +39,7 @@ pub struct Health {
} }
impl Health { impl Health {
pub fn new(body: Body, level: u32) -> Self { pub fn new(body: Body, level: u16) -> Self {
let mut health = Health::empty(); let mut health = Health::empty();
health.update_max_hp(Some(body), level); health.update_max_hp(Some(body), level);
@ -103,10 +103,10 @@ impl Health {
} }
// TODO: Delete this once stat points will be a thing // TODO: Delete this once stat points will be a thing
pub fn update_max_hp(&mut self, body: Option<Body>, level: u32) { pub fn update_max_hp(&mut self, body: Option<Body>, level: u16) {
if let Some(body) = body { if let Some(body) = body {
self.set_base_max(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); self.set_maximum(body.base_health() + body.base_health_increase() * level as u32);
} }
} }

View File

@ -113,7 +113,7 @@ impl<'a> System<'a> for Sys {
.copied() .copied()
.flatten() .flatten()
.unwrap_or(0); .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(); let mut stat = stats.get_mut_unchecked();
stat.skill_set.modify_health = false; stat.skill_set.modify_health = false;
} }
@ -126,9 +126,8 @@ impl<'a> System<'a> for Sys {
.get(&Skill::General(GeneralSkill::EnergyIncrease)) .get(&Skill::General(GeneralSkill::EnergyIncrease))
.copied() .copied()
.flatten() .flatten()
.unwrap_or(0) as u32; .unwrap_or(0);
let energy_max = stat.body_type.base_energy() + 50 * energy_level; energy.update_max_energy(Some(stat.body_type), energy_level);
energy.set_maximum(energy_max);
let mut stat = stats.get_mut_unchecked(); let mut stat = stats.get_mut_unchecked();
stat.skill_set.modify_energy = false; stat.skill_set.modify_energy = false;
} }

View File

@ -251,14 +251,18 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
} }
} }
let num_pools = xp_pools.len() as f32; let num_pools = xp_pools.len() as f32;
let exp = (exp / num_pools).ceil() as i32;
for pool in xp_pools.drain() { 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 state
.ecs() .ecs()
.write_resource::<Vec<Outcome>>() .write_resource::<Vec<Outcome>>()
.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 num_pools = xp_pools.len() as f32;
let exp = (exp_reward / num_pools).ceil() as i32;
for pool in xp_pools.drain() { 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 state
.ecs() .ecs()
.write_resource::<Vec<Outcome>>() .write_resource::<Vec<Outcome>>()
.push(Outcome::ExpChange { .push(Outcome::ExpChange {
uid: *attacker_uid, uid: *attacker_uid,
exp, exp: exp_reward as i32,
}); });
} }
})(); })();

View File

@ -4,8 +4,11 @@ use crate::{
}; };
use common::{ use common::{
character::CharacterId, character::CharacterId,
comp, comp::{
comp::Inventory, self,
skills::{GeneralSkill, Skill},
Inventory,
},
effect::Effect, effect::Effect,
uid::{Uid, UidAllocator}, uid::{Uid, UidAllocator},
util::Dir, util::Dir,
@ -139,7 +142,7 @@ impl StateExt for State {
.with(stats) .with(stats)
.with(health) .with(health)
.with(comp::Alignment::Npc) .with(comp::Alignment::Npc)
.with(comp::Energy::new(body.base_energy())) .with(comp::Energy::new(body, 0))
.with(comp::Gravity(1.0)) .with(comp::Gravity(1.0))
.with(comp::CharacterState::default()) .with(comp::CharacterState::default())
.with(inventory) .with(inventory)
@ -219,7 +222,6 @@ impl StateExt for State {
fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId) { fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId) {
let spawn_point = self.ecs().read_resource::<SpawnPoint>().0; let spawn_point = self.ecs().read_resource::<SpawnPoint>().0;
self.write_component(entity, comp::Energy::new(1000));
self.write_component(entity, comp::Controller::default()); self.write_component(entity, comp::Controller::default());
self.write_component(entity, comp::Pos(spawn_point)); self.write_component(entity, comp::Pos(spawn_point));
self.write_component(entity, comp::Vel(Vec3::zero())); self.write_component(entity, comp::Vel(Vec3::zero()));
@ -269,10 +271,24 @@ impl StateExt for State {
z_max: body.height(), z_max: body.height(),
}); });
self.write_component(entity, body); self.write_component(entity, body);
self.write_component( let (health_level, energy_level) = (
entity, stats
comp::Health::new(stats.body_type, 0), //Placeholder 0 .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, stats);
self.write_component(entity, inventory); self.write_component(entity, inventory);
self.write_component( self.write_component(

View File

@ -1,7 +1,7 @@
use super::{ use super::{
img_ids::{Imgs, ImgsRot}, img_ids::{Imgs, ImgsRot},
item_imgs::{ItemImgs, ItemKey::Tool}, 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::{ use crate::{
i18n::Localization, i18n::Localization,
@ -1148,20 +1148,29 @@ impl<'a> Widget for Diary<'a> {
let skill = Skill::Sword(DCost); let skill = Skill::Sword(DCost);
let prereqs_met = tweak!(true); let prereqs_met = tweak!(true);
let suff_pts = tweak!(false); 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)), 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) if Button::image(self.imgs.sword_whirlwind)
.w_h(tweak!(74.0), tweak!(74.0)) .w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_top_r[2]) .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_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.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_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id) .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( .with_tooltip(
self.tooltip_manager, self.tooltip_manager,
"Dash Cost", "Dash Cost",

View File

@ -77,6 +77,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 rand::Rng;
use specs::{Join, WorldExt}; use specs::{Join, WorldExt};
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
@ -285,6 +286,13 @@ pub struct BuffInfo {
dur: Option<Duration>, dur: Option<Duration>,
} }
pub struct ExpFloater {
pub owner: Uid,
pub exp_change: i32,
pub timer: f32,
pub rand_offset: (f32, f32),
}
pub struct DebugInfo { pub struct DebugInfo {
pub tps: f64, pub tps: f64,
pub frame_time: Duration, pub frame_time: Duration,
@ -681,6 +689,7 @@ pub struct Hud {
hotbar: hotbar::State, hotbar: hotbar::State,
events: Vec<Event>, events: Vec<Event>,
crosshair_opacity: f32, crosshair_opacity: f32,
exp_floaters: Vec<ExpFloater>,
} }
impl Hud { impl Hud {
@ -779,6 +788,7 @@ impl Hud {
hotbar: hotbar_state, hotbar: hotbar_state,
events: Vec::new(), events: Vec::new(),
crosshair_opacity: 0.0, crosshair_opacity: 0.0,
exp_floaters: Vec::new(),
} }
} }
@ -1060,26 +1070,10 @@ impl Hud {
} }
} }
// EXP Numbers // EXP Numbers
self.exp_floaters.retain(|f| f.timer > 0_f32);
if let Some(uid) = uids.get(me) { if let Some(uid) = uids.get(me) {
for exp in ecs for floater in self.exp_floaters.iter_mut().filter(|f| f.owner == *uid) {
.read_resource::<Vec<Outcome>>()
.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);
let number_speed = 50.0; // Number Speed for Single EXP 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( let player_sct_bg_id = player_sct_bg_id_walker.next(
&mut self.ids.player_sct_bgs, &mut self.ids.player_sct_bgs,
&mut ui_widgets.widget_id_generator(), &mut ui_widgets.widget_id_generator(),
@ -1091,34 +1085,35 @@ 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 = 30
+ ((*exp as f32 / 300.0).min(1.0) * 50.0) as u32 + ((floater.exp_change as f32 / 300.0).min(1.0) * 50.0) as u32
+ if timer < 0.1 { + if floater.timer < 0.1 {
FLASH_MAX * (((1.0 - timer / 0.1) * 10.0) as u32) FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32)
} else { } else {
0 0
}; };
let y = 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 - 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
Text::new(&format!("{} Exp", exp)) 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)
.color(Color::Rgba(0.0, 0.0, 0.0, fade)) .color(Color::Rgba(0.0, 0.0, 0.0, fade))
.x_y( .x_y(
ui_widgets.win_w * (0.5 * rand1 as f64 - 0.25), ui_widgets.win_w * (0.5 * floater.rand_offset.0 as f64 - 0.25),
ui_widgets.win_h * (0.15 * rand2 as f64) + y - 3.0, ui_widgets.win_h * (0.15 * floater.rand_offset.1 as f64) + y - 3.0,
) )
.set(player_sct_bg_id, ui_widgets); .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_size(font_size_xp)
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.color(Color::Rgba(0.59, 0.41, 0.67, fade)) .color(Color::Rgba(0.59, 0.41, 0.67, fade))
.x_y( .x_y(
ui_widgets.win_w * (0.5 * rand1 as f64 - 0.25), ui_widgets.win_w * (0.5 * floater.rand_offset.0 as f64 - 0.25),
ui_widgets.win_h * (0.15 * rand2 as f64) + y, ui_widgets.win_h * (0.15 * floater.rand_offset.1 as f64) + y,
) )
.set(player_sct_id, ui_widgets); .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 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 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 // Get item qualities of equipped items and assign a tooltip title/frame color
pub fn get_quality_col<I: ItemDesc>(item: &I) -> Color { pub fn get_quality_col<I: ItemDesc>(item: &I) -> Color {

View File

@ -1250,6 +1250,7 @@ impl PlayState for SessionState {
for outcome in outcomes { for outcome in outcomes {
self.scene self.scene
.handle_outcome(&outcome, &scene_data, &mut global_state.audio); .handle_outcome(&outcome, &scene_data, &mut global_state.audio);
self.hud.handle_outcome(&outcome);
} }
} }
} }