Initial main menu implementation

This commit is contained in:
griffi-gh 2024-12-12 17:39:14 +01:00
parent ef28e5d497
commit eec074ae43
6 changed files with 192 additions and 15 deletions

View file

@ -12,18 +12,20 @@ pub fn initialize_from_args(
// If an address is provided, we're in multiplayer mode (the first argument is the address) // If an address is provided, we're in multiplayer mode (the first argument is the address)
// Otherwise, we're in singleplayer mode and working with local stuff // Otherwise, we're in singleplayer mode and working with local stuff
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
if args.len() > 1 { if args.get(1) == Some(&"play".into()) {
// Parse the address and switch the state to connecting
let address = args[1].parse::<SocketAddr>().expect("invalid address");
all_storages.add_unique(GameType::Muliplayer);
all_storages.add_unique(ServerAddress(address));
all_storages.borrow::<UniqueViewMut<NextState>>().unwrap().0 = Some(GameState::Connecting);
} else {
// Open the local save file // Open the local save file
let save_file = open_local_save_file(Path::new("./world.kubi")).expect("failed to open save file"); let save_file = open_local_save_file(Path::new("./world.kubi")).expect("failed to open save file");
all_storages.add_unique(IOThreadManager::new(save_file)); all_storages.add_unique(IOThreadManager::new(save_file));
// Switch the state and kick off the world loading // Switch the state and kick off the world loading
all_storages.add_unique(GameType::Singleplayer); all_storages.add_unique(GameType::Singleplayer);
all_storages.borrow::<UniqueViewMut<NextState>>().unwrap().0 = Some(GameState::LoadingWorld); all_storages.borrow::<UniqueViewMut<NextState>>().unwrap().0 = Some(GameState::LoadingWorld);
} else if args.len() > 1 {
// Parse the address and switch the state to connecting
let address = args[1].parse::<SocketAddr>().expect("invalid address");
all_storages.add_unique(GameType::Muliplayer);
all_storages.add_unique(ServerAddress(address));
all_storages.borrow::<UniqueViewMut<NextState>>().unwrap().0 = Some(GameState::Connecting);
} else {
all_storages.borrow::<UniqueViewMut<NextState>>().unwrap().0 = Some(GameState::MainMenu);
} }
} }

View file

