From 22d90a46b42c1d70cffc9e4ddb051960d4863a4a Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Sun, 9 Jul 2023 09:51:42 +0100 Subject: [PATCH] scroll of confusion --- src/components.rs | 5 ++++ src/inventory_system.rs | 39 ++++++++++++++++++++++--- src/main.rs | 1 + src/monster_ai_system.rs | 63 +++++++++++++++++++++++++++------------- src/spawner.rs | 27 ++++++++++++++--- 5 files changed, 107 insertions(+), 28 deletions(-) diff --git a/src/components.rs b/src/components.rs index 5087f56..972cc8e 100644 --- a/src/components.rs +++ b/src/components.rs @@ -89,6 +89,11 @@ pub struct AOE { pub radius: i32, } +#[derive(Component, Debug)] +pub struct Confusion { + pub turns: i32, +} + #[derive(Component, Debug, Clone)] pub struct InBackpack { pub owner: Entity, diff --git a/src/inventory_system.rs b/src/inventory_system.rs index 33b525c..36d7e85 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -1,6 +1,6 @@ use super::{ - gamelog::GameLog, CombatStats, Consumable, Destructible, InBackpack, InflictsDamage, Map, Name, ParticleBuilder, - Position, ProvidesHealing, SufferDamage, WantsToDropItem, WantsToPickupItem, WantsToUseItem, AOE, + gamelog::GameLog, CombatStats, Confusion, Consumable, Destructible, InBackpack, InflictsDamage, Map, Name, + ParticleBuilder, Position, ProvidesHealing, SufferDamage, WantsToDropItem, WantsToPickupItem, WantsToUseItem, AOE, DEFAULT_PARTICLE_LIFETIME, }; use specs::prelude::*; @@ -53,6 +53,7 @@ impl<'a> System<'a> for ItemUseSystem { ReadStorage<'a, Position>, ReadStorage<'a, InflictsDamage>, ReadStorage<'a, AOE>, + WriteStorage<'a, Confusion>, ); fn run(&mut self, data: Self::SystemData) { @@ -72,6 +73,7 @@ impl<'a> System<'a> for ItemUseSystem { positions, inflicts_damage, aoe, + mut confused, ) = data; for (entity, wants_to_use) in (&entities, &wants_to_use).join() { @@ -122,7 +124,16 @@ impl<'a> System<'a> for ItemUseSystem { // HEALING ITEM let item_heals = provides_healing.get(wants_to_use.item); match item_heals { - None => {} + None => { + // This is here because the two are mutually exclusive as of now. Later, + // if more items are added (AOE healing scroll?), this'll need to be + // brought out of here. + // + // Ideally, replace it with something that picks the correct verb for + // whatever the item is being used, probably tied to a component. + // i.e. quaffs, uses, reads + gamelog.entries.push(format!("You use the {}!", item_being_used.name)); + } Some(heal) => { for target in targets.iter() { let stats = combat_stats.get_mut(*target); @@ -156,7 +167,6 @@ impl<'a> System<'a> for ItemUseSystem { None => {} Some(damage) => { let target_point = wants_to_use.target.unwrap(); - gamelog.entries.push(format!("You use the {}!", item_being_used.name)); if !aoe_item { particle_builder.request_star( target_point.x, @@ -190,6 +200,27 @@ impl<'a> System<'a> for ItemUseSystem { } } } + + // CONFUSION + let mut add_confusion = Vec::new(); + { + let causes_confusion = confused.get(wants_to_use.item); + match causes_confusion { + None => {} + Some(confusion) => { + used_item = false; + for mob in targets.iter() { + add_confusion.push((*mob, confusion.turns)); + // Gamelog entry for this is handled turn-by-turn in AI. + } + } + } + } + for mob in add_confusion.iter() { + confused.insert(mob.0, Confusion { turns: mob.1 }).expect("Unable to insert status"); + } + + // ITEM DELETION AFTER USE if used_item { let consumable = consumables.get(wants_to_use.item); match consumable { diff --git a/src/main.rs b/src/main.rs index 23eeb7e..7550725 100644 --- a/src/main.rs +++ b/src/main.rs @@ -260,6 +260,7 @@ 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::(); diff --git a/src/monster_ai_system.rs b/src/monster_ai_system.rs index 9968e7f..a0a6803 100644 --- a/src/monster_ai_system.rs +++ b/src/monster_ai_system.rs @@ -1,4 +1,4 @@ -use super::{Map, Monster, Position, RunState, Viewshed, WantsToMelee}; +use super::{gamelog::GameLog, Confusion, Map, Monster, Name, Position, RunState, Viewshed, WantsToMelee}; use rltk::Point; use specs::prelude::*; @@ -8,6 +8,7 @@ impl<'a> System<'a> for MonsterAI { #[allow(clippy::type_complexity)] type SystemData = ( WriteExpect<'a, Map>, + WriteExpect<'a, GameLog>, ReadExpect<'a, Point>, ReadExpect<'a, Entity>, ReadExpect<'a, RunState>, @@ -16,11 +17,14 @@ impl<'a> System<'a> for MonsterAI { ReadStorage<'a, Monster>, WriteStorage<'a, Position>, WriteStorage<'a, WantsToMelee>, + WriteStorage<'a, Confusion>, + ReadStorage<'a, Name>, ); fn run(&mut self, data: Self::SystemData) { let ( mut map, + mut gamelog, player_pos, player_entity, runstate, @@ -29,6 +33,8 @@ impl<'a> System<'a> for MonsterAI { monster, mut position, mut wants_to_melee, + mut confused, + name, ) = data; if *runstate != RunState::MonsterTurn { @@ -36,25 +42,42 @@ 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 { - wants_to_melee - .insert(entity, WantsToMelee { target: *player_entity }) - .expect("Unable to insert attack."); - } else if viewshed.visible_tiles.contains(&*player_pos) { - // If the player is visible, but the path is obstructed, this will currently search - // the entire map (i.e. Will do a huge ASTAR to find an alternate route), and the - // mob will follow that path until it leaves vision, then lose sight of the player - // and stop. - let path = rltk::a_star_search(map.xy_idx(pos.x, pos.y), map.xy_idx(player_pos.x, player_pos.y), &*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; + let mut can_act = true; + + // Check confusion + 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); + } + let entity_name = name.get(entity).unwrap(); + gamelog.entries.push(format!("{} is confused!", entity_name.name)); + can_act = false; + } + + if can_act { + let distance = rltk::DistanceAlg::Pythagoras.distance2d(Point::new(pos.x, pos.y), *player_pos); + if distance < 1.5 { + wants_to_melee + .insert(entity, WantsToMelee { target: *player_entity }) + .expect("Unable to insert attack."); + } else if viewshed.visible_tiles.contains(&*player_pos) { + // If the player is visible, but the path is obstructed, this will currently search + // the entire map (i.e. Will do a huge ASTAR to find an alternate route), and the + // mob will follow that path until it leaves vision, then lose sight of the player + // and stop. + let path = + rltk::a_star_search(map.xy_idx(pos.x, pos.y), map.xy_idx(player_pos.x, player_pos.y), &*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 d793929..c55ed9a 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -1,6 +1,6 @@ use super::{ - BlocksTile, CombatStats, Consumable, Destructible, InflictsDamage, Item, Monster, Name, Player, Position, - ProvidesHealing, Ranged, Rect, Renderable, Viewshed, AOE, MAPWIDTH, + BlocksTile, CombatStats, Confusion, Consumable, Destructible, InflictsDamage, Item, Monster, Name, Player, + Position, ProvidesHealing, Ranged, Rect, Renderable, Viewshed, AOE, MAPWIDTH, }; use rltk::{RandomNumberGenerator, RGB}; use specs::prelude::*; @@ -40,19 +40,20 @@ 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); + roll = rng.roll_dice(1, 6); } match roll { 1 => health_potion(ecs, x, y), 2 => poison_potion(ecs, x, y), 3 => magic_missile_scroll(ecs, x, y), 4 => fireball_scroll(ecs, x, y), + 5 => confusion_scroll(ecs, x, y), _ => weak_health_potion(ecs, x, y), } } const MAX_MONSTERS: i32 = 4; -const MAX_ITEMS: i32 = 6; +const MAX_ITEMS: i32 = 3; fn monster(ecs: &mut World, x: i32, y: i32, glyph: rltk::FontCharType, name: S) { ecs.create_entity() @@ -215,3 +216,21 @@ fn fireball_scroll(ecs: &mut World, x: i32, y: i32) { .with(AOE { 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::PURPLE), + bg: RGB::named(rltk::BLACK), + render_order: 2, + }) + .with(Name { name: "scroll of confusion".to_string() }) + .with(Item {}) + .with(Consumable {}) + .with(Destructible {}) + .with(Ranged { range: 6 }) + .with(Confusion { turns: 4 }) + .build(); +}