use godot::{ classes::{Button, Camera3D, FileDialog, Label, LineEdit, OptionButton, Panel, SpinBox}, prelude::*, register::property::SimpleVar, }; use crate::{ bgm::{Music, MusicManager}, game_settings::{GameSettingsManager, RecentServer}, net::network_manager::NetworkManager, popup_queue::{Popup, PopupQueue}, replay::StandaloneReplayManager, ui::credits::CreditsMenu, }; #[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>, #[export] main_position: Option>, #[export] host_position: Option>, #[export] main_panel: Option>, #[export] host_panel: Option>, #[export] join_panel: Option>, #[export] host_port: Option>, #[export] join_addr: Option>, #[export] join_port: Option>, #[export] recent_servers_button: Option>, #[export] popup_panel: Option>, #[export] popup_title: Option>, #[export] popup_text: Option>, #[export] popup_button: Option>, #[export] current_menu: Menu, #[export] credits: Option>, #[export] open_replay_dialog: Option>, prev_panel: Option>, recent_servers: Vec, focus_next: bool, base: Base, } #[godot_api] impl INode3D for MainMenu { fn ready(&mut self) { if let Some(mut recent_servers_button) = self.recent_servers_button.clone() { self.recent_servers = GameSettingsManager::singleton() .bind() .settings .recent_servers .clone(); for server in &self.recent_servers { recent_servers_button.add_item(&format!("{}:{}", server.host, server.port)); } recent_servers_button.select(-1); recent_servers_button .signals() .item_selected() .connect_other(self, |s, idx| s.on_recent_server_selected(idx)); } if let Some(dialog) = &self.open_replay_dialog { dialog .signals() .file_selected() .connect_other(self, Self::on_open_replay); } self.hide_popup(); MusicManager::singleton() .bind_mut() .play(Some(Music::MainMenu), false); self.focus_next = 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 { let mut changed = false; if let Some(prev_panel) = &mut self.prev_panel { if prev_panel.instance_id() != panel.instance_id() { changed = true; prev_panel.set_visible(false); } } self.prev_panel = Some(panel.clone()); panel.set_visible(true); if changed || self.focus_next { if let Some(focus) = &mut panel.find_next_valid_focus() { focus.grab_focus(); } self.focus_next = false; } } } } 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 open_replay_dialog(&mut self) { if let Some(dialog) = &mut self.open_replay_dialog { dialog.popup_file_dialog(); } } pub fn on_open_replay(&mut self, path: GString) { self.run_deferred(move |_| { StandaloneReplayManager::singleton() .bind_mut() .open_replay(&path); }); } #[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() && let Some(port) = self.join_port.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 NetworkManager::singleton() .bind_mut() .join(addr.get_text().to_string(), port.get_value() as u16) { Ok(_) => {} Err(_) => { PopupQueue::singleton().bind_mut().queue(Popup { title: "Error".to_string(), message: "Address is invalid".to_string(), 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) { godot_print!("what?"); 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(); } #[func] pub fn open_credits(&mut self) { if let Some(credits) = self.credits.clone() { let credits = credits.instantiate_as::(); self.base_mut().add_child(&credits); } } 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); } if let Some(focus) = &mut panel.find_next_valid_focus() { focus.grab_focus(); } } } #[func] pub fn hide_popup(&mut self) { if let Some(panel) = &mut self.popup_panel { panel.set_visible(false); self.focus_next = true; } } pub fn on_recent_server_selected(&mut self, idx: i64) { if idx >= 0 { if let Some(server) = self.recent_servers.get(idx as usize) && let Some(addr) = &mut self.join_addr && let Some(port) = &mut self.join_port { addr.set_text(&server.host); port.set_value(server.port as f64); } if let Some(button) = &mut self.recent_servers_button { button.select(-1); } } } #[func] pub fn focus_next(&mut self) { self.focus_next = true; } }