Cleanup code structure to match the end of the first section of the tutorial
This commit is contained in:
parent
416af96be3
commit
ffc997ce20
@ -142,11 +142,9 @@ pub struct WantsToDropItem {
|
|||||||
pub item: Entity,
|
pub item: Entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SerializeMe;
|
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||||
|
pub struct WantsToRemoveItem {
|
||||||
#[derive(Component, Serialize, Deserialize, Clone)]
|
pub item: Entity,
|
||||||
pub struct SerializationHelper {
|
|
||||||
pub map: crate::map::Map,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||||
@ -176,7 +174,13 @@ pub struct DefenseBonus {
|
|||||||
pub defense: i32,
|
pub defense: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
// Serialization helper code. We need to implement ConvertSaveLoad for each type that contains an
|
||||||
pub struct WantsToRemoveItem {
|
// Entity.
|
||||||
pub item: Entity,
|
|
||||||
|
pub struct SerializeMe;
|
||||||
|
|
||||||
|
// Special component that exists to help serialize the game data
|
||||||
|
#[derive(Component, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct SerializationHelper {
|
||||||
|
pub map: super::map::Map,
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,8 @@ pub struct GameLog {
|
|||||||
|
|
||||||
impl GameLog {
|
impl GameLog {
|
||||||
pub fn new<S: ToString>(first_entry: S) -> Self {
|
pub fn new<S: ToString>(first_entry: S) -> Self {
|
||||||
let mut entries: Vec<String> = Vec::new();
|
GameLog {
|
||||||
entries.push(first_entry.to_string());
|
entries: vec![first_entry.to_string()],
|
||||||
|
}
|
||||||
GameLog { entries }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
204
src/gui.rs
204
src/gui.rs
@ -3,19 +3,6 @@ use crate::{game_log::GameLog, Equipped, Map, RunState, State};
|
|||||||
use rltk::{Point, Rltk, VirtualKeyCode, RGB};
|
use rltk::{Point, Rltk, VirtualKeyCode, RGB};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
|
||||||
pub enum MainMenuSelection {
|
|
||||||
NewGame,
|
|
||||||
LoadGame,
|
|
||||||
Quit,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
|
||||||
pub enum MainMenuResult {
|
|
||||||
NoSelection { selected: MainMenuSelection },
|
|
||||||
Selected { selected: MainMenuSelection },
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
|
pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
|
||||||
ctx.draw_box(
|
ctx.draw_box(
|
||||||
0,
|
0,
|
||||||
@ -359,6 +346,95 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option<Entity>) {
|
||||||
|
let player_entity = gs.ecs.fetch::<Entity>();
|
||||||
|
let names = gs.ecs.read_storage::<Name>();
|
||||||
|
let backpack = gs.ecs.read_storage::<Equipped>();
|
||||||
|
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),
|
||||||
|
"Remove 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<Entity> = 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 ranged_target(
|
pub fn ranged_target(
|
||||||
gs: &mut State,
|
gs: &mut State,
|
||||||
ctx: &mut Rltk,
|
ctx: &mut Rltk,
|
||||||
@ -418,6 +494,19 @@ pub fn ranged_target(
|
|||||||
(ItemMenuResult::NoResponse, None)
|
(ItemMenuResult::NoResponse, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
|
pub enum MainMenuSelection {
|
||||||
|
NewGame,
|
||||||
|
LoadGame,
|
||||||
|
Quit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
|
pub enum MainMenuResult {
|
||||||
|
NoSelection { selected: MainMenuSelection },
|
||||||
|
Selected { selected: MainMenuSelection },
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main_menu(gs: &mut State, ctx: &mut Rltk) -> MainMenuResult {
|
pub fn main_menu(gs: &mut State, ctx: &mut Rltk) -> MainMenuResult {
|
||||||
let save_exists = crate::saveload_system::does_save_exist();
|
let save_exists = crate::saveload_system::does_save_exist();
|
||||||
let runstate = gs.ecs.fetch::<RunState>();
|
let runstate = gs.ecs.fetch::<RunState>();
|
||||||
@ -531,95 +620,6 @@ pub fn main_menu(gs: &mut State, ctx: &mut Rltk) -> MainMenuResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option<Entity>) {
|
|
||||||
let player_entity = gs.ecs.fetch::<Entity>();
|
|
||||||
let names = gs.ecs.read_storage::<Name>();
|
|
||||||
let backpack = gs.ecs.read_storage::<Equipped>();
|
|
||||||
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),
|
|
||||||
"Remove 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<Entity> = 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)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
pub enum GameOverResult {
|
pub enum GameOverResult {
|
||||||
NoSelection,
|
NoSelection,
|
||||||
|
@ -65,6 +65,7 @@ impl<'a> System<'a> for ItemUseSystem {
|
|||||||
WriteStorage<'a, InBackpack>,
|
WriteStorage<'a, InBackpack>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
let (
|
let (
|
||||||
player_entity,
|
player_entity,
|
||||||
@ -267,6 +268,7 @@ impl<'a> System<'a> for ItemUseSystem {
|
|||||||
pub struct ItemDropSystem {}
|
pub struct ItemDropSystem {}
|
||||||
|
|
||||||
impl<'a> System<'a> for ItemDropSystem {
|
impl<'a> System<'a> for ItemDropSystem {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
ReadExpect<'a, Entity>,
|
ReadExpect<'a, Entity>,
|
||||||
WriteExpect<'a, GameLog>,
|
WriteExpect<'a, GameLog>,
|
||||||
@ -323,6 +325,7 @@ impl<'a> System<'a> for ItemDropSystem {
|
|||||||
pub struct ItemRemoveSystem {}
|
pub struct ItemRemoveSystem {}
|
||||||
|
|
||||||
impl<'a> System<'a> for ItemRemoveSystem {
|
impl<'a> System<'a> for ItemRemoveSystem {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
WriteStorage<'a, WantsToRemoveItem>,
|
WriteStorage<'a, WantsToRemoveItem>,
|
||||||
|
62
src/main.rs
62
src/main.rs
@ -43,8 +43,6 @@ macro_rules! register {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const MAP_SIZE: usize = 80 * 50;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
pub enum RunState {
|
pub enum RunState {
|
||||||
AwaitingInput,
|
AwaitingInput,
|
||||||
@ -213,6 +211,25 @@ impl GameState for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RunState::ShowRemoveItem => {
|
||||||
|
let result = gui::remove_item_menu(self, ctx);
|
||||||
|
match result.0 {
|
||||||
|
gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput,
|
||||||
|
gui::ItemMenuResult::NoResponse => {}
|
||||||
|
gui::ItemMenuResult::Selected => {
|
||||||
|
let item_entity = result.1.unwrap();
|
||||||
|
let mut intent = self.ecs.write_storage::<WantsToRemoveItem>();
|
||||||
|
intent
|
||||||
|
.insert(
|
||||||
|
*self.ecs.fetch::<Entity>(),
|
||||||
|
WantsToRemoveItem { item: item_entity },
|
||||||
|
)
|
||||||
|
.expect("Unable to insert intent to remove item");
|
||||||
|
|
||||||
|
newrunstate = RunState::PlayerTurn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
RunState::ShowTargeting { range, item } => {
|
RunState::ShowTargeting { range, item } => {
|
||||||
let result = gui::ranged_target(self, ctx, range);
|
let result = gui::ranged_target(self, ctx, range);
|
||||||
match result.0 {
|
match result.0 {
|
||||||
@ -253,6 +270,15 @@ impl GameState for State {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
RunState::GameOver => match gui::game_over(ctx) {
|
||||||
|
gui::GameOverResult::NoSelection => {}
|
||||||
|
gui::GameOverResult::QuitToMenu => {
|
||||||
|
self.game_over_cleanup();
|
||||||
|
newrunstate = RunState::MainMenu {
|
||||||
|
menu_selection: gui::MainMenuSelection::NewGame,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
RunState::SaveGame => {
|
RunState::SaveGame => {
|
||||||
saveload_system::save_game(&mut self.ecs);
|
saveload_system::save_game(&mut self.ecs);
|
||||||
|
|
||||||
@ -264,34 +290,6 @@ impl GameState for State {
|
|||||||
self.goto_next_level();
|
self.goto_next_level();
|
||||||
newrunstate = RunState::PreRun;
|
newrunstate = RunState::PreRun;
|
||||||
}
|
}
|
||||||
RunState::ShowRemoveItem => {
|
|
||||||
let result = gui::remove_item_menu(self, ctx);
|
|
||||||
match result.0 {
|
|
||||||
gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput,
|
|
||||||
gui::ItemMenuResult::NoResponse => {}
|
|
||||||
gui::ItemMenuResult::Selected => {
|
|
||||||
let item_entity = result.1.unwrap();
|
|
||||||
let mut intent = self.ecs.write_storage::<WantsToRemoveItem>();
|
|
||||||
intent
|
|
||||||
.insert(
|
|
||||||
*self.ecs.fetch::<Entity>(),
|
|
||||||
WantsToRemoveItem { item: item_entity },
|
|
||||||
)
|
|
||||||
.expect("Unable to insert intent to remove item");
|
|
||||||
|
|
||||||
newrunstate = RunState::PlayerTurn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RunState::GameOver => match gui::game_over(ctx) {
|
|
||||||
gui::GameOverResult::NoSelection => {}
|
|
||||||
gui::GameOverResult::QuitToMenu => {
|
|
||||||
self.game_over_cleanup();
|
|
||||||
newrunstate = RunState::MainMenu {
|
|
||||||
menu_selection: gui::MainMenuSelection::NewGame,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -504,7 +502,9 @@ fn main() -> rltk::BError {
|
|||||||
gs.ecs.insert(map);
|
gs.ecs.insert(map);
|
||||||
gs.ecs.insert(Point::new(player_x, player_y));
|
gs.ecs.insert(Point::new(player_x, player_y));
|
||||||
gs.ecs.insert(player_entity);
|
gs.ecs.insert(player_entity);
|
||||||
gs.ecs.insert(RunState::PreRun);
|
gs.ecs.insert(RunState::MainMenu {
|
||||||
|
menu_selection: gui::MainMenuSelection::NewGame,
|
||||||
|
});
|
||||||
gs.ecs.insert(GameLog::new("Welcome to Rusty Roguelike"));
|
gs.ecs.insert(GameLog::new("Welcome to Rusty Roguelike"));
|
||||||
|
|
||||||
rltk::main_loop(context, gs)
|
rltk::main_loop(context, gs)
|
||||||
|
56
src/map.rs
56
src/map.rs
@ -65,6 +65,28 @@ impl Map {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_exit_valid(&self, x: i32, y: i32) -> bool {
|
||||||
|
if x < 1 || x > self.width - 1 || y < 1 || y > self.height - 1 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let idx = self.xy_idx(x, y);
|
||||||
|
|
||||||
|
!self.blocked[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn populate_blocked(&mut self) {
|
||||||
|
for (i, tile) in self.tiles.iter_mut().enumerate() {
|
||||||
|
self.blocked[i] = *tile == TileType::Wall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_content_index(&mut self) {
|
||||||
|
for content in self.tile_content.iter_mut() {
|
||||||
|
content.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Makes a new map using the algorithm from http://rogueliketutorials.com/tutorials/tcod/part-3/
|
/// Makes a new map using the algorithm from http://rogueliketutorials.com/tutorials/tcod/part-3/
|
||||||
/// This gives a handful of random rooms and corridors joining them together
|
/// This gives a handful of random rooms and corridors joining them together
|
||||||
pub fn new_map_rooms_and_corridors(new_depth: i32) -> Map {
|
pub fn new_map_rooms_and_corridors(new_depth: i32) -> Map {
|
||||||
@ -127,34 +149,6 @@ impl Map {
|
|||||||
|
|
||||||
map
|
map
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_exit_valid(&self, x: i32, y: i32) -> bool {
|
|
||||||
if x < 1 || x > self.width - 1 || y < 1 || y > self.height - 1 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let idx = self.xy_idx(x, y);
|
|
||||||
|
|
||||||
!self.blocked[idx]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn populate_blocked(&mut self) {
|
|
||||||
for (i, tile) in self.tiles.iter_mut().enumerate() {
|
|
||||||
self.blocked[i] = *tile == TileType::Wall;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_content_index(&mut self) {
|
|
||||||
for content in self.tile_content.iter_mut() {
|
|
||||||
content.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Algorithm2D for Map {
|
|
||||||
fn dimensions(&self) -> Point {
|
|
||||||
Point::new(self.width, self.height)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BaseMap for Map {
|
impl BaseMap for Map {
|
||||||
@ -208,6 +202,12 @@ impl BaseMap for Map {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Algorithm2D for Map {
|
||||||
|
fn dimensions(&self) -> Point {
|
||||||
|
Point::new(self.width, self.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn draw_map(ecs: &World, ctx: &mut Rltk) {
|
pub fn draw_map(ecs: &World, ctx: &mut Rltk) {
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ use specs::prelude::*;
|
|||||||
pub struct MeleeCombatSystem {}
|
pub struct MeleeCombatSystem {}
|
||||||
|
|
||||||
impl<'a> System<'a> for MeleeCombatSystem {
|
impl<'a> System<'a> for MeleeCombatSystem {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
WriteExpect<'a, GameLog>,
|
WriteExpect<'a, GameLog>,
|
||||||
|
160
src/player.rs
160
src/player.rs
@ -54,6 +54,86 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_next_level(ecs: &mut World) -> bool {
|
||||||
|
let player_pos = ecs.fetch::<Point>();
|
||||||
|
let map = ecs.fetch::<Map>();
|
||||||
|
let player_idx = map.xy_idx(player_pos.x, player_pos.y);
|
||||||
|
|
||||||
|
if map.tiles[player_idx] == TileType::DownStairs {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
let mut gamelog = ecs.fetch_mut::<GameLog>();
|
||||||
|
gamelog
|
||||||
|
.entries
|
||||||
|
.push("There is no way down from here.".to_string());
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_item(ecs: &mut World) {
|
||||||
|
let player_pos = ecs.fetch::<Point>();
|
||||||
|
let player_entity = ecs.fetch::<Entity>();
|
||||||
|
let entities = ecs.entities();
|
||||||
|
let items = ecs.read_storage::<Item>();
|
||||||
|
let positions = ecs.read_storage::<Position>();
|
||||||
|
let mut gamelog = ecs.fetch_mut::<GameLog>();
|
||||||
|
|
||||||
|
let mut target_item: Option<Entity> = None;
|
||||||
|
for (item_entity, _item, position) in (&entities, &items, &positions).join() {
|
||||||
|
if position.x == player_pos.x && position.y == player_pos.y {
|
||||||
|
target_item = Some(item_entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match target_item {
|
||||||
|
None => gamelog
|
||||||
|
.entries
|
||||||
|
.push("There is nothing here to pick up.".to_string()),
|
||||||
|
Some(item) => {
|
||||||
|
let mut pickup = ecs.write_storage::<WantsToPickupItem>();
|
||||||
|
pickup
|
||||||
|
.insert(
|
||||||
|
*player_entity,
|
||||||
|
WantsToPickupItem {
|
||||||
|
collected_by: *player_entity,
|
||||||
|
item,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("Unable to pick up item?!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip_turn(ecs: &mut World) -> RunState {
|
||||||
|
let player_entity = ecs.fetch::<Entity>();
|
||||||
|
let viewshed_components = ecs.read_storage::<Viewshed>();
|
||||||
|
let monsters = ecs.read_storage::<Monster>();
|
||||||
|
|
||||||
|
let worldmap_resource = ecs.fetch::<Map>();
|
||||||
|
|
||||||
|
let mut can_heal = true;
|
||||||
|
let viewshed = viewshed_components.get(*player_entity).unwrap();
|
||||||
|
for tile in viewshed.visible_tiles.iter() {
|
||||||
|
let idx = worldmap_resource.xy_idx(tile.x, tile.y);
|
||||||
|
for entity_id in worldmap_resource.tile_content[idx].iter() {
|
||||||
|
match monsters.get(*entity_id) {
|
||||||
|
None => {}
|
||||||
|
Some(_) => {
|
||||||
|
can_heal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if can_heal {
|
||||||
|
let mut health_components = ecs.write_storage::<CombatStats>();
|
||||||
|
let player_hp = health_components.get_mut(*player_entity).unwrap();
|
||||||
|
player_hp.hp = i32::min(player_hp.hp + 1, player_hp.max_hp);
|
||||||
|
}
|
||||||
|
|
||||||
|
RunState::PlayerTurn
|
||||||
|
}
|
||||||
|
|
||||||
pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
||||||
// Player movement
|
// Player movement
|
||||||
match ctx.key {
|
match ctx.key {
|
||||||
@ -126,83 +206,3 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
|||||||
|
|
||||||
RunState::PlayerTurn
|
RunState::PlayerTurn
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_item(ecs: &mut World) {
|
|
||||||
let player_pos = ecs.fetch::<Point>();
|
|
||||||
let player_entity = ecs.fetch::<Entity>();
|
|
||||||
let entities = ecs.entities();
|
|
||||||
let items = ecs.read_storage::<Item>();
|
|
||||||
let positions = ecs.read_storage::<Position>();
|
|
||||||
let mut gamelog = ecs.fetch_mut::<GameLog>();
|
|
||||||
|
|
||||||
let mut target_item: Option<Entity> = None;
|
|
||||||
for (item_entity, _item, position) in (&entities, &items, &positions).join() {
|
|
||||||
if position.x == player_pos.x && position.y == player_pos.y {
|
|
||||||
target_item = Some(item_entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match target_item {
|
|
||||||
None => gamelog
|
|
||||||
.entries
|
|
||||||
.push("There is nothing here to pick up.".to_string()),
|
|
||||||
Some(item) => {
|
|
||||||
let mut pickup = ecs.write_storage::<WantsToPickupItem>();
|
|
||||||
pickup
|
|
||||||
.insert(
|
|
||||||
*player_entity,
|
|
||||||
WantsToPickupItem {
|
|
||||||
collected_by: *player_entity,
|
|
||||||
item,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.expect("Unable to pick up item?!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_next_level(ecs: &mut World) -> bool {
|
|
||||||
let player_pos = ecs.fetch::<Point>();
|
|
||||||
let map = ecs.fetch::<Map>();
|
|
||||||
let player_idx = map.xy_idx(player_pos.x, player_pos.y);
|
|
||||||
|
|
||||||
if map.tiles[player_idx] == TileType::DownStairs {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
let mut gamelog = ecs.fetch_mut::<GameLog>();
|
|
||||||
gamelog
|
|
||||||
.entries
|
|
||||||
.push("There is no way down from here.".to_string());
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn skip_turn(ecs: &mut World) -> RunState {
|
|
||||||
let player_entity = ecs.fetch::<Entity>();
|
|
||||||
let viewshed_components = ecs.read_storage::<Viewshed>();
|
|
||||||
let monsters = ecs.read_storage::<Monster>();
|
|
||||||
|
|
||||||
let worldmap_resource = ecs.fetch::<Map>();
|
|
||||||
|
|
||||||
let mut can_heal = true;
|
|
||||||
let viewshed = viewshed_components.get(*player_entity).unwrap();
|
|
||||||
for tile in viewshed.visible_tiles.iter() {
|
|
||||||
let idx = worldmap_resource.xy_idx(tile.x, tile.y);
|
|
||||||
for entity_id in worldmap_resource.tile_content[idx].iter() {
|
|
||||||
match monsters.get(*entity_id) {
|
|
||||||
None => {}
|
|
||||||
Some(_) => {
|
|
||||||
can_heal = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if can_heal {
|
|
||||||
let mut health_components = ecs.write_storage::<CombatStats>();
|
|
||||||
let player_hp = health_components.get_mut(*player_entity).unwrap();
|
|
||||||
player_hp.hp = i32::min(player_hp.hp + 1, player_hp.max_hp);
|
|
||||||
}
|
|
||||||
|
|
||||||
RunState::PlayerTurn
|
|
||||||
}
|
|
||||||
|
@ -22,21 +22,6 @@ macro_rules! serialize_individually {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! deserialize_individually {
|
|
||||||
($ecs:expr, $de:expr, $data:expr, $( $type:ty),*) => {
|
|
||||||
$(
|
|
||||||
DeserializeComponents::<NoError, _>::deserialize(
|
|
||||||
&mut ( &mut $ecs.write_storage::<$type>(), ),
|
|
||||||
&mut $data.0, // entities
|
|
||||||
&mut $data.1, // marker
|
|
||||||
&mut $data.2, // allocater
|
|
||||||
&mut $de,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
)*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub fn save_game(_ecs: &mut World) {}
|
pub fn save_game(_ecs: &mut World) {}
|
||||||
|
|
||||||
@ -102,6 +87,21 @@ pub fn does_save_exist() -> bool {
|
|||||||
Path::new("./savegame.json").exists()
|
Path::new("./savegame.json").exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! deserialize_individually {
|
||||||
|
($ecs:expr, $de:expr, $data:expr, $( $type:ty),*) => {
|
||||||
|
$(
|
||||||
|
DeserializeComponents::<NoError, _>::deserialize(
|
||||||
|
&mut ( &mut $ecs.write_storage::<$type>(), ),
|
||||||
|
&mut $data.0, // entities
|
||||||
|
&mut $data.1, // marker
|
||||||
|
&mut $data.2, // allocater
|
||||||
|
&mut $de,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_game(ecs: &mut World) {
|
pub fn load_game(ecs: &mut World) {
|
||||||
{
|
{
|
||||||
// Delete everything
|
// Delete everything
|
||||||
@ -183,7 +183,7 @@ pub fn load_game(ecs: &mut World) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ecs.delete_entity(deleteme.unwrap())
|
ecs.delete_entity(deleteme.unwrap())
|
||||||
.expect("Unable to delete helper entitiy");
|
.expect("Unable to delete helper entity");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_save() {
|
pub fn delete_save() {
|
||||||
|
@ -3,7 +3,7 @@ use crate::components::{
|
|||||||
Equippable, InflictsDamage, Item, MeleePowerBonus, Monster, Name, Player, Position,
|
Equippable, InflictsDamage, Item, MeleePowerBonus, Monster, Name, Player, Position,
|
||||||
ProvidesHealing, Ranged, Renderable, SerializeMe, Viewshed,
|
ProvidesHealing, Ranged, Renderable, SerializeMe, Viewshed,
|
||||||
};
|
};
|
||||||
use crate::{random_table::RandomTable, Rect, MAP_WIDTH};
|
use crate::{random_table::RandomTable, Rect, map::MAP_WIDTH};
|
||||||
use rltk::{RandomNumberGenerator, RGB};
|
use rltk::{RandomNumberGenerator, RGB};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use specs::saveload::{MarkedBuilder, SimpleMarker};
|
use specs::saveload::{MarkedBuilder, SimpleMarker};
|
||||||
@ -146,6 +146,7 @@ fn health_potion(ecs: &mut World, x: i32, y: i32) {
|
|||||||
.with(Item {})
|
.with(Item {})
|
||||||
.with(Consumable {})
|
.with(Consumable {})
|
||||||
.with(ProvidesHealing { heal_amount: 8 })
|
.with(ProvidesHealing { heal_amount: 8 })
|
||||||
|
.marked::<SimpleMarker<SerializeMe>>()
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +163,7 @@ fn magic_missile_scroll(ecs: &mut World, x: i32, y: i32) {
|
|||||||
.with(Item {})
|
.with(Item {})
|
||||||
.with(Consumable {})
|
.with(Consumable {})
|
||||||
.with(Ranged { range: 6 })
|
.with(Ranged { range: 6 })
|
||||||
.with(InflictsDamage { damage: 8 })
|
.with(InflictsDamage { damage: 20 })
|
||||||
.marked::<SimpleMarker<SerializeMe>>()
|
.marked::<SimpleMarker<SerializeMe>>()
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user