Refactor confusion effect, and update gui to show confusion status

This commit is contained in:
Timothy Warren 2022-01-24 10:58:37 -05:00
parent af46a7631b
commit ee5db23f6b
12 changed files with 178 additions and 56 deletions

View File

@ -1,7 +1,9 @@
use ::rltk::{DistanceAlg, Point, RandomNumberGenerator};
use ::specs::prelude::*;
use crate::components::{Attributes, Initiative, MyTurn, Pools, Position};
use crate::components::{
Attributes, Duration, EquipmentChanged, Initiative, MyTurn, Pools, Position, StatusEffect,
};
use crate::RunState;
pub struct InitiativeSystem {}
@ -19,6 +21,9 @@ impl<'a> System<'a> for InitiativeSystem {
ReadExpect<'a, Entity>,
ReadExpect<'a, Point>,
ReadStorage<'a, Pools>,
WriteStorage<'a, Duration>,
WriteStorage<'a, EquipmentChanged>,
ReadStorage<'a, StatusEffect>,
);
fn run(&mut self, data: Self::SystemData) {
@ -33,6 +38,9 @@ impl<'a> System<'a> for InitiativeSystem {
player,
player_pos,
pools,
mut durations,
mut dirty,
statuses,
) = data;
if *runstate != RunState::Ticking {
@ -82,5 +90,19 @@ impl<'a> System<'a> for InitiativeSystem {
}
}
}
if *runstate == RunState::AwaitingInput {
for (effect_entity, duration, status) in (&entities, &mut durations, &statuses).join() {
duration.turns -= 1;
if duration.turns < 1 {
dirty
.insert(status.target, EquipmentChanged {})
.expect("Unable to insert EquipmentChanged tag");
entities
.delete(effect_entity)
.expect("Unable to delete status effect tag entity");
}
}
}
}
}

View File

