From 377d74286be725e92c743b1e60ea97bbc8d91519 Mon Sep 17 00:00:00 2001 From: unfa Date: Wed, 22 Sep 2021 01:51:21 +0200 Subject: [PATCH] Implemented fake death to workaround a Godot bug. Added health and jetpack bars to HUD. --- Game/Assets/Characters/Player.gd | 38 +++++++++- Game/Assets/HUD/BarOver.svg | 71 ++++++++++++++++++ Game/Assets/HUD/BarOver.svg.import | 35 +++++++++ Game/Assets/HUD/BarProgress.svg | 71 ++++++++++++++++++ Game/Assets/HUD/BarProgress.svg.import | 35 +++++++++ Game/Assets/HUD/BarUnder.svg | 71 ++++++++++++++++++ Game/Assets/HUD/BarUnder.svg.import | 35 +++++++++ Game/Assets/HUD/HUD.tscn | 60 +++++++++++++++- Game/Assets/HUD/Panel.svg | 71 ++++++++++++++++++ Game/Assets/HUD/Panel.svg.import | 35 +++++++++ .../SFX/Weapons_Handgun_Casing_01.wav.import | 22 ++++++ Game/Main.gd | 72 +++++++++++++++---- 12 files changed, 602 insertions(+), 14 deletions(-) create mode 100644 Game/Assets/HUD/BarOver.svg create mode 100644 Game/Assets/HUD/BarOver.svg.import create mode 100644 Game/Assets/HUD/BarProgress.svg create mode 100644 Game/Assets/HUD/BarProgress.svg.import create mode 100644 Game/Assets/HUD/BarUnder.svg create mode 100644 Game/Assets/HUD/BarUnder.svg.import create mode 100644 Game/Assets/HUD/Panel.svg create mode 100644 Game/Assets/HUD/Panel.svg.import create mode 100644 Game/Assets/SFX/Weapons_Handgun_Casing_01.wav.import diff --git a/Game/Assets/Characters/Player.gd b/Game/Assets/Characters/Player.gd index b0ad5ab..0ed566a 100644 --- a/Game/Assets/Characters/Player.gd +++ b/Game/Assets/Characters/Player.gd @@ -88,12 +88,35 @@ var jetpack_was_active = false var velocity := Vector3.ZERO var gravity_vec := Vector3.ZERO +var dead = false: # used to workaround Godot crash when destroying player_nodes + set(value): + match value: + true: + #input_active = false + self.hide() + $Body.disabled = true + #set_physics_process(false) + false: + #input_active = true + self.show() + $Body.disabled = false + #set_physics_process(true) + dead = value + @rpc(auth, nosync,unreliable) func update_movement(player_transform, head_rotation, lin_velocity, jetpack): global_transform = player_transform head.set_rotation(head_rotation) jetpack_active = jetpack linear_velocity = lin_velocity +@rpc(any, sync, reliable) func set_dead(is_dead: bool): + #print("Recieved RPC call for set_dead ", is_dead) + self.dead = is_dead + +# if is_multiplayer_authority(): +# print("Rebroadcasting RPC call for set_dead ", dead) +# rpc(&'set_dead', dead) + func _ready() -> void: #Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) view_zoom_target = 1.0 @@ -124,6 +147,9 @@ func aim(event) -> void: head.rotation.x = clamp(current_tilt, deg2rad(-90), deg2rad(90)) func _input(event) -> void: + if dead: + return + if not input_active: return @@ -161,6 +187,9 @@ func _input(event) -> void: weapon.trigger(1, false) func _process(delta): + if dead: + return + $Jetpack/GPUParticles3D.emitting = jetpack_active if jetpack_active: @@ -185,12 +214,14 @@ func _process(delta): elif not view_zoom_direction and view_zoom > view_zoom_target: view_zoom = max(view_zoom_target, view_zoom - delta * 4) + hud.get_node("Stats").get_node("JetpackBar").value = (jetpack_fuel / jetpack_tank) * 100 + func damage(hp: int): var target = main.player_list.players[self.get_multiplayer_authority()] target.health -= hp -@rpc(any, nosync, reliable) func die(killer_pid: int): +@rpc(any, sync, reliable) func die(killer_pid: int): var gibs = gibs_vfx.instantiate() get_tree().root.add_child(gibs) gibs.global_transform = self.global_transform @@ -200,6 +231,7 @@ func damage(hp: int): # return #main.rpc(&'destroy_player', self.get_multiplayer_authority()) main.destroy_player(self.get_multiplayer_authority()) + set_dead(true) #main.chat.rpc(&'chat_notification', "Player [/i][b][color=" + main.player_list.players[self.get_multiplayer_authority()].color.to_html() + "]" + main.player_list.players[self.get_multiplayer_authority()].name + "[/color][/b][i] was killed by " + main.player_list.players[killer_pid].name ) main.chat.chat_notification("Player [/i][b][color=" + main.player_list.players[self.get_multiplayer_authority()].color.to_html() + "]" + main.player_list.players[self.get_multiplayer_authority()].name + "[/color][/b][i] was killed by " + main.player_list.players[killer_pid].name ) @@ -216,6 +248,10 @@ func update_color(color) -> void: #change player's wolrldmodel color func _physics_process(delta): + if dead: # workaround for Godot player destruction crash + linear_velocity = Vector3.ZERO + return + if not is_multiplayer_authority(): move_and_slide() return diff --git a/Game/Assets/HUD/BarOver.svg b/Game/Assets/HUD/BarOver.svg new file mode 100644 index 0000000..fef00ac --- /dev/null +++ b/Game/Assets/HUD/BarOver.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Game/Assets/HUD/BarOver.svg.import b/Game/Assets/HUD/BarOver.svg.import new file mode 100644 index 0000000..57aa7e2 --- /dev/null +++ b/Game/Assets/HUD/BarOver.svg.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture2D" +uid="uid://dvt25wji1pdyl" +path="res://.godot/imported/BarOver.svg-3e206b7d7b36feafb9ac8b93edd2c46b.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/HUD/BarOver.svg" +dest_files=["res://.godot/imported/BarOver.svg-3e206b7d7b36feafb9ac8b93edd2c46b.stex"] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/bptc_ldr=0 +compress/normal_map=0 +compress/channel_pack=0 +compress/streamed=false +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/HDR_as_SRGB=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 diff --git a/Game/Assets/HUD/BarProgress.svg b/Game/Assets/HUD/BarProgress.svg new file mode 100644 index 0000000..5eaba6a --- /dev/null +++ b/Game/Assets/HUD/BarProgress.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Game/Assets/HUD/BarProgress.svg.import b/Game/Assets/HUD/BarProgress.svg.import new file mode 100644 index 0000000..8ca73be --- /dev/null +++ b/Game/Assets/HUD/BarProgress.svg.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture2D" +uid="uid://0j6rxd7ncmu1" +path="res://.godot/imported/BarProgress.svg-38f8fe7383fae81cc6603349ccd802d9.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/HUD/BarProgress.svg" +dest_files=["res://.godot/imported/BarProgress.svg-38f8fe7383fae81cc6603349ccd802d9.stex"] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/bptc_ldr=0 +compress/normal_map=0 +compress/channel_pack=0 +compress/streamed=false +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/HDR_as_SRGB=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 diff --git a/Game/Assets/HUD/BarUnder.svg b/Game/Assets/HUD/BarUnder.svg new file mode 100644 index 0000000..52de9b7 --- /dev/null +++ b/Game/Assets/HUD/BarUnder.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Game/Assets/HUD/BarUnder.svg.import b/Game/Assets/HUD/BarUnder.svg.import new file mode 100644 index 0000000..51685b6 --- /dev/null +++ b/Game/Assets/HUD/BarUnder.svg.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture2D" +uid="uid://ck4exwgifde4n" +path="res://.godot/imported/BarUnder.svg-95ef68273b3476991eb89e9f1df4b93d.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/HUD/BarUnder.svg" +dest_files=["res://.godot/imported/BarUnder.svg-95ef68273b3476991eb89e9f1df4b93d.stex"] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/bptc_ldr=0 +compress/normal_map=0 +compress/channel_pack=0 +compress/streamed=false +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/HDR_as_SRGB=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 diff --git a/Game/Assets/HUD/HUD.tscn b/Game/Assets/HUD/HUD.tscn index dad234c..68fab7c 100644 --- a/Game/Assets/HUD/HUD.tscn +++ b/Game/Assets/HUD/HUD.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=13 format=3 uid="uid://bff5uslrxesjx"] +[gd_scene load_steps=16 format=3 uid="uid://bff5uslrxesjx"] [ext_resource type="Texture2D" uid="uid://blnjjtjifk22i" path="res://Assets/HUD/Vignette.png" id="1"] [ext_resource type="Script" path="res://Assets/HUD/HUD.gd" id="1_wc430"] @@ -8,6 +8,9 @@ [ext_resource type="AudioStream" uid="uid://bllgajqussdi5" path="res://Assets/SFX/HUD_Confirm_Kill.wav" id="5_hh74r"] [ext_resource type="Script" path="res://Assets/HUD/Chat.gd" id="6"] [ext_resource type="Theme" uid="uid://ddtu7o1dbp0s8" path="res://Assets/HUD/Theme.tres" id="7"] +[ext_resource type="Texture2D" uid="uid://ck4exwgifde4n" path="res://Assets/HUD/BarUnder.svg" id="9_q07oy"] +[ext_resource type="Texture2D" uid="uid://dvt25wji1pdyl" path="res://Assets/HUD/BarOver.svg" id="10_5i332"] +[ext_resource type="Texture2D" uid="uid://0j6rxd7ncmu1" path="res://Assets/HUD/BarProgress.svg" id="11_tlgqu"] [sub_resource type="Animation" id="1"] resource_name = "Default" @@ -310,4 +313,59 @@ __meta__ = { "_edit_use_anchors_": false } +[node name="Stats" type="VBoxContainer" parent="."] +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -200.0 +offset_top = -68.0 +offset_right = -32.0 +offset_bottom = -32.0 +grow_horizontal = 0 +grow_vertical = 0 +script = null +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="HealthBar" type="TextureProgressBar" parent="Stats"] +offset_right = 200.0 +offset_bottom = 32.0 +rect_min_size = Vector2(200, 32) +value = 100.0 +allow_greater = true +allow_lesser = true +texture_under = ExtResource( "9_q07oy" ) +texture_over = ExtResource( "10_5i332" ) +texture_progress = ExtResource( "11_tlgqu" ) +tint_under = Color(0, 0, 0, 0.12549) +tint_over = Color(0, 0.780392, 1, 1) +tint_progress = Color(0, 0.745098, 1, 1) +nine_patch_stretch = true +stretch_margin_left = 16 +stretch_margin_top = 16 +stretch_margin_right = 16 +stretch_margin_bottom = 16 +script = null + +[node name="JetpackBar" type="TextureProgressBar" parent="Stats"] +offset_top = 36.0 +offset_right = 200.0 +offset_bottom = 68.0 +rect_min_size = Vector2(200, 32) +value = 100.0 +texture_under = ExtResource( "9_q07oy" ) +texture_over = ExtResource( "10_5i332" ) +texture_progress = ExtResource( "11_tlgqu" ) +tint_under = Color(0, 0, 0, 0.12549) +tint_over = Color(1, 0.764706, 0, 1) +tint_progress = Color(1, 0.760784, 0, 1) +nine_patch_stretch = true +stretch_margin_left = 16 +stretch_margin_top = 16 +stretch_margin_right = 16 +stretch_margin_bottom = 16 +script = null + [connection signal="text_submitted" from="Chat/VBoxContainer/Typing/Editor" to="Chat" method="_on_Editor_text_submitted"] diff --git a/Game/Assets/HUD/Panel.svg b/Game/Assets/HUD/Panel.svg new file mode 100644 index 0000000..aed61ce --- /dev/null +++ b/Game/Assets/HUD/Panel.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Game/Assets/HUD/Panel.svg.import b/Game/Assets/HUD/Panel.svg.import new file mode 100644 index 0000000..2ac7fe7 --- /dev/null +++ b/Game/Assets/HUD/Panel.svg.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture2D" +uid="uid://duxv0vin55qyy" +path="res://.godot/imported/Panel.svg-123d44a61e9822e83e9dafcfaffa99a9.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/HUD/Panel.svg" +dest_files=["res://.godot/imported/Panel.svg-123d44a61e9822e83e9dafcfaffa99a9.stex"] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/bptc_ldr=0 +compress/normal_map=0 +compress/channel_pack=0 +compress/streamed=false +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/HDR_as_SRGB=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 diff --git a/Game/Assets/SFX/Weapons_Handgun_Casing_01.wav.import b/Game/Assets/SFX/Weapons_Handgun_Casing_01.wav.import new file mode 100644 index 0000000..88bd831 --- /dev/null +++ b/Game/Assets/SFX/Weapons_Handgun_Casing_01.wav.import @@ -0,0 +1,22 @@ +[remap] + +importer="wav" +type="AudioStreamSample" +uid="uid://d2siwe07rwepl" +path="res://.godot/imported/Weapons_Handgun_Casing_01.wav-80f393f0b224823a59ba1d5e69dc8cdd.sample" + +[deps] + +source_file="res://Assets/SFX/Weapons_Handgun_Casing_01.wav" +dest_files=["res://.godot/imported/Weapons_Handgun_Casing_01.wav-80f393f0b224823a59ba1d5e69dc8cdd.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=false +compress/mode=0 diff --git a/Game/Main.gd b/Game/Main.gd index cb4f2e2..07afad7 100644 --- a/Game/Main.gd +++ b/Game/Main.gd @@ -73,6 +73,8 @@ var spawn_queue = {} var game_score_limit = 15 +const destroy_free_player_crash_workaround = true + func _process(delta): uptime += delta @@ -88,6 +90,10 @@ func _process(delta): $Label.text += "\n\nspawn_queue: \n" $Label.text += str(spawn_queue) + if local_player: + $Label.text += "\n\nLOCAL PLAYER DEAD: " + str(local_player.dead) + + for i in spawn_queue.keys(): if spawn_queue[i] <= uptime: var is_local = true if i == get_tree().multiplayer.get_unique_id() else false @@ -109,25 +115,27 @@ class PlayerList: @onready var player_list = PlayerList.new() -var focus = GameFocus.MENU : +var focus:GameFocus: set(new_focus): if local_player != null: assert(local_player != null, "local_player is not null but it is null, WTF?!") match new_focus: - 0: + 0: # MENU Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) gui.show() + hud.hide() if local_player: local_player.input_active = false - 1: + 1: # GAME Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) gui.hide() + hud.show() if local_player: local_player.input_active = true - 2: + 2: # CHAT if local_player: local_player.input_active = false - 3: + 3: #AWAY if local_player: local_player.input_active = true @@ -155,6 +163,16 @@ func check_game_win_condition(): game_over() func update_hud(): + + ### Health + + hud.get_node("Stats").get_node("HealthBar").value = player_list.get( get_tree().multiplayer.get_unique_id() ).health + + if player_list.players.size() <= 1: + return # if we're the sole player in the server, stop here + + ### SCORE, RANK, GAP/LEAD + hud.get_node("ScoreRank").text = "SCORE: " + str(player_list.get( get_tree().multiplayer.get_unique_id() ).score) var score = player_list.get( get_tree().multiplayer.get_unique_id() ).score @@ -199,11 +217,11 @@ func update_hud(): update_hud() func push_local_player_info(): # - var id = get_tree().multiplayer.get_unique_id() + var pid = get_tree().multiplayer.get_unique_id() - rpc(&'player_list_update', player_list.get(id).serialize(), get_tree().multiplayer.get_unique_id()) + rpc(&'player_list_update', player_list.get(pid).serialize(), pid) -@rpc func destroy_player(pid: int): +@rpc(sync, any, reliable) func destroy_player(pid: int): print("DESTROY_PLAYER called on ", get_tree().multiplayer.get_unique_id(), " by ", get_tree().multiplayer.get_remote_sender_id(), " regarding ", pid) #assert($Players.has_node(str(pid)), "Attempting to destroy a player that does not exist") @@ -218,13 +236,40 @@ func push_local_player_info(): # #assert(player_node != null, "Attempting to delete a player node that does not exist") - player_node.name = str(player_node.name) + "_dead" # avoids name collision when instancing another player scene - print("before free") - player_node.queue_free() - print("after free") + # alternative finale to this function that is a workaround to avoid Godot crashing due to an engine bug + # https://github.com/godotengine/godot/issues/52853 + if destroy_free_player_crash_workaround: + print("Setting player ", pid, " as DEAD") + player_node.set_dead(true) + else: # regular method follows + player_node.name = str(player_node.name) + "_dead" # avoids name collision when instancing another player scene + + print("before free") + player_node.queue_free() + print("after free") + # respawn queue applies ot both implementations of death spawn_queue[pid] = uptime + respawn_delay + + if pid == get_tree().multiplayer.get_unique_id(): + update_hud() func create_player(pid: int, is_local:= false, respawn:= false) -> void: + if destroy_free_player_crash_workaround: + var player_node + if $Players.has_node(str(pid)): # if this is a respawn + player_node = $Players.get_node(str(pid)) + print("Setting player ", pid, " as ALIVE") + player_node.set_dead(false) + + var spawnpoint = $Map/SpawnPoints.get_children()[randi() % len($Map/SpawnPoints.get_children())] + player_node.global_transform = spawnpoint.global_transform + player_list.players[pid].health = 100 + push_local_player_info() + + #focus = GameFocus.GAME # make sure the player has no menu displayed when they respawn + return # avoid running the rest of this function + else: + pass # if this is not a respawn, let it carry on var new_player new_player = player_scene.instantiate() @@ -261,6 +306,8 @@ func create_player(pid: int, is_local:= false, respawn:= false) -> void: # new_player.get_node("Head/Camera").current = false # local_player.get_node("Head/Camera").current = true #new_player.rpc(&'set_color', player_list.get(pid).color) + + #update_hud() func start_dedicated_server(): # start server without creating a local player role = MultiplayerRole.DEDICATED_SERVER @@ -326,6 +373,7 @@ func _server_disconnected() -> void: func _ready() -> void: + hud.hide() # start in the menu #peer.compression_mode = NetworkedMultiplayerENet.COMPRESS_ZSTD print("Commandline arguments: ", OS.get_cmdline_args())