skullball/rust/src/ui/main_menu.rs
2026-07-23 19:38:05 +03:00

247 lines
7.2 KiB
Rust

use godot::{
classes::{Button, Camera3D, Label, Panel, SpinBox, TextEdit},
prelude::*,
register::property::SimpleVar,
};
use crate::{
net::network_manager::NetworkManager,
popup_queue::{Popup, PopupQueue},
};
#[derive(Debug, Clone, GodotConvert, Default, Export)]
#[godot(via = u8)]
pub enum Menu {
#[default]
MainMenu,
HostMenu,
JoinMenu,
}
impl SimpleVar for Menu {}
#[derive(GodotClass)]
#[class(base=Node3D, init, tool)]
pub struct MainMenu {
#[export]
camera: Option<Gd<Camera3D>>,
#[export]
main_position: Option<Gd<Node3D>>,
#[export]
host_position: Option<Gd<Node3D>>,
#[export]
main_panel: Option<Gd<Panel>>,
#[export]
host_panel: Option<Gd<Panel>>,
#[export]
join_panel: Option<Gd<Panel>>,
#[export]
host_port: Option<Gd<SpinBox>>,
#[export]
join_addr: Option<Gd<TextEdit>>,
#[export]
popup_panel: Option<Gd<Panel>>,
#[export]
popup_title: Option<Gd<Label>>,
#[export]
popup_text: Option<Gd<Label>>,
#[export]
popup_button: Option<Gd<Button>>,
#[export]
current_menu: Menu,
prev_panel: Option<Gd<Panel>>,
base: Base<Node3D>,
}
#[godot_api]
impl INode3D for MainMenu {
fn ready(&mut self) {
self.hide_popup();
NetworkManager::singleton()
.bind_mut()
.signals()
.on_client_disconnected()
.connect_other(self, |_, msg| {
if msg.is_empty() {
PopupQueue::singleton().bind_mut().queue(Popup {
title: "Disconnected".to_owned(),
message: "disconnected".to_owned(),
ok: true,
});
} else {
PopupQueue::singleton().bind_mut().queue(Popup {
title: "Error".to_owned(),
message: msg.to_string(),
ok: true,
});
}
});
NetworkManager::singleton()
.bind_mut()
.signals()
.on_error_hosting_server()
.connect_other(self, |_, msg| {
PopupQueue::singleton().bind_mut().queue(Popup {
title: "Error hosting server".to_owned(),
message: msg.to_string(),
ok: true,
});
});
}
fn process(&mut self, _delta: f64) {
if let Some(camera) = &mut self.camera {
let target = match self.current_menu {
Menu::MainMenu => {
if let Some(pos) = &self.main_position {
Some((
pos.get_global_position(),
pos.get_global_rotation(),
self.main_panel.clone(),
))
} else {
None
}
}
Menu::HostMenu => {
if let Some(pos) = &self.host_position {
Some((
pos.get_global_position(),
pos.get_global_rotation(),
self.host_panel.clone(),
))
} else {
None
}
}
Menu::JoinMenu => {
if let Some(pos) = &self.host_position {
Some((
pos.get_global_position(),
pos.get_global_rotation(),
self.join_panel.clone(),
))
} else {
None
}
}
};
if let Some((target_pos, target_rot, target_panel)) = target {
let prev_pos = camera.get_global_position();
let prev_rot = camera.get_global_rotation();
let new_pos = prev_pos.lerp(target_pos, 0.2);
let new_rot = prev_rot.lerp(target_rot, 0.2);
camera.set_global_position(new_pos);
camera.set_global_rotation(new_rot);
if let Some(mut panel) = target_panel {
if let Some(prev_panel) = &mut self.prev_panel {
if prev_panel.instance_id() != panel.instance_id() {
prev_panel.set_visible(false);
}
}
self.prev_panel = Some(panel.clone());
panel.set_visible(true);
}
}
}
if let Some(popup_panel) = self.popup_panel.clone()
&& let Some(button) = self.popup_button.clone()
{
if !popup_panel.is_visible() || !button.is_visible() {
if let Some(popup) = PopupQueue::singleton().bind_mut().poll() {
self.show_popup(&popup.title, &popup.message, popup.ok);
}
}
}
}
}
#[godot_api]
impl MainMenu {
#[func]
pub fn host_game(&mut self) {
if let Some(host_port) = &self.host_port {
NetworkManager::singleton()
.bind_mut()
.host(host_port.get_value() as u16);
}
}
#[func]
pub fn join_game(&mut self) {
if let Some(addr) = self.join_addr.clone() {
self.show_popup("Joining", "joining game", false);
PopupQueue::singleton().bind_mut().queue(Popup {
title: "Joining".to_owned(),
message: "joining game".to_owned(),
ok: false,
});
match addr.get_text().to_string().parse() {
Ok(addr) => {
NetworkManager::singleton().bind_mut().join(addr);
}
Err(err) => {
PopupQueue::singleton().bind_mut().queue(Popup {
title: "Error".to_owned(),
message: format!("Error joining: {}", err),
ok: true,
});
}
}
}
}
#[func]
pub fn go_to_main_menu(&mut self) {
self.current_menu = Menu::MainMenu;
}
#[func]
pub fn go_to_host_game(&mut self) {
self.current_menu = Menu::HostMenu;
}
#[func]
pub fn go_to_join_game(&mut self) {
self.current_menu = Menu::JoinMenu;
}
#[func]
pub fn quit_game(&mut self) {
self.base().get_tree().quit();
}
pub fn show_popup(&mut self, title: &str, text: &str, ok: bool) {
if let Some(panel) = &mut self.popup_panel {
panel.set_visible(true);
if let Some(title_label) = &mut self.popup_title {
title_label.set_text(title);
}
if let Some(text_label) = &mut self.popup_text {
text_label.set_text(text);
}
if let Some(button) = &mut self.popup_button {
button.set_visible(ok);
}
}
}
#[func]
pub fn hide_popup(&mut self) {
if let Some(panel) = &mut self.popup_panel {
panel.set_visible(false);
}
}
}