@ -1,7 +1,10 @@
use std::collections::HashSet;
use ::specs::prelude::*;
use crate::components::{Confusion, MyTurn};
use crate::RunState;
use crate::components::{Confusion, MyTurn, StatusEffect};
use crate::effects::{add_effect, EffectType, Targets};
use crate::{colors, RunState};
pub struct TurnStatusSystem {}
@ -9,37 +12,50 @@ impl<'a> System<'a> for TurnStatusSystem {
#[allow(clippy::type_complexity)]
type SystemData = (
WriteStorage<'a, MyTurn>,
WriteStorage<'a, Confusion>,
ReadStorage<'a, Confusion>,
Entities<'a>,
ReadExpect<'a, RunState>,
ReadStorage<'a, StatusEffect>,
);
fn run(&mut self, data: Self::SystemData) {
let (mut turns, mut confusion, entities, runstate) = data;
let (mut turns, confusion, entities, runstate, statuses) = data;
if *runstate != RunState::Ticking {
return;
}
// Collect a set of all entities whose turn it is
let mut entity_turns = HashSet::new();
for (entity, _turn) in (&entities, &turns).join() {
entity_turns.insert(entity);
}
// Find status effects affecting entities whose turn it is
let mut not_my_turn: Vec<Entity> = Vec::new();
let mut not_confused: Vec<Entity> = Vec::new();
for (entity, _turn, confused) in (&entities, &mut turns, &mut confusion).join() {
confused.turns -= 1;
if confused.turns < 1 {
not_confused.push(entity)
} else {
not_my_turn.push(entity);
for (effect_entity, status_effect) in (&entities, &statuses).join() {
if entity_turns.contains(&status_effect.target) {
// Skip turn for confusion
if confusion.get(effect_entity).is_some() {
add_effect(
None,
EffectType::Particle {
glyph: ::rltk::to_cp437('?'),
fg: colors::CYAN,
bg: colors::BLACK,
lifespan: 200.0,
},
Targets::Single {
target: status_effect.target,
},
);
not_my_turn.push(status_effect.target);
}
}
}
for e in not_my_turn {
turns.remove(e);
}
for e in not_confused {
confusion.remove(e);
}
}
}

View File

@ -106,11 +106,6 @@ pub struct AreaOfEffect {
pub radius: i32,
}
#[derive(Component, Debug, ConvertSaveload, Clone)]
pub struct Confusion {
pub turns: i32,
}
#[derive(Component, Debug, ConvertSaveload, Clone)]
pub struct ProvidesHealing {
pub heal_amount: i32,
@ -390,3 +385,19 @@ pub struct AttributeBonus {
pub quickness: Option<i32>,
pub intelligence: Option<i32>,
}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Consumable {
pub max_charges: i32,
pub charges: i32,
}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Duration {
pub turns: i32,
}
#[derive(Component, Debug, ConvertSaveload, Clone)]
pub struct StatusEffect {
pub target: Entity,
}

View File

@ -7,9 +7,6 @@ use ::specs_derive::*;
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Player {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Consumable {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct BlocksTile {}
@ -51,3 +48,6 @@ pub struct ProvidesRemoveCurse {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct ProvidesIdentification {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Confusion {}

View File

@ -1,10 +1,11 @@
use ::rltk::Point;
use ::specs::prelude::*;
use ::specs::saveload::{MarkedBuilder, SimpleMarker};
use super::{add_effect, entity_position, EffectSpawner, EffectType, Targets};
use crate::components::{Attributes, Confusion, Player, Pools};
use crate::components::{Attributes, Confusion, Duration, Name, Player, Pools, SerializeMe};
use crate::gamesystem::{mana_at_level, player_hp_at_level};
use crate::{colors, GameLog, Map};
use crate::{colors, GameLog, Map, StatusEffect};
pub fn inflict_damage(ecs: &mut World, damage: &EffectSpawner, target: Entity) {
let mut pools = ecs.write_storage::<Pools>();
@ -130,8 +131,12 @@ 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::<Confusion>()
.insert(target, Confusion { turns: *turns })
.expect("Unable to make target confused");
ecs.create_entity()
.with(StatusEffect { target })
.with(Confusion {})
.with(Duration { turns: *turns })
.with(Name::from("Confusion"))
.marked::<SimpleMarker<SerializeMe>>()
.build();
}
}

View File

@ -2,7 +2,7 @@ use ::specs::prelude::*;
use super::{add_effect, EffectType, Targets};
use crate::components::{
Confusion, Consumable, Hidden, InflictsDamage, MagicMapper, Name, ProvidesFood,
Confusion, Consumable, Duration, Hidden, InflictsDamage, MagicMapper, Name, ProvidesFood,
ProvidesHealing, ProvidesRemoveCurse, SingleActivation, SpawnParticleBurst, SpawnParticleLine,
TeleportTo, TownPortal,
};
@ -10,16 +10,36 @@ use crate::effects::{entity_position, targeting};
use crate::{colors, GameLog, Map, RunState};
pub fn item_trigger(creator: Option<Entity>, item: Entity, targets: &Targets, ecs: &mut World) {
// Check charges
if let Some(c) = ecs.write_storage::<Consumable>().get_mut(item) {
if c.charges < 1 {
// Cancel
let mut gamelog = ecs.fetch_mut::<GameLog>();
gamelog.append(format!(
"{} is out of charges!",
ecs.read_storage::<Name>().get(item).unwrap().name
));
return;
} else {
c.charges -= 1;
}
}
// Use the item via the generic system
let did_something = event_trigger(creator, item, targets, ecs);
// If it was a consumable, then it gets deleted
if did_something && ecs.read_storage::<Consumable>().get(item).is_some() {
if did_something {
if let Some(c) = ecs.read_storage::<Consumable>().get(item) {
if c.max_charges == 0 {
ecs.entities()
.delete(item)
.expect("Failed to delete consumable item");
}
}
}
}
pub fn trigger(creator: Option<Entity>, trigger: Entity, targets: &Targets, ecs: &mut World) {
// The triggering item is no longer hidden
@ -159,17 +179,19 @@ fn event_trigger(
}
// Confusion
if let Some(confusion) = ecs.read_storage::<Confusion>().get(entity) {
if ecs.read_storage::<Confusion>().get(entity).is_some() {
if let Some(duration) = ecs.read_storage::<Duration>().get(entity) {
add_effect(
creator,
EffectType::Confusion {
turns: confusion.turns,
turns: duration.turns,
},
targets.clone(),
);
did_something = true;
}
}
// Teleport
if let Some(teleport) = ecs.read_storage::<TeleportTo>().get(entity) {

View File

@ -11,8 +11,8 @@ pub use menu::*;
use tooltip::draw_tooltips;
use crate::components::{
Attribute, Attributes, Consumable, CursedItem, Equipped, HungerClock, HungerState, InBackpack,
MagicItem, MagicItemClass, Name, ObfuscatedName, Pools, Viewshed,
Attribute, Attributes, Consumable, CursedItem, Duration, Equipped, HungerClock, HungerState,
InBackpack, MagicItem, MagicItemClass, Name, ObfuscatedName, Pools, StatusEffect, Viewshed,
};
use crate::game_log::GameLog;
use crate::{camera, colors, Map, MasterDungeonMap, State};
@ -45,7 +45,15 @@ pub fn get_item_display_name(ecs: &World, item: Entity) -> String {
if ecs.read_storage::<MagicItem>().get(item).is_some() {
let dm = ecs.fetch::<MasterDungeonMap>();
if dm.identified_items.contains(&name.name) {
if let Some(c) = ecs.read_storage::<Consumable>().get(item) {
if c.max_charges > 1 {
format!("{} ({})", name.name.clone(), c.charges)
} else {
name.name.clone()
}
} else {
name.name.clone()
}
} else if let Some(obfuscated) = ecs.read_storage::<ObfuscatedName>().get(item) {
obfuscated.name.clone()
} else {
@ -236,6 +244,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
}
// Status
y = 44;
let hunger = ecs.read_storage::<HungerClock>();
let hc = hunger.get(*player_entity).unwrap();
match hc.state {
@ -244,6 +253,21 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
HungerState::Hungry => ctx.print_color(50, 44, colors::ORANGE, colors::BLACK, "Hungry"),
HungerState::Starving => ctx.print_color(50, 44, colors::RED, colors::BLACK, "Starving"),
}
let statuses = ecs.read_storage::<StatusEffect>();
let durations = ecs.read_storage::<Duration>();
let names = ecs.read_storage::<Name>();
for (status, duration, name) in (&statuses, &durations, &names).join() {
if status.target == *player_entity {
ctx.print_color(
50,
y,
colors::RED,
colors::BLACK,
&format!("{} ({})", name.name, duration.turns),
);
y -= 1;
}
}
// Draw the log
let log = ecs.fetch::<GameLog>();

View File

@ -1,7 +1,7 @@
use ::rltk::Rltk;
use ::specs::prelude::*;
use crate::components::{Attributes, Hidden, Pools, Position};
use crate::components::{Attributes, Duration, Hidden, Name, Pools, Position, StatusEffect};
use crate::{camera, colors, Map};
struct Tooltip {
@ -126,6 +126,16 @@ pub(super) fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {
tip.add(format!("Level: {}", stat.level));
}
// Status effects
let statuses = ecs.read_storage::<StatusEffect>();
let durations = ecs.read_storage::<Duration>();
let names = ecs.read_storage::<Name>();
for (status, duration, name) in (&statuses, &durations, &names).join() {
if status.target == entity {
tip.add(format!("{} ({})", name.name, duration.turns));
}
}
tip_boxes.push(tip);
}
}

View File

@ -94,6 +94,7 @@ fn init_state() -> State {
CursedItem,
Door,
DMSerializationHelper,
Duration,
EntityMoved,
EntryTrigger,
Equippable,
@ -135,6 +136,7 @@ fn init_state() -> State {
Skills,
SpawnParticleBurst,
SpawnParticleLine,
StatusEffect,
TeleportTo,
TownPortal,
Vendor,

View File

@ -28,6 +28,7 @@ pub struct Renderable {
#[derive(Deserialize, Debug)]
pub struct Consumable {
pub effects: HashMap<String, String>,
pub charges: Option<i32>,
}
#[derive(Deserialize, Debug)]

View File

@ -294,27 +294,28 @@ macro_rules! apply_effects {
"provides_healing" => {
$eb = $eb.with(ProvidesHealing {
heal_amount: effect.1.parse::<i32>().unwrap(),
})
});
}
"ranged" => {
$eb = $eb.with(Ranged {
range: effect.1.parse::<i32>().unwrap(),
})
});
}
"damage" => {
$eb = $eb.with(InflictsDamage {
damage: effect.1.parse::<i32>().unwrap(),
})
});
}
"area_of_effect" => {
$eb = $eb.with(AreaOfEffect {
radius: effect.1.parse::<i32>().unwrap(),
})
});
}
"confusion" => {
$eb = $eb.with(Confusion {
$eb = $eb.with(Confusion {});
$eb = $eb.with(Duration {
turns: effect.1.parse::<i32>().unwrap(),
})
});
}
"magic_mapping" => $eb = $eb.with(MagicMapper {}),
"town_portal" => $eb = $eb.with(TownPortal {}),
@ -368,7 +369,11 @@ pub fn spawn_named_item(
});
if let Some(consumable) = &item_template.consumable {
eb = eb.with(Consumable {});
let max_charges = consumable.charges.unwrap_or(1);
eb = eb.with(Consumable {
max_charges,
charges: max_charges,
});
apply_effects!(consumable.effects, eb);
}

View File

@ -73,6 +73,7 @@ pub fn save_game(ecs: &mut World) {
CursedItem,
Door,
DMSerializationHelper,
Duration,
EntityMoved,
EntryTrigger,
Equippable,
@ -113,6 +114,7 @@ pub fn save_game(ecs: &mut World) {
Skills,
SpawnParticleBurst,
SpawnParticleLine,
StatusEffect,
TeleportTo,
TownPortal,
Vendor,
@ -194,6 +196,7 @@ pub fn load_game(ecs: &mut World) {
CursedItem,
Door,
DMSerializationHelper,
Duration,
EntityMoved,
EntryTrigger,
Equippable,
@ -234,6 +237,7 @@ pub fn load_game(ecs: &mut World) {
Skills,
SpawnParticleBurst,
SpawnParticleLine,
StatusEffect,
TeleportTo,
TownPortal,
Vendor,