Add experience and leveling up, completing section 5.11

This commit is contained in:
Timothy Warren 2022-01-05 14:59:45 -05:00
parent c787ccfd25
commit dd894452f3
7 changed files with 102 additions and 14 deletions

View File

@ -83,16 +83,21 @@ pub struct WantsToMelee {
#[derive(Component, Debug, ConvertSaveload, Clone)] #[derive(Component, Debug, ConvertSaveload, Clone)]
pub struct SufferDamage { pub struct SufferDamage {
pub amount: Vec<i32>, pub amount: Vec<(i32, bool)>,
} }
impl SufferDamage { impl SufferDamage {
pub fn new_damage(store: &mut WriteStorage<SufferDamage>, victim: Entity, amount: i32) { pub fn new_damage(
store: &mut WriteStorage<SufferDamage>,
victim: Entity,
amount: i32,
from_player: bool,
) {
if let Some(suffering) = store.get_mut(victim) { if let Some(suffering) = store.get_mut(victim) {
suffering.amount.push(amount); suffering.amount.push((amount, from_player));
} else { } else {
let dmg = SufferDamage { let dmg = SufferDamage {
amount: vec![amount], amount: vec![(amount, from_player)],
}; };
store.insert(victim, dmg).expect("Unable to insert damage"); store.insert(victim, dmg).expect("Unable to insert damage");
} }

View File

@ -1,33 +1,96 @@
use ::rltk::RandomNumberGenerator; use ::rltk::{Point, RandomNumberGenerator};
use ::specs::prelude::*; use ::specs::prelude::*;
use rltk::RGB;
use crate::components::{ 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::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::raws::{get_item_drop, spawn_named_item, SpawnType, RAWS};
use crate::{Map, RunState}; use crate::{Map, RunState};
pub struct DamageSystem {} pub struct DamageSystem {}
impl<'a> System<'a> for DamageSystem { impl<'a> System<'a> for DamageSystem {
#[allow(clippy::type_complexity)]
type SystemData = ( type SystemData = (
WriteStorage<'a, Pools>, WriteStorage<'a, Pools>,
WriteStorage<'a, SufferDamage>, WriteStorage<'a, SufferDamage>,
ReadStorage<'a, Position>, ReadStorage<'a, Position>,
WriteExpect<'a, Map>, WriteExpect<'a, Map>,
Entities<'a>, Entities<'a>,
ReadExpect<'a, Entity>,
ReadStorage<'a, Attributes>,
WriteExpect<'a, GameLog>,
WriteExpect<'a, ParticleBuilder>,
ReadExpect<'a, Point>,
); );
fn run(&mut self, data: Self::SystemData) { 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() { for (entity, mut stats, damage) in (&entities, &mut stats, &damage).join() {
stats.hit_points.current -= damage.amount.iter().sum::<i32>(); 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) { if stats.hit_points.current < 1 && dmg.1 {
let idx = map.xy_idx(pos.x, pos.y); xp_gain += stats.level * 100;
map.bloodstains.insert(idx); }
}
}
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,
);
}
}
} }
} }

View File

@ -82,8 +82,10 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
"Mana: {}/{}", "Mana: {}/{}",
player_pools.mana.current, player_pools.mana.max 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, 1, white, black, &health);
ctx.print_color(50, 2, white, black, &mana); ctx.print_color(50, 2, white, black, &mana);
ctx.print_color(50, 3, white, black, &xp);
ctx.draw_bar_horizontal( ctx.draw_bar_horizontal(
64, 64,
1, 1,
@ -102,6 +104,16 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
RGB::named(rltk::BLUE), RGB::named(rltk::BLUE),
RGB::named(rltk::BLACK), 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 // Attributes
let attributes = ecs.read_storage::<Attributes>(); let attributes = ecs.read_storage::<Attributes>();

View File

@ -73,7 +73,7 @@ impl<'a> System<'a> for HungerSystem {
log.append("Your hunger pangs are getting painful!"); 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);
} }
} }
} }

View File

@ -270,7 +270,7 @@ impl<'a> System<'a> for ItemUseSystem {
used_item = false; used_item = false;
for mob in targets.iter() { 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 { if entity == *player_entity {
let mob_name = names.get(*mob).unwrap(); let mob_name = names.get(*mob).unwrap();
let item_name = names.get(useitem.item).unwrap(); let item_name = names.get(useitem.item).unwrap();

View File

@ -31,6 +31,7 @@ impl<'a> System<'a> for MeleeCombatSystem {
ReadStorage<'a, MeleeWeapon>, ReadStorage<'a, MeleeWeapon>,
ReadStorage<'a, Wearable>, ReadStorage<'a, Wearable>,
ReadStorage<'a, NaturalAttackDefense>, ReadStorage<'a, NaturalAttackDefense>,
ReadExpect<'a, Entity>,
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
@ -51,6 +52,7 @@ impl<'a> System<'a> for MeleeCombatSystem {
meleeweapons, meleeweapons,
wearables, wearables,
natural, natural,
player_entity,
) = data; ) = data;
for (entity, wants_melee, name, attacker_attributes, attacker_skills, attacker_pools) in ( 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 + skill_damage_bonus
+ weapon_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!( log.append(format!(
"{} hits {} for {} hp.", "{} hits {} for {} hp.",
&name.name, &target_name.name, damage &name.name, &target_name.name, damage

View File

@ -77,6 +77,7 @@ impl<'a> System<'a> for TriggerSystem {
&mut inflict_damage, &mut inflict_damage,
entity, entity,
damage.damage, damage.damage,
false,
); );
} }