diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..76a72c6
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/roguelike.iml b/.idea/roguelike.iml
new file mode 100644
index 0000000..c254557
--- /dev/null
+++ b/.idea/roguelike.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components.rs b/src/components.rs
index 88cc0d5..eafc560 100644
--- a/src/components.rs
+++ b/src/components.rs
@@ -13,6 +13,7 @@ pub struct Renderable {
pub glyph: rltk::FontCharType,
pub fg: RGB,
pub bg: RGB,
+ pub render_order: i32,
}
#[derive(Component, Debug)]
@@ -66,3 +67,34 @@ impl SufferDamage {
}
}
}
+
+#[derive(Component, Debug)]
+pub struct Item {}
+
+#[derive(Component, Debug)]
+pub struct Potion {
+ pub heal_amount: i32,
+}
+
+#[derive(Component, Debug)]
+pub struct InBackpack {
+ pub owner: Entity,
+}
+
+#[derive(Component, Debug, Clone)]
+pub struct WantsToPickupItem {
+ pub collected_by: Entity,
+ pub item: Entity,
+}
+
+#[derive(Component, Debug, Clone)]
+pub struct WantsToDrinkPotion {
+ pub potion: Entity,
+}
+
+#[derive(Component, Debug, Clone)]
+pub struct WantsToDropItem {
+ pub item: Entity,
+}
+
+
diff --git a/src/gui.rs b/src/gui.rs
index 8e0134d..f301289 100644
--- a/src/gui.rs
+++ b/src/gui.rs
@@ -1,13 +1,14 @@
+use rltk::{BTerm, Point, VirtualKeyCode};
+use rltk::RGB;
+use rltk::Rltk;
+use specs::prelude::*;
+
+use crate::{CombatStats, InBackpack, State};
use crate::gamelog::GameLog;
-use crate::CombatStats;
use crate::Map;
use crate::Name;
use crate::Player;
use crate::Position;
-use rltk::Point;
-use rltk::Rltk;
-use rltk::RGB;
-use specs::prelude::*;
pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
ctx.draw_box(
@@ -150,3 +151,140 @@ fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {
}
}
}
+
+#[derive(PartialEq, Copy, Clone)]
+pub enum ItemMenuResult {
+ Cancel,
+ NoResponse,
+ Selected,
+}
+
+pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) {
+ let player_entity = gs.ecs.fetch::();
+ let names = gs.ecs.read_storage::();
+ let backpack = gs.ecs.read_storage::();
+ let entities = gs.ecs.entities();
+
+ let inventory = (&backpack, &names)
+ .join()
+ .filter(|item| item.0.owner == *player_entity);
+ let count = inventory.count();
+
+ let mut y = (25 - (count / 2)) as i32;
+ ctx.draw_box(
+ 15,
+ y - 2,
+ 31,
+ (count + 3) as i32,
+ RGB::named(rltk::WHITE),
+ RGB::named(rltk::BLACK),
+ );
+ ctx.print_color(
+ 18,
+ y - 2,
+ RGB::named(rltk::YELLOW),
+ RGB::named(rltk::BLACK),
+ "Inventory",
+ );
+ ctx.print_color(
+ 18,
+ y + count as i32 + 1,
+ RGB::named(rltk::YELLOW),
+ RGB::named(rltk::BLACK),
+ "ESCAPE to cancel",
+ );
+
+ let mut equippable: Vec = Vec::new();
+ let mut j = 0;
+ for (entity, _pack, name) in (&entities, &backpack, &names)
+ .join()
+ .filter(|item| item.1.owner == *player_entity)
+ {
+ ctx.set(
+ 17,
+ y,
+ RGB::named(rltk::WHITE),
+ RGB::named(rltk::BLACK),
+ rltk::to_cp437('('),
+ );
+ ctx.set(
+ 18,
+ y,
+ RGB::named(rltk::YELLOW),
+ RGB::named(rltk::BLACK),
+ 97 + j as rltk::FontCharType,
+ );
+ ctx.set(
+ 19,
+ y,
+ RGB::named(rltk::WHITE),
+ RGB::named(rltk::BLACK),
+ rltk::to_cp437(')'),
+ );
+
+ ctx.print(21, y, &name.name.to_string());
+ equippable.push(entity);
+ y += 1;
+ j += 1;
+ }
+
+ match ctx.key {
+ None => (ItemMenuResult::NoResponse, None),
+ Some(key) => {
+ match key {
+ VirtualKeyCode::Escape => { (ItemMenuResult::Cancel, None) }
+ _ => {
+ let selection = rltk::letter_to_option(key);
+ if selection > -1 && selection < count as i32 {
+ return (ItemMenuResult::Selected, Some(equippable[selection as usize]));
+ }
+ (ItemMenuResult::NoResponse, None)
+ }
+ }
+ }
+ }
+}
+
+pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) {
+ let player_entity = gs.ecs.fetch::();
+ let names = gs.ecs.read_storage::();
+ let backpack = gs.ecs.read_storage::();
+ let entities = gs.ecs.entities();
+
+ let inventory = (&backpack, &names).join().filter(|item| item.0.owner == *player_entity);
+ let count = inventory.count();
+
+ let mut y = (25 - (count / 2)) as i32;
+ ctx.draw_box(15, y - 2, 31, (count + 3) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK));
+ ctx.print_color(18, y - 2, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "Drop Which Item?");
+ ctx.print_color(18, y + count as i32 + 1, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "ESCAPE to cancel");
+
+ let mut equippable: Vec = Vec::new();
+ let mut j = 0;
+ for (entity, _pack, name) in (&entities, &backpack, &names).join().filter(|item| item.1.owner == *player_entity) {
+ ctx.set(17, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), rltk::to_cp437('('));
+ ctx.set(18, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType);
+ ctx.set(19, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), rltk::to_cp437(')'));
+
+ ctx.print(21, y, &name.name.to_string());
+ equippable.push(entity);
+ y += 1;
+ j += 1;
+ }
+
+ match ctx.key {
+ None => (ItemMenuResult::NoResponse, None),
+ Some(key) => {
+ match key {
+ VirtualKeyCode::Escape => { (ItemMenuResult::Cancel, None) }
+ _ => {
+ let selection = rltk::letter_to_option(key);
+ if selection > -1 && selection < count as i32 {
+ return (ItemMenuResult::Selected, Some(equippable[selection as usize]));
+ }
+ (ItemMenuResult::NoResponse, None)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/inventory_system.rs b/src/inventory_system.rs
new file mode 100644
index 0000000..aa9ccee
--- /dev/null
+++ b/src/inventory_system.rs
@@ -0,0 +1,116 @@
+use rltk::Rltk;
+use specs::prelude::*;
+
+use crate::{CombatStats, InBackpack, Name, Position, Potion, State, WantsToDrinkPotion, WantsToDropItem, WantsToPickupItem};
+use crate::gamelog::GameLog;
+use crate::gui::ItemMenuResult;
+
+pub struct ItemCollectionSystem {}
+
+impl<'a> System<'a> for ItemCollectionSystem {
+ type SystemData = (
+ ReadExpect<'a, Entity>,
+ WriteExpect<'a, GameLog>,
+ WriteStorage<'a, WantsToPickupItem>,
+ WriteStorage<'a, Position>,
+ ReadStorage<'a, Name>,
+ WriteStorage<'a, InBackpack>,
+ );
+
+ fn run(&mut self, data: Self::SystemData) {
+ let (player_entity, mut gamelog, mut wants_pickup, mut positions, names, mut backpack) =
+ data;
+
+ for pickup in wants_pickup.join() {
+ positions.remove(pickup.item);
+ backpack
+ .insert(
+ pickup.item,
+ InBackpack {
+ owner: pickup.collected_by,
+ },
+ )
+ .expect("Unable to insert backpack entry");
+
+ if pickup.collected_by == *player_entity {
+ gamelog.entries.push(format!(
+ "You pick up the {}.",
+ names.get(pickup.item).unwrap().name
+ ))
+ }
+ }
+
+ wants_pickup.clear();
+ }
+}
+
+pub struct PotionUseSystem {}
+
+impl<'a> System<'a> for PotionUseSystem {
+ type SystemData = (
+ ReadExpect<'a, Entity>,
+ WriteExpect<'a, GameLog>,
+ Entities<'a>,
+ WriteStorage<'a, WantsToDrinkPotion>,
+ ReadStorage<'a, Name>,
+ ReadStorage<'a, Potion>,
+ WriteStorage<'a, CombatStats>
+ );
+
+ fn run(&mut self, data: Self::SystemData) {
+ let (player_entity, mut gamelog, entities, mut wants_drink, names, potions, mut combat_stats) = data;
+
+ for (entity, drink, stats) in (&entities, &wants_drink, &mut combat_stats).join() {
+ let potion = potions.get(drink.potion);
+ match potion {
+ None => {}
+ Some(potion) => {
+ stats.hp = i32::min(stats.max_hp, stats.hp + potion.heal_amount);
+ if entity == *player_entity {
+ gamelog.entries.push(format!("You drink the {}, healing {} hp.", names.get(drink.potion).unwrap().name, potion.heal_amount));
+ }
+ entities.delete(drink.potion).expect("Delete failed");
+ }
+ }
+ }
+
+ wants_drink.clear();
+ }
+}
+
+pub struct ItemDropSystem {}
+
+impl<'a> System<'a> for ItemDropSystem {
+ type SystemData = (
+ ReadExpect<'a, Entity>,
+ WriteExpect<'a, GameLog>,
+ Entities<'a>,
+ WriteStorage<'a, WantsToDropItem>,
+ ReadStorage<'a, Name>,
+ WriteStorage<'a, Position>,
+ WriteStorage<'a, InBackpack>
+ );
+
+ fn run(&mut self, data: Self::SystemData) {
+ let (player_entity, mut gamelog, entities, mut wants_drop, names, mut positions, mut backpack) = data;
+
+ for (entity, to_drop) in (&entities, &wants_drop).join() {
+ let mut dropper_pos: Position = Position { x: 0, y: 0 };
+ {
+ let dropped_pos = positions.get(entity).unwrap();
+ dropper_pos.x = dropped_pos.x;
+ dropper_pos.y = dropped_pos.y;
+ }
+ positions.insert(to_drop.item, Position { x: dropper_pos.x, y: dropper_pos.y }).expect("Unable to insert position");
+ backpack.remove(to_drop.item);
+
+ if entity == *player_entity {
+ gamelog.entries.push(format!("You drop the {}.", names.get(to_drop.item).unwrap().name));
+ }
+ }
+
+ wants_drop.clear();
+ }
+}
+
+
diff --git a/src/main.rs b/src/main.rs
index b61c925..a961221 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,8 @@
+use std::thread::spawn;
+
+use rltk::{GameState, Point, RGB};
+use specs::prelude::*;
+
use components::*;
use damage_system::DamageSystem;
use map::*;
@@ -5,21 +10,22 @@ use map_indexing_system::MapIndexingSystem;
use melee_combat_system::MeleeCombatSystem;
use monster_ai_system::*;
use player::*;
-use rltk::{GameState, Point, RGB};
use visibility_system::*;
-use specs::prelude::*;
+use crate::inventory_system::{ItemCollectionSystem, ItemDropSystem, PotionUseSystem};
mod components;
mod damage_system;
mod gamelog;
mod gui;
+mod inventory_system;
mod map;
mod map_indexing_system;
mod melee_combat_system;
mod monster_ai_system;
mod player;
mod rect;
+mod spawner;
mod visibility_system;
#[derive(PartialEq, Copy, Clone)]
@@ -28,6 +34,8 @@ pub enum RunState {
PreRun,
PlayerTurn,
MonsterTurn,
+ ShowInventory,
+ ShowDropItem,
}
pub struct State {
@@ -51,6 +59,15 @@ impl State {
let mut damagesystem = DamageSystem {};
damagesystem.run_now(&self.ecs);
+ let mut inventory = ItemCollectionSystem {};
+ inventory.run_now(&self.ecs);
+
+ let mut potions = PotionUseSystem {};
+ potions.run_now(&self.ecs);
+
+ let mut drop_items = ItemDropSystem {};
+ drop_items.run_now(&self.ecs);
+
self.ecs.maintain();
}
}
@@ -59,6 +76,25 @@ impl GameState for State {
fn tick(&mut self, ctx: &mut rltk::Rltk) {
ctx.cls();
+ {
+ draw_map(&self.ecs, ctx);
+
+ let positions = self.ecs.read_storage::();
+ let renderables = self.ecs.read_storage::();
+ let map = self.ecs.fetch::