diff --git a/src/components.rs b/src/components.rs index c3cf556..5070f6e 100644 --- a/src/components.rs +++ b/src/components.rs @@ -200,6 +200,12 @@ pub struct WantsToUseItem { #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Consumable {} +#[derive(Component, Debug, ConvertSaveload)] +pub struct Wand { + pub uses: i32, + pub max_uses: i32, +} + #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Destructible {} diff --git a/src/inventory_system.rs b/src/inventory_system.rs index 79d23c0..c34bba7 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -1,8 +1,8 @@ use super::{ gamelog, CombatStats, Confusion, Consumable, Cursed, Destructible, Equippable, Equipped, HungerClock, HungerState, InBackpack, InflictsDamage, MagicMapper, Map, Name, ParticleBuilder, Point, Position, ProvidesHealing, - ProvidesNutrition, RunState, SufferDamage, WantsToDropItem, WantsToPickupItem, WantsToRemoveItem, WantsToUseItem, - AOE, DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME, + ProvidesNutrition, RandomNumberGenerator, RunState, SufferDamage, Wand, WantsToDropItem, WantsToPickupItem, + WantsToRemoveItem, WantsToUseItem, AOE, DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME, }; use specs::prelude::*; @@ -44,10 +44,12 @@ impl<'a> System<'a> for ItemUseSystem { type SystemData = ( ReadExpect<'a, Entity>, ReadExpect<'a, Map>, + WriteExpect<'a, RandomNumberGenerator>, Entities<'a>, WriteStorage<'a, WantsToUseItem>, ReadStorage<'a, Name>, - ReadStorage<'a, Consumable>, + WriteStorage<'a, Consumable>, + WriteStorage<'a, Wand>, ReadStorage<'a, Destructible>, ReadStorage<'a, Cursed>, ReadStorage<'a, ProvidesHealing>, @@ -71,10 +73,12 @@ impl<'a> System<'a> for ItemUseSystem { let ( player_entity, map, + mut rng, entities, mut wants_to_use, names, - consumables, + mut consumables, + mut wands, destructibles, cursed_items, provides_healing, @@ -95,13 +99,29 @@ impl<'a> System<'a> for ItemUseSystem { ) = data; for (entity, wants_to_use) in (&entities, &wants_to_use).join() { + let mut verb = "use"; let mut used_item = true; let mut aoe_item = false; let item_being_used = names.get(wants_to_use.item).unwrap(); let is_cursed = cursed_items.get(wants_to_use.item); - - let mut verb = "use"; + let wand = wands.get_mut(wants_to_use.item); + if let Some(wand) = wand { + // If want has no uses, roll 1d121. On a 121, wrest the wand, then delete it. + if wand.uses == 0 { + if rng.roll_dice(1, 121) != 121 { + gamelog::Logger::new().append("The wand does nothing.").log(); + break; + } + gamelog::Logger::new() + .colour(rltk::YELLOW) + .append("You wrest one last charge from the worn-out wand.") + .log(); + consumables.insert(wants_to_use.item, Consumable {}).expect("Could not insert consumable"); + } + verb = "zap"; + wand.uses -= 1; + } let is_edible = provides_nutrition.get(wants_to_use.item); if let Some(_) = is_edible { diff --git a/src/main.rs b/src/main.rs index 61e3cd2..c4d5a86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -283,13 +283,11 @@ impl GameState for State { } RunState::AwaitingInput => { new_runstate = player_input(self, ctx); - if new_runstate != RunState::AwaitingInput { - gamelog::record_event("Turn", 1); - } } RunState::PlayerTurn => { self.run_systems(); self.ecs.maintain(); + gamelog::record_event("Turn", 1); match *self.ecs.fetch::() { RunState::MagicMapReveal { row, cursed } => { new_runstate = RunState::MagicMapReveal { row: row, cursed: cursed } @@ -508,6 +506,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/saveload_system.rs b/src/saveload_system.rs index e72f3fb..e092981 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -76,6 +76,7 @@ pub fn save_game(ecs: &mut World) { SufferDamage, Telepath, Viewshed, + Wand, WantsToDropItem, WantsToMelee, WantsToPickupItem, @@ -163,6 +164,7 @@ pub fn load_game(ecs: &mut World) { SufferDamage, Telepath, Viewshed, + Wand, WantsToDropItem, WantsToMelee, WantsToPickupItem, diff --git a/src/spawner.rs b/src/spawner.rs index d55a3b3..8218b90 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -2,7 +2,7 @@ use super::{ random_table::RandomTable, BlocksTile, CombatStats, Confusion, Consumable, Cursed, DefenceBonus, Destructible, EquipmentSlot, Equippable, HungerClock, HungerState, InflictsDamage, Item, MagicMapper, MeleePowerBonus, Mind, Monster, Name, Player, Position, ProvidesHealing, ProvidesNutrition, Ranged, Rect, Renderable, SerializeMe, - Viewshed, AOE, MAPWIDTH, + Viewshed, Wand, AOE, MAPWIDTH, }; use rltk::{console, RandomNumberGenerator, RGB}; use specs::prelude::*; @@ -135,6 +135,9 @@ pub fn spawn_room(ecs: &mut World, room: &Rect, map_depth: i32) { "magic missile scroll" => magic_missile_scroll(ecs, x, y), "magic map scroll" => magic_map_scroll(ecs, x, y), "cursed magic map scroll" => cursed_magic_map_scroll(ecs, x, y), + // Wands + "magic missile wand" => magic_missile_wand(ecs, x, y), + "fireball wand" => fireball_wand(ecs, x, y), // Food "rations" => rations(ecs, x, y), _ => console::log("Tried to spawn nothing. Bugfix needed!"), @@ -164,20 +167,23 @@ fn mob_table(map_depth: i32) -> RandomTable { fn item_table(_map_depth: i32) -> RandomTable { return RandomTable::new() // Equipment - .add("dagger", 2) - .add("shortsword", 2) - .add("buckler", 2) - .add("shield", 1) + .add("dagger", 4) + .add("shortsword", 4) + .add("buckler", 4) + .add("shield", 2) // Potions - .add("weak health potion", 7) - .add("health potion", 3) + .add("weak health potion", 14) + .add("health potion", 6) // Scrolls - .add("fireball scroll", 1) - .add("cursed fireball scroll", 1) - .add("confusion scroll", 2) - .add("magic missile scroll", 5) - .add("magic map scroll", 2) - .add("cursed magic map scroll", 1); + .add("fireball scroll", 2) + .add("cursed fireball scroll", 2) + .add("confusion scroll", 4) + .add("magic missile scroll", 10) + .add("magic map scroll", 4) + .add("cursed magic map scroll", 2) + // Wands + .add("magic missile wand", 1) + .add("fireball wand", 1); } fn food_table(_map_depth: i32) -> RandomTable { @@ -445,3 +451,44 @@ fn rations(ecs: &mut World, x: i32, y: i32) { .marked::>() .build(); } + +// WANDS + +fn fireball_wand(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 { name: "wand of fireball".to_string() }) + .with(Item {}) + .with(Wand { uses: 3, max_uses: 3 }) + .with(Destructible {}) + .with(Ranged { range: 10 }) + .with(InflictsDamage { amount: 20 }) + .with(AOE { radius: 3 }) + .marked::>() + .build(); +} + +fn magic_missile_wand(ecs: &mut World, x: i32, y: i32) { + ecs.create_entity() + .with(Position { x, y }) + .with(Renderable { + glyph: rltk::to_cp437('?'), + fg: RGB::named(rltk::BLUE), + bg: RGB::named(rltk::BLACK), + render_order: 2, + }) + .with(Name { name: "wand of magic missile".to_string() }) + .with(Item {}) + .with(Wand { uses: 3, max_uses: 3 }) + .with(Destructible {}) + .with(Ranged { range: 12 }) // Long range - as far as default vision range + .with(InflictsDamage { amount: 10 }) // Low~ damage + .marked::>() + .build(); +}