@ -16,6 +16,7 @@ use shipyard::{
WorkloadModificator, WorkloadModificator,
SystemModificator SystemModificator
}; };
use ui::main_menu::update_main_menu;
use winit::{ use winit::{
event_loop::{EventLoop, ControlFlow}, event_loop::{EventLoop, ControlFlow},
event::{Event, WindowEvent} event::{Event, WindowEvent}
@ -35,6 +36,7 @@ pub(crate) use ui::{
crosshair_ui, crosshair_ui,
settings_ui, settings_ui,
shutdown_screen, shutdown_screen,
main_menu,
}; };
pub(crate) mod rendering; pub(crate) mod rendering;
pub(crate) mod world; pub(crate) mod world;
@ -77,10 +79,11 @@ use block_placement::update_block_placement;
use delta_time::{DeltaTime, init_delta_time}; use delta_time::{DeltaTime, init_delta_time};
use cursor_lock::{debug_toggle_lock, insert_lock_state, lock_cursor_now, update_cursor_lock_state}; use cursor_lock::{debug_toggle_lock, insert_lock_state, lock_cursor_now, update_cursor_lock_state};
use control_flow::{exit_on_esc, insert_control_flow_unique, RequestExit}; use control_flow::{exit_on_esc, insert_control_flow_unique, RequestExit};
use state::{init_state, is_connecting, is_ingame, is_ingame_or_loading, is_loading, is_shutting_down, update_state}; use state::{init_state, is_connecting, is_ingame, is_ingame_or_loading, is_ingame_or_loading_or_connecting_or_shutting_down, is_loading, is_main_menu, is_shutting_down, update_state};
use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer}; use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer};
use init::initialize_from_args; use init::initialize_from_args;
use hui_integration::{kubi_ui_begin, /*kubi_ui_draw,*/ kubi_ui_end, kubi_ui_init}; use hui_integration::{kubi_ui_begin, /*kubi_ui_draw,*/ kubi_ui_end, kubi_ui_init};
use main_menu::render_main_menu_ui;
use loading_screen::update_loading_screen; use loading_screen::update_loading_screen;
use shutdown_screen::update_shutdown_screen; use shutdown_screen::update_shutdown_screen;
use connecting_screen::update_connecting_screen; use connecting_screen::update_connecting_screen;
@ -109,7 +112,7 @@ fn startup() -> Workload {
insert_lock_state, insert_lock_state,
init_state, init_state,
initialize_from_args, initialize_from_args,
lock_cursor_now, // lock_cursor_now,
init_input, init_input,
insert_control_flow_unique, insert_control_flow_unique,
init_delta_time, init_delta_time,
@ -126,13 +129,18 @@ fn update() -> Workload {
update_cursor_lock_state, update_cursor_lock_state,
process_inputs, process_inputs,
kubi_ui_begin, kubi_ui_begin,
(
update_main_menu
).into_sequential_workload().run_if(is_main_menu),
( (
init_game_world.run_if_missing_unique::<ChunkTaskManager>(), init_game_world.run_if_missing_unique::<ChunkTaskManager>(),
( (
spawn_player.run_if_storage_empty::<MainPlayer>(), spawn_player.run_if_storage_empty::<MainPlayer>(),
).into_sequential_workload().run_if(is_singleplayer), ).into_sequential_workload().run_if(is_singleplayer),
).into_sequential_workload().run_if(is_ingame_or_loading), ).into_sequential_workload().run_if(is_ingame_or_loading),
update_networking().run_if(is_multiplayer), (
update_networking
).into_sequential_workload().run_if(is_multiplayer).run_if(is_ingame_or_loading_or_connecting_or_shutting_down),
( (
update_connecting_screen, update_connecting_screen,
).into_sequential_workload().run_if(is_connecting), ).into_sequential_workload().run_if(is_connecting),
@ -158,7 +166,9 @@ fn update() -> Workload {
( (
update_shutdown_screen, update_shutdown_screen,
).into_sequential_workload().run_if(is_shutting_down), ).into_sequential_workload().run_if(is_shutting_down),
update_networking_late.run_if(is_multiplayer), (
update_networking_late
).into_sequential_workload().run_if(is_multiplayer).run_if(is_ingame_or_loading_or_connecting_or_shutting_down),
compute_cameras, compute_cameras,
kubi_ui_end, kubi_ui_end,
exit_on_esc, exit_on_esc,

View file

@ -11,9 +11,9 @@ use kubi_shared::networking::{
}; };
use crate::{ use crate::{
events::EventComponent, events::EventComponent,
fixed_timestamp::FixedTimestamp,
state::{is_ingame_or_loading, is_ingame_or_loading_or_connecting_or_shutting_down},
world::tasks::ChunkTaskManager, world::tasks::ChunkTaskManager,
state::is_ingame_or_loading,
fixed_timestamp::FixedTimestamp
}; };
mod handshake; mod handshake;
@ -185,13 +185,15 @@ fn is_join_state<const STATE: u8>(
} }
pub fn is_multiplayer( pub fn is_multiplayer(
game_type: UniqueView<GameType> game_type: Option<UniqueView<GameType>>
) -> bool { ) -> bool {
let Some(game_type) = game_type else { return false };
*game_type == GameType::Muliplayer *game_type == GameType::Muliplayer
} }
pub fn is_singleplayer( pub fn is_singleplayer(
game_type: UniqueView<GameType> game_type: Option<UniqueView<GameType>>
) -> bool { ) -> bool {
let Some(game_type) = game_type else { return false };
*game_type == GameType::Singleplayer *game_type == GameType::Singleplayer
} }

View file

@ -5,6 +5,7 @@ use std::mem::take;
pub enum GameState { pub enum GameState {
#[default] #[default]
Initial, Initial,
MainMenu,
Connecting, Connecting,
LoadingWorld, LoadingWorld,
InGame, InGame,
@ -34,6 +35,12 @@ pub fn is_changing_state(
state.0.is_some() state.0.is_some()
} }
pub fn is_main_menu(
state: UniqueView<GameState>
) -> bool {
*state == GameState::MainMenu
}
pub fn is_connecting( pub fn is_connecting(
state: UniqueView<GameState> state: UniqueView<GameState>
) -> bool { ) -> bool {
@ -64,6 +71,12 @@ pub fn is_ingame_or_loading(
matches!(*state, GameState::InGame | GameState::LoadingWorld) matches!(*state, GameState::InGame | GameState::LoadingWorld)
} }
pub fn is_ingame_or_loading_or_connecting_or_shutting_down(
state: UniqueView<GameState>
) -> bool {
matches!(*state, GameState::InGame | GameState::LoadingWorld | GameState::Connecting)
}
pub fn is_ingame_or_shutting_down( pub fn is_ingame_or_shutting_down(
state: UniqueView<GameState> state: UniqueView<GameState>
) -> bool { ) -> bool {

View file

@ -1,3 +1,4 @@
pub(crate) mod main_menu;
pub(crate) mod loading_screen; pub(crate) mod loading_screen;
pub(crate) mod connecting_screen; pub(crate) mod connecting_screen;
pub(crate) mod shutdown_screen; pub(crate) mod shutdown_screen;

149
kubi/src/ui/main_menu.rs Normal file
View file

@ -0,0 +1,149 @@
use std::path::Path;
use glam::vec4;
use hui::{
element::{
container::Container,
interactable::ElementInteractableExt,
text::Text,
UiElementExt
},
layout::Alignment,
rect::Corners,
signal::Signal,
rect_frame,
size,
};
use kubi_shared::data::{io_thread::IOThreadManager, open_local_save_file};
use shipyard::{AllStoragesView, AllStoragesViewMut, IntoWorkload, NonSendSync, Unique, UniqueView, UniqueViewMut, Workload};
use crate::{
control_flow::RequestExit,
hui_integration::UiState, networking::GameType, rendering::Renderer, state::{GameState, NextState}};
#[derive(Clone, Copy)]
enum MainMenuPage {
TopMenu,
// ListWorlds {
// list: Vec<String>,
// },
}
#[derive(Unique)]
struct MainMenuState {
page: MainMenuPage,
}
#[derive(Signal, Clone, Copy)]
enum MainMenuSignal {
GotoPage(MainMenuPage),
Play,
Quit,
// CreatePlayWorld {
// name: String,
// },
// PlayWorld {
// path: PathBuf,
// },
}
pub fn main_menu_leave(
storages: AllStoragesView,
) {
if storages.remove_unique::<MainMenuState>().is_err() {
log::warn!("what the fuck? shouldn't matter tho")
}
}
pub fn render_main_menu_ui(
mut hui: NonSendSync<UniqueViewMut<UiState>>,
ren: UniqueView<Renderer>,
) {
Container::default()
.with_size(size!(100%, 100%))
.with_padding(30.)
.with_gap(20.)
.with_background(Corners::top_bottom(
vec4(0.0, 0.0, 0.0, 0.85),
vec4(0.0, 0.0, 0.0, 0.0))
)
.with_children(|ui| {
Container::default()
.with_size(size!(100%, auto))
.with_align(Alignment::Center)
.with_children(|ui| {
Text::new("Kubi")
.with_text_size(120)
.add_child(ui);
})
.add_child(ui);
Container::default()
.with_size(size!(100%, 100%=))
.with_align(Alignment::Center)
.with_children(|ui| {
Container::default()
.with_align((Alignment::Center, Alignment::Begin))
.with_padding(20.)
.with_gap(10.)
.with_background(rect_frame! {
color: (0.1, 0.1, 0.1),
corner_radius: 10.,
})
.with_children(|ui| {
for (button_text, button_signal) in [
("Play", MainMenuSignal::Play),
("Quit", MainMenuSignal::Quit),
] {
Container::default()
.with_size(size!(300, 50))
.with_align(Alignment::Center)
.with_background(rect_frame! {
color: (0.2, 0.2, 0.2),
corner_radius: 3.,
})
.with_children(|ui| {
Text::new(button_text)
.with_text_size(24)
.add_child(ui);
})
.on_click(move || button_signal)
.add_child(ui)
}
})
.add_child(ui);
})
.add_child(ui);
})
.add_root(&mut hui.hui, ren.size_vec2());
}
fn main_menu_process_signals(
storages: AllStoragesViewMut,
) {
let mut hui = storages.borrow::<NonSendSync<UniqueViewMut<UiState>>>().unwrap();
let mut quit = storages.borrow::<UniqueViewMut<RequestExit>>().unwrap();
hui.hui.process_signals(|signal| {
match signal {
MainMenuSignal::Play => {
log::info!("play button pressed");
// Open the local save file
let save_file = open_local_save_file(Path::new("./world.kubi")).expect("failed to open save file");
storages.add_unique(IOThreadManager::new(save_file));
// Switch the state and kick off the world loading
storages.add_unique(GameType::Singleplayer);
storages.borrow::<UniqueViewMut<NextState>>().unwrap().0 = Some(GameState::LoadingWorld);
}
MainMenuSignal::Quit => {
log::info!("quit button pressed");
quit.0 = true;
}
_ => {}
}
});
}
pub fn update_main_menu() -> Workload {
(
render_main_menu_ui,
main_menu_process_signals,
).into_sequential_workload()
}