Add some preliminary chat messages

This commit is contained in:
Sofia 2026-07-24 02:49:46 +03:00
parent 466d9ea827
commit cdedd61feb
6 changed files with 122 additions and 9 deletions

93
rust/src/chat.rs Normal file
View File

@ -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<ChatMessage>,
base: Base<Node>,
}
#[godot_api]
impl Chat {
#[signal]
pub fn on_new_message(idx: Gd<RichTextLabel>);
pub fn singleton() -> Option<Gd<Chat>> {
Game::singleton().and_then(|g| g.get_child(0).map(|c| c.cast()))
}
pub fn get_messages(&self) -> Vec<Gd<RichTextLabel>> {
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<RichTextLabel> {
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<RichTextLabel>, 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]"));
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -311,7 +311,7 @@ pub enum Package {
DespawnBall,
SwapWeapon(u16, WeaponType),
Goal(Vec<u8>),
Announcement(Announcement),
Announcement(u16, Announcement),
// End-of-Game packages
FinishGame,

View File

@ -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);
}
}
_ => {}

View File

@ -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,