Move state management out of main file
This commit is contained in:
parent
878ee5b480
commit
485151e37c
521
src/main.rs
521
src/main.rs
@ -23,32 +23,21 @@ mod rex_assets;
|
||||
pub mod saveload_system;
|
||||
mod spatial;
|
||||
mod spawner;
|
||||
mod state;
|
||||
mod trigger_system;
|
||||
mod visibility_system;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use ::rltk::{GameState, Point, RandomNumberGenerator, Rltk};
|
||||
use ::rltk::{Point, RandomNumberGenerator};
|
||||
use ::specs::prelude::*;
|
||||
use ::specs::saveload::{SimpleMarker, SimpleMarkerAllocator};
|
||||
use components::*;
|
||||
use damage_system::DamageSystem;
|
||||
pub use game_log::GameLog;
|
||||
use gui::{show_cheat_mode, CheatMenuResult};
|
||||
use hunger_system::HungerSystem;
|
||||
use inventory_system::{ItemCollectionSystem, ItemDropSystem, ItemRemoveSystem, ItemUseSystem};
|
||||
use lighting_system::LightingSystem;
|
||||
pub use map::*;
|
||||
use map_indexing_system::MapIndexingSystem;
|
||||
use melee_combat_system::MeleeCombatSystem;
|
||||
use movement_system::MovementSystem;
|
||||
use particle_system::ParticleSpawnSystem;
|
||||
use player::*;
|
||||
use raws::*;
|
||||
pub use rect::Rect;
|
||||
use trigger_system::TriggerSystem;
|
||||
use visibility_system::VisibilitySystem;
|
||||
pub use state::*;
|
||||
|
||||
/// Cut down on the amount of syntax to register components
|
||||
macro_rules! register {
|
||||
@ -61,510 +50,6 @@ macro_rules! register {
|
||||
}
|
||||
}
|
||||
|
||||
const SHOW_MAPGEN_VISUALIZER: bool = false;
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum VendorMode {
|
||||
Buy,
|
||||
Sell,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum RunState {
|
||||
AwaitingInput,
|
||||
PreRun,
|
||||
Ticking,
|
||||
ShowInventory,
|
||||
ShowDropItem,
|
||||
ShowTargeting {
|
||||
range: i32,
|
||||
item: Entity,
|
||||
},
|
||||
MainMenu {
|
||||
menu_selection: gui::MainMenuSelection,
|
||||
},
|
||||
SaveGame,
|
||||
NextLevel,
|
||||
PreviousLevel,
|
||||
TownPortal,
|
||||
ShowRemoveItem,
|
||||
GameOver,
|
||||
MagicMapReveal {
|
||||
row: i32,
|
||||
},
|
||||
MapGeneration,
|
||||
ShowCheatMenu,
|
||||
ShowVendor {
|
||||
vendor: Entity,
|
||||
mode: VendorMode,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
pub ecs: World,
|
||||
mapgen_next_state: Option<RunState>,
|
||||
mapgen_history: Vec<Map>,
|
||||
mapgen_index: usize,
|
||||
mapgen_timer: f32,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn new() -> Self {
|
||||
State {
|
||||
ecs: World::new(),
|
||||
mapgen_next_state: Some(RunState::MainMenu {
|
||||
menu_selection: gui::MainMenuSelection::NewGame,
|
||||
}),
|
||||
mapgen_index: 0,
|
||||
mapgen_history: Vec::new(),
|
||||
mapgen_timer: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
fn run_systems(&mut self) {
|
||||
let mut mapindex = MapIndexingSystem {};
|
||||
mapindex.run_now(&self.ecs);
|
||||
|
||||
let mut vis = VisibilitySystem {};
|
||||
vis.run_now(&self.ecs);
|
||||
|
||||
let mut encumbrance = ai::EncumbranceSystem {};
|
||||
encumbrance.run_now(&self.ecs);
|
||||
|
||||
let mut initiative = ai::InitiativeSystem {};
|
||||
initiative.run_now(&self.ecs);
|
||||
|
||||
let mut turnstatus = ai::TurnStatusSystem {};
|
||||
turnstatus.run_now(&self.ecs);
|
||||
|
||||
let mut quipper = ai::QuipSystem {};
|
||||
quipper.run_now(&self.ecs);
|
||||
|
||||
let mut adjacent = ai::AdjacentAI {};
|
||||
adjacent.run_now(&self.ecs);
|
||||
|
||||
let mut visible = ai::VisibleAI {};
|
||||
visible.run_now(&self.ecs);
|
||||
|
||||
let mut approach = ai::ApproachAI {};
|
||||
approach.run_now(&self.ecs);
|
||||
|
||||
let mut flee = ai::FleeAI {};
|
||||
flee.run_now(&self.ecs);
|
||||
|
||||
let mut chase = ai::ChaseAI {};
|
||||
chase.run_now(&self.ecs);
|
||||
|
||||
let mut defaultmove = ai::DefaultMoveAI {};
|
||||
defaultmove.run_now(&self.ecs);
|
||||
|
||||
let mut moving = MovementSystem {};
|
||||
moving.run_now(&self.ecs);
|
||||
|
||||
let mut triggers = TriggerSystem {};
|
||||
triggers.run_now(&self.ecs);
|
||||
|
||||
let mut melee = MeleeCombatSystem {};
|
||||
melee.run_now(&self.ecs);
|
||||
|
||||
let mut damage = DamageSystem {};
|
||||
damage.run_now(&self.ecs);
|
||||
|
||||
let mut pickup = ItemCollectionSystem {};
|
||||
pickup.run_now(&self.ecs);
|
||||
|
||||
let mut items = ItemUseSystem {};
|
||||
items.run_now(&self.ecs);
|
||||
|
||||
let mut drop_items = ItemDropSystem {};
|
||||
drop_items.run_now(&self.ecs);
|
||||
|
||||
let mut item_remove = ItemRemoveSystem {};
|
||||
item_remove.run_now(&self.ecs);
|
||||
|
||||
let mut hunger = HungerSystem {};
|
||||
hunger.run_now(&self.ecs);
|
||||
|
||||
let mut particles = ParticleSpawnSystem {};
|
||||
particles.run_now(&self.ecs);
|
||||
|
||||
let mut lighting = LightingSystem {};
|
||||
lighting.run_now(&self.ecs);
|
||||
|
||||
self.ecs.maintain();
|
||||
}
|
||||
}
|
||||
|
||||
impl GameState for State {
|
||||
fn tick(&mut self, ctx: &mut Rltk) {
|
||||
let mut newrunstate;
|
||||
{
|
||||
let runstate = self.ecs.fetch::<RunState>();
|
||||
newrunstate = *runstate;
|
||||
}
|
||||
|
||||
ctx.cls();
|
||||
particle_system::cull_dead_particles(&mut self.ecs, ctx);
|
||||
|
||||
match newrunstate {
|
||||
RunState::MainMenu { .. } => {}
|
||||
RunState::GameOver { .. } => {}
|
||||
_ => {
|
||||
camera::render_camera(&self.ecs, ctx);
|
||||
gui::draw_ui(&self.ecs, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
match newrunstate {
|
||||
RunState::MapGeneration => {
|
||||
if !SHOW_MAPGEN_VISUALIZER {
|
||||
newrunstate = self.mapgen_next_state.unwrap();
|
||||
}
|
||||
ctx.cls();
|
||||
if self.mapgen_index < self.mapgen_history.len() {
|
||||
camera::render_debug_map(&self.mapgen_history[self.mapgen_index], ctx);
|
||||
}
|
||||
|
||||
self.mapgen_timer += ctx.frame_time_ms;
|
||||
if self.mapgen_timer > 300.0 {
|
||||
self.mapgen_timer = 0.0;
|
||||
self.mapgen_index += 1;
|
||||
if self.mapgen_index >= self.mapgen_history.len() {
|
||||
newrunstate = self.mapgen_next_state.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
RunState::PreRun => {
|
||||
self.run_systems();
|
||||
self.ecs.maintain();
|
||||
newrunstate = RunState::AwaitingInput;
|
||||
}
|
||||
RunState::AwaitingInput => {
|
||||
newrunstate = player_input(self, ctx);
|
||||
}
|
||||
RunState::Ticking => {
|
||||
while newrunstate == RunState::Ticking {
|
||||
self.run_systems();
|
||||
self.ecs.maintain();
|
||||
|
||||
newrunstate = match *self.ecs.fetch::<RunState>() {
|
||||
RunState::AwaitingInput => RunState::AwaitingInput,
|
||||
RunState::MagicMapReveal { .. } => RunState::MagicMapReveal { row: 0 },
|
||||
RunState::TownPortal => RunState::TownPortal,
|
||||
_ => RunState::Ticking,
|
||||
};
|
||||
}
|
||||
}
|
||||
RunState::ShowInventory => {
|
||||
let result = gui::show_inventory(self, ctx);
|
||||
match result.0 {
|
||||
gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput,
|
||||
gui::ItemMenuResult::NoResponse => {}
|
||||
gui::ItemMenuResult::Selected => {
|
||||
let item_entity = result.1.unwrap();
|
||||
let is_ranged = self.ecs.read_storage::<Ranged>();
|
||||
|
||||
if let Some(is_item_ranged) = is_ranged.get(item_entity) {
|
||||
newrunstate = RunState::ShowTargeting {
|
||||
range: is_item_ranged.range,
|
||||
item: item_entity,
|
||||
};
|
||||
} else {
|
||||
let mut intent = self.ecs.write_storage::<WantsToUseItem>();
|
||||
intent
|
||||
.insert(
|
||||
*self.ecs.fetch::<Entity>(),
|
||||
WantsToUseItem {
|
||||
item: item_entity,
|
||||
target: None,
|
||||
},
|
||||
)
|
||||
.expect("failed to add intent to use item");
|
||||
|
||||
newrunstate = RunState::Ticking;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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("failed to add intent to drop item");
|
||||
|
||||
newrunstate = RunState::Ticking;
|
||||
}
|
||||
}
|
||||
}
|
||||
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::Ticking;
|
||||
}
|
||||
}
|
||||
}
|
||||
RunState::ShowTargeting { range, item } => {
|
||||
let result = gui::ranged_target(self, ctx, range);
|
||||
match result.0 {
|
||||
gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput,
|
||||
gui::ItemMenuResult::NoResponse => {}
|
||||
gui::ItemMenuResult::Selected => {
|
||||
let mut intent = self.ecs.write_storage::<WantsToUseItem>();
|
||||
|
||||
intent
|
||||
.insert(
|
||||
*self.ecs.fetch::<Entity>(),
|
||||
WantsToUseItem {
|
||||
item,
|
||||
target: result.1,
|
||||
},
|
||||
)
|
||||
.expect("failed to add intent to use item");
|
||||
|
||||
newrunstate = RunState::Ticking;
|
||||
}
|
||||
}
|
||||
}
|
||||
RunState::MainMenu { .. } => match gui::main_menu(self, ctx) {
|
||||
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 => {
|
||||
saveload_system::load_game(&mut self.ecs);
|
||||
newrunstate = RunState::AwaitingInput;
|
||||
saveload_system::delete_save();
|
||||
}
|
||||
gui::MainMenuSelection::Quit => {
|
||||
::std::process::exit(0);
|
||||
}
|
||||
},
|
||||
},
|
||||
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 => {
|
||||
saveload_system::save_game(&mut self.ecs);
|
||||
|
||||
newrunstate = RunState::MainMenu {
|
||||
menu_selection: gui::MainMenuSelection::LoadGame,
|
||||
}
|
||||
}
|
||||
RunState::NextLevel => {
|
||||
self.goto_level(1);
|
||||
newrunstate = RunState::PreRun;
|
||||
}
|
||||
RunState::PreviousLevel => {
|
||||
self.goto_level(-1);
|
||||
self.mapgen_next_state = Some(RunState::PreRun);
|
||||
newrunstate = RunState::MapGeneration;
|
||||
}
|
||||
RunState::MagicMapReveal { row } => {
|
||||
let mut map = self.ecs.fetch_mut::<Map>();
|
||||
for x in 0..map.width {
|
||||
let idx = map.xy_idx(x as i32, row);
|
||||
map.revealed_tiles[idx] = true;
|
||||
}
|
||||
|
||||
if row == map.height - 1 {
|
||||
newrunstate = RunState::Ticking;
|
||||
} else {
|
||||
newrunstate = RunState::MagicMapReveal { row: row + 1 };
|
||||
}
|
||||
}
|
||||
RunState::ShowCheatMenu => match show_cheat_mode(self, ctx) {
|
||||
CheatMenuResult::Cancel => newrunstate = RunState::AwaitingInput,
|
||||
CheatMenuResult::NoResponse => {}
|
||||
CheatMenuResult::TeleportToExit => {
|
||||
self.goto_level(1);
|
||||
self.mapgen_next_state = Some(RunState::PreRun);
|
||||
|
||||
newrunstate = RunState::MapGeneration
|
||||
}
|
||||
CheatMenuResult::Heal => {
|
||||
let player = self.ecs.fetch::<Entity>();
|
||||
let mut pools = self.ecs.write_storage::<Pools>();
|
||||
let mut player_pools = pools.get_mut(*player).unwrap();
|
||||
player_pools.hit_points.current = player_pools.hit_points.max;
|
||||
|
||||
newrunstate = RunState::AwaitingInput;
|
||||
}
|
||||
CheatMenuResult::Reveal => {
|
||||
let mut map = self.ecs.fetch_mut::<Map>();
|
||||
for v in map.revealed_tiles.iter_mut() {
|
||||
*v = true;
|
||||
}
|
||||
|
||||
newrunstate = RunState::AwaitingInput;
|
||||
}
|
||||
CheatMenuResult::GodMode => {
|
||||
let player = self.ecs.fetch::<Entity>();
|
||||
let mut pools = self.ecs.write_storage::<Pools>();
|
||||
let mut player_pools = pools.get_mut(*player).unwrap();
|
||||
player_pools.god_mode = true;
|
||||
|
||||
newrunstate = RunState::AwaitingInput;
|
||||
}
|
||||
},
|
||||
RunState::ShowVendor { vendor, mode } => {
|
||||
let result = gui::show_vendor_menu(self, ctx, vendor, mode);
|
||||
match result.0 {
|
||||
gui::VendorResult::Cancel => newrunstate = RunState::AwaitingInput,
|
||||
gui::VendorResult::NoResponse => {}
|
||||
gui::VendorResult::Sell => {
|
||||
let price = self
|
||||
.ecs
|
||||
.read_storage::<Item>()
|
||||
.get(result.1.unwrap())
|
||||
.unwrap()
|
||||
.base_value
|
||||
* 0.8;
|
||||
self.ecs
|
||||
.write_storage::<Pools>()
|
||||
.get_mut(*self.ecs.fetch::<Entity>())
|
||||
.unwrap()
|
||||
.gold += price;
|
||||
self.ecs
|
||||
.delete_entity(result.1.unwrap())
|
||||
.expect("Unable to delete sold item");
|
||||
}
|
||||
gui::VendorResult::Buy => {
|
||||
let tag = result.2.unwrap();
|
||||
let price = result.3.unwrap();
|
||||
let mut pools = self.ecs.write_storage::<Pools>();
|
||||
let player_pools = pools.get_mut(*self.ecs.fetch::<Entity>()).unwrap();
|
||||
if player_pools.gold >= price {
|
||||
player_pools.gold -= price;
|
||||
std::mem::drop(pools);
|
||||
|
||||
let player_entity = *self.ecs.fetch::<Entity>();
|
||||
spawn_named_item(
|
||||
&RAWS.lock().unwrap(),
|
||||
&mut self.ecs,
|
||||
&tag,
|
||||
SpawnType::Carried { by: player_entity },
|
||||
);
|
||||
}
|
||||
}
|
||||
gui::VendorResult::BuyMode => {
|
||||
newrunstate = RunState::ShowVendor {
|
||||
vendor,
|
||||
mode: VendorMode::Buy,
|
||||
}
|
||||
}
|
||||
gui::VendorResult::SellMode => {
|
||||
newrunstate = RunState::ShowVendor {
|
||||
vendor,
|
||||
mode: VendorMode::Sell,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RunState::TownPortal => {
|
||||
// Spawn the portal
|
||||
spawner::spawn_town_portal(&mut self.ecs);
|
||||
|
||||
// Transition
|
||||
let map_depth = self.ecs.fetch::<Map>().depth;
|
||||
let destination_offset = 0 - (map_depth - 1);
|
||||
self.goto_level(destination_offset);
|
||||
self.mapgen_next_state = Some(RunState::PreRun);
|
||||
|
||||
newrunstate = RunState::MapGeneration;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut runwriter = self.ecs.write_resource::<RunState>();
|
||||
*runwriter = newrunstate;
|
||||
}
|
||||
|
||||
damage_system::delete_the_dead(&mut self.ecs);
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn goto_level(&mut self, offset: i32) {
|
||||
freeze_level_entities(&mut self.ecs);
|
||||
|
||||
// Build a new map and place the player
|
||||
let current_depth = self.ecs.fetch::<Map>().depth;
|
||||
self.generate_world_map(current_depth + offset, offset);
|
||||
|
||||
// Notify the player
|
||||
let mut gamelog = self.ecs.fetch_mut::<GameLog>();
|
||||
gamelog.append("You change level.");
|
||||
}
|
||||
|
||||
fn game_over_cleanup(&mut self) {
|
||||
// Delete everything
|
||||
let mut to_delete = Vec::new();
|
||||
for e in self.ecs.entities().join() {
|
||||
to_delete.push(e);
|
||||
}
|
||||
for del in to_delete.iter() {
|
||||
self.ecs
|
||||
.delete_entity(*del)
|
||||
.expect("Failed to delete entity");
|
||||
}
|
||||
|
||||
// Spawn a new player
|
||||
{
|
||||
let player_entity = spawner::player(&mut self.ecs, 0, 0);
|
||||
let mut player_entity_writer = self.ecs.write_resource::<Entity>();
|
||||
*player_entity_writer = player_entity;
|
||||
}
|
||||
|
||||
// Replace the world maps
|
||||
self.ecs.insert(map::MasterDungeonMap::new());
|
||||
|
||||
// Build a new map and place the player
|
||||
self.generate_world_map(1, 0);
|
||||
}
|
||||
|
||||
fn generate_world_map(&mut self, new_depth: i32, offset: i32) {
|
||||
self.mapgen_index = 0;
|
||||
self.mapgen_timer = 0.0;
|
||||
self.mapgen_history.clear();
|
||||
|
||||
if let Some(history) = map::level_transition(&mut self.ecs, new_depth, offset) {
|
||||
self.mapgen_history = history;
|
||||
} else {
|
||||
map::thaw_level_entities(&mut self.ecs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> ::rltk::BError {
|
||||
let context = ::rltk::RltkBuilder::simple(80, 60)
|
||||
.unwrap()
|
||||
|
533
src/state.rs
Normal file
533
src/state.rs
Normal file
@ -0,0 +1,533 @@
|
||||
use ::rltk::{GameState, Point, Rltk};
|
||||
use ::specs::prelude::*;
|
||||
|
||||
use crate::components::*;
|
||||
use crate::damage_system::{self, DamageSystem};
|
||||
use crate::game_log::GameLog;
|
||||
use crate::gui::{self, show_cheat_mode, CheatMenuResult, MainMenuSelection};
|
||||
use crate::hunger_system::HungerSystem;
|
||||
use crate::inventory_system::{
|
||||
ItemCollectionSystem, ItemDropSystem, ItemRemoveSystem, ItemUseSystem,
|
||||
};
|
||||
use crate::lighting_system::LightingSystem;
|
||||
use crate::map::{self, *};
|
||||
use crate::map_indexing_system::MapIndexingSystem;
|
||||
use crate::melee_combat_system::MeleeCombatSystem;
|
||||
use crate::movement_system::MovementSystem;
|
||||
use crate::particle_system::{self, ParticleSpawnSystem};
|
||||
use crate::player::*;
|
||||
use crate::raws::*;
|
||||
use crate::trigger_system::TriggerSystem;
|
||||
use crate::visibility_system::VisibilitySystem;
|
||||
use crate::{ai, camera, saveload_system, spawner};
|
||||
|
||||
pub const SHOW_MAPGEN_VISUALIZER: bool = false;
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum VendorMode {
|
||||
Buy,
|
||||
Sell,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum RunState {
|
||||
AwaitingInput,
|
||||
PreRun,
|
||||
Ticking,
|
||||
ShowInventory,
|
||||
ShowDropItem,
|
||||
ShowTargeting { range: i32, item: Entity },
|
||||
MainMenu { menu_selection: MainMenuSelection },
|
||||
SaveGame,
|
||||
NextLevel,
|
||||
PreviousLevel,
|
||||
TownPortal,
|
||||
ShowRemoveItem,
|
||||
GameOver,
|
||||
MagicMapReveal { row: i32 },
|
||||
MapGeneration,
|
||||
ShowCheatMenu,
|
||||
ShowVendor { vendor: Entity, mode: VendorMode },
|
||||
TeleportingToOtherLevel { x: i32, y: i32, depth: i32 },
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
pub ecs: World,
|
||||
mapgen_next_state: Option<RunState>,
|
||||
mapgen_history: Vec<Map>,
|
||||
mapgen_index: usize,
|
||||
mapgen_timer: f32,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
State {
|
||||
ecs: World::new(),
|
||||
mapgen_next_state: Some(RunState::MainMenu {
|
||||
menu_selection: MainMenuSelection::NewGame,
|
||||
}),
|
||||
mapgen_index: 0,
|
||||
mapgen_history: Vec::new(),
|
||||
mapgen_timer: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
fn run_systems(&mut self) {
|
||||
let mut mapindex = MapIndexingSystem {};
|
||||
mapindex.run_now(&self.ecs);
|
||||
|
||||
let mut vis = VisibilitySystem {};
|
||||
vis.run_now(&self.ecs);
|
||||
|
||||
let mut encumbrance = ai::EncumbranceSystem {};
|
||||
encumbrance.run_now(&self.ecs);
|
||||
|
||||
let mut initiative = ai::InitiativeSystem {};
|
||||
initiative.run_now(&self.ecs);
|
||||
|
||||
let mut turnstatus = ai::TurnStatusSystem {};
|
||||
turnstatus.run_now(&self.ecs);
|
||||
|
||||
let mut quipper = ai::QuipSystem {};
|
||||
quipper.run_now(&self.ecs);
|
||||
|
||||
let mut adjacent = ai::AdjacentAI {};
|
||||
adjacent.run_now(&self.ecs);
|
||||
|
||||
let mut visible = ai::VisibleAI {};
|
||||
visible.run_now(&self.ecs);
|
||||
|
||||
let mut approach = ai::ApproachAI {};
|
||||
approach.run_now(&self.ecs);
|
||||
|
||||
let mut flee = ai::FleeAI {};
|
||||
flee.run_now(&self.ecs);
|
||||
|
||||
let mut chase = ai::ChaseAI {};
|
||||
chase.run_now(&self.ecs);
|
||||
|
||||
let mut defaultmove = ai::DefaultMoveAI {};
|
||||
defaultmove.run_now(&self.ecs);
|
||||
|
||||
let mut moving = MovementSystem {};
|
||||
moving.run_now(&self.ecs);
|
||||
|
||||
let mut triggers = TriggerSystem {};
|
||||
triggers.run_now(&self.ecs);
|
||||
|
||||
let mut melee = MeleeCombatSystem {};
|
||||
melee.run_now(&self.ecs);
|
||||
|
||||
let mut damage = DamageSystem {};
|
||||
damage.run_now(&self.ecs);
|
||||
|
||||
let mut pickup = ItemCollectionSystem {};
|
||||
pickup.run_now(&self.ecs);
|
||||
|
||||
let mut items = ItemUseSystem {};
|
||||
items.run_now(&self.ecs);
|
||||
|
||||
let mut drop_items = ItemDropSystem {};
|
||||
drop_items.run_now(&self.ecs);
|
||||
|
||||
let mut item_remove = ItemRemoveSystem {};
|
||||
item_remove.run_now(&self.ecs);
|
||||
|
||||
let mut hunger = HungerSystem {};
|
||||
hunger.run_now(&self.ecs);
|
||||
|
||||
let mut particles = ParticleSpawnSystem {};
|
||||
particles.run_now(&self.ecs);
|
||||
|
||||
let mut lighting = LightingSystem {};
|
||||
lighting.run_now(&self.ecs);
|
||||
|
||||
self.ecs.maintain();
|
||||
}
|
||||
|
||||
fn goto_level(&mut self, offset: i32) {
|
||||
freeze_level_entities(&mut self.ecs);
|
||||
|
||||
// Build a new map and place the player
|
||||
let current_depth = self.ecs.fetch::<Map>().depth;
|
||||
self.generate_world_map(current_depth + offset, offset);
|
||||
|
||||
// Notify the player
|
||||
let mut gamelog = self.ecs.fetch_mut::<GameLog>();
|
||||
gamelog.append("You change level.");
|
||||
}
|
||||
|
||||
fn game_over_cleanup(&mut self) {
|
||||
// Delete everything
|
||||
let mut to_delete = Vec::new();
|
||||
for e in self.ecs.entities().join() {
|
||||
to_delete.push(e);
|
||||
}
|
||||
for del in to_delete.iter() {
|
||||
self.ecs
|
||||
.delete_entity(*del)
|
||||
.expect("Failed to delete entity");
|
||||
}
|
||||
|
||||
// Spawn a new player
|
||||
{
|
||||
let player_entity = spawner::player(&mut self.ecs, 0, 0);
|
||||
let mut player_entity_writer = self.ecs.write_resource::<Entity>();
|
||||
*player_entity_writer = player_entity;
|
||||
}
|
||||
|
||||
// Replace the world maps
|
||||
self.ecs.insert(map::MasterDungeonMap::new());
|
||||
|
||||
// Build a new map and place the player
|
||||
self.generate_world_map(1, 0);
|
||||
}
|
||||
|
||||
pub fn generate_world_map(&mut self, new_depth: i32, offset: i32) {
|
||||
self.mapgen_index = 0;
|
||||
self.mapgen_timer = 0.0;
|
||||
self.mapgen_history.clear();
|
||||
|
||||
if let Some(history) = map::level_transition(&mut self.ecs, new_depth, offset) {
|
||||
self.mapgen_history = history;
|
||||
} else {
|
||||
map::thaw_level_entities(&mut self.ecs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GameState for State {
|
||||
fn tick(&mut self, ctx: &mut Rltk) {
|
||||
let mut newrunstate;
|
||||
{
|
||||
let runstate = self.ecs.fetch::<RunState>();
|
||||
newrunstate = *runstate;
|
||||
}
|
||||
|
||||
ctx.cls();
|
||||
particle_system::cull_dead_particles(&mut self.ecs, ctx);
|
||||
|
||||
match newrunstate {
|
||||
RunState::MainMenu { .. } => {}
|
||||
RunState::GameOver { .. } => {}
|
||||
_ => {
|
||||
camera::render_camera(&self.ecs, ctx);
|
||||
gui::draw_ui(&self.ecs, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
match newrunstate {
|
||||
RunState::MapGeneration => {
|
||||
if !SHOW_MAPGEN_VISUALIZER {
|
||||
newrunstate = self.mapgen_next_state.unwrap();
|
||||
}
|
||||
ctx.cls();
|
||||
if self.mapgen_index < self.mapgen_history.len() {
|
||||
camera::render_debug_map(&self.mapgen_history[self.mapgen_index], ctx);
|
||||
}
|
||||
|
||||
self.mapgen_timer += ctx.frame_time_ms;
|
||||
if self.mapgen_timer > 300.0 {
|
||||
self.mapgen_timer = 0.0;
|
||||
self.mapgen_index += 1;
|
||||
if self.mapgen_index >= self.mapgen_history.len() {
|
||||
newrunstate = self.mapgen_next_state.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
RunState::PreRun => {
|
||||
self.run_systems();
|
||||
self.ecs.maintain();
|
||||
newrunstate = RunState::AwaitingInput;
|
||||
}
|
||||
RunState::AwaitingInput => {
|
||||
newrunstate = player_input(self, ctx);
|
||||
}
|
||||
RunState::Ticking => {
|
||||
while newrunstate == RunState::Ticking {
|
||||
self.run_systems();
|
||||
self.ecs.maintain();
|
||||
|
||||
newrunstate = match *self.ecs.fetch::<RunState>() {
|
||||
RunState::AwaitingInput => RunState::AwaitingInput,
|
||||
RunState::MagicMapReveal { .. } => RunState::MagicMapReveal { row: 0 },
|
||||
RunState::TownPortal => RunState::TownPortal,
|
||||
RunState::TeleportingToOtherLevel { x, y, depth } => {
|
||||
RunState::TeleportingToOtherLevel { x, y, depth }
|
||||
}
|
||||
_ => RunState::Ticking,
|
||||
};
|
||||
}
|
||||
}
|
||||
RunState::ShowInventory => {
|
||||
let result = gui::show_inventory(self, ctx);
|
||||
match result.0 {
|
||||
gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput,
|
||||
gui::ItemMenuResult::NoResponse => {}
|
||||
gui::ItemMenuResult::Selected => {
|
||||
let item_entity = result.1.unwrap();
|
||||
let is_ranged = self.ecs.read_storage::<Ranged>();
|
||||
|
||||
if let Some(is_item_ranged) = is_ranged.get(item_entity) {
|
||||
newrunstate = RunState::ShowTargeting {
|
||||
range: is_item_ranged.range,
|
||||
item: item_entity,
|
||||
};
|
||||
} else {
|
||||
let mut intent = self.ecs.write_storage::<WantsToUseItem>();
|
||||
intent
|
||||
.insert(
|
||||
*self.ecs.fetch::<Entity>(),
|
||||
WantsToUseItem {
|
||||
item: item_entity,
|
||||
target: None,
|
||||
},
|
||||
)
|
||||
.expect("failed to add intent to use item");
|
||||
|
||||
newrunstate = RunState::Ticking;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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("failed to add intent to drop item");
|
||||
|
||||
newrunstate = RunState::Ticking;
|
||||
}
|
||||
}
|
||||
}
|
||||
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::Ticking;
|
||||
}
|
||||
}
|
||||
}
|
||||
RunState::ShowTargeting { range, item } => {
|
||||
let result = gui::ranged_target(self, ctx, range);
|
||||
match result.0 {
|
||||
gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput,
|
||||
gui::ItemMenuResult::NoResponse => {}
|
||||
gui::ItemMenuResult::Selected => {
|
||||
let mut intent = self.ecs.write_storage::<WantsToUseItem>();
|
||||
|
||||
intent
|
||||
.insert(
|
||||
*self.ecs.fetch::<Entity>(),
|
||||
WantsToUseItem {
|
||||
item,
|
||||
target: result.1,
|
||||
},
|
||||
)
|
||||
.expect("failed to add intent to use item");
|
||||
|
||||
newrunstate = RunState::Ticking;
|
||||
}
|
||||
}
|
||||
}
|
||||
RunState::MainMenu { .. } => match gui::main_menu(self, ctx) {
|
||||
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 => {
|
||||
saveload_system::load_game(&mut self.ecs);
|
||||
newrunstate = RunState::AwaitingInput;
|
||||
saveload_system::delete_save();
|
||||
}
|
||||
gui::MainMenuSelection::Quit => {
|
||||
::std::process::exit(0);
|
||||
}
|
||||
},
|
||||
},
|
||||
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 => {
|
||||
saveload_system::save_game(&mut self.ecs);
|
||||
|
||||
newrunstate = RunState::MainMenu {
|
||||
menu_selection: gui::MainMenuSelection::LoadGame,
|
||||
}
|
||||
}
|
||||
RunState::NextLevel => {
|
||||
self.goto_level(1);
|
||||
newrunstate = RunState::PreRun;
|
||||
}
|
||||
RunState::PreviousLevel => {
|
||||
self.goto_level(-1);
|
||||
self.mapgen_next_state = Some(RunState::PreRun);
|
||||
newrunstate = RunState::MapGeneration;
|
||||
}
|
||||
RunState::MagicMapReveal { row } => {
|
||||
let mut map = self.ecs.fetch_mut::<Map>();
|
||||
for x in 0..map.width {
|
||||
let idx = map.xy_idx(x as i32, row);
|
||||
map.revealed_tiles[idx] = true;
|
||||
}
|
||||
|
||||
if row == map.height - 1 {
|
||||
newrunstate = RunState::Ticking;
|
||||
} else {
|
||||
newrunstate = RunState::MagicMapReveal { row: row + 1 };
|
||||
}
|
||||
}
|
||||
RunState::ShowCheatMenu => match show_cheat_mode(self, ctx) {
|
||||
CheatMenuResult::Cancel => newrunstate = RunState::AwaitingInput,
|
||||
CheatMenuResult::NoResponse => {}
|
||||
CheatMenuResult::TeleportToExit => {
|
||||
self.goto_level(1);
|
||||
self.mapgen_next_state = Some(RunState::PreRun);
|
||||
|
||||
newrunstate = RunState::MapGeneration
|
||||
}
|
||||
CheatMenuResult::Heal => {
|
||||
let player = self.ecs.fetch::<Entity>();
|
||||
let mut pools = self.ecs.write_storage::<Pools>();
|
||||
let mut player_pools = pools.get_mut(*player).unwrap();
|
||||
player_pools.hit_points.current = player_pools.hit_points.max;
|
||||
|
||||
newrunstate = RunState::AwaitingInput;
|
||||
}
|
||||
CheatMenuResult::Reveal => {
|
||||
let mut map = self.ecs.fetch_mut::<Map>();
|
||||
for v in map.revealed_tiles.iter_mut() {
|
||||
*v = true;
|
||||
}
|
||||
|
||||
newrunstate = RunState::AwaitingInput;
|
||||
}
|
||||
CheatMenuResult::GodMode => {
|
||||
let player = self.ecs.fetch::<Entity>();
|
||||
let mut pools = self.ecs.write_storage::<Pools>();
|
||||
let mut player_pools = pools.get_mut(*player).unwrap();
|
||||
player_pools.god_mode = true;
|
||||
|
||||
newrunstate = RunState::AwaitingInput;
|
||||
}
|
||||
},
|
||||
RunState::ShowVendor { vendor, mode } => {
|
||||
let result = gui::show_vendor_menu(self, ctx, vendor, mode);
|
||||
match result.0 {
|
||||
gui::VendorResult::Cancel => newrunstate = RunState::AwaitingInput,
|
||||
gui::VendorResult::NoResponse => {}
|
||||
gui::VendorResult::Sell => {
|
||||
let price = self
|
||||
.ecs
|
||||
.read_storage::<Item>()
|
||||
.get(result.1.unwrap())
|
||||
.unwrap()
|
||||
.base_value
|
||||
* 0.8;
|
||||
self.ecs
|
||||
.write_storage::<Pools>()
|
||||
.get_mut(*self.ecs.fetch::<Entity>())
|
||||
.unwrap()
|
||||
.gold += price;
|
||||
self.ecs
|
||||
.delete_entity(result.1.unwrap())
|
||||
.expect("Unable to delete sold item");
|
||||
}
|
||||
gui::VendorResult::Buy => {
|
||||
let tag = result.2.unwrap();
|
||||
let price = result.3.unwrap();
|
||||
let mut pools = self.ecs.write_storage::<Pools>();
|
||||
let player_pools = pools.get_mut(*self.ecs.fetch::<Entity>()).unwrap();
|
||||
if player_pools.gold >= price {
|
||||
player_pools.gold -= price;
|
||||
std::mem::drop(pools);
|
||||
|
||||
let player_entity = *self.ecs.fetch::<Entity>();
|
||||
spawn_named_item(
|
||||
&RAWS.lock().unwrap(),
|
||||
&mut self.ecs,
|
||||
&tag,
|
||||
SpawnType::Carried { by: player_entity },
|
||||
);
|
||||
}
|
||||
}
|
||||
gui::VendorResult::BuyMode => {
|
||||
newrunstate = RunState::ShowVendor {
|
||||
vendor,
|
||||
mode: VendorMode::Buy,
|
||||
}
|
||||
}
|
||||
gui::VendorResult::SellMode => {
|
||||
newrunstate = RunState::ShowVendor {
|
||||
vendor,
|
||||
mode: VendorMode::Sell,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RunState::TownPortal => {
|
||||
// Spawn the portal
|
||||
spawner::spawn_town_portal(&mut self.ecs);
|
||||
|
||||
// Transition
|
||||
let map_depth = self.ecs.fetch::<Map>().depth;
|
||||
let destination_offset = 0 - (map_depth - 1);
|
||||
self.goto_level(destination_offset);
|
||||
self.mapgen_next_state = Some(RunState::PreRun);
|
||||
|
||||
newrunstate = RunState::MapGeneration;
|
||||
}
|
||||
RunState::TeleportingToOtherLevel { x, y, depth } => {
|
||||
self.goto_level(depth - 1);
|
||||
let player_entity = self.ecs.fetch::<Entity>();
|
||||
if let Some(pos) = self.ecs.write_storage::<Position>().get_mut(*player_entity) {
|
||||
pos.x = x;
|
||||
pos.y = y;
|
||||
}
|
||||
|
||||
let mut ppos = self.ecs.fetch_mut::<Point>();
|
||||
ppos.x = x;
|
||||
ppos.y = y;
|
||||
self.mapgen_next_state = Some(RunState::PreRun);
|
||||
|
||||
newrunstate = RunState::MapGeneration;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut runwriter = self.ecs.write_resource::<RunState>();
|
||||
*runwriter = newrunstate;
|
||||
}
|
||||
|
||||
damage_system::delete_the_dead(&mut self.ecs);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user