diff --git a/raws/items.json b/raws/items.json index a14c346..7bbea97 100644 --- a/raws/items.json +++ b/raws/items.json @@ -34,7 +34,7 @@ "name": { "name": "scroll of fireball", "plural": "scrolls of fireball" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, "weight": 0.5, - "value": 150, + "value": 200, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "ranged": "10", "damage": "8d6", "aoe": "3" }, "magic": { "class": "rare", "naming": "scroll" } @@ -44,7 +44,7 @@ "name": { "name": "cursed scroll of fireball", "plural": "cursed scrolls of fireball" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, "weight": 0.5, - "value": 150, + "value": 200, "flags": ["CONSUMABLE", "DESTRUCTIBLE", "CURSED"], "effects": { "ranged": "10", "damage": "8d6", "aoe": "3" }, "magic": { "class": "rare", "naming": "scroll" } @@ -59,6 +59,16 @@ "effects": { "ranged": "10", "confusion": "4" }, "magic": { "class": "uncommon", "naming": "scroll" } }, + { + "id": "scroll_mass_confusion", + "name": { "name": "scroll of mass confusion", "plural": "scrolls of mass confusion" }, + "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 0.5, + "value": 200, + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], + "effects": { "ranged": "10", "aoe": "4", "confusion": "3" }, + "magic": { "class": "veryrare", "naming": "scroll" } + }, { "id": "scroll_magicmap", "name": { "name": "scroll of magic mapping", "plural": "scrolls of magic mapping" }, diff --git a/src/ai/regen_system.rs b/src/ai/regen_system.rs index 8e17e3d..c831808 100644 --- a/src/ai/regen_system.rs +++ b/src/ai/regen_system.rs @@ -1,4 +1,4 @@ -use crate::{gamelog, Clock, Player, Pools, Position, RunState, TakingTurn}; +use crate::{gamelog, Clock, Player, Pools, Position, TakingTurn}; use specs::prelude::*; pub struct RegenSystem {} @@ -11,7 +11,6 @@ impl<'a> System<'a> for RegenSystem { type SystemData = ( ReadStorage<'a, Clock>, Entities<'a>, - ReadExpect<'a, RunState>, ReadStorage<'a, Position>, WriteStorage<'a, Pools>, ReadStorage<'a, TakingTurn>, @@ -19,22 +18,24 @@ impl<'a> System<'a> for RegenSystem { ); fn run(&mut self, data: Self::SystemData) { - let (clock, entities, runstate, positions, mut pools, turns, player) = data; - if *runstate != RunState::Ticking { + let (clock, entities, positions, mut pools, turns, player) = data; + let mut clock_turn = false; + for (_e, _c, _t) in (&entities, &clock, &turns).join() { + clock_turn = true; + } + if !clock_turn { return; } - for (_e, _c, _t) in (&entities, &clock, &turns).join() { - let current_turn = gamelog::get_event_count("turns") + 1; - if current_turn % MONSTER_HP_REGEN_TURN == 0 { - for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, !&player).join() { - try_hp_regen_tick(pool, MONSTER_HP_REGEN_PER_TICK); - } + let current_turn = gamelog::get_event_count("turns"); + if current_turn % MONSTER_HP_REGEN_TURN == 0 { + for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, !&player).join() { + try_hp_regen_tick(pool, MONSTER_HP_REGEN_PER_TICK); } - let level = gamelog::get_event_count("player_level"); - if current_turn % get_player_hp_regen_turn(level) == 0 { - for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, &player).join() { - try_hp_regen_tick(pool, get_player_hp_regen_per_tick(level)); - } + } + let level = gamelog::get_event_count("player_level"); + if current_turn % get_player_hp_regen_turn(level) == 0 { + for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, &player).join() { + try_hp_regen_tick(pool, get_player_hp_regen_per_tick(level)); } } } diff --git a/src/ai/turn_status_system.rs b/src/ai/turn_status_system.rs index 0bd7854..936d30f 100644 --- a/src/ai/turn_status_system.rs +++ b/src/ai/turn_status_system.rs @@ -1,4 +1,9 @@ -use crate::{gamelog, Confusion, Name, ParticleBuilder, Position, RunState, TakingTurn}; +use crate::{ + effects::{add_effect, EffectType, Targets}, + gamelog, + gui::renderable_colour, + Clock, Confusion, Name, Renderable, TakingTurn, +}; use rltk::prelude::*; use specs::prelude::*; @@ -8,37 +13,93 @@ impl<'a> System<'a> for TurnStatusSystem { #[allow(clippy::type_complexity)] type SystemData = ( WriteStorage<'a, TakingTurn>, + ReadStorage<'a, Clock>, WriteStorage<'a, Confusion>, Entities<'a>, - ReadExpect<'a, RunState>, ReadStorage<'a, Name>, - ReadStorage<'a, Position>, - WriteExpect<'a, ParticleBuilder>, + ReadExpect<'a, Entity>, + ReadStorage<'a, Renderable>, ); fn run(&mut self, data: Self::SystemData) { - let (mut turns, mut confusion, entities, runstate, names, positions, mut particle_builder) = data; - if *runstate != RunState::Ticking { + let (mut turns, clock, mut confusion, entities, names, player_entity, renderables) = data; + let mut clock_tick = false; + for (_e, _c, _t) in (&entities, &clock, &turns).join() { + clock_tick = true; + } + if !clock_tick { return; } - let mut remove_turn: Vec = Vec::new(); - let mut remove_confusion: Vec = Vec::new(); - for (entity, _turn, confused, name, pos) in (&entities, &mut turns, &mut confusion, &names, &positions).join() { + let mut logger = gamelog::Logger::new(); + let mut log = false; + let mut not_my_turn: Vec = Vec::new(); + let mut not_confused: Vec = Vec::new(); + for (entity, _turn, confused, name) in (&entities, &mut turns, &mut confusion, &names).join() { + log = true; confused.turns -= 1; if confused.turns < 1 { - remove_confusion.push(entity); - gamelog::Logger::new().npc_name(&name.name).colour(WHITE).append("snaps out of it.").log(); - particle_builder.request(pos.x, pos.y, RGB::named(LIGHT_BLUE), RGB::named(BLACK), to_cp437('!'), 200.0); + not_confused.push(entity); + if entity == *player_entity { + logger = logger + .colour(renderable_colour(&renderables, entity)) + .append(&name.name) + .colour(WHITE) + .append("snap out of it."); + } else { + logger = logger + .append("The") + .colour(renderable_colour(&renderables, entity)) + .append(&name.name) + .colour(WHITE) + .append("snaps out of it."); + } + add_effect( + None, + EffectType::Particle { + glyph: to_cp437('!'), + fg: RGB::named(LIGHT_BLUE), + bg: RGB::named(BLACK), + lifespan: 200.0, + delay: 0.0, + }, + Targets::Entity { target: entity }, + ); } else { - remove_turn.push(entity); - gamelog::Logger::new().npc_name(&name.name).colour(WHITE).append("is confused.").log(); - particle_builder.request(pos.x, pos.y, RGB::named(MAGENTA), RGB::named(BLACK), to_cp437('?'), 200.0); + not_my_turn.push(entity); + if entity == *player_entity { + logger = logger + .colour(renderable_colour(&renderables, entity)) + .append(&name.name) + .colour(WHITE) + .append("are confused!"); + } else { + logger = logger + .append("The") + .colour(renderable_colour(&renderables, entity)) + .append(&name.name) + .colour(WHITE) + .append("is confused!"); + } + add_effect( + None, + EffectType::Particle { + glyph: to_cp437('?'), + fg: RGB::named(MAGENTA), + bg: RGB::named(BLACK), + lifespan: 200.0, + delay: 0.0, + }, + Targets::Entity { target: entity }, + ); } } - for e in remove_turn { + if log { + logger.log(); + } + for e in not_my_turn { turns.remove(e); } - for e in remove_confusion { + for e in not_confused { confusion.remove(e); } } diff --git a/src/effects/damage.rs b/src/effects/damage.rs index 1b0b1c4..71cfbd6 100644 --- a/src/effects/damage.rs +++ b/src/effects/damage.rs @@ -2,7 +2,7 @@ use super::{add_effect, targeting, EffectSpawner, EffectType, Entity, Targets, W use crate::{ gamelog, gamesystem::{hp_per_level, mana_per_level}, - Attributes, GrantsXP, Map, Player, Pools, DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME, + Attributes, Confusion, GrantsXP, Map, Player, Pools, DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME, }; use rltk::prelude::*; use specs::prelude::*; @@ -53,6 +53,14 @@ pub fn heal_damage(ecs: &mut World, heal: &EffectSpawner, target: Entity) { } } +pub fn add_confusion(ecs: &mut World, effect: &EffectSpawner, target: Entity) { + if let EffectType::Confusion { turns } = &effect.effect_type { + ecs.write_storage::() + .insert(target, Confusion { turns: *turns }) + .expect("Unable to insert Confusion"); + } +} + pub fn bloodstain(ecs: &mut World, target: usize) { let mut map = ecs.fetch_mut::(); // If the current tile isn't bloody, bloody it. diff --git a/src/effects/mod.rs b/src/effects/mod.rs index 51e9fd8..d0482fd 100644 --- a/src/effects/mod.rs +++ b/src/effects/mod.rs @@ -20,6 +20,7 @@ lazy_static! { pub enum EffectType { Damage { amount: i32 }, Healing { amount: i32 }, + Confusion { turns: i32 }, Bloodstain, Particle { glyph: FontCharType, fg: RGB, bg: RGB, lifespan: f32, delay: f32 }, EntityDeath, @@ -98,6 +99,7 @@ fn tile_effect_hits_entities(effect: &EffectType) -> bool { EffectType::Damage { .. } => true, EffectType::Healing { .. } => true, EffectType::RestoreNutrition { .. } => true, + EffectType::Confusion { .. } => true, _ => false, } } @@ -107,6 +109,7 @@ fn affect_entity(ecs: &mut World, effect: &EffectSpawner, target: Entity) { match &effect.effect_type { EffectType::Damage { .. } => damage::inflict_damage(ecs, effect, target), EffectType::Healing { .. } => damage::heal_damage(ecs, effect, target), + EffectType::Confusion { .. } => damage::add_confusion(ecs, effect, target), EffectType::Bloodstain { .. } => { if let Some(pos) = targeting::entity_position(ecs, target) { damage::bloodstain(ecs, pos) diff --git a/src/effects/triggers.rs b/src/effects/triggers.rs index fda69f1..ff27219 100644 --- a/src/effects/triggers.rs +++ b/src/effects/triggers.rs @@ -1,7 +1,7 @@ use super::{add_effect, spatial, EffectType, Entity, Targets, World}; use crate::{ - gamelog, gui::item_colour_ecs, gui::obfuscate_name_ecs, Consumable, Cursed, InflictsDamage, MagicMapper, Prop, - ProvidesHealing, ProvidesNutrition, RandomNumberGenerator, Renderable, RunState, + gamelog, gui::item_colour_ecs, gui::obfuscate_name_ecs, Confusion, Consumable, Cursed, InflictsDamage, MagicMapper, + Prop, ProvidesHealing, ProvidesNutrition, RandomNumberGenerator, Renderable, RunState, }; use rltk::prelude::*; use specs::prelude::*; @@ -43,6 +43,8 @@ fn event_trigger(source: Option, entity: Entity, target: &Targets, ecs: logger = handle_healing(ecs, &mut event, logger); // DOES DAMAGE logger = handle_damage(ecs, &mut event, logger); + // APPLIES CONFUSION + logger = handle_confusion(ecs, &mut event, logger); if event.log { logger.log(); } @@ -83,7 +85,7 @@ fn handle_healing(ecs: &mut World, event: &mut EventInfo, mut logger: gamelog::L let mut rng = ecs.write_resource::(); let roll = rng.roll_dice(healing_item.n_dice, healing_item.sides) + healing_item.modifier; add_effect(event.source, EffectType::Healing { amount: roll }, event.target.clone()); - logger = logger.append("You recover some vigour."); + logger = logger.append("You recover some vigour.").buc(event.buc, None, Some("You feel great!")); event.log = true; } return logger; @@ -115,8 +117,14 @@ fn handle_damage(ecs: &mut World, event: &mut EventInfo, mut logger: gamelog::Lo return logger; } +fn handle_confusion(ecs: &mut World, event: &mut EventInfo, mut logger: gamelog::Logger) -> gamelog::Logger { + if let Some(confusion) = ecs.read_storage::().get(event.entity) { + add_effect(event.source, EffectType::Confusion { turns: confusion.turns }, event.target.clone()); + } + return logger; +} + fn get_entity_targets(target: &Targets) -> Vec { - rltk::console::log("ayo"); let mut entities: Vec = Vec::new(); match target { Targets::Entity { target } => entities.push(*target), diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 832139e..7332454 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -449,6 +449,15 @@ pub fn obfuscate_name_ecs(ecs: &World, item: Entity) -> (String, String) { return (singular, plural); } +/// Gets renderable colour as tuple of u8 +pub fn renderable_colour(renderables: &ReadStorage, entity: Entity) -> (u8, u8, u8) { + return if let Some(renderable) = renderables.get(entity) { + ((renderable.fg.r * 255.0) as u8, (renderable.fg.g * 255.0) as u8, (renderable.fg.b * 255.0) as u8) + } else { + WHITE + }; +} + pub fn item_colour_ecs(ecs: &World, item: Entity) -> (u8, u8, u8) { let dm = ecs.fetch::(); if let Some(name) = ecs.read_storage::().get(item) { diff --git a/src/gui/tooltip.rs b/src/gui/tooltip.rs index 70d0be3..528ff08 100644 --- a/src/gui/tooltip.rs +++ b/src/gui/tooltip.rs @@ -1,4 +1,4 @@ -use super::{camera::get_screen_bounds, Attributes, Hidden, Map, Pools, Position, Renderable, Rltk, World, RGB}; +use super::{camera::get_screen_bounds, Attributes, Hidden, Map, Name, Pools, Position, Renderable, Rltk, World, RGB}; use rltk::prelude::*; use specs::prelude::*; @@ -43,6 +43,7 @@ impl Tooltip { pub fn draw_tooltips(ecs: &World, ctx: &mut Rltk) { let (min_x, _max_x, min_y, _max_y, x_offset, y_offset) = get_screen_bounds(ecs, ctx); let map = ecs.fetch::(); + let names = ecs.read_storage::(); let positions = ecs.read_storage::(); let renderables = ecs.read_storage::(); let hidden = ecs.read_storage::(); @@ -69,7 +70,7 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut Rltk) { } let mut tooltips: Vec = Vec::new(); - for (entity, position, renderable, _hidden) in (&entities, &positions, &renderables, !&hidden).join() { + for (entity, position, renderable, _name, _hidden) in (&entities, &positions, &renderables, &names, !&hidden).join() { if position.x == mouse_pos_adjusted.0 && position.y == mouse_pos_adjusted.1 { let mut tip = Tooltip::new(); tip.add(crate::gui::obfuscate_name_ecs(ecs, entity).0, renderable.fg); diff --git a/src/main.rs b/src/main.rs index 7af3817..9ded95b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -90,11 +90,6 @@ impl State { fn run_systems(&mut self) { let mut mapindex = spatial::MapIndexingSystem {}; let mut vis = VisibilitySystem {}; - let mut regen_system = ai::RegenSystem {}; - let mut energy = ai::EnergySystem {}; - let mut encumbrance_system = ai::EncumbranceSystem {}; - let mut turn_status_system = ai::TurnStatusSystem {}; - let mut quip_system = ai::QuipSystem {}; let mut trigger_system = trigger_system::TriggerSystem {}; let mut melee_system = MeleeCombatSystem {}; let mut damage_system = DamageSystem {}; @@ -109,11 +104,6 @@ impl State { mapindex.run_now(&self.ecs); vis.run_now(&self.ecs); - regen_system.run_now(&self.ecs); - encumbrance_system.run_now(&self.ecs); - energy.run_now(&self.ecs); - quip_system.run_now(&self.ecs); - turn_status_system.run_now(&self.ecs); self.run_ai(); trigger_system.run_now(&self.ecs); inventory_system.run_now(&self.ecs); @@ -132,12 +122,22 @@ impl State { } fn run_ai(&mut self) { - let mut adjacent_ai = ai::AdjacentAI {}; + let mut encumbrance_system = ai::EncumbranceSystem {}; // Must run first, as it affects energy regen. + let mut energy = ai::EnergySystem {}; // Figures out who deserves a turn. + let mut regen_system = ai::RegenSystem {}; // Restores HP on appropriate clock ticks. + let mut turn_status_system = ai::TurnStatusSystem {}; // Ticks stasuses. Should anyone now lose their turn? i.e. confusion + let mut quip_system = ai::QuipSystem {}; // Quipping is "free". It doesn't use up a turn. + let mut adjacent_ai = ai::AdjacentAI {}; // AdjacentAI -> DefaultAI are all exclusive. If one acts, the entity's turn is over. let mut visible_ai = ai::VisibleAI {}; let mut approach_ai = ai::ApproachAI {}; let mut flee_ai = ai::FleeAI {}; let mut chase_ai = ai::ChaseAI {}; let mut default_move_ai = ai::DefaultAI {}; + encumbrance_system.run_now(&self.ecs); + energy.run_now(&self.ecs); + regen_system.run_now(&self.ecs); + turn_status_system.run_now(&self.ecs); + quip_system.run_now(&self.ecs); adjacent_ai.run_now(&self.ecs); visible_ai.run_now(&self.ecs); approach_ai.run_now(&self.ecs); diff --git a/src/spawner.rs b/src/spawner.rs index cc6762d..afe4ab4 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -91,27 +91,6 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { raws::SpawnType::Carried { by: player }, 0, ); - raws::spawn_named_entity( - &raws::RAWS.lock().unwrap(), - ecs, - "scroll_confusion", - raws::SpawnType::Carried { by: player }, - 0, - ); - raws::spawn_named_entity( - &raws::RAWS.lock().unwrap(), - ecs, - "scroll_confusion", - raws::SpawnType::Carried { by: player }, - 0, - ); - raws::spawn_named_entity( - &raws::RAWS.lock().unwrap(), - ecs, - "scroll_fireball", - raws::SpawnType::Carried { by: player }, - 0, - ); return player; }