From d1fe3f6bb0d94351a449617d435a93182204a30d Mon Sep 17 00:00:00 2001 From: Sofia Date: Fri, 24 Jul 2026 02:49:46 +0300 Subject: [PATCH] Add some preliminary chat messages --- rust/src/chat.rs | 93 +++++++++++++++++++++++++++++++++ rust/src/game_manager.rs | 29 ++++++++-- rust/src/lib.rs | 1 + rust/src/net/network_manager.rs | 2 +- rust/src/net/protocol.rs | 4 +- rust/src/player/mod.rs | 2 +- 6 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 rust/src/chat.rs diff --git a/rust/src/chat.rs b/rust/src/chat.rs new file mode 100644 index 0000000..f61a23e --- /dev/null +++ b/rust/src/chat.rs @@ -0,0 +1,93 @@ +use godot::{classes::RichTextLabel, prelude::*}; + +use crate::{ + announcer::Announcement, + game_manager::{Game, Player}, + player::DamageSource, +}; + +#[derive(GodotClass)] +#[class(base=Node, init)] +pub struct Chat { + messages: Vec, + base: Base, +} + +#[godot_api] +impl Chat { + #[signal] + pub fn on_new_message(idx: Gd); + + pub fn singleton() -> Option> { + Game::singleton().and_then(|g| g.get_child(0).map(|c| c.cast())) + } + + pub fn get_messages(&self) -> Vec> { + self.messages.iter().map(|m| m.as_label()).collect() + } + + pub fn new_message(&mut self, message: ChatMessage) { + self.messages.len() as i64; + self.messages.push(message.clone()); + self.signals().on_new_message().emit(&message.as_label()); + } +} + +#[derive(Debug, Clone)] +pub enum ChatMessage { + Death(DamageSource, u16), + Announcement(u16, Announcement), +} + +impl ChatMessage { + pub fn as_label(&self) -> Gd { + let mut label = RichTextLabel::new_alloc(); + match self { + ChatMessage::Death(source, player_id) => { + if let Some(game) = Game::singleton() + && let Some(player) = game.bind().find_player(*player_id) + { + match source { + DamageSource::Player(source_id) => { + if let Some(source) = game.bind().find_player(*source_id) { + add_player(&mut label, source); + label.append_text(" fragged "); + add_player(&mut label, &player); + } + } + DamageSource::Killbox => { + add_player(&mut label, &player); + label.append_text(" fell into the void"); + } + } + } + } + ChatMessage::Announcement(player_id, announcement) => { + if let Some(game) = Game::singleton() + && let Some(player) = game.bind().find_player(*player_id) + { + add_player(&mut label, &player); + label.append_text(" got "); + let what = match announcement { + Announcement::FirstBlood => "First Blood", + Announcement::DoubleKill => "a Double Kill", + Announcement::TripleKill => "a Triple Kill", + Announcement::MegaKill => "a Mega Kill", + }; + label.append_text(&format!("[color=red]{}[/color]", what)); + } + } + } + label + } +} + +fn add_player(label: &mut Gd, player: &Player) { + if let Some(game) = Game::singleton() + && let Some(team) = game.bind().get_team(player.data.team) + { + label.append_text(&format!("[color={}]", team.bind().color)); + label.add_text(&player.data.name); + label.append_text(&format!("[/color]")); + } +} diff --git a/rust/src/game_manager.rs b/rust/src/game_manager.rs index 1ae8e6c..84b4173 100644 --- a/rust/src/game_manager.rs +++ b/rust/src/game_manager.rs @@ -9,6 +9,7 @@ use crate::{ Announcer, }, ball::Ball, + chat::{Chat, ChatMessage}, map::Map, map_resource::MapResource, net::{ @@ -216,6 +217,11 @@ pub struct Game { #[godot_api] impl INode for Game { + fn ready(&mut self) { + let chat = Chat::new_alloc(); + self.base_mut().add_child(&chat); + } + fn physics_process(&mut self, delta: f64) { self.since_last_sync += delta; if self.since_last_sync > (1. / 60.) { @@ -854,6 +860,11 @@ impl Game { pub fn on_kill(&mut self, source: DamageSource, player_id: u16) { let total_prev_kills: u16 = self.players.iter().map(|p| p.data.kills).sum(); + if let Some(chat) = &mut Chat::singleton() { + chat.bind_mut() + .new_message(ChatMessage::Death(source, player_id)); + } + let killing_spree = if let DamageSource::Player(source_id) = source && let Some(source_player) = self.find_player_mut(source_id) { @@ -875,8 +886,10 @@ impl Game { && let PeerKind::Server(peer, _) = peer { if total_prev_kills == 0 { - s.on_announcement(Announcement::FirstBlood); - peer.broadcast_reliable(Package::Announcement(FirstBlood)); + if let DamageSource::Player(source_id) = source { + s.on_announcement(source_id, Announcement::FirstBlood); + peer.broadcast_reliable(Package::Announcement(source_id, FirstBlood)); + } } let announcement = match killing_spree { 2 => Some(Announcement::DoubleKill), @@ -885,14 +898,20 @@ impl Game { _ => None, }; if let Some(announcement) = announcement { - s.on_announcement(announcement); - peer.broadcast_reliable(Package::Announcement(announcement)); + if let DamageSource::Player(source_id) = source { + s.on_announcement(source_id, announcement); + peer.broadcast_reliable(Package::Announcement(source_id, announcement)); + } } } }); } - pub fn on_announcement(&mut self, announcement: Announcement) { + pub fn on_announcement(&mut self, player_id: u16, announcement: Announcement) { + if let Some(chat) = &mut Chat::singleton() { + chat.bind_mut() + .new_message(ChatMessage::Announcement(player_id, announcement)); + } Announcer::singleton().bind_mut().announce(announcement); } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 7c50b81..92c46d3 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -2,6 +2,7 @@ use godot::prelude::*; pub mod announcer; pub mod ball; +pub mod chat; pub mod cli_parser; pub mod game_manager; pub mod game_settings; diff --git a/rust/src/net/network_manager.rs b/rust/src/net/network_manager.rs index a37624f..090966e 100644 --- a/rust/src/net/network_manager.rs +++ b/rust/src/net/network_manager.rs @@ -311,7 +311,7 @@ pub enum Package { DespawnBall, SwapWeapon(u16, WeaponType), Goal(Vec), - Announcement(Announcement), + Announcement(u16, Announcement), // End-of-Game packages FinishGame, diff --git a/rust/src/net/protocol.rs b/rust/src/net/protocol.rs index 67f71df..9ebdde2 100644 --- a/rust/src/net/protocol.rs +++ b/rust/src/net/protocol.rs @@ -200,9 +200,9 @@ impl NetworkManager { game.bind_mut().finish_game(); } } - Package::Announcement(announcement) => { + Package::Announcement(source_id, announcement) => { if let Some(game) = &mut Game::singleton() { - game.bind_mut().on_announcement(announcement); + game.bind_mut().on_announcement(source_id, announcement); } } _ => {} diff --git a/rust/src/player/mod.rs b/rust/src/player/mod.rs index 39ff950..6c90bc4 100644 --- a/rust/src/player/mod.rs +++ b/rust/src/player/mod.rs @@ -897,7 +897,7 @@ impl RemotePlayer { pub fn on_take_damage(source: DamageSourceSignal, source_player: u16, damage: i32); } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub enum DamageSource { Player(u16), Killbox,