diff --git a/src/components.rs b/src/components.rs index a10267f..69ce508 100644 --- a/src/components.rs +++ b/src/components.rs @@ -128,3 +128,8 @@ pub struct Ranged { pub struct InflictsDamage { pub damage: i32, } + +#[derive(Component, Debug)] +pub struct AreaOfEffect { + pub radius: i32, +} diff --git a/src/inventory_system.rs b/src/inventory_system.rs index a08f658..4db613c 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -1,6 +1,7 @@ use crate::{ - game_log::GameLog, CombatStats, Consumable, InBackpack, InflictsDamage, Map, Name, Position, - ProvidesHealing, SufferDamage, WantsToDropItem, WantsToPickupItem, WantsToUseItem, + game_log::GameLog, AreaOfEffect, CombatStats, Consumable, InBackpack, InflictsDamage, Map, + Name, Position, ProvidesHealing, SufferDamage, WantsToDropItem, WantsToPickupItem, + WantsToUseItem, }; use specs::prelude::*; @@ -60,6 +61,7 @@ impl<'a> System<'a> for ItemUseSystem { ReadStorage<'a, InflictsDamage>, WriteStorage<'a, CombatStats>, WriteStorage<'a, SufferDamage>, + ReadStorage<'a, AreaOfEffect>, ); fn run(&mut self, data: Self::SystemData) { @@ -75,27 +77,67 @@ impl<'a> System<'a> for ItemUseSystem { inflict_damage, mut combat_stats, mut suffer_damage, + aoe, ) = data; - for (entity, useitem, stats) in (&entities, &wants_use, &mut combat_stats).join() { + for (entity, useitem) in (&entities, &wants_use).join() { let mut used_item = true; + // Targeting + let mut targets: Vec = Vec::new(); + match useitem.target { + None => { + targets.push(*player_entity); + } + Some(target) => { + match aoe.get(useitem.item) { + None => { + // Single target in tile + let idx = map.xy_idx(target.x, target.y); + for mob in map.tile_content[idx].iter() { + targets.push(*mob); + } + } + Some(area_effect) => { + // AoE + let mut blast_tiles = + rltk::field_of_view(target, area_effect.radius, &*map); + blast_tiles.retain(|p| { + p.x > 0 && p.x < map.width - 1 && p.y > 0 && p.y < map.height - 1 + }); + + for tile_idx in blast_tiles.iter() { + let idx = map.xy_idx(tile_idx.x, tile_idx.y); + for mob in map.tile_content[idx].iter() { + targets.push(*mob); + } + } + } + } + } + } + // If the item heals, apply the healing match healing.get(useitem.item) { None => {} Some(healer) => { used_item = false; - stats.hp = i32::min(stats.max_hp, stats.hp + healer.heal_amount); - if entity == *player_entity { - gamelog.entries.push(format!( - "You drink the {}, healing {} hp.", - names.get(useitem.item).unwrap().name, - healer.heal_amount - )); - } + for target in targets.iter() { + let stats = combat_stats.get_mut(*target); + if let Some(stats) = stats { + stats.hp = i32::min(stats.max_hp, stats.hp + healer.heal_amount); + if entity == *player_entity { + gamelog.entries.push(format!( + "You drink the {}, healing {} hp.", + names.get(useitem.item).unwrap().name, + healer.heal_amount + )); + } - used_item = true; + used_item = true; + } + } } } @@ -103,11 +145,9 @@ impl<'a> System<'a> for ItemUseSystem { match inflict_damage.get(useitem.item) { None => {} Some(damage) => { - let target_point = useitem.target.unwrap(); - let idx = map.xy_idx(target_point.x, target_point.y); used_item = false; - for mob in map.tile_content[idx].iter() { + for mob in targets.iter() { SufferDamage::new_damage(&mut suffer_damage, *mob, damage.damage); if entity == *player_entity { let mob_name = names.get(*mob).unwrap(); diff --git a/src/main.rs b/src/main.rs index c536ecc..af5dd68 100644 --- a/src/main.rs +++ b/src/main.rs @@ -253,6 +253,7 @@ fn main() -> rltk::BError { Consumable, Ranged, InflictsDamage, + AreaOfEffect, ); let map = Map::new_map_rooms_and_corridors(); diff --git a/src/spawner.rs b/src/spawner.rs index 2f43687..4c19225 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -1,6 +1,6 @@ use crate::{ - BlocksTile, CombatStats, Consumable, InflictsDamage, Item, Monster, Name, Player, Position, - ProvidesHealing, Ranged, Rect, Renderable, Viewshed, MAP_WIDTH, + AreaOfEffect, BlocksTile, CombatStats, Consumable, InflictsDamage, Item, Monster, Name, Player, + Position, ProvidesHealing, Ranged, Rect, Renderable, Viewshed, MAP_WIDTH, }; use rltk::{RandomNumberGenerator, RGB}; use specs::prelude::*; @@ -172,11 +172,30 @@ fn random_item(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 => health_potion(ecs, x, y), + 2 => fireball_scroll(ecs, x, y), _ => magic_missile_scroll(ecs, x, y), } } + +fn fireball_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::ORANGE), + bg: RGB::named(rltk::BLACK), + render_order: 2, + }) + .with(Name::new("Fireball Scroll")) + .with(Item {}) + .with(Consumable {}) + .with(Ranged { range: 6 }) + .with(InflictsDamage { damage: 20 }) + .with(AreaOfEffect { radius: 3 }) + .build(); +}