From 9b7671de4878cdf9c6a8205432b5beb4bba861c0 Mon Sep 17 00:00:00 2001 From: Rob Kelly Date: Thu, 20 Mar 2025 22:45:19 -0600 Subject: [PATCH] Player tank has limited capacity --- assets/level/floor/floor_6x9.mesh | Bin 1471 -> 1477 bytes assets/level/hallway/hallway_4.mesh | Bin 1662 -> 1677 bytes src/equipment/spray.gd | 8 ++- src/equipment/toothbrush/toothbrush.gd | 2 +- src/game/game.gd | 2 + src/game/game_manager.gd | 11 ++++ src/player/player.gd | 11 +++- src/props/overhead_light/overhead_light.tscn | 54 +++++++++---------- src/ui/hud/grunk_counter/grunk_counter.gd | 7 ++- src/ui/hud/grunk_counter/grunk_counter.tscn | 3 +- src/ui/hud/player_hud.gd | 5 ++ src/ui/hud/player_hud.tscn | 49 ++++++++++++++++- 12 files changed, 117 insertions(+), 35 deletions(-) diff --git a/assets/level/floor/floor_6x9.mesh b/assets/level/floor/floor_6x9.mesh index 4c1520f366e66e344424a17036fb39a75ae775df..6009bfc0b85217d2a1daa7af5a8f05ad9ec84f1e 100644 GIT binary patch literal 1477 zcmV;$1v>gtQ$s@n000005C8zo4*&qI1pojjwJ-f(%nltb046)LK@fG44j>$a6#)Pc zh9V6BAYcdrRAC^?g1Xs8v`>KUVCVHwnJM_qt!=F=Xy+>^I^;%Z9ObW+3hKnn^+#w_movGx!W8_rS|h* z!GfsGcA9j>O_WN}#5bXcWaN(1!38I+k+JS$&^6_la{QD{m8TR(*a1}wa%|<5k=H%x z`c>2td3fQj26w^e=5*Ay%(?L6sUp9HP(N%vNbsfL%)O_*)PDn?+N9P>oL0-1tDLu> zeVo}lr*b$S=DvJHhXo}`s*8#}D>j$uno>XWvv!7iEgdrgA%)Imp?h7$^+4tIHss`* zt~H&Os2$}(k>z}DA9KyR($Gb@Gu&KlLvDgqq;%0l_WTzhcV`%|M(I5#F_n=f(!{nP zw`^HgE^j|Kbvm6+=cGN3G?Mi*7c0~!9e3}XrU(P7ct!dasM^d-rJVLQmjum}rwt-h ztWZ(XCe&!OT}h6+5zrX@KSn=B8dy*Q@E7Vsv6R9A0A6yFn>6S;pem;3@9+}DM~KZ| z6Z6;L_?gN_qyH2BOSrbSlm$7K1txq`(o=;0GT@7tnEy{vfx?3ZAZq+8f60H7pGXD| zAr2V+_ZNn8o;Bn#W*`OEhz(C2Z>vmWZBTm!hx38LO9~23Lz2VD36}^rT5{C%NTOm! z3n4au6e(ds#)S?Nqm2zD5bl98gNOyQIL0>xfmf)JF+yl$MnqDOB!v!v1cq1=LYFrZ zKvXe_f=(fXNJeIYGKvb4Qn6-N0iD(FFia7l!_g3eyBAAR&j$pWQ3a1i>I+Y(0H|}a z^#WqJe;YXdMvt+w`SS$@A6&)YOYl$spAyO)a;^Q~PXaCjKr8~`&1X?Bh~l2a1X ziO6Hqo<%yeg)S|gIa4+1h`9MHha{YUWFV@&&&VEI!o`Id2!k~VPihuXayskRiGY9|(&aC>QV#3!>PN;*>aRcGV`7&U1!fAx(-6?Qi}nB^a?b zd*6P7Y&cVT@<=)Fs0*SViHFN)oHKouxni)rKV66OXna;A^$Ill6}U7PiM^b;=K0_1+WhT zVtMOTrv~6==j(Kr9hO+p3slD%Lf5k^POKl`C}sg%7objm4E#kDul8NOa4_LX_yfc; zCGwRp+Q!40kU!HG5QxeMFP3cU(3mjjdHWuU5yL~Go->Mct1+yqQgct}77Py8owdzB f?f@or0gj2n>Vkdc*YnD#TV_v=PQd2`iR$jZ? literal 1471 zcmV;w1wi^zQ$s@n000005C8zY4*&qC1pojjwJ-f(ybkRw00w)qKoE734j>$a000#M z5n?9*Aiw|wqGKRVin`fGV)HYRA#B zT)Yjgd?|$ozHr%GvDZ@;-&)31oXh_m{I3opNeho7y~E`{fOqDa04Xzw|0n+k1o#6p zOrxGLMi!Pq?RL}!=w>=S!R4O3-4a(bs-}w??gX`$bWdveN8eLcx#VthAeGwAe+3Jo zHd{&36<4BEiY7h@1tcSPoDMEHX^o6^AA_zb$CTrzY^pq^I6{u6T99KauZ+CzN!PEU z7HHyyyBgdDqnp!F>oVuU&QwKy3!!${dXV5t!I^tcd#V2hF11N5mMATkFIOF&eVf@k zr*1YL=E7VAhXoZys*8#}D>j$uno>Knvv!7iEgdrgABFB^p?h7$ouKl1`*Ctj*P2QT z)Q)nW$Z|fnkGW=LY3QQd8E&q&A1A>oQo3j&PyYqT-5Ca~F?!EMOl72rG_kG6EnC)= z%iGUM91e%WIcbk0jb!c2#R~OF$K5+6DaLpzUXZ@!sa9sDQcio@OM+&~((*|mRIE@@ z(k4_Wv|UAxyAjX`{XjxMLK<370`M2=L$Q>?0RUcdlbbZ?IG#$T=I;O_#0Q9#zeeV- z!SOSdheH1+{FiWTZ7J({ov|7sLl;Yc3XpSjqLL zOQhswlIH3fJ1#R%2}Q1qmISzcFiP+KkEpXg8~QI^d)6zw39oXP6Nt|d<8mVFoR|8f zLR2YX&V<3meogM9IYqWEd!m%5l{6+|cL?XzuLI(@9!7(2f+P}NK26HoIL;Ge^g(<7 z=w`zxN*-T9H5TzgER0r`)xR>x;YN#7RBqnkoDhTOBF&;SiA1(P{8g1;kf?R(^Mw0w z2>#C_U1{XC5N9AYZDoDWUk~04&P-Qcz6CMe%?haJanq?98Y2BZn%S0=e1&j`3Ek>G zg1=5Fa~U!*FH_rTDL-(TzAtsDA)Y02V2+~1kBchwoWfF*PsD?H>FBf;^=BeN))36xu ZL|f|Ms#wcP2wtL+pmHo<+;|CWmyTvj=ua{qVv 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 void: func _fire() -> void: + rumbler.intensity = RUMBLE_INTENSITY + + if Game.manager.is_tank_full(): + Player.instance.hud.play_tank_full_alert() + idle() + return + _spray() spray_effect.visible = true beam_particles_1.emitting = true beam_particles_2.emitting = true - rumbler.intensity = RUMBLE_INTENSITY func _idle() -> void: diff --git a/src/equipment/toothbrush/toothbrush.gd b/src/equipment/toothbrush/toothbrush.gd index 372d65f..9d58228 100644 --- a/src/equipment/toothbrush/toothbrush.gd +++ b/src/equipment/toothbrush/toothbrush.gd @@ -14,7 +14,7 @@ const BRUSH_SCALE := 0.2 func _fire() -> void: - if raycast.is_colliding(): + if raycast.is_colliding() and not Game.manager.is_tank_full(): brush_animation.play("brush") var collider := raycast.get_collider() if collider is GunkBody: diff --git a/src/game/game.gd b/src/game/game.gd index f1ceb58..d5d9630 100644 --- a/src/game/game.gd +++ b/src/game/game.gd @@ -5,3 +5,5 @@ class_name Game extends Node static var manager: GameManagerType: get(): return GameManager + +static var hud: PlayerHUD diff --git a/src/game/game_manager.gd b/src/game/game_manager.gd index 95024c3..96d2850 100644 --- a/src/game/game_manager.gd +++ b/src/game/game_manager.gd @@ -15,6 +15,9 @@ signal alert_cleared const MAX_ALERT := 6 +## Maximum amount of grunk the player can carry in their tank. +@export var grunk_tank_limit := 120000 + ## Amount of grunk the player is currently carrying. var grunk_tank := 0.0 @@ -44,6 +47,14 @@ func deposit_tank() -> void: empty_tank() +func is_tank_full() -> bool: + return grunk_tank >= grunk_tank_limit + + +func get_tank_fill_pct() -> float: + return grunk_tank / grunk_tank_limit + + ## Raise the alert level, if possible. func raise_alert(delta: int) -> void: var new_value := clampi(alert_level + delta, 0, MAX_ALERT) diff --git a/src/player/player.gd b/src/player/player.gd index 7be6cbf..4404092 100644 --- a/src/player/player.gd +++ b/src/player/player.gd @@ -16,7 +16,7 @@ var gravity: Vector3 = ( var selected_interactive: Interactive var firing := false -@onready var player_hud: PlayerHUD = %PlayerHUD +@onready var hud: PlayerHUD = %PlayerHUD @onready var camera_pivot: CameraController = %CameraPivot @@ -27,6 +27,13 @@ var firing := false @onready var wide_spray: WideSpray = %WideSpray @onready var toothbrush: Tool = %Toothbrush +## Global static access to player singleton +static var instance: Player + + +func _ready() -> void: + instance = self + func get_speed() -> float: if is_on_floor(): @@ -51,7 +58,7 @@ func get_tool() -> Tool: func _physics_process(delta: float) -> void: # Will be null if no valid interactor is selected. var interactive: Interactive = interact_ray.get_collider() as Interactive - player_hud.select_interactive(interactive) + hud.select_interactive(interactive) # World interaction if interactive and Input.is_action_just_pressed("interact"): diff --git a/src/props/overhead_light/overhead_light.tscn b/src/props/overhead_light/overhead_light.tscn index 4ee14ec..bf7fccf 100644 --- a/src/props/overhead_light/overhead_light.tscn +++ b/src/props/overhead_light/overhead_light.tscn @@ -55,6 +55,33 @@ _surfaces = [{ blend_shape_mode = 0 shadow_mesh = SubResource("ArrayMesh_3gl0p") +[sub_resource type="Animation" id="Animation_g27yp"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:mesh:surface_0/material:emission_energy_multiplier") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [12.0] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("../SpotLight3D:light_energy") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [2.0] +} + [sub_resource type="Animation" id="Animation_whqf3"] resource_name = "flicker" length = 0.01 @@ -85,33 +112,6 @@ tracks/1/keys = { "values": [3.0, 2.7] } -[sub_resource type="Animation" id="Animation_g27yp"] -length = 0.001 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath(".:mesh:surface_0/material:emission_energy_multiplier") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 0, -"values": [12.0] -} -tracks/1/type = "value" -tracks/1/imported = false -tracks/1/enabled = true -tracks/1/path = NodePath("../SpotLight3D:light_energy") -tracks/1/interp = 1 -tracks/1/loop_wrap = true -tracks/1/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 0, -"values": [2.0] -} - [sub_resource type="AnimationLibrary" id="AnimationLibrary_ngq1d"] _data = { &"RESET": SubResource("Animation_g27yp"), diff --git a/src/ui/hud/grunk_counter/grunk_counter.gd b/src/ui/hud/grunk_counter/grunk_counter.gd index b3f38cf..1b31269 100644 --- a/src/ui/hud/grunk_counter/grunk_counter.gd +++ b/src/ui/hud/grunk_counter/grunk_counter.gd @@ -4,6 +4,8 @@ extends HBoxContainer const COUNTER_BUMP_RATE := 0.15 const COUNTER_SPINDOWN_TIME := 0.4 +const TANK_WARNING_BUFFER_PCT := 0.1 + @onready var counter: Label = %Counter @@ -14,11 +16,14 @@ func _ready() -> void: func on_grunk_collected(delta: float) -> void: - counter.text = str(int(Game.manager.grunk_tank)) + counter.text = str(int(clampf(Game.manager.grunk_tank, 0.0, Game.manager.grunk_tank_limit))) counter.scale = Vector2.ONE + Vector2.ONE * clampf(delta / 128.0, 0.1, 1.0) + var buffer := remap(Game.manager.get_tank_fill_pct(), 1 - TANK_WARNING_BUFFER_PCT, 1, 0, 1) + counter.modulate = Color.WHITE.lerp(Color.RED, clampf(buffer, 0, 1)) func on_grunk_emptied(amount: float) -> void: + counter.modulate = Color.WHITE create_tween().tween_method(_set_counter, int(amount), 0, COUNTER_SPINDOWN_TIME).set_trans( Tween.TRANS_EXPO ) diff --git a/src/ui/hud/grunk_counter/grunk_counter.tscn b/src/ui/hud/grunk_counter/grunk_counter.tscn index 3f35997..bb189fc 100644 --- a/src/ui/hud/grunk_counter/grunk_counter.tscn +++ b/src/ui/hud/grunk_counter/grunk_counter.tscn @@ -23,8 +23,9 @@ text = "GRUNK: " [node name="Counter" type="Label" parent="."] unique_name_in_owner = true texture_filter = 6 +custom_minimum_size = Vector2(120, 0) layout_mode = 2 size_flags_horizontal = 4 size_flags_vertical = 8 -text = " 0" +text = "0" horizontal_alignment = 2 diff --git a/src/ui/hud/player_hud.gd b/src/ui/hud/player_hud.gd index e25de67..3e6448e 100644 --- a/src/ui/hud/player_hud.gd +++ b/src/ui/hud/player_hud.gd @@ -7,6 +7,7 @@ class_name PlayerHUD extends Control func _ready() -> void: Game.manager.alert_raised.connect(_on_raise_alert) + Game.hud = self func select_interactive(prop: Interactive) -> void: @@ -15,3 +16,7 @@ func select_interactive(prop: Interactive) -> void: func _on_raise_alert(_new_value: int) -> void: alert_player.play("grunk_alert") + + +func play_tank_full_alert() -> void: + alert_player.play("tank_full_alert") diff --git a/src/ui/hud/player_hud.tscn b/src/ui/hud/player_hud.tscn index 5bac013..9bc6e54 100644 --- a/src/ui/hud/player_hud.tscn +++ b/src/ui/hud/player_hud.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=3 uid="uid://dq1x21tq06dud"] +[gd_scene load_steps=9 format=3 uid="uid://dq1x21tq06dud"] [ext_resource type="Theme" uid="uid://b07fevr214mmr" path="res://src/ui/hud/hud_theme.tres" id="1_lirk3"] [ext_resource type="Script" uid="uid://lrsv0185bfu" path="res://src/ui/hud/player_hud.gd" id="2_j6lpx"] @@ -31,6 +31,18 @@ tracks/1/keys = { "update": 0, "values": [Color(1, 1, 1, 0)] } +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("TankAlert:modulate") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 0)] +} [sub_resource type="Animation" id="Animation_5be8f"] resource_name = "grunk_alert" @@ -61,10 +73,28 @@ tracks/1/keys = { "values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0)] } +[sub_resource type="Animation" id="Animation_ud8na"] +resource_name = "tank_full_alert" +length = 3.6 +step = 0.1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("TankAlert:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.6, 1.2, 1.8, 2.4, 3, 3.6), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0)] +} + [sub_resource type="AnimationLibrary" id="AnimationLibrary_ud8na"] _data = { &"RESET": SubResource("Animation_n6jee"), -&"grunk_alert": SubResource("Animation_5be8f") +&"grunk_alert": SubResource("Animation_5be8f"), +&"tank_full_alert": SubResource("Animation_ud8na") } [node name="PlayerHUD" type="Control"] @@ -148,6 +178,21 @@ grow_horizontal = 2 grow_vertical = 2 mouse_filter = 1 +[node name="TankAlert" type="Label" parent="AlertHUD"] +modulate = Color(1, 1, 1, 0) +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -268.0 +offset_right = 268.0 +offset_bottom = 88.0 +grow_horizontal = 2 +theme_type_variation = &"AlertLabel" +text = "TANK FULL +RETURN TO SHIP" +horizontal_alignment = 1 + [node name="GrunkAlert" type="Label" parent="AlertHUD"] modulate = Color(1, 1, 1, 0) layout_mode = 1