From 7d0200b2620cf41a70ba18127cd3893962dea0c9 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Fri, 5 Nov 2021 14:32:14 -0400 Subject: [PATCH] Complete chapter 2.9 --- src/components.rs | 15 ++++++--- src/game_log.rs | 9 +++++ src/inventory_system.rs | 38 +++++++++++++++++++-- src/main.rs | 9 +++-- src/map.rs | 2 +- src/monster_ai_system.rs | 73 +++++++++++++++++++++++++--------------- src/spawner.rs | 27 ++++++++++++--- 7 files changed, 128 insertions(+), 45 deletions(-) diff --git a/src/components.rs b/src/components.rs index 69ce508..2ff5660 100644 --- a/src/components.rs +++ b/src/components.rs @@ -16,7 +16,7 @@ pub struct Renderable { pub render_order: i32, } -#[derive(Component, Debug)] +#[derive(Component, Debug, Default)] pub struct Player {} #[derive(Component)] @@ -36,7 +36,7 @@ impl Default for Viewshed { } } -#[derive(Component, Debug)] +#[derive(Component, Debug, Default)] pub struct Monster {} #[derive(Component, Debug)] @@ -52,7 +52,7 @@ impl Name { } } -#[derive(Component, Debug)] +#[derive(Component, Debug, Default)] pub struct BlocksTile {} #[derive(Component, Debug)] @@ -86,7 +86,7 @@ impl SufferDamage { } } -#[derive(Component, Debug)] +#[derive(Component, Debug, Default)] pub struct Item {} #[derive(Component, Debug)] @@ -116,7 +116,7 @@ pub struct WantsToDropItem { pub item: Entity, } -#[derive(Component, Debug)] +#[derive(Component, Debug, Default)] pub struct Consumable {} #[derive(Component, Debug)] @@ -133,3 +133,8 @@ pub struct InflictsDamage { pub struct AreaOfEffect { pub radius: i32, } + +#[derive(Component, Debug)] +pub struct Confusion { + pub turns: i32, +} diff --git a/src/game_log.rs b/src/game_log.rs index dbe28c0..d236c93 100644 --- a/src/game_log.rs +++ b/src/game_log.rs @@ -1,3 +1,12 @@ pub struct GameLog { pub entries: Vec, } + +impl GameLog { + pub fn new(first_entry: S) -> Self { + let mut entries: Vec = Vec::new(); + entries.push(first_entry.to_string()); + + GameLog { entries } + } +} diff --git a/src/inventory_system.rs b/src/inventory_system.rs index 4db613c..4546b67 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -1,7 +1,7 @@ use crate::{ - game_log::GameLog, AreaOfEffect, CombatStats, Consumable, InBackpack, InflictsDamage, Map, - Name, Position, ProvidesHealing, SufferDamage, WantsToDropItem, WantsToPickupItem, - WantsToUseItem, + game_log::GameLog, AreaOfEffect, CombatStats, Confusion, Consumable, InBackpack, + InflictsDamage, Map, Name, Position, ProvidesHealing, SufferDamage, WantsToDropItem, + WantsToPickupItem, WantsToUseItem, }; use specs::prelude::*; @@ -62,6 +62,7 @@ impl<'a> System<'a> for ItemUseSystem { WriteStorage<'a, CombatStats>, WriteStorage<'a, SufferDamage>, ReadStorage<'a, AreaOfEffect>, + WriteStorage<'a, Confusion>, ); fn run(&mut self, data: Self::SystemData) { @@ -78,6 +79,7 @@ impl<'a> System<'a> for ItemUseSystem { mut combat_stats, mut suffer_damage, aoe, + mut confused, ) = data; for (entity, useitem) in (&entities, &wants_use).join() { @@ -164,6 +166,36 @@ impl<'a> System<'a> for ItemUseSystem { } } + // Can it pass along confusion? Note the use of scopes + // to escape from the borrow checker! + let mut add_confusion = Vec::new(); + { + match confused.get(useitem.item) { + None => {} + Some(confusion) => { + used_item = false; + + for mob in targets.iter() { + add_confusion.push((*mob, confusion.turns)); + if entity == *player_entity { + let mob_name = names.get(*mob).unwrap(); + let item_name = names.get(useitem.item).unwrap(); + + gamelog.entries.push(format!( + "You use {} on {}, confusing them.", + item_name.name, mob_name.name + )); + } + } + } + } + } + for mob in add_confusion.iter() { + confused + .insert(mob.0, Confusion { turns: mob.1 }) + .expect("Unable to add confused status"); + } + // If it's a consumable, delete it on use if used_item { let consumable = consumables.get(useitem.item); diff --git a/src/main.rs b/src/main.rs index af5dd68..31e3cf2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,7 @@ mod rect; mod spawner; mod visibility_system; -pub use components::*; +use components::*; use damage_system::DamageSystem; pub use game_log::GameLog; use inventory_system::{ItemCollectionSystem, ItemDropSystem, ItemUseSystem}; @@ -100,7 +100,7 @@ impl GameState for State { let renderables = self.ecs.read_storage::(); let map = self.ecs.fetch::(); - let mut data = (&positions, &renderables).join().collect::>(); + let mut data: Vec<_> = (&positions, &renderables).join().collect(); data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order)); for (pos, render) in data.iter() { let idx = map.xy_idx(pos.x, pos.y); @@ -254,6 +254,7 @@ fn main() -> rltk::BError { Ranged, InflictsDamage, AreaOfEffect, + Confusion, ); let map = Map::new_map_rooms_and_corridors(); @@ -270,9 +271,7 @@ fn main() -> rltk::BError { gs.ecs.insert(Point::new(player_x, player_y)); gs.ecs.insert(player_entity); gs.ecs.insert(RunState::PreRun); - gs.ecs.insert(game_log::GameLog { - entries: vec!["Welcome to Rusty Roguelike".to_string()], - }); + gs.ecs.insert(GameLog::new("Welcome to Rusty Roguelike")); rltk::main_loop(context, gs) } diff --git a/src/map.rs b/src/map.rs index fc180ab..6bdf698 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,4 +1,4 @@ -use super::Rect; +use crate::Rect; use rltk::{Algorithm2D, BaseMap, Point, RandomNumberGenerator, Rltk, SmallVec, RGB}; use specs::prelude::*; use std::cmp::{max, min}; diff --git a/src/monster_ai_system.rs b/src/monster_ai_system.rs index 21fe9aa..dbd97ec 100644 --- a/src/monster_ai_system.rs +++ b/src/monster_ai_system.rs @@ -1,4 +1,6 @@ -use crate::{Map, Monster, Position, RunState, Viewshed, WantsToMelee}; +use crate::components::{Confusion, Monster, Position, Viewshed, WantsToMelee}; +use crate::{Map, RunState}; + use rltk::Point; use specs::prelude::*; @@ -16,6 +18,7 @@ impl<'a> System<'a> for MonsterAI { ReadStorage<'a, Monster>, WriteStorage<'a, Position>, WriteStorage<'a, WantsToMelee>, + WriteStorage<'a, Confusion>, ); fn run(&mut self, data: Self::SystemData) { @@ -29,6 +32,7 @@ impl<'a> System<'a> for MonsterAI { monster, mut position, mut wants_to_melee, + mut confused, ) = data; if *runstate != RunState::MonsterTurn { @@ -38,35 +42,50 @@ impl<'a> System<'a> for MonsterAI { for (entity, mut viewshed, _monster, mut pos) in (&entities, &mut viewshed, &monster, &mut position).join() { - let distance = - rltk::DistanceAlg::Pythagoras.distance2d(Point::new(pos.x, pos.y), *player_pos); - if distance < 1.5 { - // Attack goes here - wants_to_melee - .insert( - entity, - WantsToMelee { - target: *player_entity, - }, - ) - .expect("Unable to insert attack"); - } else if viewshed.visible_tiles.contains(&*player_pos) { - let path = rltk::a_star_search( - map.xy_idx(pos.x, pos.y) as i32, - map.xy_idx(player_pos.x, player_pos.y) as i32, - &mut *map, - ); + let mut can_act = true; - if path.success && path.steps.len() > 1 { - let mut idx = map.xy_idx(pos.x, pos.y); + let is_confused = confused.get_mut(entity); + if let Some(i_am_confused) = is_confused { + i_am_confused.turns -= 1; + if i_am_confused.turns < 1 { + confused.remove(entity); + } - map.blocked[idx] = false; - pos.x = path.steps[1] as i32 % map.width; - pos.y = path.steps[1] as i32 / map.width; + can_act = false; + } - idx = map.xy_idx(pos.x, pos.y); - map.blocked[idx] = true; - viewshed.dirty = true; + if can_act { + let distance = + rltk::DistanceAlg::Pythagoras.distance2d(Point::new(pos.x, pos.y), *player_pos); + if distance < 1.5 { + // Attack goes here + wants_to_melee + .insert( + entity, + WantsToMelee { + target: *player_entity, + }, + ) + .expect("Unable to insert attack"); + } else if viewshed.visible_tiles.contains(&*player_pos) { + // The path to the player + let path = rltk::a_star_search( + map.xy_idx(pos.x, pos.y) as i32, + map.xy_idx(player_pos.x, player_pos.y) as i32, + &mut *map, + ); + + if path.success && path.steps.len() > 1 { + let mut idx = map.xy_idx(pos.x, pos.y); + + map.blocked[idx] = false; + pos.x = path.steps[1] as i32 % map.width; + pos.y = path.steps[1] as i32 / map.width; + + idx = map.xy_idx(pos.x, pos.y); + map.blocked[idx] = true; + viewshed.dirty = true; + } } } } diff --git a/src/spawner.rs b/src/spawner.rs index 4c19225..9e3a661 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -1,7 +1,8 @@ -use crate::{ - AreaOfEffect, BlocksTile, CombatStats, Consumable, InflictsDamage, Item, Monster, Name, Player, - Position, ProvidesHealing, Ranged, Rect, Renderable, Viewshed, MAP_WIDTH, +use crate::components::{ + AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, InflictsDamage, Item, Monster, + Name, Player, Position, ProvidesHealing, Ranged, Renderable, Viewshed, }; +use crate::{Rect, MAP_WIDTH}; use rltk::{RandomNumberGenerator, RGB}; use specs::prelude::*; @@ -172,12 +173,13 @@ fn random_item(ecs: &mut World, x: i32, y: i32) { let roll: i32; { let mut rng = ecs.write_resource::(); - roll = rng.roll_dice(1, 3); + roll = rng.roll_dice(1, 4); } match roll { 1 => health_potion(ecs, x, y), 2 => fireball_scroll(ecs, x, y), + 3 => confusion_scroll(ecs, x, y), _ => magic_missile_scroll(ecs, x, y), } } @@ -199,3 +201,20 @@ fn fireball_scroll(ecs: &mut World, x: i32, y: i32) { .with(AreaOfEffect { radius: 3 }) .build(); } + +fn confusion_scroll(ecs: &mut World, x: i32, y: i32) { + ecs.create_entity() + .with(Position { x, y }) + .with(Renderable { + glyph: rltk::to_cp437(')'), + fg: RGB::named(rltk::PINK), + bg: RGB::named(rltk::BLACK), + render_order: 2, + }) + .with(Name::new("Confusion Scroll")) + .with(Item {}) + .with(Consumable {}) + .with(Ranged { range: 6 }) + .with(Confusion { turns: 4 }) + .build(); +}