556 lines
20 KiB
Rust
556 lines
20 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use godot::{
|
|
classes::{
|
|
Button, CheckBox, CompressedTexture2D, Control, GridContainer, IPanel, Label, LineEdit,
|
|
OptionButton, Panel, SpinBox, TabContainer, VBoxContainer,
|
|
},
|
|
prelude::*,
|
|
};
|
|
|
|
use crate::{
|
|
bgm::{Music, MusicManager},
|
|
game::{
|
|
Game, GameManager,
|
|
opts::{GameOption, GameOptionValue, GameOptions, TeamOptions},
|
|
},
|
|
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>>,
|
|
#[export]
|
|
options_tabs: Option<Gd<TabContainer>>,
|
|
#[export]
|
|
alt_colors: Option<Gd<CheckBox>>,
|
|
|
|
option_nodes: HashMap<GameOption, Gd<Control>>,
|
|
team_grids: HashMap<u8, Gd<GridContainer>>,
|
|
team_option_nodes: HashMap<u8, HashMap<GameOption, Gd<Control>>>,
|
|
|
|
#[export]
|
|
start_game_icon: Option<Gd<CompressedTexture2D>>,
|
|
#[export]
|
|
ready_icon: Option<Gd<CompressedTexture2D>>,
|
|
#[export]
|
|
not_ready_icon: Option<Gd<CompressedTexture2D>>,
|
|
|
|
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() {
|
|
if let Some(mut alt_colors) = self.alt_colors.clone() {
|
|
alt_colors.set_pressed(game.bind().private_opts.use_alternate_team_colors);
|
|
alt_colors.signals().pressed().connect_other(self, |s| {
|
|
if let Some(alt_colors) = &s.alt_colors {
|
|
s.use_alt_colors_changed(alt_colors.is_pressed());
|
|
}
|
|
s.update_players(false);
|
|
s.signals().on_alt_colors_changed().emit();
|
|
});
|
|
}
|
|
|
|
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(),
|
|
);
|
|
}
|
|
});
|
|
});
|
|
game.signals()
|
|
.team_options_changed()
|
|
.connect_other(self, |s, team| {
|
|
s.run_deferred(move |s| {
|
|
if let Some(game) = Game::singleton() {
|
|
if let Some(opts) = game.bind().get_team_opts(team) {
|
|
s.on_team_opts_update(team, &opts);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
game.signals().on_map_changed().connect_other(self, |s, _| {
|
|
s.run_deferred(|s| {
|
|
s.generate_team_options();
|
|
});
|
|
});
|
|
|
|
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);
|
|
|
|
self.update_ready_button();
|
|
|
|
let nodes = if let Some(game) = Game::singleton() {
|
|
self.generate_options(&game.bind().opts.values, None)
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
if let Some(grid) = &mut self.options_grid {
|
|
for node in nodes {
|
|
grid.add_child(&node);
|
|
}
|
|
}
|
|
|
|
self.generate_team_options();
|
|
|
|
MusicManager::singleton()
|
|
.bind_mut()
|
|
.play(Some(Music::MainMenu), false);
|
|
}
|
|
}
|
|
|
|
#[godot_api]
|
|
impl LobbyPanel {
|
|
#[signal]
|
|
pub fn on_alt_colors_changed();
|
|
|
|
#[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 use_alt_colors_changed(&mut self, value: bool) {
|
|
if let Some(game) = &mut Game::singleton() {
|
|
game.bind_mut().private_opts.use_alternate_team_colors = value;
|
|
}
|
|
}
|
|
|
|
fn option_changed(&mut self, key: GameOption, value: GameOptionValue) {
|
|
if let Some(game) = &mut Game::singleton() {
|
|
game.bind_mut().change_option(key, value);
|
|
}
|
|
}
|
|
|
|
fn team_option_changed(&mut self, team: u8, key: GameOption, value: GameOptionValue) {
|
|
if let Some(game) = &mut Game::singleton() {
|
|
game.bind_mut().change_team_option(team, 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() {
|
|
update_control(control, value);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
fn on_team_opts_update(&mut self, team: u8, opts: &TeamOptions) {
|
|
let values = opts.values.clone();
|
|
if let Some(nodes) = self.team_option_nodes.get(&team) {
|
|
let nodes = nodes.clone();
|
|
self.run_deferred_gd(move |_| {
|
|
for (key, value) in values {
|
|
if let Some(control) = nodes.get(&key).cloned() {
|
|
update_control(control, 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_text("Start Game");
|
|
if let Some(icon) = &self.start_game_icon {
|
|
button.set_button_icon(icon);
|
|
}
|
|
button.set_disabled(!all_ready);
|
|
} else {
|
|
if let Some(button) = &mut self.ready_button {
|
|
if let Some(icon) = &self.ready_icon
|
|
&& self.is_ready
|
|
{
|
|
button.set_button_icon(icon);
|
|
button.set_text("Ready");
|
|
} else if let Some(icon) = &self.not_ready_icon
|
|
&& !self.is_ready
|
|
{
|
|
button.set_button_icon(icon);
|
|
button.set_text("Not 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;
|
|
self.update_ready_button();
|
|
|
|
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) {
|
|
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
|
|
}
|
|
|
|
fn generate_team_options(&mut self) {
|
|
if let Some(mut tabs) = self.options_tabs.clone() {
|
|
for (_, grid) in self.team_grids.clone() {
|
|
tabs.remove_child(&grid);
|
|
}
|
|
self.team_grids.clear();
|
|
self.team_option_nodes.clear();
|
|
|
|
if let Some(game) = Game::singleton() {
|
|
for (id, team) in game.bind().get_teams().iter_shared().enumerate() {
|
|
let mut grid = GridContainer::new_alloc();
|
|
grid.set_name(&team.bind().name.to_string());
|
|
grid.set_columns(2);
|
|
|
|
if let Some(opts) = game.bind().get_team_opts(id as u8) {
|
|
let nodes = self.generate_options(&opts.values, Some(id as u8));
|
|
for node in nodes {
|
|
grid.add_child(&node);
|
|
}
|
|
}
|
|
tabs.add_child(&grid);
|
|
self.team_grids.insert(id as u8, grid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn generate_options(
|
|
&mut self,
|
|
opts: &HashMap<GameOption, GameOptionValue>,
|
|
team: Option<u8>,
|
|
) -> Vec<Gd<Control>> {
|
|
let mut nodes = Vec::new();
|
|
|
|
let mut keys = opts.keys().collect::<Vec<_>>();
|
|
keys.sort();
|
|
for option in keys {
|
|
let value = opts.get(option).unwrap();
|
|
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| {
|
|
if let Some(team) = team {
|
|
s.team_option_changed(
|
|
team,
|
|
option,
|
|
GameOptionValue::Boolean(checkbox_clone.is_pressed()),
|
|
);
|
|
} else {
|
|
s.option_changed(
|
|
option,
|
|
GameOptionValue::Boolean(checkbox_clone.is_pressed()),
|
|
);
|
|
}
|
|
});
|
|
|
|
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| {
|
|
if let Some(team) = team {
|
|
s.team_option_changed(
|
|
team,
|
|
option,
|
|
GameOptionValue::Number(new_value),
|
|
);
|
|
} else {
|
|
s.option_changed(option, GameOptionValue::Number(new_value));
|
|
}
|
|
});
|
|
|
|
spinbox.upcast()
|
|
}
|
|
};
|
|
if let Some(team) = team {
|
|
if let Some(nodes) = self.team_option_nodes.get_mut(&team) {
|
|
nodes.insert(option.clone(), value_node.clone());
|
|
} else {
|
|
let mut nodes = HashMap::new();
|
|
nodes.insert(option.clone(), value_node.clone());
|
|
self.team_option_nodes.insert(team, nodes);
|
|
}
|
|
} else {
|
|
self.option_nodes.insert(option.clone(), value_node.clone());
|
|
}
|
|
|
|
nodes.push(value_node);
|
|
}
|
|
nodes
|
|
}
|
|
}
|
|
|
|
fn update_control(control: Gd<Control>, value: GameOptionValue) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|