Merge branch 'fix-singleplayer' into 'master'

Implement StartSingleplayerState

See merge request veloren/veloren!81

Former-commit-id: 64bf5ff7e4db2640dedfccdcff5c0e1e34a2f6b3
This commit is contained in:
Forest Anderson
2019-04-27 21:20:57 +00:00
6 changed files with 95 additions and 22 deletions

View File

@ -25,7 +25,6 @@ use crate::{
menu::main::MainMenuState, menu::main::MainMenuState,
window::Window, window::Window,
settings::Settings, settings::Settings,
singleplayer::Singleplayer,
}; };
/// The URL of the default public server that Voxygen will connect to /// 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 { pub struct GlobalState {
settings: Settings, settings: Settings,
window: Window, window: Window,
singleplayer: Option<Singleplayer>,
} }
impl GlobalState { 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, // 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 // or switch to a totally different state
pub enum PlayStateResult { pub enum PlayStateResult {
@ -65,7 +68,7 @@ pub enum PlayStateResult {
pub trait PlayState { pub trait PlayState {
/// Play the state until some change of state is required (i.e: a menu is opened or the game /// Play the state until some change of state is required (i.e: a menu is opened or the game
/// is closed). /// 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 /// Get a descriptive name for this state type
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
@ -123,7 +126,6 @@ The information below is intended for developers and testers.
let mut global_state = GlobalState { let mut global_state = GlobalState {
settings, settings,
window, window,
singleplayer: None,
}; };
// Set up the initial play state // 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. // 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 // 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. // 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 // Implement state transfer logic
match state_result { match state_result {
PlayStateResult::Shutdown => { PlayStateResult::Shutdown => {
direction = Direction::Backwards;
log::info!("Shutting down all states..."); log::info!("Shutting down all states...");
while states.last().is_some() { while states.last().is_some() {
states.pop().map(|old_state| { states.pop().map(|old_state| {
@ -154,17 +158,20 @@ The information below is intended for developers and testers.
} }
}, },
PlayStateResult::Pop => { PlayStateResult::Pop => {
direction = Direction::Backwards;
states.pop().map(|old_state| { states.pop().map(|old_state| {
log::info!("Popped state '{}'", old_state.name()); log::info!("Popped state '{}'", old_state.name());
global_state.on_play_state_changed(); global_state.on_play_state_changed();
}); });
}, },
PlayStateResult::Push(new_state) => { PlayStateResult::Push(new_state) => {
direction = Direction::Forwards;
log::info!("Pushed state '{}'", new_state.name()); log::info!("Pushed state '{}'", new_state.name());
states.push(new_state); states.push(new_state);
global_state.on_play_state_changed(); global_state.on_play_state_changed();
}, },
PlayStateResult::Switch(mut new_state) => { PlayStateResult::Switch(mut new_state) => {
direction = Direction::Forwards;
states.last_mut().map(|old_state| { states.last_mut().map(|old_state| {
log::info!("Switching to state '{}' from state '{}'", new_state.name(), old_state.name()); log::info!("Switching to state '{}' from state '{}'", new_state.name(), old_state.name());
mem::swap(old_state, &mut new_state); mem::swap(old_state, &mut new_state);

View File

@ -3,7 +3,7 @@ mod ui;
use crate::{ use crate::{
window::{Event, Window}, window::{Event, Window},
session::SessionState, session::SessionState,
GlobalState, PlayState, PlayStateResult, Direction, GlobalState, PlayState, PlayStateResult,
}; };
use client::{self, Client}; use client::{self, Client};
use common::{ use common::{
@ -40,7 +40,7 @@ const BG_COLOR: Rgba<f32> = Rgba {
}; };
impl PlayState for CharSelectionState { 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 // Set up an fps clock
let mut clock = Clock::new(); let mut clock = Clock::new();
@ -49,7 +49,6 @@ impl PlayState for CharSelectionState {
for event in global_state.window.fetch_events() { for event in global_state.window.fetch_events() {
match event { match event {
Event::Close => { Event::Close => {
global_state.singleplayer = None;
return PlayStateResult::Shutdown; return PlayStateResult::Shutdown;
}, },
// Pass events to ui // 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()) { for event in self.char_selection_ui.maintain(global_state.window.renderer_mut()) {
match event { match event {
ui::Event::Logout => { ui::Event::Logout => {
global_state.singleplayer = None;
return PlayStateResult::Pop; return PlayStateResult::Pop;
}, },
ui::Event::Play => { ui::Event::Play => {

View File

@ -1,11 +1,12 @@
mod client_init; mod client_init;
mod start_singleplayer;
mod ui; mod ui;
use start_singleplayer::StartSingleplayerState;
use super::char_selection::CharSelectionState; use super::char_selection::CharSelectionState;
use crate::{ use crate::{
window::{Event, Window}, window::{Event, Window},
GlobalState, PlayState, PlayStateResult, Direction, GlobalState, PlayState, PlayStateResult,
singleplayer::Singleplayer,
}; };
use client_init::{ClientInit, Error as InitError}; use client_init::{ClientInit, Error as InitError};
use common::{clock::Clock, comp}; use common::{clock::Clock, comp};
@ -28,6 +29,8 @@ impl MainMenuState {
} }
} }
const DEFAULT_PORT: u16 = 59003;
// Background colour // Background colour
const BG_COLOR: Rgba<f32> = Rgba { const BG_COLOR: Rgba<f32> = Rgba {
r: 0.0, r: 0.0,
@ -37,7 +40,7 @@ const BG_COLOR: Rgba<f32> = Rgba {
}; };
impl PlayState for MainMenuState { 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 // Set up an fps clock
let mut clock = Clock::new(); let mut clock = Clock::new();
@ -98,7 +101,6 @@ impl PlayState for MainMenuState {
net_settings.servers.push(server_address.clone()); net_settings.servers.push(server_address.clone());
} }
global_state.settings.save_to_file(); global_state.settings.save_to_file();
const DEFAULT_PORT: u16 = 59003;
// Don't try to connect if there is already a connection in progress // Don't try to connect if there is already a connection in progress
client_init = client_init.or(Some(ClientInit::new( client_init = client_init.or(Some(ClientInit::new(
(server_address, DEFAULT_PORT, false), (server_address, DEFAULT_PORT, false),
@ -109,7 +111,7 @@ impl PlayState for MainMenuState {
))); )));
}, },
MainMenuEvent::StartSingleplayer => { MainMenuEvent::StartSingleplayer => {
global_state.singleplayer = Some(Singleplayer::new()); return PlayStateResult::Push(Box::new(StartSingleplayerState::new()));
}, },
MainMenuEvent::Quit => return PlayStateResult::Shutdown, MainMenuEvent::Quit => return PlayStateResult::Shutdown,
} }

View File

@ -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"
}
}

View File

@ -176,10 +176,11 @@ impl MainMenuUi {
.top_left_with_margins(30.0, 30.0) .top_left_with_margins(30.0, 30.0)
.set(self.ids.v_logo, ui_widgets); .set(self.ids.v_logo, ui_widgets);
Text::new(version) Text::new(version)
.top_left_with_margins_on(ui_widgets.window, 5.0, 5.0) .top_left_with_margins_on(ui_widgets.window, 5.0, 5.0)
.font_size(14) .font_size(14)
.color(TEXT_COLOR) .color(TEXT_COLOR)
.set(self.ids.version, ui_widgets); .set(self.ids.version, ui_widgets);
// Input fields // Input fields
// Used when the login button is pressed, or enter is pressed within input field // Used when the login button is pressed, or enter is pressed within input field
macro_rules! login { macro_rules! login {
@ -390,7 +391,7 @@ impl MainMenuUi {
}; };
// Singleplayer button // Singleplayer button
if Button::image(self.imgs.button) if Button::image(self.imgs.button)
.hover_image(self.imgs.button_hover) .hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press) .press_image(self.imgs.button_press)
.w_h(258.0, 68.0) .w_h(258.0, 68.0)

View File

@ -6,6 +6,7 @@ use client::{
Client, Client,
}; };
use crate::{ use crate::{
Direction,
Error, Error,
PlayState, PlayState,
PlayStateResult, PlayStateResult,
@ -100,7 +101,7 @@ impl SessionState {
} }
impl PlayState for 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 // Trap the cursor
global_state.window.grab_cursor(true); global_state.window.grab_cursor(true);
@ -129,7 +130,6 @@ impl PlayState for SessionState {
} }
let _handled = match event { let _handled = match event {
Event::Close => { Event::Close => {
global_state.singleplayer = None;
return PlayStateResult::Shutdown; return PlayStateResult::Shutdown;
}, },
// Toggle cursor grabbing // Toggle cursor grabbing
@ -171,7 +171,6 @@ impl PlayState for SessionState {
}, },
HudEvent::Logout => return PlayStateResult::Pop, HudEvent::Logout => return PlayStateResult::Pop,
HudEvent::Quit => { HudEvent::Quit => {
global_state.singleplayer = None;
return PlayStateResult::Shutdown; return PlayStateResult::Shutdown;
}, },
} }