skullball/rust/src/ui/lobby.rs
2026-07-30 19:20:47 +03:00

412 lines
14 KiB
Rust

use std::collections::HashMap;
use godot::{
classes::{
Button, CheckBox, Control, GridContainer, IPanel, Label, LineEdit, OptionButton, Panel,
SpinBox, VBoxContainer,
},
prelude::*,
};
use crate::{
bgm::{Music, MusicManager},
cli_parser::CliParser,
game::{
Game, GameManager,
opts::{GameOption, GameOptionValue, GameOptions},
},
net::network_manager::{NetworkManager, Package, PeerKind},
ui::lobby_player_listing::LobbyPlayerListing,
};
#[derive(GodotClass)]
#[class(base=Panel, init)]
pub struct LobbyPanel {
#[export]
players_list: Option<Gd<VBoxContainer>>,
#[export]
name_field: Option<Gd<LineEdit>>,
#[export]
player_listing_prefab: Option<Gd<PackedScene>>,
#[export]
ready_button: Option<Gd<Button>>,
#[export]
map_selection: Option<Gd<OptionButton>>,
#[export]
options_panel: Option<Gd<Panel>>,
#[export]
options_grid: Option<Gd<GridContainer>>,
option_nodes: HashMap<GameOption, Gd<Control>>,
is_ready: bool,
base: Base<Panel>,
}
#[godot_api]
impl IPanel for LobbyPanel {
fn ready(&mut self) {
GameManager::singleton()
.signals()
.on_new_player()
.connect_other(self, |s, _| s.update_players(true));
GameManager::singleton()
.signals()
.on_player_disconnected()
.connect_other(self, |s, _| s.update_players(true));
GameManager::singleton()
.signals()
.on_players_updated()
.connect_other(self, |s| s.update_players(false));
GameManager::singleton()
.signals()
.on_map_changed()
.connect_other(self, |s, id| s.update_map_selection(id));
if let Some(ready_button) = &self.ready_button {
ready_button
.signals()
.pressed()
.connect_other(self, |s| s.on_ready());
}
if let Some(map_selection) = &self.map_selection {
map_selection
.signals()
.item_selected()
.connect_other(self, |s, _| {
if let Some(map_selection) = &s.map_selection {
s.update_map_selection(map_selection.get_selected_id() as u8);
}
});
}
if let Some(game) = Game::singleton() {
game.signals().options_changed().connect_other(self, |s| {
s.run_deferred(|s| {
if let Some(game) = Game::singleton() {
s.on_game_opts_update(
&game.bind().opts,
NetworkManager::singleton().bind().is_host(),
);
}
});
});
let is_host = if let Some(peer) = &NetworkManager::singleton().bind().peer {
peer.is_host()
} else {
false
};
if let Some(mut map_selection) = self.map_selection.clone() {
let mut prev_selection = game.bind().selected_map_idx;
let prev_selection_hidden =
if let Some(map) = game.bind().maps.get(prev_selection as usize) {
map.bind().hidden
} else {
false
};
if prev_selection_hidden && !game.bind().forced_map_selection {
if let Some((id, _)) = game
.bind()
.maps
.iter_shared()
.enumerate()
.find(|(_, m)| !m.bind().hidden)
{
prev_selection = id as u8;
}
}
for (id, map) in game.bind().maps.iter_shared().enumerate() {
if map.bind().hidden {
continue;
}
if let Some(icon) = &map.bind().icon {
map_selection
.add_icon_item_ex(icon, &map.bind().name)
.id(id as i32)
.done();
} else {
map_selection
.add_item_ex(&map.bind().name)
.id(id as i32)
.done();
}
}
let idx = self.find_idx(prev_selection);
map_selection.select(idx);
if is_host {
self.update_map_selection(prev_selection as u8);
}
}
self.on_game_opts_update(&game.bind().opts, is_host);
}
if let Some(name_field) = &self.name_field {
name_field
.signals()
.text_changed()
.connect_other(self, |_, value| {
if let Some(game) = &mut Game::singleton() {
let player_id = game.bind().self_id;
if let Some(player_id) = player_id {
game.bind_mut()
.update_player_nick(player_id, value.to_string());
}
}
NetworkManager::singleton()
.bind_mut()
.update_nick(value.to_string());
});
}
self.update_players(true);
if let Some(peer) = &NetworkManager::singleton().bind().peer
&& peer.is_host()
{
if let Some(button) = &mut self.ready_button {
button.set_text("Start Game");
button.set_disabled(true);
}
}
self.update_ready_button();
let mut nodes = Vec::new();
if let Some(game) = Game::singleton() {
for (option, value) in &game.bind().opts.values {
let mut label = Label::new_alloc();
label.set_text(&format!("{}:", option.to_string()));
nodes.push(label.upcast());
let value_node = match value {
GameOptionValue::Boolean(value) => {
let mut checkbox = CheckBox::new_alloc();
checkbox.set_pressed(*value);
checkbox.set_disabled(!NetworkManager::singleton().bind().is_host());
let checkbox_clone = checkbox.clone();
let option = option.clone();
checkbox.signals().pressed().connect_other(self, move |s| {
s.option_changed(
option,
GameOptionValue::Boolean(checkbox_clone.is_pressed()),
);
});
self.option_nodes.insert(option, checkbox.clone().upcast());
checkbox.upcast::<Control>()
}
GameOptionValue::Number(value) => {
let mut spinbox = SpinBox::new_alloc();
spinbox.set_value(*value);
spinbox.set_editable(NetworkManager::singleton().bind().is_host());
let option = option.clone();
spinbox.signals().value_changed().connect_other(
self,
move |s, new_value| {
s.option_changed(option, GameOptionValue::Number(new_value));
},
);
self.option_nodes.insert(option, spinbox.clone().upcast());
spinbox.upcast()
}
};
nodes.push(value_node);
}
}
if let Some(grid) = &mut self.options_grid {
for node in nodes {
grid.add_child(&node);
}
}
MusicManager::singleton()
.bind_mut()
.play(Some(Music::MainMenu), false);
}
}
#[godot_api]
impl LobbyPanel {
#[func]
fn open_options(&mut self) {
if let Some(panel) = &mut self.options_panel {
panel.set_visible(true);
}
}
#[func]
fn close_options(&mut self) {
if let Some(panel) = &mut self.options_panel {
panel.set_visible(false);
}
}
#[func]
pub fn exit(&mut self) {
GameManager::singleton().bind_mut().exit_game();
NetworkManager::singleton().bind_mut().disconnect();
}
fn option_changed(&mut self, key: GameOption, value: GameOptionValue) {
if let Some(game) = &mut Game::singleton() {
game.bind_mut().change_option(key, value);
}
}
fn on_game_opts_update(&mut self, opts: &GameOptions, is_host: bool) {
if let Some(selection) = &mut self.map_selection {
selection.set_disabled(!is_host && !opts.is_true(GameOption::AllowAnyMap));
}
let values = opts.values.clone();
let nodes = self.option_nodes.clone();
self.run_deferred_gd(move |_| {
for (key, value) in values {
if let Some(control) = nodes.get(&key).cloned() {
match value {
GameOptionValue::Boolean(value) => {
if let Ok(mut checkbox) = control.try_cast::<CheckBox>() {
checkbox.set_pressed(value);
}
}
GameOptionValue::Number(value) => {
if let Ok(mut spinbox) = control.try_cast::<SpinBox>() {
spinbox.set_value(value);
}
}
}
}
}
});
}
fn update_ready_button(&mut self) {
if let Some(game) = Game::singleton()
&& let Some(peer) = &NetworkManager::singleton().bind().peer
{
if let PeerKind::Server(_, _) = peer
&& let Some(button) = &mut self.ready_button
{
let all_ready = game.bind().players.iter().all(|p| p.data.ready);
button.set_disabled(!all_ready);
}
}
}
pub fn update_players(&mut self, replace: bool) {
if let Some(game) = &Game::singleton()
&& let Some(player) = game.bind().find_player(game.bind().self_id.unwrap_or(0))
&& let Some(name_label) = &mut self.name_field
{
if player.data.name != name_label.get_text().to_string() {
name_label.set_text(&player.data.name);
}
}
self.update_ready_button();
if let Some(list) = &mut self.players_list {
if replace {
for child in list.get_children().iter_shared() {
list.remove_child(&child);
}
if let Some(game) = &Game::singleton() {
if let Some(prefab) = &self.player_listing_prefab {
for player in &game.bind().players {
if let Some(node) = prefab.instantiate() {
let mut listing = node.cast::<LobbyPlayerListing>();
listing.bind_mut().player_id = Some(player.id());
listing.bind_mut().update_teams(game.bind().get_teams());
listing.bind_mut().update();
list.add_child(&listing);
}
}
}
}
} else {
for child in list.get_children().iter_shared() {
let mut listing = child.cast::<LobbyPlayerListing>();
if let Some(game) = Game::singleton() {
listing.bind_mut().update_teams(game.bind().get_teams());
}
listing.bind_mut().update();
}
}
}
}
pub fn on_ready(&mut self) {
let is_host = if let Some(peer) = &NetworkManager::singleton().bind().peer {
peer.is_host()
} else {
false
};
if !is_host {
self.is_ready = !self.is_ready;
if let Some(button) = &mut self.ready_button {
if self.is_ready {
button.set_text("Ready");
} else {
button.set_text("Not Ready");
}
}
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
if let PeerKind::Client(peer, socket_addr) = peer {
peer.send_reliable(&socket_addr, Package::SetReady(0, self.is_ready));
}
}
} else {
if let Some(game) = &mut Game::singleton() {
game.bind_mut().start_game();
}
}
}
pub fn update_map_selection(&mut self, id: u8) {
godot_print!("update map selection");
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
match peer {
PeerKind::Client(peer, addr) => {
if let Some(mut map_selection) = self.map_selection.clone() {
map_selection.select(self.find_idx(id));
self.run_deferred(|s| s.update_players(false));
peer.send_reliable(addr, Package::SelectMap(id));
}
}
PeerKind::Server(peer, _) => {
if let Some(mut map_selection) = self.map_selection.clone() {
map_selection.select(self.find_idx(id));
}
if let Some(game) = &mut Game::singleton() {
game.bind_mut().change_map_selection(id, false);
}
self.run_deferred(|s| s.update_players(false));
peer.broadcast_reliable(Package::SelectMap(id));
}
}
}
}
pub fn find_idx(&self, map_id: u8) -> i32 {
let mut idx = 0;
if let Some(map_selection) = &self.map_selection {
for i in 0..map_selection.get_item_count() {
if map_selection.get_item_id(i) == map_id as i32 {
idx = i;
}
}
}
idx
}
}