From 485151e37cd042c7c0488a88c32aba92cbd7afe1 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Tue, 18 Jan 2022 11:40:31 -0500 Subject: [PATCH] Move state management out of main file --- src/main.rs | 521 +------------------------------------------------ src/state.rs | 533 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 536 insertions(+), 518 deletions(-) create mode 100644 src/state.rs diff --git a/src/main.rs b/src/main.rs index 9ac699c..6a62712 100644 --- a/src/main.rs +++ b/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, - mapgen_history: Vec, - 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::(); - 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::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::(); - - 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::(); - intent - .insert( - *self.ecs.fetch::(), - 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::(); - intent - .insert( - *self.ecs.fetch::(), - 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::(); - intent - .insert( - *self.ecs.fetch::(), - 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::(); - - intent - .insert( - *self.ecs.fetch::(), - 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::(); - 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::(); - let mut pools = self.ecs.write_storage::(); - 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::(); - for v in map.revealed_tiles.iter_mut() { - *v = true; - } - - newrunstate = RunState::AwaitingInput; - } - CheatMenuResult::GodMode => { - let player = self.ecs.fetch::(); - let mut pools = self.ecs.write_storage::(); - 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::() - .get(result.1.unwrap()) - .unwrap() - .base_value - * 0.8; - self.ecs - .write_storage::() - .get_mut(*self.ecs.fetch::()) - .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::(); - let player_pools = pools.get_mut(*self.ecs.fetch::()).unwrap(); - if player_pools.gold >= price { - player_pools.gold -= price; - std::mem::drop(pools); - - let player_entity = *self.ecs.fetch::(); - 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::().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::(); - *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::().depth; - self.generate_world_map(current_depth + offset, offset); - - // Notify the player - let mut gamelog = self.ecs.fetch_mut::(); - 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::(); - *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() diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..e79874a --- /dev/null +++ b/src/state.rs @@ -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, + mapgen_history: Vec, + 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::().depth; + self.generate_world_map(current_depth + offset, offset); + + // Notify the player + let mut gamelog = self.ecs.fetch_mut::(); + 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::(); + *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::(); + 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::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::(); + + 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::(); + intent + .insert( + *self.ecs.fetch::(), + 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::(); + intent + .insert( + *self.ecs.fetch::(), + 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::(); + intent + .insert( + *self.ecs.fetch::(), + 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::(); + + intent + .insert( + *self.ecs.fetch::(), + 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::(); + 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::(); + let mut pools = self.ecs.write_storage::(); + 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::(); + for v in map.revealed_tiles.iter_mut() { + *v = true; + } + + newrunstate = RunState::AwaitingInput; + } + CheatMenuResult::GodMode => { + let player = self.ecs.fetch::(); + let mut pools = self.ecs.write_storage::(); + 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::() + .get(result.1.unwrap()) + .unwrap() + .base_value + * 0.8; + self.ecs + .write_storage::() + .get_mut(*self.ecs.fetch::()) + .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::(); + let player_pools = pools.get_mut(*self.ecs.fetch::()).unwrap(); + if player_pools.gold >= price { + player_pools.gold -= price; + std::mem::drop(pools); + + let player_entity = *self.ecs.fetch::(); + 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::().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::(); + if let Some(pos) = self.ecs.write_storage::().get_mut(*player_entity) { + pos.x = x; + pos.y = y; + } + + let mut ppos = self.ecs.fetch_mut::(); + ppos.x = x; + ppos.y = y; + self.mapgen_next_state = Some(RunState::PreRun); + + newrunstate = RunState::MapGeneration; + } + } + + { + let mut runwriter = self.ecs.write_resource::(); + *runwriter = newrunstate; + } + + damage_system::delete_the_dead(&mut self.ecs); + } +}