From dd894452f3d07d3044da70e69473fdc3d9b30cbe Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Wed, 5 Jan 2022 14:59:45 -0500 Subject: [PATCH] Add experience and leveling up, completing section 5.11 --- src/components.rs | 13 +++++-- src/damage_system.rs | 77 ++++++++++++++++++++++++++++++++++---- src/gui.rs | 12 ++++++ src/hunger_system.rs | 2 +- src/inventory_system.rs | 2 +- src/melee_combat_system.rs | 9 ++++- src/trigger_system.rs | 1 + 7 files changed, 102 insertions(+), 14 deletions(-) diff --git a/src/components.rs b/src/components.rs index e721036..5b6617f 100644 --- a/src/components.rs +++ b/src/components.rs @@ -83,16 +83,21 @@ pub struct WantsToMelee { #[derive(Component, Debug, ConvertSaveload, Clone)] pub struct SufferDamage { - pub amount: Vec, + pub amount: Vec<(i32, bool)>, } impl SufferDamage { - pub fn new_damage(store: &mut WriteStorage, victim: Entity, amount: i32) { + pub fn new_damage( + store: &mut WriteStorage, + victim: Entity, + amount: i32, + from_player: bool, + ) { if let Some(suffering) = store.get_mut(victim) { - suffering.amount.push(amount); + suffering.amount.push((amount, from_player)); } else { let dmg = SufferDamage { - amount: vec![amount], + amount: vec![(amount, from_player)], }; store.insert(victim, dmg).expect("Unable to insert damage"); } diff --git a/src/damage_system.rs b/src/damage_system.rs index 77cb9a3..b11c606 100644 --- a/src/damage_system.rs +++ b/src/damage_system.rs @@ -1,33 +1,96 @@ -use ::rltk::RandomNumberGenerator; +use ::rltk::{Point, RandomNumberGenerator}; use ::specs::prelude::*; +use rltk::RGB; use crate::components::{ - Equipped, InBackpack, LootTable, Name, Player, Pools, Position, SufferDamage, + Attributes, Equipped, InBackpack, LootTable, Name, Player, Pools, Position, SufferDamage, }; use crate::game_log::GameLog; +use crate::gamesystem::{mana_at_level, player_hp_at_level}; +use crate::particle_system::ParticleBuilder; use crate::raws::{get_item_drop, spawn_named_item, SpawnType, RAWS}; use crate::{Map, RunState}; pub struct DamageSystem {} impl<'a> System<'a> for DamageSystem { + #[allow(clippy::type_complexity)] type SystemData = ( WriteStorage<'a, Pools>, WriteStorage<'a, SufferDamage>, ReadStorage<'a, Position>, WriteExpect<'a, Map>, Entities<'a>, + ReadExpect<'a, Entity>, + ReadStorage<'a, Attributes>, + WriteExpect<'a, GameLog>, + WriteExpect<'a, ParticleBuilder>, + ReadExpect<'a, Point>, ); fn run(&mut self, data: Self::SystemData) { - let (mut stats, mut damage, positions, mut map, entities) = data; + let ( + mut stats, + mut damage, + positions, + mut map, + entities, + player, + attributes, + mut log, + mut particles, + player_pos, + ) = data; + let mut xp_gain = 0; for (entity, mut stats, damage) in (&entities, &mut stats, &damage).join() { - stats.hit_points.current -= damage.amount.iter().sum::(); + for dmg in damage.amount.iter() { + stats.hit_points.current -= dmg.0; + if let Some(pos) = positions.get(entity) { + let idx = map.xy_idx(pos.x, pos.y); + map.bloodstains.insert(idx); + } - if let Some(pos) = positions.get(entity) { - let idx = map.xy_idx(pos.x, pos.y); - map.bloodstains.insert(idx); + if stats.hit_points.current < 1 && dmg.1 { + xp_gain += stats.level * 100; + } + } + } + + if xp_gain != 0 { + let mut player_stats = stats.get_mut(*player).unwrap(); + let player_attributes = attributes.get(*player).unwrap(); + player_stats.xp += xp_gain; + if player_stats.xp >= player_stats.level * 1000 { + // We've gone up a level! + player_stats.level += 1; + log.append(format!( + "Congratulations, you are now level {}", + player_stats.level + )); + player_stats.hit_points.max = player_hp_at_level( + player_attributes.fitness.base + player_attributes.fitness.modifiers, + player_stats.level, + ); + player_stats.hit_points.current = player_stats.hit_points.max; + player_stats.mana.max = mana_at_level( + player_attributes.intelligence.base + player_attributes.intelligence.modifiers, + player_stats.level, + ); + player_stats.mana.current = player_stats.mana.max; + + for i in 0..10 { + if player_pos.y - i > 1 { + particles.request( + player_pos.x, + player_pos.y - 1, + RGB::named(rltk::GOLD), + RGB::named(rltk::BLACK), + rltk::to_cp437('░'), + 400.0, + ); + } + } } } diff --git a/src/gui.rs b/src/gui.rs index e622a35..9347b76 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -82,8 +82,10 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { "Mana: {}/{}", player_pools.mana.current, player_pools.mana.max ); + let xp = format!("Level: {}", player_pools.level); ctx.print_color(50, 1, white, black, &health); ctx.print_color(50, 2, white, black, &mana); + ctx.print_color(50, 3, white, black, &xp); ctx.draw_bar_horizontal( 64, 1, @@ -102,6 +104,16 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { RGB::named(rltk::BLUE), RGB::named(rltk::BLACK), ); + let xp_level_start = (player_pools.level - 1) * 1000; + ctx.draw_bar_horizontal( + 64, + 3, + 14, + player_pools.xp - xp_level_start, + 1000, + RGB::named(rltk::GOLD), + black, + ); // Attributes let attributes = ecs.read_storage::(); diff --git a/src/hunger_system.rs b/src/hunger_system.rs index da035db..31a096b 100644 --- a/src/hunger_system.rs +++ b/src/hunger_system.rs @@ -73,7 +73,7 @@ impl<'a> System<'a> for HungerSystem { log.append("Your hunger pangs are getting painful!"); } - SufferDamage::new_damage(&mut inflict_damage, entity, 1); + SufferDamage::new_damage(&mut inflict_damage, entity, 1, false); } } } diff --git a/src/inventory_system.rs b/src/inventory_system.rs index 2b91b17..51bd2ee 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -270,7 +270,7 @@ impl<'a> System<'a> for ItemUseSystem { used_item = false; for mob in targets.iter() { - SufferDamage::new_damage(&mut suffer_damage, *mob, damage.damage); + SufferDamage::new_damage(&mut suffer_damage, *mob, damage.damage, true); if entity == *player_entity { let mob_name = names.get(*mob).unwrap(); let item_name = names.get(useitem.item).unwrap(); diff --git a/src/melee_combat_system.rs b/src/melee_combat_system.rs index 6fcd494..8f5ee81 100644 --- a/src/melee_combat_system.rs +++ b/src/melee_combat_system.rs @@ -31,6 +31,7 @@ impl<'a> System<'a> for MeleeCombatSystem { ReadStorage<'a, MeleeWeapon>, ReadStorage<'a, Wearable>, ReadStorage<'a, NaturalAttackDefense>, + ReadExpect<'a, Entity>, ); fn run(&mut self, data: Self::SystemData) { @@ -51,6 +52,7 @@ impl<'a> System<'a> for MeleeCombatSystem { meleeweapons, wearables, natural, + player_entity, ) = data; for (entity, wants_melee, name, attacker_attributes, attacker_skills, attacker_pools) in ( @@ -154,7 +156,12 @@ impl<'a> System<'a> for MeleeCombatSystem { + skill_damage_bonus + weapon_damage_bonus, ); - SufferDamage::new_damage(&mut inflict_damage, wants_melee.target, damage); + SufferDamage::new_damage( + &mut inflict_damage, + wants_melee.target, + damage, + entity == *player_entity, + ); log.append(format!( "{} hits {} for {} hp.", &name.name, &target_name.name, damage diff --git a/src/trigger_system.rs b/src/trigger_system.rs index 0c7dc20..31c148f 100644 --- a/src/trigger_system.rs +++ b/src/trigger_system.rs @@ -77,6 +77,7 @@ impl<'a> System<'a> for TriggerSystem { &mut inflict_damage, entity, damage.damage, + false, ); }