diff --git a/godot/scenes/game_finished.tscn b/godot/scenes/game_finished.tscn index 8a48321..ad80cf5 100644 --- a/godot/scenes/game_finished.tscn +++ b/godot/scenes/game_finished.tscn @@ -2,6 +2,7 @@ [ext_resource type="Theme" uid="uid://drydbnrcdv4pk" path="res://default_theme.tres" id="1_6xxma"] [ext_resource type="PackedScene" uid="uid://2sfy0vq0fqt3" path="res://scenes/ui/player_listing.tscn" id="1_ou354"] +[ext_resource type="PackedScene" uid="uid://thi85gv3blli" path="res://scenes/ui/player_stats_panel.tscn" id="3_5t2yd"] [node name="game_finished" type="GameFinishedScreen" unique_id=700430082 node_paths=PackedStringArray("title_container", "back_button", "player_listings", "save_file_dialog")] title_container = NodePath("title_container") @@ -59,8 +60,9 @@ grow_vertical = 0 theme_override_font_sizes/font_size = 32 text = "Save Replay" -[node name="player_listings" type="PlayerListings" parent="." unique_id=602626392] +[node name="player_listings" type="PlayerListings" parent="." unique_id=602626392 node_paths=PackedStringArray("player_stats_panel")] player_listing_scene = ExtResource("1_ou354") +player_stats_panel = NodePath("../player_stats_panel") layout_mode = 1 anchors_preset = 5 anchor_left = 0.5 @@ -87,4 +89,11 @@ grow_horizontal = 2 access = 2 filters = PackedStringArray("*.replay") +[node name="player_stats_panel" parent="." unique_id=820294448 instance=ExtResource("3_5t2yd")] +layout_mode = 1 +offset_left = -52.0 +offset_top = -36.0 +offset_right = 52.0 +offset_bottom = 36.0 + [connection signal="pressed" from="save_replay" to="." method="open_file_dialog"] diff --git a/godot/scenes/ui/hud.tscn b/godot/scenes/ui/hud.tscn index d5deed1..7039d23 100644 --- a/godot/scenes/ui/hud.tscn +++ b/godot/scenes/ui/hud.tscn @@ -102,7 +102,7 @@ offset_bottom = 100.0 grow_horizontal = 2 size_flags_vertical = 3 -[node name="h_box_container" type="PlayerListings" parent="scoreboard" unique_id=43264288] +[node name="player_listings" type="PlayerListings" parent="scoreboard" unique_id=43264288] player_listing_scene = ExtResource("2_8qguk") layout_mode = 2 alignment = 1 diff --git a/godot/scenes/ui/player_listing.tscn b/godot/scenes/ui/player_listing.tscn index a584636..92f74ad 100644 --- a/godot/scenes/ui/player_listing.tscn +++ b/godot/scenes/ui/player_listing.tscn @@ -4,8 +4,9 @@ [ext_resource type="Texture2D" uid="uid://7u2jx45bwejo" path="res://raw_assets/ping/Ping_medium.png" id="2_h6por"] [ext_resource type="Texture2D" uid="uid://c0n5ugd867e2k" path="res://raw_assets/ping/Ping_high.png" id="3_kx1on"] [ext_resource type="Theme" uid="uid://drydbnrcdv4pk" path="res://default_theme.tres" id="4_kx1on"] +[ext_resource type="Texture2D" uid="uid://dv850alocb47k" path="res://raw_assets/icons/PNG/White/1x/menuList.png" id="5_gadtc"] -[node name="player_listing" type="PlayerListing" unique_id=965142972 node_paths=PackedStringArray("name_label", "kills_label", "deaths_label", "ping_label", "ping_icon")] +[node name="player_listing" type="PlayerListing" unique_id=965142972 node_paths=PackedStringArray("name_label", "kills_label", "deaths_label", "ping_label", "ping_icon", "stats_button")] name_label = NodePath("name") kills_label = NodePath("kills") deaths_label = NodePath("deaths") @@ -17,6 +18,7 @@ ping_medium_color = Color(0.7963379, 0.79633814, 0, 1) ping_high = ExtResource("3_kx1on") ping_high_color = Color(0.99999994, 0.22953579, 0.17418489, 1) ping_icon = NodePath("ping_icon") +stats_button = NodePath("button") size_flags_horizontal = 3 theme = ExtResource("4_kx1on") @@ -53,3 +55,9 @@ layout_mode = 2 texture = ExtResource("3_kx1on") expand_mode = 2 stretch_mode = 4 + +[node name="button" type="Button" parent="." unique_id=1272088409] +custom_minimum_size = Vector2(20, 0) +layout_mode = 2 +icon = ExtResource("5_gadtc") +expand_icon = true diff --git a/godot/scenes/ui/player_stats_panel.tscn b/godot/scenes/ui/player_stats_panel.tscn new file mode 100644 index 0000000..a79a6fb --- /dev/null +++ b/godot/scenes/ui/player_stats_panel.tscn @@ -0,0 +1,35 @@ +[gd_scene format=3 uid="uid://thi85gv3blli"] + +[node name="player_stats_panel" type="PlayerStatsPanel" unique_id=820294448 node_paths=PackedStringArray("stats_grid")] +stats_grid = NodePath("v_box_container/grid_container") +visible = false +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -6.0 +offset_top = -6.0 +offset_right = 6.0 +offset_bottom = 6.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="v_box_container" type="VBoxContainer" parent="." unique_id=1545420438] +layout_mode = 2 + +[node name="label" type="Label" parent="v_box_container" unique_id=1011731554] +layout_mode = 2 +text = "PlayerName" +horizontal_alignment = 1 + +[node name="grid_container" type="GridContainer" parent="v_box_container" unique_id=1825400706] +layout_mode = 2 +columns = 2 + +[node name="button" type="Button" parent="v_box_container" unique_id=210952362] +layout_mode = 2 +size_flags_horizontal = 4 +text = "Close" + +[connection signal="pressed" from="v_box_container/button" to="." method="close"] diff --git a/rust/src/ui/mod.rs b/rust/src/ui/mod.rs index feba837..66d68b6 100644 --- a/rust/src/ui/mod.rs +++ b/rust/src/ui/mod.rs @@ -9,6 +9,7 @@ pub mod main_menu; pub mod net_stats; pub mod player_listing; pub mod player_listings; +pub mod player_stats_panel; pub mod replay_finished_screen; pub mod replay_hud; pub mod settings; diff --git a/rust/src/ui/player_listing.rs b/rust/src/ui/player_listing.rs index 8e687d3..1dd01a6 100644 --- a/rust/src/ui/player_listing.rs +++ b/rust/src/ui/player_listing.rs @@ -1,12 +1,14 @@ use godot::{ - classes::{HBoxContainer, IHBoxContainer, Label, Texture2D, TextureRect}, + classes::{Button, HBoxContainer, IHBoxContainer, Label, Texture2D, TextureRect}, prelude::*, }; -use crate::{game::Game, replay::StandaloneReplayManager}; +use crate::{ + game::Game, replay::StandaloneReplayManager, ui::player_stats_panel::PlayerStatsPanel, +}; #[derive(GodotClass)] -#[class(base=HBoxContainer, init)] +#[class(base=HBoxContainer, init, tool)] pub struct PlayerListing { #[export] name_label: Option>, @@ -30,9 +32,14 @@ pub struct PlayerListing { ping_high_color: Color, #[export] ping_icon: Option>, + #[export] + stats_button: Option>, pub player_id: Option, + #[export] + pub stats_panel: Option>, + base: Base, } @@ -58,6 +65,20 @@ impl IHBoxContainer for PlayerListing { .connect_other(s, |s| s.update()); } }); + + if let Some(mut stats_button) = self.stats_button.clone() { + stats_button.set_visible(self.stats_panel.is_some()); + if let Some(player_id) = self.player_id { + stats_button + .signals() + .pressed() + .connect_other(self, move |s| { + if let Some(panel) = &mut s.stats_panel { + panel.bind_mut().show(player_id); + } + }); + } + } } } diff --git a/rust/src/ui/player_listings.rs b/rust/src/ui/player_listings.rs index 9bc0a30..8cabb7c 100644 --- a/rust/src/ui/player_listings.rs +++ b/rust/src/ui/player_listings.rs @@ -4,7 +4,11 @@ use godot::{ prelude::*, }; -use crate::{game::Game, replay::StandaloneReplayManager, ui::player_listing::PlayerListing}; +use crate::{ + game::Game, + replay::StandaloneReplayManager, + ui::{player_listing::PlayerListing, player_stats_panel::PlayerStatsPanel}, +}; #[derive(GodotClass)] #[class(base=HBoxContainer, init)] @@ -13,6 +17,8 @@ pub struct PlayerListings { player_listing_scene: Option>, #[export] update_on_disconnect: bool, + #[export] + player_stats_panel: Option>, base: Base, } @@ -65,6 +71,7 @@ impl PlayerListings { for player in players { let mut listing = listing_scene.instantiate_as::(); listing.bind_mut().player_id = Some(player.id()); + listing.bind_mut().stats_panel = self.player_stats_panel.clone(); vbox.add_child(&listing); } } @@ -99,6 +106,7 @@ impl PlayerListings { for (_, player) in players { let mut listing = listing_scene.instantiate_as::(); listing.bind_mut().player_id = Some(player.id); + listing.bind_mut().stats_panel = self.player_stats_panel.clone(); vbox.add_child(&listing); } } diff --git a/rust/src/ui/player_stats_panel.rs b/rust/src/ui/player_stats_panel.rs new file mode 100644 index 0000000..68ecb73 --- /dev/null +++ b/rust/src/ui/player_stats_panel.rs @@ -0,0 +1,77 @@ +use godot::{ + classes::{GridContainer, Label, PanelContainer}, + prelude::*, +}; + +use crate::{ + game::{Game, PlayerStats}, + replay::StandaloneReplayManager, +}; + +#[derive(GodotClass)] +#[class(base=PanelContainer, init)] +pub struct PlayerStatsPanel { + #[export] + player_name: Option>, + #[export] + stats_grid: Option>, + + base: Base, +} + +#[godot_api] +impl PlayerStatsPanel { + #[func] + pub fn close(&mut self) { + self.base_mut().set_visible(false); + } + + pub fn show(&mut self, player_id: u16) { + let stats = if let Some(game) = Game::singleton() + && let Some(player) = game.bind().find_player(player_id) + { + Some((player.data.name.clone(), player.stats.clone())) + } else if let Some(playback) = &StandaloneReplayManager::singleton().bind().playback + && let Some(player) = playback.bind().replay.players.get(&player_id) + { + Some((player.name.clone(), player.stats.clone())) + } else { + None + }; + + if let Some(name_label) = &mut self.player_name + && let Some((name, _)) = &stats + { + name_label.set_text(name); + } + + if let Some(grid) = &mut self.stats_grid { + for child in grid.get_children().iter_shared() { + grid.remove_child(&child); + } + + if let Some((_, stats)) = stats { + let stats = vec![ + ("Kills", stats.kills.to_string()), + ("Deaths", stats.deaths.to_string()), + ("Goals", stats.goals.to_string()), + ("Own Goals", stats.own_goals.to_string()), + ("Friendly Kills", stats.friendly_kills.to_string()), + ("Friendly Deaths", stats.friendly_deaths.to_string()), + ]; + + for (stat_name, stat) in stats { + let mut label = Label::new_alloc(); + label.set_text(&stat_name.to_owned()); + grid.add_child(&label); + + let mut label = Label::new_alloc(); + label.set_text(&stat.to_owned()); + grid.add_child(&label); + } + } + } + + self.base_mut().set_visible(true); + } +}