diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index fae86714ca..72e601c968 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -25,7 +25,6 @@ use crate::{ menu::main::MainMenuState, window::Window, settings::Settings, - singleplayer::Singleplayer, }; /// The URL of the default public server that Voxygen will connect to @@ -35,7 +34,6 @@ const DEFAULT_PUBLIC_SERVER: &'static str = "server.veloren.net"; pub struct GlobalState { settings: Settings, window: Window, - singleplayer: Option, } impl GlobalState { @@ -47,6 +45,11 @@ impl GlobalState { } } +pub enum Direction { + Forwards, + Backwards, +} + // States can either close (and revert to a previous state), push a new state on top of themselves, // or switch to a totally different state pub enum PlayStateResult { @@ -65,7 +68,7 @@ pub enum PlayStateResult { pub trait PlayState { /// Play the state until some change of state is required (i.e: a menu is opened or the game /// is closed). - fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult; + fn play(&mut self, direction: Direction, global_state: &mut GlobalState) -> PlayStateResult; /// Get a descriptive name for this state type fn name(&self) -> &'static str; @@ -123,7 +126,6 @@ The information below is intended for developers and testers. let mut global_state = GlobalState { settings, window, - singleplayer: None, }; // Set up the initial play state @@ -141,10 +143,12 @@ The information below is intended for developers and testers. // which can in turn push a "settings" state or a "game session" state on top of it. // The code below manages the state transfer logic automatically so that we don't have to // re-engineer it for each menu we decide to add to the game. - while let Some(state_result) = states.last_mut().map(|last| last.play(&mut global_state)){ + let mut direction = Direction::Forwards; + while let Some(state_result) = states.last_mut().map(|last| last.play(direction, &mut global_state)){ // Implement state transfer logic match state_result { PlayStateResult::Shutdown => { + direction = Direction::Backwards; log::info!("Shutting down all states..."); while states.last().is_some() { states.pop().map(|old_state| { @@ -154,17 +158,20 @@ The information below is intended for developers and testers. } }, PlayStateResult::Pop => { + direction = Direction::Backwards; states.pop().map(|old_state| { log::info!("Popped state '{}'", old_state.name()); global_state.on_play_state_changed(); }); }, PlayStateResult::Push(new_state) => { + direction = Direction::Forwards; log::info!("Pushed state '{}'", new_state.name()); states.push(new_state); global_state.on_play_state_changed(); }, PlayStateResult::Switch(mut new_state) => { + direction = Direction::Forwards; states.last_mut().map(|old_state| { log::info!("Switching to state '{}' from state '{}'", new_state.name(), old_state.name()); mem::swap(old_state, &mut new_state); diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index b4786602e4..031f6be195 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -3,7 +3,7 @@ mod ui; use crate::{ window::{Event, Window}, session::SessionState, - GlobalState, PlayState, PlayStateResult, + Direction, GlobalState, PlayState, PlayStateResult, }; use client::{self, Client}; use common::{ @@ -40,7 +40,7 @@ const BG_COLOR: Rgba = Rgba { }; impl PlayState for CharSelectionState { - fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult { + fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult { // Set up an fps clock let mut clock = Clock::new(); @@ -49,7 +49,6 @@ impl PlayState for CharSelectionState { for event in global_state.window.fetch_events() { match event { Event::Close => { - global_state.singleplayer = None; return PlayStateResult::Shutdown; }, // Pass events to ui @@ -67,7 +66,6 @@ impl PlayState for CharSelectionState { for event in self.char_selection_ui.maintain(global_state.window.renderer_mut()) { match event { ui::Event::Logout => { - global_state.singleplayer = None; return PlayStateResult::Pop; }, ui::Event::Play => { diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index ed62409f1e..eb879e80ed 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -1,11 +1,12 @@ mod client_init; +mod start_singleplayer; mod ui; +use start_singleplayer::StartSingleplayerState; use super::char_selection::CharSelectionState; use crate::{ window::{Event, Window}, - GlobalState, PlayState, PlayStateResult, - singleplayer::Singleplayer, + Direction, GlobalState, PlayState, PlayStateResult, }; use client_init::{ClientInit, Error as InitError}; use common::{clock::Clock, comp}; @@ -28,6 +29,8 @@ impl MainMenuState { } } +const DEFAULT_PORT: u16 = 59003; + // Background colour const BG_COLOR: Rgba = Rgba { r: 0.0, @@ -37,7 +40,7 @@ const BG_COLOR: Rgba = Rgba { }; impl PlayState for MainMenuState { - fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult { + fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult { // Set up an fps clock let mut clock = Clock::new(); @@ -98,7 +101,6 @@ impl PlayState for MainMenuState { net_settings.servers.push(server_address.clone()); } global_state.settings.save_to_file(); - const DEFAULT_PORT: u16 = 59003; // Don't try to connect if there is already a connection in progress client_init = client_init.or(Some(ClientInit::new( (server_address, DEFAULT_PORT, false), @@ -109,7 +111,7 @@ impl PlayState for MainMenuState { ))); }, MainMenuEvent::StartSingleplayer => { - global_state.singleplayer = Some(Singleplayer::new()); + return PlayStateResult::Push(Box::new(StartSingleplayerState::new())); }, MainMenuEvent::Quit => return PlayStateResult::Shutdown, } diff --git a/voxygen/src/menu/main/start_singleplayer.rs b/voxygen/src/menu/main/start_singleplayer.rs new file mode 100644 index 0000000000..19a944df86 --- /dev/null +++ b/voxygen/src/menu/main/start_singleplayer.rs @@ -0,0 +1,66 @@ +use common::comp; +use super::{DEFAULT_PORT, client_init::ClientInit}; +use crate::{ + menu::char_selection::CharSelectionState, + singleplayer::Singleplayer, + Direction, GlobalState, PlayState, PlayStateResult, +}; + +pub struct StartSingleplayerState { + singleplayer: Singleplayer, +} + +impl StartSingleplayerState { + /// Create a new `MainMenuState` + pub fn new() -> Self { + Self { + singleplayer: Singleplayer::new(), + } + } +} + +impl PlayState for StartSingleplayerState { + fn play(&mut self, direction: Direction, global_state: &mut GlobalState) -> PlayStateResult { + match direction { + Direction::Forwards => { + let username = "singleplayer".to_owned(); + let server_address = "localhost".to_owned(); + + let client_init = ClientInit::new( + (server_address.clone(), DEFAULT_PORT, false), + ( + comp::Player::new(username.clone()), + 300, + ), + ); + + // Client creation + let client = loop { + match client_init.poll() { + Some(Ok(client)) => break client, + // Should always work + Some(Err(err)) => unreachable!(), + _ => {}, + } + }; + + let mut net_settings = &mut global_state.settings.networking; + net_settings.username = username.clone(); + if !net_settings.servers.contains(&server_address) { + net_settings.servers.push(server_address.clone()); + } + global_state.settings.save_to_file(); + + PlayStateResult::Push(Box::new(CharSelectionState::new( + &mut global_state.window, + std::rc::Rc::new(std::cell::RefCell::new(client)), + ))) + }, + Direction::Backwards => PlayStateResult::Pop, + } + } + + fn name(&self) -> &'static str { + "Starting Singleplayer" + } +} diff --git a/voxygen/src/menu/main/ui.rs b/voxygen/src/menu/main/ui.rs index 63ba980561..09d59560df 100644 --- a/voxygen/src/menu/main/ui.rs +++ b/voxygen/src/menu/main/ui.rs @@ -176,10 +176,11 @@ impl MainMenuUi { .top_left_with_margins(30.0, 30.0) .set(self.ids.v_logo, ui_widgets); Text::new(version) - .top_left_with_margins_on(ui_widgets.window, 5.0, 5.0) - .font_size(14) - .color(TEXT_COLOR) - .set(self.ids.version, ui_widgets); + .top_left_with_margins_on(ui_widgets.window, 5.0, 5.0) + .font_size(14) + .color(TEXT_COLOR) + .set(self.ids.version, ui_widgets); + // Input fields // Used when the login button is pressed, or enter is pressed within input field macro_rules! login { @@ -390,7 +391,7 @@ impl MainMenuUi { }; // Singleplayer button - if Button::image(self.imgs.button) + if Button::image(self.imgs.button) .hover_image(self.imgs.button_hover) .press_image(self.imgs.button_press) .w_h(258.0, 68.0) diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 5d18409004..77d1279140 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -6,6 +6,7 @@ use client::{ Client, }; use crate::{ + Direction, Error, PlayState, PlayStateResult, @@ -100,7 +101,7 @@ impl SessionState { } impl PlayState for SessionState { - fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult { + fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult { // Trap the cursor global_state.window.grab_cursor(true); @@ -129,7 +130,6 @@ impl PlayState for SessionState { } let _handled = match event { Event::Close => { - global_state.singleplayer = None; return PlayStateResult::Shutdown; }, // Toggle cursor grabbing @@ -171,7 +171,6 @@ impl PlayState for SessionState { }, HudEvent::Logout => return PlayStateResult::Pop, HudEvent::Quit => { - global_state.singleplayer = None; return PlayStateResult::Shutdown; }, }