This commit is contained in:
Benjamyn Love 2022-11-29 20:28:10 +11:00
parent 70c46ab9d2
commit 5254209c35
10 changed files with 202 additions and 80 deletions

33
Cargo.lock generated
View File

@ -92,6 +92,7 @@ source = "git+https://github.com/amethyst/bracket-lib#851f6f08675444fb6fa088b9e6
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"parking_lot 0.12.1", "parking_lot 0.12.1",
"serde",
] ]
[[package]] [[package]]
@ -108,6 +109,7 @@ name = "bracket-geometry"
version = "0.8.7" version = "0.8.7"
source = "git+https://github.com/amethyst/bracket-lib#851f6f08675444fb6fa088b9e67bee9fd75554c6" source = "git+https://github.com/amethyst/bracket-lib#851f6f08675444fb6fa088b9e67bee9fd75554c6"
dependencies = [ dependencies = [
"serde",
"ultraviolet", "ultraviolet",
] ]
@ -155,6 +157,7 @@ dependencies = [
"rand", "rand",
"rand_xorshift", "rand_xorshift",
"regex", "regex",
"serde",
"wasm-bindgen", "wasm-bindgen",
] ]
@ -800,6 +803,8 @@ name = "hellorust"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"rltk", "rltk",
"serde",
"serde_json",
"specs", "specs",
"specs-derive", "specs-derive",
] ]
@ -856,6 +861,12 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "itoa"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
[[package]] [[package]]
name = "jni-sys" name = "jni-sys"
version = "0.3.0" version = "0.3.0"
@ -1353,6 +1364,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
dependencies = [ dependencies = [
"rand_core", "rand_core",
"serde",
] ]
[[package]] [[package]]
@ -1431,6 +1443,12 @@ dependencies = [
"bracket-lib", "bracket-lib",
] ]
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]] [[package]]
name = "safe_arch" name = "safe_arch"
version = "0.5.2" version = "0.5.2"
@ -1484,6 +1502,9 @@ name = "serde"
version = "1.0.147" version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
dependencies = [
"serde_derive",
]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
@ -1496,6 +1517,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serde_json"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "servo-fontconfig" name = "servo-fontconfig"
version = "0.5.1" version = "0.5.1"
@ -1620,6 +1652,7 @@ dependencies = [
"hibitset", "hibitset",
"log", "log",
"rayon", "rayon",
"serde",
"shred", "shred",
"shrev", "shrev",
"tuple_utils", "tuple_utils",

View File

@ -6,7 +6,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
rltk = { git = "https://github.com/amethyst/bracket-lib" } rltk = { git = "https://github.com/amethyst/bracket-lib", features= ["serde"] }
# rltk = { version = "0.8.7" } # rltk = { version = "0.8.7" }
specs = "0.18.0" specs = { version = "0.18.0", features= ["serde"]}
specs-derive = "0.4.1" specs-derive = "0.4.1"
serde= { version = "1.0.93", features = ["derive"]}
serde_json = "1.0.39"

View File

@ -2,6 +2,7 @@ use rltk::RGB;
use specs::prelude::*; use specs::prelude::*;
use specs_derive::*; use specs_derive::*;
#[derive(Component)] #[derive(Component)]
pub struct Position { pub struct Position {
pub x: i32, pub x: i32,
@ -34,17 +35,7 @@ pub struct Name {
pub name: String, pub name: String,
} }
#[derive(PartialEq, Copy, Clone)]
pub enum RunState {
AwaitingInput,
PreRun,
PlayerTurn,
MonsterTurn,
ShowInventory,
ShowDropItem,
ShowSpawnMenu,
ShowTargeting { range : i32, item : Entity},
}
#[derive(Component, Debug)] #[derive(Component, Debug)]
pub struct BlocksTile {} pub struct BlocksTile {}
@ -130,4 +121,6 @@ pub struct AreaOfEffect {
#[derive(Component, Debug)] #[derive(Component, Debug)]
pub struct Confusion { pub struct Confusion {
pub turns : i32 pub turns : i32
} }
pub struct SerializeMe;

View File

@ -1,7 +1,13 @@
use rltk::{RGB, Rltk, Point, VirtualKeyCode}; use rltk::{RGB, Rltk, Point, VirtualKeyCode};
use specs::prelude::*; use specs::prelude::*;
use super::{CombatStats, Player, GameLog, Map, Name, Position, State, InBackpack, Viewshed, spawner}; use super::{CombatStats, Player, GameLog, Map, Name, Position, State, InBackpack, Viewshed, spawner, RunState};
#[derive(PartialEq, Copy, Clone)]
pub enum MainMenuSelection { NewGame, LoadGame, Quit }
#[derive(PartialEq, Copy, Clone)]
pub enum MainMenuResult { NoSelection{ selected: MainMenuSelection}, Selected{ selected: MainMenuSelection } }
fn draw_tooltips(ecs: &World, ctx: &mut Rltk) { fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {
let map = ecs.fetch::<Map>(); let map = ecs.fetch::<Map>();
@ -160,7 +166,7 @@ pub fn spawn_item(gs: &mut State, ctx : &mut Rltk) -> (ItemMenuResult, Option<En
match ctx.key { match ctx.key {
None => (ItemMenuResult::NoResponse, None), None => (ItemMenuResult::NoResponse, None),
Some(key) => { Some(key) => {
match (key) { match key {
VirtualKeyCode::Escape => { (ItemMenuResult::Cancel, None)}, VirtualKeyCode::Escape => { (ItemMenuResult::Cancel, None)},
_ => { _ => {
let selection = rltk::letter_to_option(key); let selection = rltk::letter_to_option(key);
@ -208,7 +214,7 @@ pub fn drop_item_menu(gs: &mut State, ctx : &mut Rltk) -> (ItemMenuResult, Optio
match ctx.key { match ctx.key {
None => (ItemMenuResult::NoResponse, None), None => (ItemMenuResult::NoResponse, None),
Some(key) => { Some(key) => {
match (key) { match key {
VirtualKeyCode::Escape => { (ItemMenuResult::Cancel, None)}, VirtualKeyCode::Escape => { (ItemMenuResult::Cancel, None)},
_ => { _ => {
let selection = rltk::letter_to_option(key); let selection = rltk::letter_to_option(key);
@ -263,4 +269,62 @@ pub fn ranged_target(gs: &mut State, ctx : &mut Rltk, range: i32) -> (ItemMenuRe
} }
(ItemMenuResult::NoResponse, None) (ItemMenuResult::NoResponse, None)
}
pub fn main_menu(gs: &mut State, ctx : &mut Rltk) -> MainMenuResult {
let runstate = gs.ecs.fetch::<RunState>();
ctx.print_color_centered(15, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "Rust Roguelike Tutorial");
if let RunState::MainMenu{ menu_selection : selection } = *runstate {
if selection == MainMenuSelection::NewGame {
ctx.print_color_centered(24, RGB::named(rltk::MAGENTA), RGB::named(rltk::BLACK), "Start a new game");
} else {
ctx.print_color_centered(24, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "Start a new game");
}
if selection == MainMenuSelection::LoadGame {
ctx.print_color_centered(25, RGB::named(rltk::MAGENTA), RGB::named(rltk::BLACK), "Load a save");
} else {
ctx.print_color_centered(25, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "Load a save");
}
if selection == MainMenuSelection::Quit {
ctx.print_color_centered(26, RGB::named(rltk::MAGENTA), RGB::named(rltk::BLACK), "Quit the game");
} else {
ctx.print_color_centered(26, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "Quit the game");
}
match ctx.key {
None => return MainMenuResult::NoSelection { selected: selection },
Some(key) => {
match key {
VirtualKeyCode::Escape => {return MainMenuResult::NoSelection { selected: MainMenuSelection::Quit }},
VirtualKeyCode::Up => {
let newselection;
match selection {
MainMenuSelection::NewGame => newselection = MainMenuSelection::Quit,
MainMenuSelection::LoadGame => newselection = MainMenuSelection::NewGame,
MainMenuSelection::Quit => newselection = MainMenuSelection::LoadGame
}
return MainMenuResult::NoSelection { selected: newselection }
}
VirtualKeyCode::Down => {
let newselection;
match selection {
MainMenuSelection::NewGame => newselection = MainMenuSelection::LoadGame,
MainMenuSelection::LoadGame => newselection = MainMenuSelection::Quit,
MainMenuSelection::Quit => newselection = MainMenuSelection::NewGame
}
return MainMenuResult::NoSelection { selected: newselection }
}
VirtualKeyCode::Return => {return MainMenuResult::Selected { selected: selection }}
_ => return MainMenuResult::NoSelection { selected: selection }
}
}
}
}
MainMenuResult::NoSelection { selected: MainMenuSelection::NewGame }
} }

View File

@ -1,7 +1,5 @@
use specs::prelude::*; use specs::prelude::*;
use crate::player;
use super::{ use super::{
gamelog::GameLog, CombatStats, Consumable, InBackpack, Name, Position, ProvidesHealing, gamelog::GameLog, CombatStats, Consumable, InBackpack, Name, Position, ProvidesHealing,
WantsToDropItem, WantsToPickupItem, WantsToUseItem, InflictsDamage, Map, SufferDamage, AreaOfEffect, Confusion WantsToDropItem, WantsToPickupItem, WantsToUseItem, InflictsDamage, Map, SufferDamage, AreaOfEffect, Confusion

View File

@ -1,5 +1,8 @@
use rltk::{GameState, Point, Rltk, RGB}; use rltk::{GameState, Point, Rltk};
use specs::prelude::*; use specs::prelude::*;
use specs::saveload::{SimpleMarker, SimpleMarkerAllocator};
extern crate serde;
mod components; mod components;
pub use components::*; pub use components::*;
@ -35,11 +38,25 @@ mod gamelog;
pub use gamelog::*; pub use gamelog::*;
mod spawner; mod spawner;
use spawner::*;
mod inventory_system; mod inventory_system;
use inventory_system::*; use inventory_system::*;
#[derive(PartialEq, Copy, Clone)]
pub enum RunState {
AwaitingInput,
PreRun,
PlayerTurn,
MonsterTurn,
ShowInventory,
ShowDropItem,
ShowSpawnMenu,
ShowTargeting { range : i32, item : Entity},
MainMenu { menu_selection : gui::MainMenuSelection },
SaveGame,
}
pub struct State { pub struct State {
pub ecs: World, pub ecs: World,
} }
@ -68,39 +85,37 @@ impl State {
impl GameState for State { impl GameState for State {
fn tick(&mut self, ctx: &mut Rltk) { fn tick(&mut self, ctx: &mut Rltk) {
ctx.cls();
draw_map(&self.ecs, ctx);
{
let positions = self.ecs.read_storage::<Position>();
let renderables = self.ecs.read_storage::<Renderable>();
let map = self.ecs.fetch::<Map>();
let mut data = (&positions, &renderables).join().collect::<Vec<_>>();
data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order));
for (pos, render) in data.iter() {
let idx = map.xy_idx(pos.x, pos.y);
if map.visible_tiles[idx] {
ctx.set(pos.x, pos.y, render.fg, render.bg, render.glyph)
}
}
// for (pos, render) in (&positions, &renderables).join() {
// let idx = map.xy_idx(pos.x, pos.y);
// if map.visible_tiles[idx] {
// ctx.set(pos.x, pos.y, render.fg, render.bg, render.glyph)
// }
// }
gui::draw_ui(&self.ecs, ctx);
}
let mut newrunstate; let mut newrunstate;
{ {
let runstate = self.ecs.fetch::<RunState>(); let runstate = self.ecs.fetch::<RunState>();
newrunstate = *runstate; newrunstate = *runstate;
} }
ctx.cls();
match newrunstate {
RunState::MainMenu { .. } => {}
_ => {
draw_map(&self.ecs, ctx);
{
let positions = self.ecs.read_storage::<Position>();
let renderables = self.ecs.read_storage::<Renderable>();
let map = self.ecs.fetch::<Map>();
let mut data = (&positions, &renderables).join().collect::<Vec<_>>();
data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order));
for (pos, render) in data.iter() {
let idx = map.xy_idx(pos.x, pos.y);
if map.visible_tiles[idx] {
ctx.set(pos.x, pos.y, render.fg, render.bg, render.glyph)
}
}
gui::draw_ui(&self.ecs, ctx);
}
}
}
match newrunstate { match newrunstate {
RunState::PreRun => { RunState::PreRun => {
self.run_systems(); self.run_systems();
@ -130,7 +145,10 @@ impl GameState for State {
let is_ranged = self.ecs.read_storage::<Ranged>(); let is_ranged = self.ecs.read_storage::<Ranged>();
let is_item_ranged = is_ranged.get(item_entity); let is_item_ranged = is_ranged.get(item_entity);
if let Some(is_item_ranged) = is_item_ranged { if let Some(is_item_ranged) = is_item_ranged {
newrunstate = RunState::ShowTargeting { range: is_item_ranged.range, item: item_entity }; newrunstate = RunState::ShowTargeting {
range: is_item_ranged.range,
item: item_entity,
};
} else { } else {
let mut intent = self.ecs.write_storage::<WantsToUseItem>(); let mut intent = self.ecs.write_storage::<WantsToUseItem>();
intent intent
@ -144,25 +162,6 @@ impl GameState for State {
.expect("Failed to insert intent"); .expect("Failed to insert intent");
newrunstate = RunState::PlayerTurn; newrunstate = RunState::PlayerTurn;
} }
}
}
}
RunState::ShowDropItem => {
let result = gui::drop_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::<WantsToDropItem>();
intent
.insert(
*self.ecs.fetch::<Entity>(),
WantsToDropItem { item: item_entity },
)
.expect("Unable to insert intent");
newrunstate = RunState::PlayerTurn;
} }
} }
} }
@ -191,20 +190,46 @@ impl GameState for State {
gui::ItemMenuResult::NoResponse => {} gui::ItemMenuResult::NoResponse => {}
gui::ItemMenuResult::Selected => { gui::ItemMenuResult::Selected => {
let mut intent = self.ecs.write_storage::<WantsToUseItem>(); let mut intent = self.ecs.write_storage::<WantsToUseItem>();
intent.insert(*self.ecs.fetch::<Entity>(), WantsToUseItem { item, target: result.1 }).expect("Unable to insert intent"); intent
.insert(
*self.ecs.fetch::<Entity>(),
WantsToUseItem {
item,
target: result.1,
},
)
.expect("Unable to insert intent");
newrunstate = RunState::PlayerTurn; newrunstate = RunState::PlayerTurn;
} }
} }
} }
RunState::ShowSpawnMenu => { RunState::ShowSpawnMenu => {
let result = gui::spawn_item(self, ctx); let result = gui::spawn_item(self, ctx);
match result.0 { match result.0 {
gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput, gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput,
gui::ItemMenuResult::NoResponse => {} gui::ItemMenuResult::NoResponse => {}
gui::ItemMenuResult::Selected => {newrunstate = RunState::AwaitingInput;} gui::ItemMenuResult::Selected => {
newrunstate = RunState::AwaitingInput;
}
} }
}
RunState::MainMenu { .. } => {
let result = gui::main_menu(self, ctx);
match result {
gui::MainMenuResult::NoSelection { selected } => newrunstate = RunState::MainMenu{menu_selection: selected},
gui::MainMenuResult::Selected { selected } => {
match selected {
gui::MainMenuSelection::NewGame => newrunstate = RunState::PreRun,
gui::MainMenuSelection::LoadGame => newrunstate = RunState::PreRun,
gui::MainMenuSelection::Quit => { ::std::process::exit(0)}
}
}
}
}
RunState::SaveGame => {
let data = serde_json::to_string(&*self.ecs.fetch::<Map>()).unwrap();
println!("{}", data);
newrunstate = RunState::MainMenu { menu_selection: gui::MainMenuSelection::LoadGame };
} }
} }
@ -245,6 +270,7 @@ fn main() -> rltk::BError {
gs.ecs.register::<InflictsDamage>(); gs.ecs.register::<InflictsDamage>();
gs.ecs.register::<AreaOfEffect>(); gs.ecs.register::<AreaOfEffect>();
gs.ecs.register::<Confusion>(); gs.ecs.register::<Confusion>();
gs.ecs.register::<SimpleMarker<SerializeMe>>();
let map = Map::new_map_rooms_and_corridors(); let map = Map::new_map_rooms_and_corridors();
let (player_x, player_y) = map.rooms[0].center(); let (player_x, player_y) = map.rooms[0].center();
@ -260,10 +286,11 @@ fn main() -> rltk::BError {
gs.ecs.insert(map); gs.ecs.insert(map);
gs.ecs.insert(player_entity); gs.ecs.insert(player_entity);
gs.ecs.insert(RunState::PreRun); gs.ecs.insert(RunState::MainMenu { menu_selection: MainMenuSelection::NewGame });
gs.ecs.insert(gamelog::GameLog { gs.ecs.insert(gamelog::GameLog {
entries: vec!["Welcome to my rougelike".to_string()], entries: vec!["Welcome to my rougelike".to_string()],
}); });
gs.ecs.insert(SimpleMarkerAllocator::<SerializeMe>::new());
// Main loop runner // Main loop runner
rltk::main_loop(context, gs) rltk::main_loop(context, gs)

View File

@ -2,18 +2,19 @@ use super::Rect;
use rltk::{Algorithm2D, BaseMap, Point, RandomNumberGenerator, Rltk, RGB}; use rltk::{Algorithm2D, BaseMap, Point, RandomNumberGenerator, Rltk, RGB};
use specs::prelude::*; use specs::prelude::*;
use std::cmp::{max, min}; use std::cmp::{max, min};
use serde::{Serialize, Deserialize};
pub const MAPWIDTH : usize = 80; pub const MAPWIDTH : usize = 80;
pub const MAPHIEGHT : usize = 43; pub const MAPHIEGHT : usize = 43;
pub const MAPCOUNT : usize = MAPHIEGHT * MAPWIDTH; pub const MAPCOUNT : usize = MAPHIEGHT * MAPWIDTH;
#[derive(PartialEq, Copy, Clone)] #[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
pub enum TileType { pub enum TileType {
Wall, Wall,
Floor, Floor,
} }
#[derive(Default)] #[derive(Default, Serialize, Deserialize, Clone)]
pub struct Map { pub struct Map {
pub tiles: Vec<TileType>, pub tiles: Vec<TileType>,
pub rooms: Vec<Rect>, pub rooms: Vec<Rect>,
@ -22,6 +23,9 @@ pub struct Map {
pub revealed_tiles: Vec<bool>, pub revealed_tiles: Vec<bool>,
pub visible_tiles: Vec<bool>, pub visible_tiles: Vec<bool>,
pub blocked : Vec<bool>, pub blocked : Vec<bool>,
#[serde(skip_serializing)]
#[serde(skip_deserializing)]
pub tile_content : Vec<Vec<Entity>> pub tile_content : Vec<Vec<Entity>>
} }

View File

@ -1,6 +1,6 @@
use specs::prelude::*; use specs::prelude::*;
use super::{CombatStats, WantsToMelee, Name, SufferDamage, GameLog}; use super::{CombatStats, WantsToMelee, Name, SufferDamage, GameLog};
use rltk::console;
pub struct MeleeCombatSystem {} pub struct MeleeCombatSystem {}

View File

@ -1,5 +1,3 @@
use crate::spawn_item;
use super::{Map, Player, Position, RunState, State, Viewshed, CombatStats, WantsToMelee, Item, GameLog, WantsToPickupItem, spawner}; use super::{Map, Player, Position, RunState, State, Viewshed, CombatStats, WantsToMelee, Item, GameLog, WantsToPickupItem, spawner};
use rltk::{Point, Rltk, VirtualKeyCode}; use rltk::{Point, Rltk, VirtualKeyCode};
use specs::prelude::*; use specs::prelude::*;
@ -7,7 +5,7 @@ use std::cmp::{max, min};
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) { pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) {
let mut positions = ecs.write_storage::<Position>(); let mut positions = ecs.write_storage::<Position>();
let mut players = ecs.write_storage::<Player>(); let players = ecs.write_storage::<Player>();
let mut viewsheds = ecs.write_storage::<Viewshed>(); let mut viewsheds = ecs.write_storage::<Viewshed>();
let mut ppos = ecs.write_resource::<Point>(); let mut ppos = ecs.write_resource::<Point>();
let combat_stats = ecs.read_storage::<CombatStats>(); let combat_stats = ecs.read_storage::<CombatStats>();
@ -95,6 +93,7 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
spawner::health_potion(&mut gs.ecs, mouse_pos.0, mouse_pos.1); spawner::health_potion(&mut gs.ecs, mouse_pos.0, mouse_pos.1);
return RunState::AwaitingInput; return RunState::AwaitingInput;
} }
VirtualKeyCode::Escape => return RunState::SaveGame,
_ => return RunState::AwaitingInput, _ => return RunState::AwaitingInput,
}, },
} }

View File

@ -1,3 +1,5 @@
use serde::{Serialize, Deserialize};
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
pub struct Rect { pub struct Rect {
pub x1: i32, pub x1: i32,
pub x2: i32, pub x2: i32,