From 65d728b75af0dc1fe1d98b20624a2847dc9c784d Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Fri, 7 Jul 2023 22:39:44 +0100 Subject: [PATCH] generalised item use system --- src/components.rs | 9 ++++-- src/inventory_system.rs | 39 ++++++++++++++---------- src/main.rs | 15 +++++----- src/spawner.rs | 66 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 96 insertions(+), 33 deletions(-) diff --git a/src/components.rs b/src/components.rs index 47bc127..fee43d3 100644 --- a/src/components.rs +++ b/src/components.rs @@ -70,7 +70,7 @@ impl SufferDamage { pub struct Item {} #[derive(Component, Debug)] -pub struct Potion { +pub struct ProvidesHealing { pub heal_amount: i32, } @@ -91,6 +91,9 @@ pub struct WantsToDropItem { } #[derive(Component, Debug)] -pub struct WantsToDrinkPotion { - pub potion: Entity, +pub struct WantsToUseItem { + pub item: Entity, } + +#[derive(Component, Debug)] +pub struct Consumable {} diff --git a/src/inventory_system.rs b/src/inventory_system.rs index 384a91e..a78dff1 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -1,6 +1,6 @@ use super::{ - gamelog::GameLog, CombatStats, InBackpack, Name, Position, Potion, WantsToDrinkPotion, WantsToDropItem, - WantsToPickupItem, + gamelog::GameLog, CombatStats, Consumable, InBackpack, Name, Position, ProvidesHealing, WantsToDropItem, + WantsToPickupItem, WantsToUseItem, }; use specs::prelude::*; @@ -33,40 +33,47 @@ impl<'a> System<'a> for ItemCollectionSystem { } } -pub struct PotionUseSystem {} -impl<'a> System<'a> for PotionUseSystem { +pub struct ItemUseSystem {} +impl<'a> System<'a> for ItemUseSystem { #[allow(clippy::type_complexity)] type SystemData = ( ReadExpect<'a, Entity>, WriteExpect<'a, GameLog>, Entities<'a>, - WriteStorage<'a, WantsToDrinkPotion>, + WriteStorage<'a, WantsToUseItem>, ReadStorage<'a, Name>, - ReadStorage<'a, Potion>, + ReadStorage<'a, Consumable>, + ReadStorage<'a, ProvidesHealing>, WriteStorage<'a, CombatStats>, ); fn run(&mut self, data: Self::SystemData) { - let (player_entity, mut gamelog, entities, mut wants_drink, names, potions, mut combat_stats) = data; + let (player_entity, mut gamelog, entities, mut wants_use, names, consumables, healing, mut combat_stats) = data; - for (entity, drink, stats) in (&entities, &wants_drink, &mut combat_stats).join() { - let potion = potions.get(drink.potion); - match potion { + for (entity, use_item, stats) in (&entities, &wants_use, &mut combat_stats).join() { + let item_heals = healing.get(use_item.item); + match item_heals { None => {} - Some(potion) => { - stats.hp = i32::min(stats.max_hp, stats.hp + potion.heal_amount); + Some(healer) => { + stats.hp = i32::min(stats.max_hp, stats.hp + healer.heal_amount); if entity == *player_entity { gamelog.entries.push(format!( "You quaff the {}, and heal {} hp.", - names.get(drink.potion).unwrap().name, - potion.heal_amount + names.get(use_item.item).unwrap().name, + healer.heal_amount )); } - entities.delete(drink.potion).expect("Delete failed"); + let consumable = consumables.get(use_item.item); + match consumable { + None => {} + Some(_) => { + entities.delete(use_item.item).expect("Delete failed"); + } + } } } } - wants_drink.clear(); + wants_use.clear(); } } diff --git a/src/main.rs b/src/main.rs index a80b99a..6ae8a8e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,8 +58,8 @@ impl State { damage_system.run_now(&self.ecs); let mut inventory_system = ItemCollectionSystem {}; inventory_system.run_now(&self.ecs); - let mut potion_system = PotionUseSystem {}; - potion_system.run_now(&self.ecs); + let mut item_use_system = ItemUseSystem {}; + item_use_system.run_now(&self.ecs); let mut drop_system = ItemDropSystem {}; drop_system.run_now(&self.ecs); self.ecs.maintain(); @@ -121,9 +121,9 @@ impl GameState for State { gui::ItemMenuResult::NoResponse => {} gui::ItemMenuResult::Selected => { let item_entity = result.1.unwrap(); - let mut intent = self.ecs.write_storage::(); + let mut intent = self.ecs.write_storage::(); intent - .insert(*self.ecs.fetch::(), WantsToDrinkPotion { potion: item_entity }) + .insert(*self.ecs.fetch::(), WantsToUseItem { item: item_entity }) .expect("Unable to insert intent."); new_runstate = RunState::PlayerTurn; } @@ -160,7 +160,7 @@ fn main() -> rltk::BError { let mut context = RltkBuilder::simple80x50() .with_tile_dimensions(16, 16) //.with_fitscreen(true) - .with_title("rust-rltk-llywelwyn.github.io") + .with_title("rust-rl") .build()?; context.with_post_scanlines(true); let mut gs = State { ecs: World::new() }; @@ -176,11 +176,12 @@ fn main() -> rltk::BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); - gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); - gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); let map = Map::new_map_rooms_and_corridors(); let (player_x, player_y) = map.rooms[0].centre(); diff --git a/src/spawner.rs b/src/spawner.rs index 8e8617f..13b3156 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -1,5 +1,6 @@ use super::{ - BlocksTile, CombatStats, Item, Monster, Name, Player, Position, Potion, Rect, Renderable, Viewshed, MAPWIDTH, + BlocksTile, CombatStats, Consumable, Item, Monster, Name, Player, Position, ProvidesHealing, Rect, Renderable, + Viewshed, MAPWIDTH, }; use rltk::{RandomNumberGenerator, RGB}; use specs::prelude::*; @@ -15,7 +16,7 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { render_order: 0, }) .with(Player {}) - .with(Viewshed { visible_tiles: Vec::new(), range: 8, dirty: true }) + .with(Viewshed { visible_tiles: Vec::new(), range: 12, dirty: true }) .with(Name { name: "hero (you)".to_string() }) .with(CombatStats { max_hp: 30, hp: 30, defence: 2, power: 5 }) .build() @@ -26,13 +27,27 @@ pub fn random_monster(ecs: &mut World, x: i32, y: i32) { let roll: i32; { let mut rng = ecs.write_resource::(); - roll = rng.roll_dice(1, 2); + roll = rng.roll_dice(1, 3); } match roll { 1 => orc(ecs, x, y), + 2 => goblin_chieftain(ecs, x, y), _ => goblin(ecs, x, y), } } +/// Spawns a random item at a given loc +pub fn random_item(ecs: &mut World, x: i32, y: i32) { + let roll: i32; + { + let mut rng = ecs.write_resource::(); + roll = rng.roll_dice(1, 5); + } + match roll { + 1 => health_potion(ecs, x, y), + 2 => poison_potion(ecs, x, y), + _ => weak_health_potion(ecs, x, y), + } +} const MAX_MONSTERS: i32 = 4; const MAX_ITEMS: i32 = 2; @@ -41,7 +56,7 @@ fn monster(ecs: &mut World, x: i32, y: i32, glyph: rltk::FontCharTy ecs.create_entity() .with(Position { x, y }) .with(Renderable { glyph: glyph, fg: RGB::named(rltk::RED), bg: RGB::named(rltk::BLACK), render_order: 1 }) - .with(Viewshed { visible_tiles: Vec::new(), range: 8, dirty: true }) + .with(Viewshed { visible_tiles: Vec::new(), range: 12, dirty: true }) .with(Monster {}) .with(Name { name: name.to_string() }) .with(BlocksTile {}) @@ -57,6 +72,10 @@ fn goblin(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('g'), "goblin"); } +fn goblin_chieftain(ecs: &mut World, x: i32, y: i32) { + monster(ecs, x, y, rltk::to_cp437('G'), "goblin chieftain"); +} + pub fn spawn_room(ecs: &mut World, room: &Rect) { let mut monster_spawn_points: Vec = Vec::new(); let mut item_spawn_points: Vec = Vec::new(); @@ -103,11 +122,27 @@ pub fn spawn_room(ecs: &mut World, room: &Rect) { for idx in item_spawn_points.iter() { let x = *idx % MAPWIDTH; let y = *idx / MAPWIDTH; - health_potion(ecs, x as i32, y as i32); + random_item(ecs, x as i32, y as i32); } } fn health_potion(ecs: &mut World, x: i32, y: i32) { + ecs.create_entity() + .with(Position { x, y }) + .with(Renderable { + glyph: rltk::to_cp437('I'), + fg: RGB::named(rltk::MAGENTA), + bg: RGB::named(rltk::BLACK), + render_order: 2, + }) + .with(Name { name: "potion of health".to_string() }) + .with(Item {}) + .with(Consumable {}) + .with(ProvidesHealing { heal_amount: 12 }) + .build(); +} + +fn weak_health_potion(ecs: &mut World, x: i32, y: i32) { ecs.create_entity() .with(Position { x, y }) .with(Renderable { @@ -116,8 +151,25 @@ fn health_potion(ecs: &mut World, x: i32, y: i32) { bg: RGB::named(rltk::BLACK), render_order: 2, }) - .with(Name { name: "health potion".to_string() }) + .with(Name { name: "potion of lesser health".to_string() }) .with(Item {}) - .with(Potion { heal_amount: 8 }) + .with(Consumable {}) + .with(ProvidesHealing { heal_amount: 6 }) + .build(); +} + +fn poison_potion(ecs: &mut World, x: i32, y: i32) { + ecs.create_entity() + .with(Position { x, y }) + .with(Renderable { + glyph: rltk::to_cp437('i'), + fg: RGB::named(rltk::SEAGREEN), + bg: RGB::named(rltk::BLACK), + render_order: 2, + }) + .with(Name { name: "potion of ... health?".to_string() }) + .with(Item {}) + .with(Consumable {}) + .with(ProvidesHealing { heal_amount: -12 }) .build(); }