diff --git a/godot/scenes/player/local_player.tscn b/godot/scenes/player/local_player.tscn index 522bd60..ae0d9c1 100644 --- a/godot/scenes/player/local_player.tscn +++ b/godot/scenes/player/local_player.tscn @@ -2,21 +2,13 @@ [ext_resource type="PackedScene" uid="uid://bod6b3befmyt5" path="res://scenes/player/soldier_m.tscn" id="1_jd651"] [ext_resource type="PackedScene" uid="uid://cl66unms2gim0" path="res://scenes/player/effects.tscn" id="2_ywmwa"] -[ext_resource type="PackedScene" uid="uid://n3u0jr4wiib1" path="res://scenes/ui/settings.tscn" id="3_beej8"] -[ext_resource type="PackedScene" uid="uid://2sfy0vq0fqt3" path="res://scenes/ui/player_listing.tscn" id="4_okac2"] -[ext_resource type="PackedScene" uid="uid://ffh5txda3j1" path="res://scenes/ui/chat_box.tscn" id="5_cv68x"] +[ext_resource type="PackedScene" uid="uid://c1gs6cqek1qlq" path="res://scenes/ui/hud.tscn" id="5_cv68x"] [ext_resource type="Texture2D" uid="uid://bdfqu6p74pfwe" path="res://raw_assets/crosshair/Crosshair 1.png" id="6_7awel"] [ext_resource type="AudioStream" uid="uid://bfg31e3s4jv8c" path="res://raw_assets/sfx/Hit.mp3" id="7_dguuj"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_oh8vq"] -[sub_resource type="LabelSettings" id="LabelSettings_7awel"] -font_size = 32 -font_color = Color(1, 0, 0, 1) -outline_size = 10 -outline_color = Color(0, 0, 0, 1) - -[node name="player" type="LocalPlayer" unique_id=863055906 node_paths=PackedStringArray("camera", "soldier_mesh", "collider", "effects", "dead_cam_target", "respawn_label", "camera_origin", "weapon_node", "pause_panel", "exit_to_lobby", "settings_panel", "scoreboard", "chatbox", "health_label", "hit_sound")] +[node name="player" type="LocalPlayer" unique_id=863055906 node_paths=PackedStringArray("camera", "soldier_mesh", "collider", "effects", "dead_cam_target", "respawn_label", "camera_origin", "weapon_node", "hud", "hit_sound")] look_sensitivity = 1.5 camera = NodePath("camera_3d") move_speed = 7.0 @@ -27,12 +19,7 @@ dead_cam_target = NodePath("dead_camera_target") respawn_label = NodePath("respawn_label") camera_origin = NodePath("camera_3d") weapon_node = NodePath("camera_3d/weapon_node") -pause_panel = NodePath("pause_menu") -exit_to_lobby = NodePath("pause_menu/v_box_container/exit_to_lobby") -settings_panel = NodePath("settings") -scoreboard = NodePath("scoreboard") -chatbox = NodePath("chat_box") -health_label = NodePath("health_label") +hud = NodePath("hud") hit_sound = NodePath("hit_sound") health = 100 punch_cooldown = 1.0 @@ -91,99 +78,11 @@ grow_horizontal = 2 grow_vertical = 2 horizontal_alignment = 1 -[node name="score_view" type="SimpleScoreView" parent="." unique_id=1128041371] -anchors_preset = 5 -anchor_left = 0.5 -anchor_right = 0.5 -offset_left = -20.0 -offset_top = 18.0 -offset_right = 20.0 -offset_bottom = 58.0 -grow_horizontal = 2 -alignment = 1 - -[node name="pause_menu" type="Panel" parent="." unique_id=1542204162] -visible = false -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -67.75 -offset_top = -101.25 -offset_right = 67.75 -offset_bottom = 101.25 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="v_box_container" type="VBoxContainer" parent="pause_menu" unique_id=839758321] -layout_mode = 1 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -20.0 -offset_top = -20.0 -offset_right = 20.0 -offset_bottom = 20.0 -grow_horizontal = 2 -grow_vertical = 2 -alignment = 1 - -[node name="settings" type="Button" parent="pause_menu/v_box_container" unique_id=676182211] -layout_mode = 2 -text = "Settings" - -[node name="exit_to_lobby" type="Button" parent="pause_menu/v_box_container" unique_id=1816170399] -layout_mode = 2 -text = "Exit to Lobby" - -[node name="exit" type="Button" parent="pause_menu/v_box_container" unique_id=641638697] -layout_mode = 2 -text = "Exit" - -[node name="settings" parent="." unique_id=999912383 instance=ExtResource("3_beej8")] -visible = false - -[node name="scoreboard" type="PanelContainer" parent="." unique_id=1276565374] -custom_minimum_size = Vector2(0, -1) -anchors_preset = 5 -anchor_left = 0.5 -anchor_right = 0.5 -offset_left = -76.0 -offset_top = 58.0 -offset_right = 77.0 -offset_bottom = 100.0 -grow_horizontal = 2 -size_flags_vertical = 3 - -[node name="h_box_container" type="PlayerListings" parent="scoreboard" unique_id=256898638] -player_listing_scene = ExtResource("4_okac2") -layout_mode = 2 -alignment = 1 -metadata/_edit_lock_ = true - -[node name="chat_box" parent="." unique_id=1125601400 instance=ExtResource("5_cv68x")] -in_game = true - -[node name="health_label" type="Label" parent="." unique_id=651212450] -anchors_preset = 2 -anchor_top = 1.0 -anchor_bottom = 1.0 -offset_left = 34.0 -offset_top = -45.0 -offset_right = 74.0 -offset_bottom = -22.0 -grow_vertical = 0 -text = "100" -label_settings = SubResource("LabelSettings_7awel") -vertical_alignment = 2 - [node name="hit_sound" type="AudioStreamPlayer" parent="." unique_id=1224745673] stream = ExtResource("7_dguuj") bus = &"SFX" -[connection signal="pressed" from="pause_menu/v_box_container/settings" to="settings" method="open"] -[connection signal="pressed" from="pause_menu/v_box_container/exit_to_lobby" to="." method="exit_to_lobby"] -[connection signal="pressed" from="pause_menu/v_box_container/exit" to="." method="exit"] +[node name="hud" parent="." unique_id=2049159590 instance=ExtResource("5_cv68x")] + +[connection signal="on_exit" from="hud" to="." method="exit"] +[connection signal="on_exit_to_lobby" from="hud" to="." method="exit_to_lobby"] diff --git a/godot/scenes/ui/hud.tscn b/godot/scenes/ui/hud.tscn new file mode 100644 index 0000000..471ba41 --- /dev/null +++ b/godot/scenes/ui/hud.tscn @@ -0,0 +1,121 @@ +[gd_scene format=3 uid="uid://c1gs6cqek1qlq"] + +[ext_resource type="PackedScene" uid="uid://n3u0jr4wiib1" path="res://scenes/ui/settings.tscn" id="1_bo48s"] +[ext_resource type="PackedScene" uid="uid://2sfy0vq0fqt3" path="res://scenes/ui/player_listing.tscn" id="2_8qguk"] +[ext_resource type="PackedScene" uid="uid://ffh5txda3j1" path="res://scenes/ui/chat_box.tscn" id="3_136eg"] + +[sub_resource type="LabelSettings" id="LabelSettings_oj5la"] +font_size = 32 +font_color = Color(1, 0, 0, 1) +outline_size = 10 +outline_color = Color(0, 0, 0, 1) + +[node name="hud" type="Hud" unique_id=1365644678 node_paths=PackedStringArray("pause_panel", "settings_button", "exit_to_lobby", "exit", "settings_panel", "scoreboard", "chatbox", "health_label")] +pause_panel = NodePath("pause_menu") +settings_button = NodePath("pause_menu/v_box_container/settings") +exit_to_lobby = NodePath("pause_menu/v_box_container/exit_to_lobby") +exit = NodePath("pause_menu/v_box_container/exit") +settings_panel = NodePath("settings") +scoreboard = NodePath("scoreboard") +chatbox = NodePath("chat_box") +health_label = NodePath("health_label") +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="score_view" type="SimpleScoreView" parent="." unique_id=981879809] +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -20.0 +offset_top = 18.0 +offset_right = 20.0 +offset_bottom = 58.0 +grow_horizontal = 2 +alignment = 1 + +[node name="pause_menu" type="Panel" parent="." unique_id=990293559] +visible = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -67.75 +offset_top = -101.25 +offset_right = 67.75 +offset_bottom = 101.25 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="v_box_container" type="VBoxContainer" parent="pause_menu" unique_id=1801932370] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -20.0 +offset_top = -20.0 +offset_right = 20.0 +offset_bottom = 20.0 +grow_horizontal = 2 +grow_vertical = 2 +alignment = 1 + +[node name="settings" type="Button" parent="pause_menu/v_box_container" unique_id=1671674264] +layout_mode = 2 +text = "Settings" + +[node name="exit_to_lobby" type="Button" parent="pause_menu/v_box_container" unique_id=313653211] +layout_mode = 2 +text = "Exit to Lobby" + +[node name="exit" type="Button" parent="pause_menu/v_box_container" unique_id=1097485912] +layout_mode = 2 +text = "Exit" + +[node name="settings" parent="." unique_id=999912383 instance=ExtResource("1_bo48s")] +visible = false +layout_mode = 1 + +[node name="scoreboard" type="PanelContainer" parent="." unique_id=99762392] +custom_minimum_size = Vector2(0, -1) +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -76.0 +offset_top = 58.0 +offset_right = 77.0 +offset_bottom = 100.0 +grow_horizontal = 2 +size_flags_vertical = 3 + +[node name="h_box_container" type="PlayerListings" parent="scoreboard" unique_id=43264288] +player_listing_scene = ExtResource("2_8qguk") +layout_mode = 2 +alignment = 1 +metadata/_edit_lock_ = true + +[node name="chat_box" parent="." unique_id=1125601400 instance=ExtResource("3_136eg")] +in_game = true +layout_mode = 0 + +[node name="health_label" type="Label" parent="." unique_id=676741647] +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 34.0 +offset_top = -45.0 +offset_right = 74.0 +offset_bottom = -22.0 +grow_vertical = 0 +text = "100" +label_settings = SubResource("LabelSettings_oj5la") +vertical_alignment = 2 diff --git a/rust/src/player/local_player.rs b/rust/src/player/local_player.rs index 2ccceda..d5e986f 100644 --- a/rust/src/player/local_player.rs +++ b/rust/src/player/local_player.rs @@ -2,8 +2,8 @@ use std::{f32::consts::PI, ops::Neg}; use godot::{ classes::{ - AudioStreamPlayer, Button, Camera3D, CharacterBody3D, CollisionShape3D, ICharacterBody3D, - Input, InputEvent, InputEventMouseMotion, Label, Panel, PanelContainer, input::MouseMode, + AudioStreamPlayer, Camera3D, CharacterBody3D, CollisionShape3D, ICharacterBody3D, Input, + InputEvent, InputEventMouseMotion, Label, input::MouseMode, }, obj::WithBaseField, prelude::*, @@ -30,7 +30,7 @@ use crate::{ weapon::{BallWeapon, Raygun, Shotgun, Weapon, WeaponType}, }, replay::ReplayEvent, - ui::{chatbox::ChatBox, cli::CommandLinePanel, settings::SettingsPanel}, + ui::{cli::CommandLinePanel, hud::Hud}, }; #[derive(GodotClass)] @@ -56,18 +56,10 @@ pub struct LocalPlayer { camera_origin: Option>, #[export] weapon_node: Option>, + #[export] - pause_panel: Option>, - #[export] - exit_to_lobby: Option>, - #[export] - settings_panel: Option>, - #[export] - scoreboard: Option>, - #[export] - chatbox: Option>, - #[export] - health_label: Option>, + hud: Option>, + #[export] hit_sound: Option>, #[export] @@ -223,9 +215,6 @@ impl IPlayer for LocalPlayer { s.kill(source); } } - if let Some(health_label) = &mut s.health_label { - health_label.set_text(&s.health.max(0).to_string()); - } }); } @@ -305,6 +294,10 @@ impl IPlayer for LocalPlayer { fn is_replay_mode(&self) -> bool { false } + + fn get_health(&self) -> i32 { + self.health + } } #[godot_api] @@ -323,28 +316,6 @@ impl ICharacterBody3D for LocalPlayer { self.set_lock(true); } - CommandLinePanel::singleton() - .signals() - .on_open() - .connect_other(self, |s| s.update_lock()); - CommandLinePanel::singleton() - .signals() - .on_close() - .connect_other(self, |s| { - s.update_lock(); - }); - - if let Some(chatbox) = self.chatbox.clone() { - chatbox - .signals() - .on_opened() - .connect_other(self, |s| s.update_lock()); - chatbox - .signals() - .on_closed() - .connect_other(self, |s| s.update_lock()); - } - self.swap_weapon(WeaponType::Raygun, false); if let Some(camera) = &mut self.camera { @@ -357,11 +328,13 @@ impl ICharacterBody3D for LocalPlayer { remover.bind_mut().active = true; } - self.run_deferred(|s| { - if let Some(exit_to_lobby) = &mut s.exit_to_lobby { - exit_to_lobby.set_visible(NetworkManager::singleton().bind().is_host()); - } - }); + if let Some(mut hud) = self.hud.clone() { + hud.bind_mut().player_id = self.player_id.unwrap_or(0); + hud.bind_mut() + .signals() + .on_lock_changed() + .connect_other(self, |s, lock| s.set_lock(lock)); + } } fn process(&mut self, delta: f64) { @@ -436,10 +409,6 @@ impl ICharacterBody3D for LocalPlayer { camera.set_global_position(new_pos); } - if let Some(scoreboard) = &mut self.scoreboard { - scoreboard.set_visible(Input::singleton().is_action_pressed("scoreboard")); - } - if self.movement_dir.length_squared() > 0. && self.base().is_on_floor() && !self.is_dead { if let Some(effects) = &mut self.effects { effects.bind_mut().walk(); @@ -478,19 +447,6 @@ impl ICharacterBody3D for LocalPlayer { } fn input(&mut self, event: Gd) { - if event.is_action_pressed("pause") { - if let Some(pause_panel) = &mut self.pause_panel { - let is_visible = pause_panel.is_visible(); - pause_panel.set_visible(!is_visible); - if !pause_panel.is_visible() { - if let Some(settings_panel) = &mut self.settings_panel { - settings_panel.set_visible(false); - } - } - self.update_lock(); - } - } - if self.locked { if event.is_action_pressed("shoot") { if let Some(pos) = self.raycast_from_camera() { @@ -637,22 +593,6 @@ impl LocalPlayer { damage: i32, ); - fn update_lock(&mut self) { - self.run_deferred(|s| { - let paused = if let Some(panel) = &s.pause_panel { - panel.is_visible() - } else { - false - }; - let chat_visible = if let Some(chat) = &s.chatbox { - chat.bind().is_editing() - } else { - false - }; - s.set_lock(!CommandLinePanel::singleton().bind().opened && !paused && !chat_visible); - }); - } - fn set_lock(&mut self, locked: bool) { self.locked = locked; if self.locked { diff --git a/rust/src/player/mod.rs b/rust/src/player/mod.rs index 558782b..95c2bb0 100644 --- a/rust/src/player/mod.rs +++ b/rust/src/player/mod.rs @@ -57,6 +57,7 @@ pub trait IPlayer { fn drop_ball(&mut self, with_velocity: bool) -> bool; fn get_camera_origin(&self) -> Option>; fn is_replay_mode(&self) -> bool; + fn get_health(&self) -> i32; } pub trait PlayerCommon { diff --git a/rust/src/player/remote_player.rs b/rust/src/player/remote_player.rs index 9cfcb0a..79ba337 100644 --- a/rust/src/player/remote_player.rs +++ b/rust/src/player/remote_player.rs @@ -230,6 +230,10 @@ impl IPlayer for RemotePlayer { fn is_replay_mode(&self) -> bool { self.replay_mode } + + fn get_health(&self) -> i32 { + self.health + } } #[godot_api] diff --git a/rust/src/ui/hud.rs b/rust/src/ui/hud.rs new file mode 100644 index 0000000..d3899eb --- /dev/null +++ b/rust/src/ui/hud.rs @@ -0,0 +1,149 @@ +use godot::{ + classes::{Button, Control, IControl, Input, InputEvent, Label, Panel, PanelContainer}, + prelude::*, +}; + +use crate::{ + game_manager::Game, + net::network_manager::NetworkManager, + ui::{chatbox::ChatBox, cli::CommandLinePanel, settings::SettingsPanel}, +}; + +#[derive(GodotClass)] +#[class(base=Control, init)] +pub struct Hud { + pub player_id: u16, + + #[export] + pause_panel: Option>, + #[export] + settings_button: Option>, + #[export] + exit_to_lobby: Option>, + #[export] + exit: Option>, + #[export] + settings_panel: Option>, + #[export] + scoreboard: Option>, + #[export] + chatbox: Option>, + #[export] + health_label: Option>, + + lock: bool, + + base: Base, +} + +#[godot_api] +impl IControl for Hud { + fn ready(&mut self) { + self.run_deferred(|s| { + if let Some(exit_to_lobby) = &mut s.exit_to_lobby { + exit_to_lobby.set_visible(NetworkManager::singleton().bind().is_host()); + } + }); + + CommandLinePanel::singleton() + .signals() + .on_open() + .connect_other(self, |s| s.update_lock()); + CommandLinePanel::singleton() + .signals() + .on_close() + .connect_other(self, |s| { + s.update_lock(); + }); + + if let Some(chatbox) = self.chatbox.clone() { + chatbox + .signals() + .on_opened() + .connect_other(self, |s| s.update_lock()); + chatbox + .signals() + .on_closed() + .connect_other(self, |s| s.update_lock()); + } + + if let Some(exit_to_lobby) = &self.exit_to_lobby { + exit_to_lobby + .signals() + .pressed() + .connect_other(self, |s| s.signals().on_exit_to_lobby().emit()); + } + + if let Some(exit) = &self.exit { + exit.signals() + .pressed() + .connect_other(self, |s| s.signals().on_exit().emit()); + } + if let Some(settings_btn) = &self.settings_button { + settings_btn.signals().pressed().connect_other(self, |s| { + if let Some(settings) = &mut s.settings_panel { + settings.set_visible(true); + } + }); + } + + self.update_lock(); + } + + fn process(&mut self, _: f64) { + if let Some(scoreboard) = &mut self.scoreboard { + scoreboard.set_visible(Input::singleton().is_action_pressed("scoreboard")); + } + if let Some(game) = Game::singleton() + && let Some(player) = game.bind().find_player(self.player_id) + && let Some(character) = &player.character + && let Some(health_label) = &mut self.health_label + { + health_label.set_text(&character.dyn_bind().get_health().to_string()); + } + } + + fn input(&mut self, event: Gd) { + if event.is_action_pressed("pause") { + if let Some(pause_panel) = &mut self.pause_panel { + let is_visible = pause_panel.is_visible(); + pause_panel.set_visible(!is_visible); + if !pause_panel.is_visible() { + if let Some(settings_panel) = &mut self.settings_panel { + settings_panel.set_visible(false); + } + } + self.update_lock(); + } + } + } +} + +#[godot_api] +impl Hud { + #[signal] + pub fn on_lock_changed(lock: bool); + #[signal] + pub fn on_exit_to_lobby(); + #[signal] + pub fn on_exit(); + + pub fn update_lock(&mut self) { + let paused = if let Some(panel) = &self.pause_panel { + panel.is_visible() + } else { + false + }; + let chat_visible = if let Some(chat) = &self.chatbox { + chat.bind().is_editing() + } else { + false + }; + let prev_lock = self.lock; + self.lock = !CommandLinePanel::singleton().bind().opened && !paused && !chat_visible; + if prev_lock != self.lock { + let lock = self.lock; + self.signals().on_lock_changed().emit(lock); + } + } +} diff --git a/rust/src/ui/mod.rs b/rust/src/ui/mod.rs index 3608953..99120d7 100644 --- a/rust/src/ui/mod.rs +++ b/rust/src/ui/mod.rs @@ -2,6 +2,7 @@ pub mod chatbox; pub mod cli; pub mod credits; pub mod game_finished_screen; +pub mod hud; pub mod lobby; pub mod lobby_player_listing; pub mod main_menu;