skullball/rust/src/chat.rs
2026-08-08 21:28:31 +03:00

133 lines
5.0 KiB
Rust

use godot::{classes::RichTextLabel, prelude::*};
use crate::{
announcer::Announcement,
game::{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.run_deferred(move |s| s.signals().on_new_message().emit(&message.as_label()));
}
}
#[derive(Debug, Clone)]
pub enum ChatMessage {
Death(DamageSource, u16),
Announcement(u16, Announcement),
ChatMessage(String, Color, String),
Goal(u16, Vec<u8>),
}
impl ChatMessage {
pub fn as_label(&self) -> Gd<RichTextLabel> {
let mut label = RichTextLabel::new_alloc();
label.set_fit_content(true);
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(kind) => {
add_player(&mut label, &player);
label.append_text(&kind.as_kill_text());
}
DamageSource::God => {
add_player(&mut label, &player);
label.append_text(" was removed from the world by godly forces");
}
}
}
}
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);
let (is, what) = match announcement {
Announcement::FirstBlood => ("got", "First Blood"),
Announcement::DoubleKill => ("got", "a Double Kill"),
Announcement::TripleKill => ("got", "a Triple Kill"),
Announcement::MegaKill => ("got", "a Mega Kill"),
Announcement::KillingSpree => ("is on", "a Killing Spree"),
Announcement::Carnage => ("made", "Carnage"),
Announcement::Mayhem => ("made", "Mayhem"),
Announcement::Dominating => ("is", "Dominating"),
Announcement::Rampage => ("is on a", "Rampage"),
Announcement::Godlike => ("is", "Godlike"),
};
label.append_text(&format!(" {} [color=red]{}[/color]", is, what));
}
}
ChatMessage::ChatMessage(name, color, message) => {
label.push_color(*color);
label.add_text(&format!("{}: ", name));
label.pop();
label.add_text(message);
}
ChatMessage::Goal(player_id, team_ids) => {
if let Some(game) = Game::singleton()
&& let Some(player) = game.bind().find_player(*player_id)
{
let teams = game.bind().get_teams();
let teams = teams
.iter_shared()
.enumerate()
.filter(|(id, _)| team_ids.contains(&((*id) as u8)));
add_player(&mut label, &player);
label.add_text(" scored a goal for ");
for (idx, (_, team)) in teams.enumerate() {
if idx > 0 {
label.add_text(" and ");
}
label.push_color(team.bind().color());
label.add_text(&team.bind().name);
label.pop();
}
}
}
}
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.push_color(team.bind().color());
label.add_text(&player.data.name);
label.pop();
}
}