use specs::prelude::*; use bracket_lib::prelude::*; use super::runstate::RunState; use crate::map::*; use crate::hunger_system; use crate::particle_system; use crate::trigger_system; use crate::inventory; use crate::melee_combat_system::MeleeCombatSystem; use crate::spatial; use crate::effects; use crate::visibility_system::VisibilitySystem; use crate::ai; use crate::gamelog; use crate::spawner; use crate::consts::ids::*; use crate::consts::events::*; use crate::components::*; use crate::player::*; use crate::gui; use crate::config; use crate::camera; use crate::saveload_system; use crate::morgue; use crate::damage_system; use std::collections::HashMap; use notan::prelude::*; pub struct Fonts { normal: notan::draw::Font, bold: Option, italic: Option, } impl Fonts { pub fn new( normal: notan::draw::Font, bold: Option, italic: Option ) -> Self { Self { normal, bold, italic } } /// Returns the regular weight font. pub fn n(&self) -> ¬an::draw::Font { &self.normal } /// Returns the bold weight font, or the regular weight font if no bold font is available. pub fn b(&self) -> notan::draw::Font { if self.bold.is_some() { self.bold.unwrap() } else { self.normal } } /// Returns the italic weight font, or the regular weight font if no italic font is available. pub fn i(&self) -> notan::draw::Font { if self.italic.is_some() { self.italic.unwrap() } else { self.normal } } /// Returns in order of preference: italic, bold, regular font weights, depending on what is available. pub fn ib(&self) -> notan::draw::Font { if self.italic.is_some() { self.italic.unwrap() } else { self.b() } } } #[derive(AppState)] pub struct State { pub ecs: World, //pub audio: HashMap, pub atlas: HashMap, pub interface: HashMap, pub font: Fonts, pub mapgen_next_state: Option, pub mapgen_history: Vec, pub mapgen_index: usize, pub mapgen_timer: f32, } impl State { pub fn generate_world_map(&mut self, new_id: i32, dest_tile: TileType) { // Visualisation stuff self.mapgen_index = 0; self.mapgen_timer = 0.0; self.mapgen_history.clear(); let map_building_info = level_transition(&mut self.ecs, new_id, dest_tile); if let Some(history) = map_building_info { self.mapgen_history = history; } else { dungeon::thaw_entities(&mut self.ecs); } } fn run_systems(&mut self, ctx: &mut App) { let mut hunger_clock = hunger_system::HungerSystem {}; let mut particle_system = particle_system::ParticleSpawnSystem {}; // Order is *very* important here, to ensure effects take place in the right order, // and that the player/AI are making decisions based on the most up-to-date info. self.resolve_entity_decisions(ctx); // Push Player messages of intent to effects queue, and run it. self.refresh_indexes(); // Get up-to-date map and viewsheds prior to AI turn. self.run_ai(); // Get AI decision-making. self.resolve_entity_decisions(ctx); // Push AI messages of intent to effects queue, and run it. hunger_clock.run_now(&self.ecs); // Tick the hunger clock (on the turn clock's turn) particle_system.run_now(&self.ecs); // Spawn/delete particles (turn independent) self.ecs.maintain(); } fn resolve_entity_decisions(&mut self, ctx: &mut App) { let mut trigger_system = trigger_system::TriggerSystem {}; let mut item_equip_system = inventory::ItemEquipSystem {}; let mut item_use_system = inventory::ItemUseSystem {}; let mut item_drop_system = inventory::ItemDropSystem {}; let mut item_remove_system = inventory::ItemRemoveSystem {}; let mut inventory_system = inventory::ItemCollectionSystem {}; let mut item_id_system = inventory::ItemIdentificationSystem {}; let mut key_system = inventory::KeyHandling {}; let mut melee_system = MeleeCombatSystem {}; trigger_system.run_now(&self.ecs); inventory_system.run_now(&self.ecs); item_equip_system.run_now(&self.ecs); item_use_system.run_now(&self.ecs); item_drop_system.run_now(&self.ecs); item_remove_system.run_now(&self.ecs); item_id_system.run_now(&self.ecs); key_system.run_now(&self.ecs); melee_system.run_now(&self.ecs); effects::run_effects_queue(ctx, &mut self.ecs); } fn refresh_indexes(&mut self) { let mut mapindex = spatial::MapIndexingSystem {}; let mut vis = VisibilitySystem {}; mapindex.run_now(&self.ecs); vis.run_now(&self.ecs); } fn run_ai(&mut self) { let mut encumbrance_system = ai::EncumbranceSystem {}; // Must run first, as it affects energy regen. let mut energy = ai::EnergySystem {}; // Figures out who deserves a turn. let mut regen_system = ai::RegenSystem {}; // Restores HP on appropriate clock ticks. let mut turn_status_system = ai::TurnStatusSystem {}; // Ticks statuses. Should anyone now lose their turn? i.e. confusion let mut quip_system = ai::QuipSystem {}; // Quipping is "free". It doesn't use up a turn. let mut adjacent_ai = ai::AdjacentAI {}; // AdjacentAI -> DefaultAI are all exclusive. If one acts, the entity's turn is over. let mut visible_ai = ai::VisibleAI {}; let mut approach_ai = ai::ApproachAI {}; let mut flee_ai = ai::FleeAI {}; let mut chase_ai = ai::ChaseAI {}; let mut default_move_ai = ai::DefaultAI {}; encumbrance_system.run_now(&self.ecs); energy.run_now(&self.ecs); regen_system.run_now(&self.ecs); turn_status_system.run_now(&self.ecs); quip_system.run_now(&self.ecs); adjacent_ai.run_now(&self.ecs); visible_ai.run_now(&self.ecs); approach_ai.run_now(&self.ecs); flee_ai.run_now(&self.ecs); chase_ai.run_now(&self.ecs); default_move_ai.run_now(&self.ecs); } fn goto_id(&mut self, id: i32, dest_tile: TileType) { // Freeze curr level dungeon::freeze_entities(&mut self.ecs); self.generate_world_map(id, dest_tile); let mapname = self.ecs.fetch::().name.clone(); gamelog::Logger ::new() .append("You head to") .colour(rgb_to_u8(get_local_col(id))) .append_n(&mapname) .colour(WHITE) .period() .log(); gamelog::record_event(EVENT::ChangedFloor(mapname)); } 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("Deletion failed"); } // Spawn a new player and build new map { 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 map list self.ecs.insert(dungeon::MasterDungeonMap::new()); self.generate_world_map(1, TileType::Floor); gamelog::setup_log(); gamelog::record_event(EVENT::Level(1)); } } impl State { pub fn update(&mut self, ctx: &mut App) { let mut new_runstate; { let runstate = self.ecs.fetch::(); new_runstate = *runstate; } particle_system::particle_ticker(&mut self.ecs, ctx); match new_runstate { RunState::PreRun => { self.run_systems(ctx); self.ecs.maintain(); new_runstate = RunState::AwaitingInput; } RunState::AwaitingInput => { self.refresh_indexes(); effects::run_effects_queue(ctx, &mut self.ecs); let mut can_act = false; { let player_entity = self.ecs.fetch::(); let turns = self.ecs.read_storage::(); if let Some(_) = turns.get(*player_entity) { can_act = true; } } if can_act { let on_overmap = self.ecs.fetch::().overmap; new_runstate = player_input(self, ctx, on_overmap); } else { new_runstate = RunState::Ticking; } } RunState::Ticking => { while new_runstate == RunState::Ticking && particle_system::check_queue(&self.ecs) { self.run_systems(ctx); self.ecs.maintain(); try_spawn_interval(&mut self.ecs); maybe_map_message(&mut self.ecs); match *self.ecs.fetch::() { RunState::AwaitingInput => { new_runstate = RunState::AwaitingInput; } RunState::MagicMapReveal { row, cursed } => { new_runstate = RunState::MagicMapReveal { row, cursed }; } RunState::ShowRemoveCurse => { new_runstate = RunState::ShowRemoveCurse; } RunState::ShowIdentify => { new_runstate = RunState::ShowIdentify; } _ => { new_runstate = RunState::Ticking; } } } } RunState::Farlook { .. } => { let result = gui::show_farlook(self, ctx); match result { gui::FarlookResult::NoResponse { x, y } => { new_runstate = RunState::Farlook { x, y }; } gui::FarlookResult::Cancel => { new_runstate = RunState::AwaitingInput; } } } RunState::ShowCheatMenu => { let result = gui::show_cheat_menu(self, ctx); match result { gui::CheatMenuResult::Cancel => { new_runstate = RunState::AwaitingInput; } gui::CheatMenuResult::NoResponse => {} gui::CheatMenuResult::Ascend => { let id = self.ecs.fetch::().id - 1; self.goto_id(id, TileType::DownStair); self.mapgen_next_state = Some(RunState::PreRun); new_runstate = RunState::MapGeneration; } gui::CheatMenuResult::Descend => { let id = self.ecs.fetch::().id + 1; self.goto_id(id, TileType::UpStair); self.mapgen_next_state = Some(RunState::PreRun); new_runstate = RunState::MapGeneration; } gui::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; new_runstate = RunState::AwaitingInput; } gui::CheatMenuResult::MagicMap => { let mut map = self.ecs.fetch_mut::(); for v in map.revealed_tiles.iter_mut() { *v = true; } new_runstate = RunState::AwaitingInput; } gui::CheatMenuResult::GodMode => { let player = self.ecs.fetch::(); let mut pools = self.ecs.write_storage::(); let mut player_pools = pools.get_mut(*player).unwrap(); gamelog::Logger::new().append("TOGGLED GOD MODE!").log(); player_pools.god = !player_pools.god; new_runstate = RunState::AwaitingInput; } } } RunState::ShowInventory => { let result = gui::show_inventory(self, ctx); match result.0 { gui::ItemMenuResult::Cancel => { new_runstate = RunState::AwaitingInput; } gui::ItemMenuResult::NoResponse => {} gui::ItemMenuResult::Selected => { let item_entity = result.1.unwrap(); let is_ranged = self.ecs.read_storage::(); let ranged_item = is_ranged.get(item_entity); if let Some(ranged_item) = ranged_item { let is_aoe = self.ecs.read_storage::(); let aoe_item = is_aoe.get(item_entity); let bounds = camera::get_screen_bounds(&self.ecs, false); let ppos = self.ecs.fetch::(); if let Some(aoe_item) = aoe_item { new_runstate = RunState::ShowTargeting { x: ppos.x + bounds.x_offset - bounds.min_x, y: ppos.y + bounds.y_offset - bounds.min_y, range: ranged_item.range, item: item_entity, aoe: aoe_item.radius, }; } else { new_runstate = RunState::ShowTargeting { x: ppos.x + bounds.x_offset - bounds.min_x, y: ppos.y + bounds.y_offset - bounds.min_y, range: ranged_item.range, item: item_entity, aoe: 0, }; } } else { let mut intent = self.ecs.write_storage::(); intent .insert(*self.ecs.fetch::(), WantsToUseItem { item: item_entity, target: None, }) .expect("Unable to insert intent."); new_runstate = RunState::Ticking; } } } } RunState::ShowDropItem => { let result = gui::drop_item_menu(self, ctx); match result.0 { gui::ItemMenuResult::Cancel => { new_runstate = RunState::AwaitingInput; } gui::ItemMenuResult::NoResponse => {} gui::ItemMenuResult::Selected => { let item = result.1.unwrap(); let mut removekey = self.ecs.write_storage::(); let mut intent = self.ecs.write_storage::(); removekey .insert(item, WantsToRemoveKey {}) .expect("Unable to insert WantsToRemoveKey"); intent .insert(*self.ecs.fetch::(), WantsToDropItem { item, }) .expect("Unable to insert WantsToDropItem"); new_runstate = RunState::Ticking; } } } RunState::ShowRemoveItem => { let result = gui::remove_item_menu(self, ctx); match result.0 { gui::ItemMenuResult::Cancel => { new_runstate = 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"); new_runstate = RunState::Ticking; } } } RunState::ShowTargeting { x, y, range, item, aoe } => { let result = gui::ranged_target(self, ctx, x, y, range, aoe); match result.0 { gui::TargetResult::Cancel => { new_runstate = RunState::AwaitingInput; } gui::TargetResult::NoResponse { x, y } => { new_runstate = RunState::ShowTargeting { x, y, range, item, aoe }; } gui::TargetResult::Selected => { let mut intent = self.ecs.write_storage::(); intent .insert(*self.ecs.fetch::(), WantsToUseItem { item, target: result.1, }) .expect("Unable to insert intent."); new_runstate = RunState::Ticking; } } } // RunState::ShowRemoveCurse // RunState::ShowIdentify RunState::ActionWithDirection { function } => { new_runstate = gui::get_input_direction(&mut self.ecs, ctx, function); } RunState::MainMenu { .. } => { let result = gui::main_menu(self, ctx); match result { gui::MainMenuResult::NoSelection { selected } => { new_runstate = RunState::MainMenu { menu_selection: selected }; } gui::MainMenuResult::Selected { selected } => match selected { gui::MainMenuSelection::NewGame => { new_runstate = RunState::CharacterCreation { ancestry: gui::Ancestry::Human, class: gui::Class::Fighter, }; } gui::MainMenuSelection::LoadGame => { saveload_system::load_game(&mut self.ecs); new_runstate = RunState::AwaitingInput; saveload_system::delete_save(); } gui::MainMenuSelection::Quit => { ::std::process::exit(0); } } } } RunState::CharacterCreation { .. } => { let result = gui::character_creation(self, ctx); match result { gui::CharCreateResult::NoSelection { ancestry, class } => { new_runstate = RunState::CharacterCreation { ancestry, class }; } gui::CharCreateResult::Selected { ancestry, class } => { if ancestry == gui::Ancestry::Unset { new_runstate = RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame, }; } else { gui::setup_player_ancestry(&mut self.ecs, ancestry); gui::setup_player_class(&mut self.ecs, class, ancestry); new_runstate = RunState::PreRun; } } } } RunState::SaveGame => { saveload_system::save_game(&mut self.ecs); new_runstate = RunState::MainMenu { menu_selection: gui::MainMenuSelection::LoadGame, }; } RunState::GameOver => { let result = gui::yes_no(ctx); let write_to_morgue: Option = match result { gui::YesNoResult::NoSelection => None, gui::YesNoResult::No => Some(false), gui::YesNoResult::Yes => Some(true), }; if let Some(response) = write_to_morgue { if response { morgue::create_morgue_file(&self.ecs); } self.game_over_cleanup(); new_runstate = RunState::MapGeneration; self.mapgen_next_state = Some(RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame, }); } } RunState::GoToLevel(id, dest_tile) => { self.goto_id(id, dest_tile); self.mapgen_next_state = Some(RunState::PreRun); new_runstate = RunState::MapGeneration; } RunState::HelpScreen => { let result = gui::help_prompt(ctx); match result { gui::YesNoResult::NoSelection => {} _ => { gamelog::record_event(EVENT::LookedForHelp(1)); new_runstate = RunState::AwaitingInput; } } } RunState::MagicMapReveal { row, cursed } => { let mut map = self.ecs.fetch_mut::(); // Could probably toss this into a function somewhere, and/or // have multiple simple animations for it. for x in 0..map.width { let idx; if x % 2 == 0 { idx = map.xy_idx(x as i32, row); } else { idx = map.xy_idx(x as i32, (map.height as i32) - 1 - row); } if !cursed { map.revealed_tiles[idx] = true; } else { map.revealed_tiles[idx] = false; } } // Dirtify viewshed only if cursed, so our currently visible tiles aren't removed too if cursed { let player_entity = self.ecs.fetch::(); let mut viewshed_components = self.ecs.write_storage::(); let viewshed = viewshed_components.get_mut(*player_entity); if let Some(viewshed) = viewshed { viewshed.dirty = true; } } if (row as usize) == (map.height as usize) - 1 { new_runstate = RunState::Ticking; } else { new_runstate = RunState::MagicMapReveal { row: row + 1, cursed: cursed }; } } RunState::MapGeneration => { if !config::CONFIG.logging.show_mapgen || self.mapgen_history.len() <= 0 { new_runstate = self.mapgen_next_state.unwrap(); } else { if self.mapgen_history.len() > 0 { self.mapgen_timer += ctx.timer.delta_f32(); if self.mapgen_timer > 10.0 / (self.mapgen_history.len() as f32) || self.mapgen_timer > 1.0 { self.mapgen_timer = 0.0; self.mapgen_index += 1; if self.mapgen_index >= self.mapgen_history.len() { new_runstate = self.mapgen_next_state.unwrap(); } } } } } _ => {} } { let mut runwriter = self.ecs.write_resource::(); *runwriter = new_runstate; } damage_system::delete_the_dead(&mut self.ecs); } // Deprecated fn tick(&mut self, ctx: &mut BTerm) { let mut new_runstate; { let runstate = self.ecs.fetch::(); new_runstate = *runstate; } // Clear screen ctx.set_active_console(2); ctx.cls(); ctx.set_active_console(1); ctx.cls(); ctx.set_active_console(0); ctx.cls(); //particle_system::particle_ticker(&mut self.ecs, ctx); match new_runstate { RunState::MainMenu { .. } => {} RunState::CharacterCreation { .. } => {} _ => { // Draw map and ui camera::render_camera(&self.ecs, ctx); gui::draw_ui(&self.ecs, ctx); } } match new_runstate { RunState::PreRun => { //self.run_systems(); self.ecs.maintain(); new_runstate = RunState::AwaitingInput; } RunState::AwaitingInput => { // We refresh the index, and run anything that might // still be in the queue, just to make 100% sure that // there are no lingering effects from the last tick. self.refresh_indexes(); //effects::run_effects_queue(&mut self.ecs); // Sanity-checking that the player actually *should* // be taking a turn before giving them one. If they // don't have a turn component, go back to ticking. let mut can_act = false; { let player_entity = self.ecs.fetch::(); let turns = self.ecs.read_storage::(); if let Some(_) = turns.get(*player_entity) { can_act = true; } } if can_act { let on_overmap = self.ecs.fetch::().overmap; new_runstate = RunState::AwaitingInput; //player_input(self, ctx, on_overmap); } else { new_runstate = RunState::Ticking; } } RunState::Ticking => { while new_runstate == RunState::Ticking && particle_system::check_queue(&self.ecs) { //self.run_systems(); self.ecs.maintain(); try_spawn_interval(&mut self.ecs); maybe_map_message(&mut self.ecs); match *self.ecs.fetch::() { RunState::AwaitingInput => { new_runstate = RunState::AwaitingInput; } RunState::MagicMapReveal { row, cursed } => { new_runstate = RunState::MagicMapReveal { row: row, cursed: cursed }; } RunState::ShowRemoveCurse => { new_runstate = RunState::ShowRemoveCurse; } RunState::ShowIdentify => { new_runstate = RunState::ShowIdentify; } _ => { new_runstate = RunState::Ticking; } } } } RunState::Farlook { .. } => { let result = gui::FarlookResult::Cancel; //gui::show_farlook(self, ctx); match result { gui::FarlookResult::NoResponse { x, y } => { new_runstate = RunState::Farlook { x, y }; } gui::FarlookResult::Cancel => { new_runstate = RunState::AwaitingInput; } } } RunState::ShowCheatMenu => { let result = gui::CheatMenuResult::Cancel; //gui::show_cheat_menu(self, ctx); match result { gui::CheatMenuResult::Cancel => { new_runstate = RunState::AwaitingInput; } gui::CheatMenuResult::NoResponse => {} gui::CheatMenuResult::Ascend => { let id = self.ecs.fetch::().id - 1; self.goto_id(id, TileType::DownStair); self.mapgen_next_state = Some(RunState::PreRun); new_runstate = RunState::MapGeneration; } gui::CheatMenuResult::Descend => { let id = self.ecs.fetch::().id + 1; self.goto_id(id, TileType::UpStair); self.mapgen_next_state = Some(RunState::PreRun); new_runstate = RunState::MapGeneration; } gui::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; new_runstate = RunState::AwaitingInput; } gui::CheatMenuResult::MagicMap => { let mut map = self.ecs.fetch_mut::(); for v in map.revealed_tiles.iter_mut() { *v = true; } new_runstate = RunState::AwaitingInput; } gui::CheatMenuResult::GodMode => { let player = self.ecs.fetch::(); let mut pools = self.ecs.write_storage::(); let mut player_pools = pools.get_mut(*player).unwrap(); gamelog::Logger::new().append("TOGGLED GOD MODE!").log(); player_pools.god = !player_pools.god; new_runstate = RunState::AwaitingInput; } } } RunState::ShowInventory => { let result = gui::ItemMenuResult::Cancel; //gui::show_inventory(self, ctx); match result { gui::ItemMenuResult::Cancel => { new_runstate = RunState::AwaitingInput; } _ => {} } } RunState::ShowDropItem => { let result = (gui::ItemMenuResult::Cancel, None); //gui::drop_item_menu(self, ctx); match result.0 { gui::ItemMenuResult::Cancel => { new_runstate = 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("Unable to insert intent"); new_runstate = RunState::Ticking; } } } RunState::ShowRemoveItem => { let result = (gui::ItemMenuResult::Cancel, None); //gui::remove_item_menu(self, ctx); match result.0 { gui::ItemMenuResult::Cancel => { new_runstate = 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"); new_runstate = RunState::Ticking; } } } RunState::ShowTargeting { x, y, range, item, aoe } => { let result = (gui::TargetResult::Cancel, None); //gui::ranged_target(self, ctx, x, y, range, aoe); match result.0 { gui::TargetResult::Cancel => { new_runstate = RunState::AwaitingInput; } gui::TargetResult::NoResponse { x, y } => { new_runstate = RunState::ShowTargeting { x, y, range, item, aoe }; } gui::TargetResult::Selected => { let mut intent = self.ecs.write_storage::(); intent .insert(*self.ecs.fetch::(), WantsToUseItem { item, target: result.1, }) .expect("Unable to insert intent."); new_runstate = RunState::Ticking; } } } RunState::ShowRemoveCurse => { let result = gui::remove_curse(self, ctx); match result.0 { gui::ItemMenuResult::Cancel => { new_runstate = RunState::AwaitingInput; } gui::ItemMenuResult::NoResponse => {} gui::ItemMenuResult::Selected => { let item_entity = result.1.unwrap(); self.ecs .write_storage::() .insert(item_entity, Beatitude { buc: BUC::Uncursed, known: true }) .expect("Unable to insert beatitude"); new_runstate = RunState::Ticking; } } } RunState::ShowIdentify => { let result = gui::identify(self, ctx); match result.0 { gui::ItemMenuResult::Cancel => { new_runstate = RunState::AwaitingInput; } gui::ItemMenuResult::NoResponse => {} gui::ItemMenuResult::Selected => { let item_entity = result.1.unwrap(); if let Some(name) = self.ecs.read_storage::().get(item_entity) { let mut dm = self.ecs.fetch_mut::(); dm.identified_items.insert(name.name.clone()); } if let Some(beatitude) = self.ecs .write_storage::() .get_mut(item_entity) { beatitude.known = true; } new_runstate = RunState::Ticking; } } } RunState::ActionWithDirection { function } => { new_runstate = RunState::AwaitingInput; //gui::get_input_direction(&mut self.ecs, ctx, function); } RunState::MainMenu { .. } => { let result = gui::MainMenuResult::Selected { selected: gui::MainMenuSelection::NewGame, }; //gui::main_menu(self, ctx); match result { gui::MainMenuResult::NoSelection { selected } => { new_runstate = RunState::MainMenu { menu_selection: selected }; } gui::MainMenuResult::Selected { selected } => match selected { gui::MainMenuSelection::NewGame => { new_runstate = RunState::CharacterCreation { ancestry: gui::Ancestry::Human, class: gui::Class::Fighter, }; } gui::MainMenuSelection::LoadGame => { saveload_system::load_game(&mut self.ecs); new_runstate = RunState::AwaitingInput; saveload_system::delete_save(); } gui::MainMenuSelection::Quit => { ::std::process::exit(0); } } } } RunState::CharacterCreation { .. } => { let result = gui::CharCreateResult::NoSelection { ancestry: gui::Ancestry::Human, class: gui::Class::Fighter, }; //gui::character_creation(self, ctx); match result { gui::CharCreateResult::NoSelection { ancestry, class } => { new_runstate = RunState::CharacterCreation { ancestry, class }; } gui::CharCreateResult::Selected { ancestry, class } => { if ancestry == gui::Ancestry::Unset { new_runstate = RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame, }; } else { gui::setup_player_ancestry(&mut self.ecs, ancestry); gui::setup_player_class(&mut self.ecs, class, ancestry); new_runstate = RunState::PreRun; } } } } RunState::SaveGame => { saveload_system::save_game(&mut self.ecs); new_runstate = RunState::MainMenu { menu_selection: gui::MainMenuSelection::LoadGame, }; } RunState::GameOver => { let result = gui::YesNoResult::No; //gui::game_over(ctx); let write_to_morgue: Option = match result { gui::YesNoResult::NoSelection => None, gui::YesNoResult::No => Some(false), gui::YesNoResult::Yes => Some(true), }; if let Some(response) = write_to_morgue { if response { morgue::create_morgue_file(&self.ecs); } self.game_over_cleanup(); new_runstate = RunState::MapGeneration; self.mapgen_next_state = Some(RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame, }); } } RunState::GoToLevel(id, dest_tile) => { self.goto_id(id, dest_tile); self.mapgen_next_state = Some(RunState::PreRun); new_runstate = RunState::MapGeneration; } RunState::HelpScreen => { let result = gui::show_help(ctx); match result { gui::YesNoResult::Yes => { gamelog::record_event(EVENT::LookedForHelp(1)); new_runstate = RunState::AwaitingInput; } _ => {} } } RunState::MagicMapReveal { row, cursed } => { let mut map = self.ecs.fetch_mut::(); // Could probably toss this into a function somewhere, and/or // have multiple simple animations for it. for x in 0..map.width { let idx; if x % 2 == 0 { idx = map.xy_idx(x as i32, row); } else { idx = map.xy_idx(x as i32, (map.height as i32) - 1 - row); } if !cursed { map.revealed_tiles[idx] = true; } else { map.revealed_tiles[idx] = false; } } // Dirtify viewshed only if cursed, so our currently visible tiles aren't removed too if cursed { let player_entity = self.ecs.fetch::(); let mut viewshed_components = self.ecs.write_storage::(); let viewshed = viewshed_components.get_mut(*player_entity); if let Some(viewshed) = viewshed { viewshed.dirty = true; } } if (row as usize) == (map.height as usize) - 1 { new_runstate = RunState::Ticking; } else { new_runstate = RunState::MagicMapReveal { row: row + 1, cursed: cursed }; } } RunState::MapGeneration => { if !config::CONFIG.logging.show_mapgen { new_runstate = self.mapgen_next_state.unwrap(); } if self.mapgen_history.len() != 0 { ctx.set_active_console(2); ctx.cls(); ctx.set_active_console(1); ctx.cls(); ctx.set_active_console(0); ctx.cls(); 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() { new_runstate = self.mapgen_next_state.unwrap(); } } } } } { let mut runwriter = self.ecs.write_resource::(); *runwriter = new_runstate; } damage_system::delete_the_dead(&mut self.ecs); let _ = render_draw_buffer(ctx); } }