From 3608194e5a86d2cfb7698f5d6a1ba22b7f529c08 Mon Sep 17 00:00:00 2001 From: Sofia Date: Wed, 22 Jul 2026 01:37:13 +0300 Subject: [PATCH] Allow changing teams --- rust/src/game_manager.rs | 49 ++++++++++++++++++++++++++++++++- rust/src/net/network_manager.rs | 1 + rust/src/net/protocol.rs | 18 ++++++++++++ rust/src/ui/player_listing.rs | 41 +++++++++++++++++++++++++-- 4 files changed, 106 insertions(+), 3 deletions(-) diff --git a/rust/src/game_manager.rs b/rust/src/game_manager.rs index ad63616..c7636ed 100644 --- a/rust/src/game_manager.rs +++ b/rust/src/game_manager.rs @@ -10,7 +10,7 @@ use crate::{ net::{ network_manager::{ BallSync, NetworkManager, - Package::{self, Kill, SelfSync, SpawnPlayer, TakeDamage}, + Package::{self, Kill, SelfSync, SetTeam, SpawnPlayer, TakeDamage}, PeerKind, SyncPackage, }, util::NetVector3, @@ -195,6 +195,8 @@ impl Game { #[signal] pub fn on_player_disconnected(id: u16); #[signal] + pub fn on_player_team_changed(id: u16, team: u8); + #[signal] pub fn on_map_changed(id: u8); pub fn singleton() -> Option> { @@ -207,6 +209,21 @@ impl Game { self.selected_map_idx = map_idx; if let Some(map) = self.maps.get(map_idx as usize) { self.selected_map = Some(map); + + let teams_max = (self.get_teams().len() - 1) as u8; + self.run_deferred(move |s| { + if NetworkManager::singleton().bind().is_host() { + for player in s.players.clone() { + s.run_deferred(move |s| { + s.try_set_player_team( + player.id(), + player.data.team.clamp(0, teams_max), + true, + ) + }); + } + } + }); if emit { self.run_deferred(move |s| s.signals().on_map_changed().emit(map_idx)); } @@ -221,6 +238,36 @@ impl Game { } } + pub fn try_set_player_team(&mut self, player_id: u16, team: u8, emit: bool) { + if let Some(player) = self.find_player_mut(player_id) { + player.data.team = team; + self.run_deferred(move |_| { + if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { + match peer { + PeerKind::Client(peer, socket_addr) => { + peer.send_reliable(socket_addr, SetTeam(player_id, team)); + } + PeerKind::Server(peer, _) => { + peer.broadcast_reliable(SetTeam(player_id, team)); + } + } + } + }); + if emit { + self.run_deferred(move |s| { + s.signals().on_player_team_changed().emit(player_id, team) + }); + } + } + } + + pub fn handle_player_team_change(&mut self, player_id: u16, team: u8) { + if let Some(player) = self.find_player_mut(player_id) { + player.data.team = team; + self.run_deferred(move |s| s.signals().on_player_team_changed().emit(player_id, team)); + } + } + pub fn start_game(&mut self) { self.game_started = true; if let Some(map) = &self.selected_map { diff --git a/rust/src/net/network_manager.rs b/rust/src/net/network_manager.rs index 229c2aa..6c12af4 100644 --- a/rust/src/net/network_manager.rs +++ b/rust/src/net/network_manager.rs @@ -255,6 +255,7 @@ pub enum Package { SetSelfNick(String), NewPlayer(NetPlayer), PlayerLeft(NetPlayer), + SetTeam(u16, u8), // Lobby packages SetReady(u16, bool), diff --git a/rust/src/net/protocol.rs b/rust/src/net/protocol.rs index 0d48fce..43786cf 100644 --- a/rust/src/net/protocol.rs +++ b/rust/src/net/protocol.rs @@ -161,6 +161,11 @@ impl NetworkManager { game.bind_mut().handle_swap_weapon(id, weapon_type, true); } } + Package::SetTeam(player_id, team) => { + if let Some(game) = &mut Game::singleton() { + game.bind_mut().handle_player_team_change(player_id, team); + } + } _ => {} }, }, @@ -307,6 +312,19 @@ impl NetworkManager { } } } + Package::SetTeam(player_id, team) => { + if let Some(game) = &mut Game::singleton() { + let self_id = game + .bind() + .find_player_by_addr(&conn.address) + .map(|p| p.id()); + if let Some(self_id) = self_id { + if self_id == player_id { + game.bind_mut().try_set_player_team(player_id, team, true); + } + } + } + } _ => {} }, }, diff --git a/rust/src/ui/player_listing.rs b/rust/src/ui/player_listing.rs index f6f1506..1a1967b 100644 --- a/rust/src/ui/player_listing.rs +++ b/rust/src/ui/player_listing.rs @@ -1,5 +1,5 @@ use godot::{ - classes::{GridContainer, Label, OptionButton}, + classes::{GridContainer, IGridContainer, Label, OptionButton}, prelude::*, }; @@ -22,6 +22,39 @@ pub struct PlayerListing { base: Base, } +#[godot_api] +impl IGridContainer for PlayerListing { + fn ready(&mut self) { + self.run_deferred(|s| { + if let Some(game) = &mut Game::singleton() { + game.bind_mut() + .signals() + .on_player_team_changed() + .connect_other(s, |s, id, team| { + if id == s.player_id.unwrap_or(0) { + s.on_team_update(team); + } + }); + } + }); + + if let Some(team_dropdown) = self.team_dropdown.clone() { + team_dropdown + .signals() + .item_selected() + .connect_other(self, |s, selection| { + if let Some(mut game) = Game::singleton() { + game.bind_mut().try_set_player_team( + s.player_id.unwrap_or(0), + selection as u8, + false, + ); + } + }); + } + } +} + impl PlayerListing { pub fn update(&mut self) { if let Some(player_id) = &self.player_id @@ -46,6 +79,10 @@ impl PlayerListing { if let Some(ping_label) = &mut self.ping_label { ping_label.set_text(&format!("({}ms)", player.data.ping)); } + + if let Some(dropdown) = &mut self.team_dropdown { + dropdown.select(player.data.team as i32); + } } } @@ -60,7 +97,7 @@ impl PlayerListing { } } - pub fn select_team(&mut self, team: u8) { + fn on_team_update(&mut self, team: u8) { if let Some(dropdown) = &mut self.team_dropdown { dropdown.select(team as i32); }