diff --git a/CREDITS.md b/CREDITS.md index 2e1faa7..ee1152c 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -2,5 +2,6 @@ - [LaserFast and LaserWithRecovery](godot/raw_assets/sfx/) - [Hit](godot/raw_assets/sfx/Hit.mp3) - [Footstep sounds on Tree Trunk](godot/raw_assets/sfx/footsteps/) via [zapsplat](https://www.zapsplat.com/sound-effect-packs/footsteps-on-fallen-tree-trunk) +- [Announcer sounds](godot/raw_assets/sfx/announcer/) by [TripleSnail](https://opengameart.org/content/announcer-kill-sounds) - [Soldier model and rig](godot/raw_assets/Soldier/) by [Casti_131](https://opengameart.org/content/low-poly-soldier-with-weapons) - [Torment Textures](godot/maps/textures) (`str_*` files) by [Bradley D. (strideh.itch.io)](https://strideh.itch.io) diff --git a/godot/project.godot b/godot/project.godot index 8a16272..f806a22 100644 --- a/godot/project.godot +++ b/godot/project.godot @@ -22,6 +22,7 @@ CommandLinePanelGlobal="*uid://bsb6asp5o35w3" GameManagerGlobal="*uid://bii6018k3ip3t" CliParserGlobal="*uid://c52o7xa0fnr53" GameSettingsGlobal="*uid://bjr15hjiojecw" +AnnouncerGlobal="*uid://5gey4i80mmsa" [display] diff --git a/godot/raw_assets/sfx/announcer/Carnage.wav b/godot/raw_assets/sfx/announcer/Carnage.wav new file mode 100644 index 0000000..84b3615 Binary files /dev/null and b/godot/raw_assets/sfx/announcer/Carnage.wav differ diff --git a/godot/raw_assets/sfx/announcer/Carnage.wav.import b/godot/raw_assets/sfx/announcer/Carnage.wav.import new file mode 100644 index 0000000..5a77f74 --- /dev/null +++ b/godot/raw_assets/sfx/announcer/Carnage.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://cmx868xfm5tr" +path="res://.godot/imported/Carnage.wav-0aa25ce2dbaf67c412d878ecccaa2a43.sample" + +[deps] + +source_file="res://raw_assets/sfx/announcer/Carnage.wav" +dest_files=["res://.godot/imported/Carnage.wav-0aa25ce2dbaf67c412d878ecccaa2a43.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/godot/raw_assets/sfx/announcer/Dominating.wav b/godot/raw_assets/sfx/announcer/Dominating.wav new file mode 100644 index 0000000..39ef398 Binary files /dev/null and b/godot/raw_assets/sfx/announcer/Dominating.wav differ diff --git a/godot/raw_assets/sfx/announcer/Dominating.wav.import b/godot/raw_assets/sfx/announcer/Dominating.wav.import new file mode 100644 index 0000000..54b7d83 --- /dev/null +++ b/godot/raw_assets/sfx/announcer/Dominating.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://bwv7hydyynxo3" +path="res://.godot/imported/Dominating.wav-9094379de14f4d99f4362076b03e9be9.sample" + +[deps] + +source_file="res://raw_assets/sfx/announcer/Dominating.wav" +dest_files=["res://.godot/imported/Dominating.wav-9094379de14f4d99f4362076b03e9be9.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/godot/raw_assets/sfx/announcer/DoubleKill.wav b/godot/raw_assets/sfx/announcer/DoubleKill.wav new file mode 100644 index 0000000..f7bdb01 Binary files /dev/null and b/godot/raw_assets/sfx/announcer/DoubleKill.wav differ diff --git a/godot/raw_assets/sfx/announcer/DoubleKill.wav.import b/godot/raw_assets/sfx/announcer/DoubleKill.wav.import new file mode 100644 index 0000000..99eef85 --- /dev/null +++ b/godot/raw_assets/sfx/announcer/DoubleKill.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://b5816xcsj2ueh" +path="res://.godot/imported/DoubleKill.wav-1dae3420408145da22058102a5b9489c.sample" + +[deps] + +source_file="res://raw_assets/sfx/announcer/DoubleKill.wav" +dest_files=["res://.godot/imported/DoubleKill.wav-1dae3420408145da22058102a5b9489c.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/godot/raw_assets/sfx/announcer/FirstBlood.wav b/godot/raw_assets/sfx/announcer/FirstBlood.wav new file mode 100644 index 0000000..6a492ca Binary files /dev/null and b/godot/raw_assets/sfx/announcer/FirstBlood.wav differ diff --git a/godot/raw_assets/sfx/announcer/FirstBlood.wav.import b/godot/raw_assets/sfx/announcer/FirstBlood.wav.import new file mode 100644 index 0000000..ae0b4a9 --- /dev/null +++ b/godot/raw_assets/sfx/announcer/FirstBlood.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://bg8wugv6ij0pj" +path="res://.godot/imported/FirstBlood.wav-3a1ab2cafcafce472d28811db364622d.sample" + +[deps] + +source_file="res://raw_assets/sfx/announcer/FirstBlood.wav" +dest_files=["res://.godot/imported/FirstBlood.wav-3a1ab2cafcafce472d28811db364622d.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/godot/raw_assets/sfx/announcer/Godlike.wav b/godot/raw_assets/sfx/announcer/Godlike.wav new file mode 100644 index 0000000..c7fb691 Binary files /dev/null and b/godot/raw_assets/sfx/announcer/Godlike.wav differ diff --git a/godot/raw_assets/sfx/announcer/Godlike.wav.import b/godot/raw_assets/sfx/announcer/Godlike.wav.import new file mode 100644 index 0000000..383162e --- /dev/null +++ b/godot/raw_assets/sfx/announcer/Godlike.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://celsg1h2xc85k" +path="res://.godot/imported/Godlike.wav-5ec4df26a22987b555e4f010565e2276.sample" + +[deps] + +source_file="res://raw_assets/sfx/announcer/Godlike.wav" +dest_files=["res://.godot/imported/Godlike.wav-5ec4df26a22987b555e4f010565e2276.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/godot/raw_assets/sfx/announcer/KillingSpree.wav b/godot/raw_assets/sfx/announcer/KillingSpree.wav new file mode 100644 index 0000000..788b6a7 Binary files /dev/null and b/godot/raw_assets/sfx/announcer/KillingSpree.wav differ diff --git a/godot/raw_assets/sfx/announcer/KillingSpree.wav.import b/godot/raw_assets/sfx/announcer/KillingSpree.wav.import new file mode 100644 index 0000000..45e2f4d --- /dev/null +++ b/godot/raw_assets/sfx/announcer/KillingSpree.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://cc5t6p6w468hi" +path="res://.godot/imported/KillingSpree.wav-448dff3e279f17fef54b67c2bf084908.sample" + +[deps] + +source_file="res://raw_assets/sfx/announcer/KillingSpree.wav" +dest_files=["res://.godot/imported/KillingSpree.wav-448dff3e279f17fef54b67c2bf084908.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/godot/raw_assets/sfx/announcer/Massacre.wav b/godot/raw_assets/sfx/announcer/Massacre.wav new file mode 100644 index 0000000..bc4a2d7 Binary files /dev/null and b/godot/raw_assets/sfx/announcer/Massacre.wav differ diff --git a/godot/raw_assets/sfx/announcer/Massacre.wav.import b/godot/raw_assets/sfx/announcer/Massacre.wav.import new file mode 100644 index 0000000..33016be --- /dev/null +++ b/godot/raw_assets/sfx/announcer/Massacre.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://baml6py7t1ycd" +path="res://.godot/imported/Massacre.wav-26fd0dde29cf79df78d170f36281a8b1.sample" + +[deps] + +source_file="res://raw_assets/sfx/announcer/Massacre.wav" +dest_files=["res://.godot/imported/Massacre.wav-26fd0dde29cf79df78d170f36281a8b1.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/godot/raw_assets/sfx/announcer/Mayhem.wav b/godot/raw_assets/sfx/announcer/Mayhem.wav new file mode 100644 index 0000000..e11200b Binary files /dev/null and b/godot/raw_assets/sfx/announcer/Mayhem.wav differ diff --git a/godot/raw_assets/sfx/announcer/Mayhem.wav.import b/godot/raw_assets/sfx/announcer/Mayhem.wav.import new file mode 100644 index 0000000..fa8ee8a --- /dev/null +++ b/godot/raw_assets/sfx/announcer/Mayhem.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://bjewmgxjuwsqo" +path="res://.godot/imported/Mayhem.wav-ea221444f4057bff04fd5e46ce2da957.sample" + +[deps] + +source_file="res://raw_assets/sfx/announcer/Mayhem.wav" +dest_files=["res://.godot/imported/Mayhem.wav-ea221444f4057bff04fd5e46ce2da957.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/godot/raw_assets/sfx/announcer/MegaKill.wav b/godot/raw_assets/sfx/announcer/MegaKill.wav new file mode 100644 index 0000000..e80d60a Binary files /dev/null and b/godot/raw_assets/sfx/announcer/MegaKill.wav differ diff --git a/godot/raw_assets/sfx/announcer/MegaKill.wav.import b/godot/raw_assets/sfx/announcer/MegaKill.wav.import new file mode 100644 index 0000000..cbd6fb8 --- /dev/null +++ b/godot/raw_assets/sfx/announcer/MegaKill.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dfxrsl808f6o0" +path="res://.godot/imported/MegaKill.wav-809778ac21e83a3da5eb84b1403e21a2.sample" + +[deps] + +source_file="res://raw_assets/sfx/announcer/MegaKill.wav" +dest_files=["res://.godot/imported/MegaKill.wav-809778ac21e83a3da5eb84b1403e21a2.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/godot/raw_assets/sfx/announcer/Ownage.wav b/godot/raw_assets/sfx/announcer/Ownage.wav new file mode 100644 index 0000000..89750b7 Binary files /dev/null and b/godot/raw_assets/sfx/announcer/Ownage.wav differ diff --git a/godot/raw_assets/sfx/announcer/Ownage.wav.import b/godot/raw_assets/sfx/announcer/Ownage.wav.import new file mode 100644 index 0000000..44da601 --- /dev/null +++ b/godot/raw_assets/sfx/announcer/Ownage.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://4i6t6yh2j45t" +path="res://.godot/imported/Ownage.wav-7853435740c868c6707b6c2ef354df86.sample" + +[deps] + +source_file="res://raw_assets/sfx/announcer/Ownage.wav" +dest_files=["res://.godot/imported/Ownage.wav-7853435740c868c6707b6c2ef354df86.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/godot/raw_assets/sfx/announcer/Rampage.wav b/godot/raw_assets/sfx/announcer/Rampage.wav new file mode 100644 index 0000000..8a61f3e Binary files /dev/null and b/godot/raw_assets/sfx/announcer/Rampage.wav differ diff --git a/godot/raw_assets/sfx/announcer/Rampage.wav.import b/godot/raw_assets/sfx/announcer/Rampage.wav.import new file mode 100644 index 0000000..be73645 --- /dev/null +++ b/godot/raw_assets/sfx/announcer/Rampage.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://ccg5hd454nqpe" +path="res://.godot/imported/Rampage.wav-328d28840941970a1c0afd53cf5a6790.sample" + +[deps] + +source_file="res://raw_assets/sfx/announcer/Rampage.wav" +dest_files=["res://.godot/imported/Rampage.wav-328d28840941970a1c0afd53cf5a6790.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/godot/raw_assets/sfx/announcer/TripleKill.wav b/godot/raw_assets/sfx/announcer/TripleKill.wav new file mode 100644 index 0000000..701d480 Binary files /dev/null and b/godot/raw_assets/sfx/announcer/TripleKill.wav differ diff --git a/godot/raw_assets/sfx/announcer/TripleKill.wav.import b/godot/raw_assets/sfx/announcer/TripleKill.wav.import new file mode 100644 index 0000000..da0bb8a --- /dev/null +++ b/godot/raw_assets/sfx/announcer/TripleKill.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://co82glo13i8at" +path="res://.godot/imported/TripleKill.wav-5cd8a2f25db2a8430eba6089c5c04d0e.sample" + +[deps] + +source_file="res://raw_assets/sfx/announcer/TripleKill.wav" +dest_files=["res://.godot/imported/TripleKill.wav-5cd8a2f25db2a8430eba6089c5c04d0e.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/godot/scenes/misc/announcer.tscn b/godot/scenes/misc/announcer.tscn new file mode 100644 index 0000000..3b61663 --- /dev/null +++ b/godot/scenes/misc/announcer.tscn @@ -0,0 +1,15 @@ +[gd_scene format=3 uid="uid://5gey4i80mmsa"] + +[ext_resource type="AudioStream" uid="uid://bg8wugv6ij0pj" path="res://raw_assets/sfx/announcer/FirstBlood.wav" id="1_h17tq"] +[ext_resource type="AudioStream" uid="uid://b5816xcsj2ueh" path="res://raw_assets/sfx/announcer/DoubleKill.wav" id="2_6jrtu"] +[ext_resource type="AudioStream" uid="uid://co82glo13i8at" path="res://raw_assets/sfx/announcer/TripleKill.wav" id="3_6ob50"] +[ext_resource type="AudioStream" uid="uid://dfxrsl808f6o0" path="res://raw_assets/sfx/announcer/MegaKill.wav" id="4_0lbme"] + +[node name="announcer" type="Announcer" unique_id=1062756235 node_paths=PackedStringArray("player")] +player = NodePath("audio_stream_player") +first_blood = ExtResource("1_h17tq") +double_kill = ExtResource("2_6jrtu") +triple_kill = ExtResource("3_6ob50") +mega_kill = ExtResource("4_0lbme") + +[node name="audio_stream_player" type="AudioStreamPlayer" parent="." unique_id=1168314007] diff --git a/rust/src/announcer.rs b/rust/src/announcer.rs new file mode 100644 index 0000000..69e36e7 --- /dev/null +++ b/rust/src/announcer.rs @@ -0,0 +1,62 @@ +use godot::{ + classes::{AudioStream, AudioStreamPlayer}, + prelude::*, + tools::get_autoload_by_name, +}; +use serde::{Deserialize, Serialize}; + +pub const ANNOUCER_GLOBAL: &str = "AnnouncerGlobal"; + +#[derive(GodotClass)] +#[class(base=Node3D, init)] +pub struct Announcer { + #[export] + player: Option>, + + #[export] + #[export_group(name = "Sounds")] + first_blood: Option>, + + #[export] + #[export_group(name = "Sounds")] + double_kill: Option>, + + #[export] + #[export_group(name = "Sounds")] + triple_kill: Option>, + + #[export] + #[export_group(name = "Sounds")] + mega_kill: Option>, + + base: Base, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub enum Announcement { + FirstBlood, + DoubleKill, + TripleKill, + MegaKill, +} + +impl Announcer { + pub fn singleton() -> Gd { + get_autoload_by_name::(ANNOUCER_GLOBAL) + } + + pub fn announce(&mut self, announcement: Announcement) { + let sound = match announcement { + Announcement::FirstBlood => &self.first_blood, + Announcement::DoubleKill => &self.double_kill, + Announcement::TripleKill => &self.triple_kill, + Announcement::MegaKill => &self.mega_kill, + }; + if let Some(sound) = sound + && let Some(player) = &mut self.player + { + player.set_stream(sound); + player.play(); + } + } +} diff --git a/rust/src/game_manager.rs b/rust/src/game_manager.rs index 4a08684..1ae8e6c 100644 --- a/rust/src/game_manager.rs +++ b/rust/src/game_manager.rs @@ -4,6 +4,10 @@ use godot::{classes::CharacterBody3D, prelude::*, tools::get_autoload_by_name}; use serde::{Deserialize, Serialize}; use crate::{ + announcer::{ + Announcement::{self, FirstBlood}, + Announcer, + }, ball::Ball, map::Map, map_resource::MapResource, @@ -282,6 +286,13 @@ impl INode for Game { } } } + + for player in &mut self.players { + player.killing_spree_cd -= delta; + if player.killing_spree_cd <= 0. { + player.killing_spree = 0; + } + } } } @@ -483,6 +494,8 @@ impl Game { kills: 0, deaths: 0, }, + killing_spree: 0, + killing_spree_cd: 0., character: None, }); self.run_deferred(move |s| s.signals().on_new_player().emit(id)); @@ -839,15 +852,48 @@ impl Game { // Handle for when a player is killed, run both on client and server pub fn on_kill(&mut self, source: DamageSource, player_id: u16) { - if let DamageSource::Player(source_id) = source + let total_prev_kills: u16 = self.players.iter().map(|p| p.data.kills).sum(); + + let killing_spree = if let DamageSource::Player(source_id) = source && let Some(source_player) = self.find_player_mut(source_id) { source_player.data.kills += 1; - } + source_player.killing_spree += 1; + source_player.killing_spree_cd = 10.; + source_player.killing_spree + } else { + 0 + }; + if let Some(player) = self.find_player_mut(player_id) { player.data.deaths += 1; } - self.run_deferred(move |s| s.signals().on_player_killed().emit(player_id)); + self.run_deferred(move |s| { + s.signals().on_player_killed().emit(player_id); + + if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer + && let PeerKind::Server(peer, _) = peer + { + if total_prev_kills == 0 { + s.on_announcement(Announcement::FirstBlood); + peer.broadcast_reliable(Package::Announcement(FirstBlood)); + } + let announcement = match killing_spree { + 2 => Some(Announcement::DoubleKill), + 3 => Some(Announcement::TripleKill), + 4 => Some(Announcement::MegaKill), + _ => None, + }; + if let Some(announcement) = announcement { + s.on_announcement(announcement); + peer.broadcast_reliable(Package::Announcement(announcement)); + } + } + }); + } + + pub fn on_announcement(&mut self, announcement: Announcement) { + Announcer::singleton().bind_mut().announce(announcement); } // Handle taking damage, from server to client @@ -894,6 +940,8 @@ pub struct Player { pub connection_addr: SocketAddr, pub data: NetPlayer, pub character: Option>, + pub killing_spree: u16, + pub killing_spree_cd: f64, } impl Player { @@ -1009,6 +1057,8 @@ impl From for Player { connection_addr: SocketAddr::from(([0, 0, 0, 0], 0)), data: value, character: None, + killing_spree: 0, + killing_spree_cd: 0., } } } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a1e7c50..7c50b81 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,5 +1,6 @@ use godot::prelude::*; +pub mod announcer; pub mod ball; pub mod cli_parser; pub mod game_manager; diff --git a/rust/src/net/network_manager.rs b/rust/src/net/network_manager.rs index 007fc1d..a37624f 100644 --- a/rust/src/net/network_manager.rs +++ b/rust/src/net/network_manager.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize}; use teanet::{Peer, PeerConfig, PeerMessage}; use crate::{ + announcer::Announcement, game_manager::{Game, GameManager, GameOption, GameOptionValue, NetPlayer}, game_settings::GameSettingsManager, net::{net_stats::Stats, util::NetVector3}, @@ -310,6 +311,7 @@ pub enum Package { DespawnBall, SwapWeapon(u16, WeaponType), Goal(Vec), + Announcement(Announcement), // End-of-Game packages FinishGame, diff --git a/rust/src/net/protocol.rs b/rust/src/net/protocol.rs index e5c8e9f..67f71df 100644 --- a/rust/src/net/protocol.rs +++ b/rust/src/net/protocol.rs @@ -200,6 +200,11 @@ impl NetworkManager { game.bind_mut().finish_game(); } } + Package::Announcement(announcement) => { + if let Some(game) = &mut Game::singleton() { + game.bind_mut().on_announcement(announcement); + } + } _ => {} }, },