Compare commits

...

13 Commits

Author SHA1 Message Date
ca58dd65a6 Debug menu
Some checks failed
linting & formatting / build (push) Failing after 13s
itch.io publish action / build (linux64, x86_64) (push) Successful in 2m52s
itch.io publish action / build (osx, app) (push) Successful in 1m52s
itch.io publish action / build (win64, exe) (push) Successful in 1m58s
2025-04-22 23:22:54 -06:00
c28c016dbd Title screen SFX 2025-04-22 22:38:06 -06:00
4bfa1c88a6 Chained loading of save states 2025-04-22 22:10:08 -06:00
bc9e928c3b Sweet title screen! 2025-04-22 21:30:11 -06:00
9747e80f1d Saves are ID'd by scene 2025-04-22 16:00:55 -06:00
656e1d26bb Monster respawns when loading save 2025-04-22 15:26:55 -06:00
f560b88de8 Overhead light checks persistent state 2025-04-22 15:26:31 -06:00
e4a2f31de9 Bulkhead & wall switch defer state initialization until after loading 2025-04-22 14:23:11 -06:00
48c9953c53 Ship grunk level is persisted 2025-04-22 14:20:46 -06:00
22a22a6409 Save icon is displayed while saving 2025-04-22 13:59:24 -06:00
7957a47243 Progress persistence 2025-04-22 12:08:09 -06:00
cfb7b28971 Added loading screen 2025-04-21 18:07:11 -06:00
dcd5d5d57d GameManager is now WorldManager, a resource managed as part of the world 2025-04-21 16:58:56 -06:00
86 changed files with 2294 additions and 256 deletions

BIN
asset_dev/ui/corpo_logo.xcf Normal file

Binary file not shown.

BIN
asset_dev/ui/title.xcf Normal file

Binary file not shown.

View File

@ -9,6 +9,7 @@ UI_OFF,Off
UI_BACK,"⏎ Back"
UI_LOCKED,Locked
UI_QUIT,Quit
UI_LOADING,Loading
,
PAUSE_HEADING,Paused
PAUSE_RESUME,Resume
@ -17,6 +18,7 @@ PAUSE_END,"Quit to Title"
PAUSE_END_MSG,"End the game and return to the title screen?\nUnsaved progress will be lost."
PAUSE_QUIT,"Quit to Desktop"
PAUSE_QUIT_MSG,"Quit to desktop?\nUnsaved progress will be lost."
PAUSE_DEBUG,"Debug Menu"
,
SETTINGS_GAME,Game
SETTINGS_GAME_HEADING,"Game Configuration"

1 keys en
9 UI_BACK ⏎ Back
10 UI_LOCKED Locked
11 UI_QUIT Quit
12 UI_LOADING Loading
13
14 PAUSE_HEADING Paused
15 PAUSE_RESUME Resume
18 PAUSE_END_MSG End the game and return to the title screen?\nUnsaved progress will be lost.
19 PAUSE_QUIT Quit to Desktop
20 PAUSE_QUIT_MSG Quit to desktop?\nUnsaved progress will be lost.
21 PAUSE_DEBUG Debug Menu
22
23 SETTINGS_GAME Game
24 SETTINGS_GAME_HEADING Game Configuration

BIN
assets/ui/corpo_logo/corpo_logo_128.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dixpjnlaj86x2"
path="res://.godot/imported/corpo_logo_128.png-3de86c663523882d9e0ebffd7de5c43c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/ui/corpo_logo/corpo_logo_128.png"
dest_files=["res://.godot/imported/corpo_logo_128.png-3de86c663523882d9e0ebffd7de5c43c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
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/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

BIN
assets/ui/corpo_logo/corpo_logo_128_bg.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b7ds08rj0yk7j"
path="res://.godot/imported/corpo_logo_128_bg.png-89eb09aa3ea1cb5cb304d1d6904ce810.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/ui/corpo_logo/corpo_logo_128_bg.png"
dest_files=["res://.godot/imported/corpo_logo_128_bg.png-89eb09aa3ea1cb5cb304d1d6904ce810.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
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/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

BIN
assets/ui/corpo_logo/corpo_logo_256.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dbr66ubeql7gi"
path="res://.godot/imported/corpo_logo_256.png-58bfe662ab369a650f7147768bd46884.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/ui/corpo_logo/corpo_logo_256.png"
dest_files=["res://.godot/imported/corpo_logo_256.png-58bfe662ab369a650f7147768bd46884.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
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/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

BIN
assets/ui/corpo_logo/corpo_logo_512.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cbosbnfdmhu27"
path="res://.godot/imported/corpo_logo_512.png-b05524db051b0e137f14c9416740afff.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/ui/corpo_logo/corpo_logo_512.png"
dest_files=["res://.godot/imported/corpo_logo_512.png-b05524db051b0e137f14c9416740afff.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
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/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

BIN
assets/ui/title.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://buc0oy5col0et"
path="res://.godot/imported/title.png-534804d7bf6a63fbbb0c46e68d8e4660.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/ui/title.png"
dest_files=["res://.godot/imported/title.png-534804d7bf6a63fbbb0c46e68d8e4660.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
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/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

View File

@ -16,7 +16,7 @@ var boosted := false
func _ready() -> void:
Game.manager.alert_raised.connect(_on_alert_raised)
World.instance.manager.alert_raised.connect(_on_alert_raised)
func get_target_volume() -> float:
@ -81,7 +81,7 @@ func _on_player_exits_ship(_body: Node3D) -> void:
func _on_alert_raised(new_level: int) -> void:
if new_level == Game.manager.MAX_ALERT:
if new_level == World.instance.manager.MAX_ALERT:
suppress(30)
else:
boost(10.0)

View File

@ -1,7 +1,8 @@
[gd_scene load_steps=21 format=3 uid="uid://bov4ok76woyc"]
[gd_scene load_steps=22 format=3 uid="uid://bov4ok76woyc"]
[ext_resource type="PackedScene" uid="uid://crydi5cjgfwe5" path="res://levels/ghost_ship/ghost_ship_level.tscn" id="1_aj2m7"]
[ext_resource type="Environment" uid="uid://bkvij3ljl5ox3" path="res://levels/ghost_ship/environment_3.tres" id="1_h081y"]
[ext_resource type="Script" uid="uid://68r4ht5ut1ct" path="res://src/game/level.gd" id="1_law5k"]
[ext_resource type="PackedScene" uid="uid://bwe2jdmvinhqd" path="res://src/player/player.tscn" id="2_0ef5p"]
[ext_resource type="PackedScene" uid="uid://d1kacn4b60ucy" path="res://src/ui/post_processing.tscn" id="3_o7mxe"]
[ext_resource type="AudioStream" uid="uid://dx4d8a3mgpws" path="res://assets/sfx/ambient/drone_loop.wav" id="5_ejh2c"]
@ -42,6 +43,9 @@ stream_10/stream = ExtResource("10_h081y")
stream_10/weight = 0.2
[node name="GhostShip" type="Node3D"]
script = ExtResource("1_law5k")
id = "ghost_ship"
pretty_name = "The HFCS Gnurk"
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = ExtResource("1_h081y")

View File

@ -22,28 +22,28 @@ const STALLING_MSG := "\n\n\n\n\n. . . C a l c u l a t i n G . . ."
func _ready() -> void:
Game.manager.grunk_emptied.connect(_on_tank_emptied)
World.instance.manager.grunk_emptied.connect(_on_tank_emptied)
recompute()
func _next_milestone_name() -> String:
var next_milestone := Game.manager.next_milestone()
var next_milestone := World.instance.manager.next_milestone()
return next_milestone.name if next_milestone else "NOTHING"
func _next_milestone_amount() -> String:
var next_milestone_amt := Game.manager.next_milestone_amount()
var next_milestone_amt := World.instance.manager.next_milestone_amount()
return str(next_milestone_amt) if next_milestone_amt >= 0 else "NEVER"
func build_message() -> String:
var msg_lines := Game.manager.latest_milestone().message.split("\n")
var msg_lines := World.instance.manager.latest_milestone().message.split("\n")
var line_1 := msg_lines[0]
var line_2 := msg_lines[1] if len(msg_lines) >= 2 else ""
return MESSAGE_FMT.format(
[
int(Game.manager.grunk_quota),
int(Game.manager.grunk_vault),
int(World.instance.manager.grunk_quota),
int(World.instance.manager.grunk_vault),
line_1,
line_2,
_next_milestone_name(),

View File

@ -3,6 +3,7 @@ extends Node3D
# TODO figure out whatever this is lol
const MAX_GRUNK := 6400000.0
const MIN_LIQUID_PCT := 0.001
const LIQUID_FACTOR := 2.8
const TANK_FILL_TIME := 1.0
@ -13,7 +14,12 @@ const TANK_FILL_TIME := 1.0
func _ready() -> void:
Game.manager.grunk_collected.connect(_enable_tank)
World.instance.manager.grunk_collected.connect(_enable_tank)
_deferred_init.call_deferred()
func _deferred_init() -> void:
set_liquid_level(vault_fill_pct())
func _enable_tank(_delta: float) -> void:
@ -24,16 +30,21 @@ func _enable_tank(_delta: float) -> void:
func deposit_grunk() -> void:
# Tank is disabled until the player collects more grunk.
tank_interactor.enabled = false
Game.manager.deposit_tank()
World.instance.manager.deposit_tank()
set_liquid_level(clampf(Game.manager.grunk_vault / MAX_GRUNK, 0.0, 1.0))
set_liquid_level(vault_fill_pct())
grunk_pump_sfx.play()
func vault_fill_pct() -> float:
return clampf(World.instance.manager.grunk_vault / MAX_GRUNK, 0.0, 1.0)
func set_liquid_level(proportion: float) -> void:
(
create_tween()
. tween_property(grunk_liquid, "position:y", proportion * LIQUID_FACTOR, TANK_FILL_TIME)
. set_trans(Tween.TRANS_EXPO)
)
if proportion > MIN_LIQUID_PCT:
(
create_tween()
. tween_property(grunk_liquid, "position:y", proportion * LIQUID_FACTOR, TANK_FILL_TIME)
. set_trans(Tween.TRANS_EXPO)
)

View File

@ -1,4 +1,4 @@
extends Node3D
extends Level
## Game mechanic testing level
@onready var gunk_hall: GunkBody = %GunkHallBody

View File

@ -1,16 +1,16 @@
[gd_scene load_steps=31 format=4 uid="uid://b8rv6dg4tgaeb"]
[ext_resource type="Script" uid="uid://bvua1l2hb3an6" path="res://levels/mechanic_test/mechanic_test.gd" id="1_umjw2"]
[ext_resource type="PackedScene" uid="uid://bwe2jdmvinhqd" path="res://src/player/player.tscn" id="2_gut8u"]
[ext_resource type="PackedScene" uid="uid://b6eg8t04rkh0c" path="res://src/props/wall_switch/wall_switch.tscn" id="3_4okgx"]
[ext_resource type="PackedScene" uid="uid://c2omlx4ptrc01" path="res://src/world/gunk_body/gunk_body.tscn" id="4_7v7un"]
[ext_resource type="Texture2D" uid="uid://8cm835h4gxwe" path="res://assets/debug_mask.png" id="5_llot1"]
[ext_resource type="Shader" uid="uid://ckxc0ngd37rtk" path="res://src/shaders/gunk.gdshader" id="6_6agnv"]
[ext_resource type="FastNoiseLite" uid="uid://cnlvdtx68giv6" path="res://assets/materials/gunk_noise.tres" id="7_aqwgb"]
[ext_resource type="PackedScene" uid="uid://cubwniraol1qn" path="res://src/props/bulkhead/bulkhead.tscn" id="8_ny31q"]
[ext_resource type="PackedScene" uid="uid://b5jubpjj3d277" path="res://levels/mechanic_test/signal_test.tscn" id="9_dub8r"]
[ext_resource type="PackedScene" uid="uid://cfqirm2o3uo4k" path="res://levels/mechanic_test/prop_test.tscn" id="10_ovu60"]
[ext_resource type="PackedScene" uid="uid://dbabcsp38wmid" path="res://levels/mechanic_test/item_test.tscn" id="11_crh2u"]
[ext_resource type="Script" uid="uid://bvua1l2hb3an6" path="res://levels/mechanic_test/mechanic_test.gd" id="1_iyuyb"]
[ext_resource type="PackedScene" uid="uid://bwe2jdmvinhqd" path="res://src/player/player.tscn" id="2_qjnj2"]
[ext_resource type="PackedScene" uid="uid://b6eg8t04rkh0c" path="res://src/props/wall_switch/wall_switch.tscn" id="3_awnx0"]
[ext_resource type="PackedScene" uid="uid://c2omlx4ptrc01" path="res://src/world/gunk_body/gunk_body.tscn" id="4_cgmn0"]
[ext_resource type="Texture2D" uid="uid://8cm835h4gxwe" path="res://assets/debug_mask.png" id="5_aix42"]
[ext_resource type="Shader" uid="uid://ckxc0ngd37rtk" path="res://src/shaders/gunk.gdshader" id="6_6frcc"]
[ext_resource type="FastNoiseLite" uid="uid://cnlvdtx68giv6" path="res://assets/materials/gunk_noise.tres" id="7_7cbja"]
[ext_resource type="PackedScene" uid="uid://cubwniraol1qn" path="res://src/props/bulkhead/bulkhead.tscn" id="8_ujrcv"]
[ext_resource type="PackedScene" uid="uid://b5jubpjj3d277" path="res://levels/mechanic_test/signal_test.tscn" id="9_ix0jw"]
[ext_resource type="PackedScene" uid="uid://cfqirm2o3uo4k" path="res://levels/mechanic_test/prop_test.tscn" id="10_xrfi2"]
[ext_resource type="PackedScene" uid="uid://dbabcsp38wmid" path="res://levels/mechanic_test/item_test.tscn" id="11_8natv"]
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_goufh"]
@ -93,7 +93,7 @@ height = 256
depth = 32
seamless = true
seamless_blend_skirt = 0.5
noise = ExtResource("7_aqwgb")
noise = ExtResource("7_7cbja")
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_2pd8h"]
width = 256
@ -101,12 +101,12 @@ height = 256
depth = 32
seamless = true
seamless_blend_skirt = 0.5
noise = ExtResource("7_aqwgb")
noise = ExtResource("7_7cbja")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_umjw2"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_iyuyb"]
resource_local_to_scene = true
render_priority = 0
shader = ExtResource("6_6agnv")
shader = ExtResource("6_6frcc")
shader_parameter/color_1 = Color(0, 0.03, 0.1, 1)
shader_parameter/color_2 = Color(0, 0.1, 0.3, 1)
shader_parameter/emission_color = Color(0.25, 0.88, 1, 1)
@ -132,9 +132,11 @@ shader_parameter/overlay_emission_scale = 1.0
data = PackedVector3Array(-1.5, 0, -6, -1.5, 0, 6, -1.5, 3, -6, -1.5, 3, -6, -1.5, 0, 6, -1.5, 3, 6, 1.5, 0, 6, 1.5, 0, -6, 1.5, 3, 6, 1.5, 3, 6, 1.5, 0, -6, 1.5, 3, -6, -1.5, 0, 6, -1.5, 0, -6, 1.5, 0, 6, 1.5, 0, 6, -1.5, 0, -6, 1.5, 0, -6, 1.5, 3, 6, 1.5, 3, -6, -1.5, 3, 6, -1.5, 3, 6, 1.5, 3, -6, -1.5, 3, -6)
[node name="MechanicTest" type="Node3D"]
script = ExtResource("1_umjw2")
script = ExtResource("1_iyuyb")
id = "mechanic_test"
pretty_name = "Mechanics Test Sandbox"
[node name="Player" parent="." instance=ExtResource("2_gut8u")]
[node name="Player" parent="." instance=ExtResource("2_qjnj2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.65, 0)
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
@ -161,7 +163,7 @@ mesh = SubResource("BoxMesh_goufh")
[node name="CollisionShape3D" type="CollisionShape3D" parent="ResetPodium"]
shape = SubResource("ConcavePolygonShape3D_bg05n")
[node name="ResetSwitch" parent="ResetPodium" instance=ExtResource("3_4okgx")]
[node name="ResetSwitch" parent="ResetPodium" instance=ExtResource("3_awnx0")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0.35)
clean = true
@ -178,7 +180,7 @@ mesh = SubResource("BoxMesh_goufh")
[node name="CollisionShape3D" type="CollisionShape3D" parent="NoduleSpawner"]
shape = SubResource("ConcavePolygonShape3D_bg05n")
[node name="WallSwitch" parent="NoduleSpawner" instance=ExtResource("3_4okgx")]
[node name="WallSwitch" parent="NoduleSpawner" instance=ExtResource("3_awnx0")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0.35)
clean = true
@ -200,7 +202,7 @@ mesh = SubResource("BoxMesh_goufh")
[node name="CollisionShape3D" type="CollisionShape3D" parent="AlarmSpawner"]
shape = SubResource("ConcavePolygonShape3D_bg05n")
[node name="WallSwitch" parent="AlarmSpawner" instance=ExtResource("3_4okgx")]
[node name="WallSwitch" parent="AlarmSpawner" instance=ExtResource("3_awnx0")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0.35)
clean = true
@ -222,7 +224,7 @@ mesh = SubResource("BoxMesh_goufh")
[node name="CollisionShape3D" type="CollisionShape3D" parent="AlarmTrigger"]
shape = SubResource("ConcavePolygonShape3D_bg05n")
[node name="AlarmTriggerSwitch" parent="AlarmTrigger" instance=ExtResource("3_4okgx")]
[node name="AlarmTriggerSwitch" parent="AlarmTrigger" instance=ExtResource("3_awnx0")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0.35)
clean = true
@ -235,15 +237,15 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.5, 0, -31)
mesh = SubResource("ArrayMesh_x2vho")
skeleton = NodePath("GunkHallBody")
[node name="GunkHallBody" parent="GunkHall" instance=ExtResource("4_7v7un")]
[node name="GunkHallBody" parent="GunkHall" instance=ExtResource("4_cgmn0")]
unique_name_in_owner = true
initial_mask = ExtResource("5_llot1")
source_gunk_material = SubResource("ShaderMaterial_umjw2")
initial_mask = ExtResource("5_aix42")
source_gunk_material = SubResource("ShaderMaterial_iyuyb")
[node name="CollisionShape3D" type="CollisionShape3D" parent="GunkHall/GunkHallBody"]
shape = SubResource("ConcavePolygonShape3D_qjnj2")
[node name="Bulkhead" parent="." instance=ExtResource("8_ny31q")]
[node name="Bulkhead" parent="." instance=ExtResource("8_ujrcv")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.5, 0, -2)
[node name="Podium" type="StaticBody3D" parent="Bulkhead"]
@ -255,7 +257,7 @@ mesh = SubResource("BoxMesh_goufh")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Bulkhead/Podium"]
shape = SubResource("ConcavePolygonShape3D_bg05n")
[node name="OpenSwitch" parent="Bulkhead/Podium" instance=ExtResource("3_4okgx")]
[node name="OpenSwitch" parent="Bulkhead/Podium" instance=ExtResource("3_awnx0")]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0.35)
clean = true
@ -273,7 +275,7 @@ mesh = SubResource("BoxMesh_goufh")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Bulkhead/Podium2"]
shape = SubResource("ConcavePolygonShape3D_bg05n")
[node name="CloseSwitch" parent="Bulkhead/Podium2" instance=ExtResource("3_4okgx")]
[node name="CloseSwitch" parent="Bulkhead/Podium2" instance=ExtResource("3_awnx0")]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0.35)
clean = true
@ -287,19 +289,19 @@ text = "Close"
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.5, 0, -6)
[node name="SignalTest" parent="SignalTestSpawnPoint" instance=ExtResource("9_dub8r")]
[node name="SignalTest" parent="SignalTestSpawnPoint" instance=ExtResource("9_ix0jw")]
[node name="PropTestSpawnPoint" type="Marker3D" parent="."]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.5, 0, 4.5)
[node name="PropTest" parent="PropTestSpawnPoint" instance=ExtResource("10_ovu60")]
[node name="PropTest" parent="PropTestSpawnPoint" instance=ExtResource("10_xrfi2")]
[node name="ItemTestSpawnPoint" type="Marker3D" parent="."]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -13, 0, -2)
[node name="ItemTest" parent="ItemTestSpawnPoint" instance=ExtResource("11_crh2u")]
[node name="ItemTest" parent="ItemTestSpawnPoint" instance=ExtResource("11_8natv")]
[connection signal="activated" from="ResetPodium/ResetSwitch" to="." method="reset"]
[connection signal="activated" from="NoduleSpawner/WallSwitch" to="." method="spawn_nodule"]

View File

@ -1,6 +1,7 @@
[gd_scene load_steps=12 format=4 uid="uid://cfqirm2o3uo4k"]
[gd_scene load_steps=13 format=4 uid="uid://cfqirm2o3uo4k"]
[ext_resource type="PackedScene" uid="uid://c2omlx4ptrc01" path="res://src/world/gunk_body/gunk_body.tscn" id="1_cr8wn"]
[ext_resource type="Texture2D" uid="uid://8cm835h4gxwe" path="res://assets/debug_mask.png" id="2_7477u"]
[ext_resource type="Shader" uid="uid://ckxc0ngd37rtk" path="res://src/shaders/gunk.gdshader" id="2_lrgpr"]
[ext_resource type="FastNoiseLite" uid="uid://cnlvdtx68giv6" path="res://assets/materials/gunk_noise.tres" id="3_7477u"]
@ -57,7 +58,7 @@ seamless = true
seamless_blend_skirt = 0.5
noise = ExtResource("3_7477u")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_qjnj2"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_lrgpr"]
resource_local_to_scene = true
render_priority = 0
shader = ExtResource("2_lrgpr")
@ -69,6 +70,7 @@ shader_parameter/time_pixellation = 30.0
shader_parameter/roughness = 0.15
shader_parameter/specular_contribution = 0.8
shader_parameter/emission_strength = 0.02
shader_parameter/normal_scale = 1.0
shader_parameter/uv_scale = Vector2(4, 4)
shader_parameter/time_scale = 0.2
shader_parameter/edge_bleed = 0.25
@ -79,6 +81,7 @@ shader_parameter/jitter_magnitude = 0.0
shader_parameter/jitter_time_scale = 0.1
shader_parameter/vertex_inflation = 0.0
shader_parameter/inflation_pixellation = 10.0
shader_parameter/overlay_emission_scale = 1.0
[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_x2vho"]
data = PackedVector3Array(-1, 1, 0.1, -1, -0.8, 0.1, -1, 1, -0.1, -1, 1, -0.1, -1, -0.8, 0.1, -1, -0.8, -0.1, -1, -1, 0.1, -1, -0.8, -0.1, -1, -0.8, 0.1, -1, -1, -2.3, -1, -0.8, -0.1, -1, -1, 0.1, -1, -0.8, -2.3, -1, -0.8, -0.1, -1, -1, -2.3, -1, -0.8, -2.1, -1, -0.8, -0.1, -1, -0.8, -2.3, -1, -0.8, -2.1, -1, -0.8, -2.3, -1, 1, -2.1, -1, 1, -2.1, -1, -0.8, -2.3, -1, 1, -2.3, -1, 1, -0.1, -1, -0.8, -0.1, 1, 1, -0.1, 1, 1, -0.1, -1, -0.8, -0.1, 1, -0.8, -0.1, 1, 1, -0.1, 1, -0.8, -0.1, 1, 1, 0.1, 1, 1, 0.1, 1, -0.8, -0.1, 1, -0.8, 0.1, 1, -0.8, -0.1, 1, -1, 0.1, 1, -0.8, 0.1, 1, -0.8, -2.1, 1, -1, 0.1, 1, -0.8, -0.1, 1, -0.8, -2.3, 1, -1, 0.1, 1, -0.8, -2.1, 1, -1, -2.3, 1, -1, 0.1, 1, -0.8, -2.3, 1, -0.8, -2.3, 1, -0.8, -2.1, 1, 1, -2.3, 1, 1, -2.3, 1, -0.8, -2.1, 1, 1, -2.1, 1, 1, 0.1, 1, -0.8, 0.1, -1, 1, 0.1, -1, 1, 0.1, 1, -0.8, 0.1, -1, -0.8, 0.1, 1, -0.8, 0.1, 1, -1, 0.1, -1, -0.8, 0.1, -1, -0.8, 0.1, 1, -1, 0.1, -1, -1, 0.1, -1, -1, -2.3, -1, -1, 0.1, 1, -1, -2.3, 1, -1, -2.3, -1, -1, 0.1, 1, -1, 0.1, -1, 1, -0.1, 1, 1, -0.1, -1, 1, 0.1, -1, 1, 0.1, 1, 1, -0.1, 1, 1, 0.1, 1, -0.8, -0.1, -1, -0.8, -0.1, 1, -0.8, -2.1, 1, -0.8, -2.1, -1, -0.8, -0.1, -1, -0.8, -2.1, -1, -0.8, -2.3, -1, -1, -2.3, 1, -0.8, -2.3, 1, -0.8, -2.3, -1, -1, -2.3, 1, -1, -2.3, -1, -0.8, -2.3, 1, -0.8, -2.3, -1, 1, -2.3, -1, 1, -2.3, 1, -0.8, -2.3, 1, 1, -2.3, 1, 1, -2.1, -1, 1, -2.1, 1, 1, -2.3, 1, 1, -2.3, -1, 1, -2.1, -1, 1, -2.3, 1, -0.8, -2.1, -1, -0.8, -2.1, 1, 1, -2.1, 1, 1, -2.1, -1, -0.8, -2.1, -1, 1, -2.1)
@ -92,7 +95,8 @@ skeleton = NodePath("GunkBody")
[node name="GunkBody" parent="Parallel" instance=ExtResource("1_cr8wn")]
mask_dim = 128
source_gunk_material = SubResource("ShaderMaterial_qjnj2")
initial_mask = ExtResource("2_7477u")
source_gunk_material = SubResource("ShaderMaterial_lrgpr")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Parallel/GunkBody"]
shape = SubResource("ConcavePolygonShape3D_x2vho")

View File

@ -16,7 +16,7 @@ warnings/threads/thread_model=2
[application]
config/name="Grunk"
run/main_scene="uid://884jqafhtrv0"
run/main_scene="uid://qpq2cm1hgeha"
config/project_settings_override="user://settings.godot"
config/features=PackedStringArray("4.4", "Forward Plus")
run/max_fps=60
@ -28,7 +28,6 @@ BeehaveGlobalDebugger="*res://addons/beehave/debug/global_debugger.gd"
GameRuntime="*res://src/game/game_runtime.gd"
ItemCatalog="*res://src/items/item_catalog.tscn"
GameSettings="*res://src/game/game_settings.gd"
GameManager="*res://src/game/game_manager.tscn"
[debug]
@ -42,7 +41,6 @@ gdscript/warnings/unsafe_call_argument=2
window/size/viewport_width=1920
window/size/viewport_height=1080
window/stretch/mode="canvas_items"
[dotnet]
@ -80,6 +78,8 @@ config/accessibility/enable_head_bob=true
config/input/hold_to_sneak=true
debug/enable_navigation_agent_debug=false
debug/enable_navigation_agent_debug.editor_runtime=true
debug/enable_debug_menu=false
debug/enable_debug_menu.debug=true
[global_group]
@ -89,6 +89,7 @@ MeetSpookSource="meet-spook event sources"
LurkPoint="Point which a lurking beast may wander to."
BeastSpawnPoint="Spawn point for a grunkbeast"
GrunkBeast="GrunkBeast instances."
Persistent="Nodes which implement save and load methods"
[importer_defaults]

View File

@ -0,0 +1,12 @@
@tool
extends ColorRect
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
_update_aspect_ratio()
func _update_aspect_ratio() -> void:
var aspect_ratio := size.y / size.x
(material as ShaderMaterial).set_shader_parameter("aspect_ratio", aspect_ratio)

View File

@ -0,0 +1 @@
uid://06n7qs3l4tf6

View File

@ -0,0 +1,58 @@
[gd_scene load_steps=9 format=3 uid="uid://b6dx0ovy15g5o"]
[ext_resource type="Shader" uid="uid://dnytoirugot2e" path="res://src/shaders/canvas_grunk.gdshader" id="1_pgbvb"]
[ext_resource type="FastNoiseLite" uid="uid://cnlvdtx68giv6" path="res://assets/materials/gunk_noise.tres" id="2_g1nf5"]
[ext_resource type="Script" uid="uid://06n7qs3l4tf6" path="res://src/effects/grunk_2d/grunk_2d.gd" id="3_g1nf5"]
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_7v5ka"]
width = 256
height = 256
seamless = true
seamless_blend_skirt = 0.5
noise = ExtResource("2_g1nf5")
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_d72jk"]
width = 256
height = 256
seamless = true
seamless_blend_skirt = 0.5
noise = ExtResource("2_g1nf5")
[sub_resource type="FastNoiseLite" id="FastNoiseLite_qsvii"]
frequency = 0.0006
fractal_type = 2
fractal_octaves = 4
fractal_gain = 0.667
domain_warp_type = 2
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_pgbvb"]
width = 1024
height = 1024
noise = SubResource("FastNoiseLite_qsvii")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_rq45f"]
resource_local_to_scene = true
shader = ExtResource("1_pgbvb")
shader_parameter/color_1 = Color(0, 0.03, 0.1, 1)
shader_parameter/color_2 = Color(0, 0.1, 0.3, 1)
shader_parameter/emission_color = Color(0.25, 0.88, 1, 1)
shader_parameter/pixellation = 30.0
shader_parameter/time_pixellation = 30.0
shader_parameter/emission_strength = 0.05
shader_parameter/aspect_ratio = 0.5625
shader_parameter/time_scale = 0.01
shader_parameter/gunk_noise = SubResource("NoiseTexture3D_7v5ka")
shader_parameter/gunk_normal_map = SubResource("NoiseTexture3D_d72jk")
shader_parameter/mask_progress = 1.0
shader_parameter/mask_noise = SubResource("NoiseTexture2D_pgbvb")
[node name="Grunk2D" type="ColorRect"]
material = SubResource("ShaderMaterial_rq45f")
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("3_g1nf5")
[connection signal="item_rect_changed" from="." to="." method="_update_aspect_ratio"]

View File

@ -18,7 +18,7 @@ func _spray() -> void:
func _fire() -> void:
rumbler.intensity = RUMBLE_INTENSITY
if Game.manager.is_tank_full():
if World.instance.manager.is_tank_full():
Player.instance.hud.play_tank_full_alert()
idle()
return

View File

@ -16,11 +16,11 @@ const BRUSH_SCALE := 0.2
func unlocked() -> bool:
return Game.manager.toothbrush_unlocked
return World.instance.manager.toothbrush_unlocked
func _fire() -> void:
if raycast.is_colliding() and not Game.manager.is_tank_full():
if raycast.is_colliding() and not World.instance.manager.is_tank_full():
brush_animation.play("brush")
var collider := raycast.get_collider()
if collider is GunkBody:

View File

@ -1,13 +1,14 @@
class_name Game extends Node
## Interface to the game as an application.
@export_category("Game Scenes")
@export var world_scene: PackedScene
@export_file("*.tscn") var start_scene: String
var _loading_resources: Dictionary[String, Promise] = {}
@onready var content: Node = %Content
@onready var loading_screen: Control = %LoadingScreen
## Handy typed singleton access.
static var manager: GameManagerType:
get():
return GameManager
static var settings: GameSettingsType:
get():
return GameSettings
@ -19,5 +20,92 @@ static var runtime: GameRuntimeType:
static var instance: Game
class Promise:
var _callbacks: Array[Callable] = []
var _end_callbacks: Array[Callable] = []
func then(fn: Callable) -> Promise:
_callbacks.push_back(fn)
return self
func finally(fn: Callable) -> Promise:
_end_callbacks.push_back(fn)
return self
func resolve(res: Variant) -> void:
for fn: Callable in _callbacks + _end_callbacks:
fn.call(res)
class ScenePromise:
extends Promise
func resolve(res: Variant) -> void:
@warning_ignore("unsafe_cast")
var instance: Node = (res as PackedScene).instantiate()
super.resolve(instance)
func _ready() -> void:
Game.instance = self
_initial_load.call_deferred()
func _initial_load() -> void:
queue_scene(start_scene)
## Unload the running scene & queue up a new scene to be loaded in the background.
##
## The loading screen will be shown until the scene is loaded.
func queue_scene(path: String) -> Promise:
_unload_content()
return queue_load(path, ScenePromise.new(), "PackedScene").finally(_finish_scene_load)
## Queue a resource to be loaded in the background.
##
## Returns a `Promise` which can be used to attach callbacks
## which will be called with the resource after it is loaded.
func queue_load(path: String, promise: Promise = null, type_hint: String = "") -> Promise:
if not promise:
promise = Promise.new()
_loading_resources[path] = promise
ResourceLoader.load_threaded_request(path, type_hint)
return promise
func _unload_content() -> void:
for child: Node in content.get_children():
child.queue_free()
func _finish_scene_load(scene_instance: Node) -> void:
# Unpause in case the previous scene was paused.
get_tree().paused = false
# Reset time scale in case it's been changed.
Engine.time_scale = 1.0
content.add_child(scene_instance)
scene_instance.reparent(content)
func _process(_delta: float) -> void:
if _loading_resources:
loading_screen.visible = true
for key: String in _loading_resources.keys():
match ResourceLoader.load_threaded_get_status(key):
ResourceLoader.THREAD_LOAD_LOADED:
_loading_resources[key].resolve(ResourceLoader.load_threaded_get(key))
_loading_resources.erase(key)
ResourceLoader.THREAD_LOAD_FAILED:
assert(false, "Failed loading resource: " + key)
ResourceLoader.THREAD_LOAD_INVALID_RESOURCE:
assert(false, "Can't load invalid resource: " + key)
_:
# Continue loading
pass
if not _loading_resources:
loading_screen.visible = false

View File

@ -1,8 +1,236 @@
[gd_scene load_steps=3 format=3 uid="uid://qpq2cm1hgeha"]
[gd_scene load_steps=14 format=3 uid="uid://qpq2cm1hgeha"]
[ext_resource type="Script" uid="uid://dxl25lkyped4" path="res://src/game/game.gd" id="1_qnjlk"]
[ext_resource type="PackedScene" uid="uid://884jqafhtrv0" path="res://src/world/world.tscn" id="2_s6lek"]
[ext_resource type="FontFile" uid="uid://oq8ue2qrfijg" path="res://assets/fonts/Silkscreen/Silkscreen-Regular.ttf" id="2_s6lek"]
[ext_resource type="Script" uid="uid://ctf1if4ly6nun" path="res://src/game/loading_screen.gd" id="3_kgj8g"]
[sub_resource type="Theme" id="Theme_s6lek"]
Label/colors/font_color = Color(0.137255, 0.984314, 0.34902, 1)
Label/constants/outline_size = 16
Label/font_sizes/font_size = 32
Label/fonts/font = ExtResource("2_s6lek")
[sub_resource type="Animation" id="Animation_kgj8g"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("MarginContainer/HBoxContainer/Loading:visible_ratio")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("MarginContainer/HBoxContainer/Ellipsis:visible_characters")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [0]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("MarginContainer/HBoxContainer/Blinker:visible")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
[sub_resource type="Animation" id="Animation_l80un"]
resource_name = "ellipsis_loop"
length = 1.2
loop_mode = 1
step = 0.05
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("MarginContainer/HBoxContainer/Loading:visible_ratio")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [1.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("MarginContainer/HBoxContainer/Blinker:visible")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("MarginContainer/HBoxContainer/Ellipsis:visible_characters")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0, 0.3, 0.6, 0.9),
"transitions": PackedFloat32Array(1, 1, 1, 1),
"update": 1,
"values": [0, 1, 2, 3]
}
[sub_resource type="Animation" id="Animation_s6lek"]
resource_name = "initial_display"
step = 0.05
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("MarginContainer/HBoxContainer/Loading:visible_ratio")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.8, 1),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 0,
"values": [0.0, 0.0, 1.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("MarginContainer/HBoxContainer/Ellipsis:visible_characters")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [0]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("MarginContainer/HBoxContainer/Blinker:visible")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8),
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1),
"update": 1,
"values": [false, true, false, true, false, true, false, true, false]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_kgj8g"]
_data = {
&"RESET": SubResource("Animation_kgj8g"),
&"ellipsis_loop": SubResource("Animation_l80un"),
&"initial_display": SubResource("Animation_s6lek")
}
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_s6lek"]
animation = &"ellipsis_loop"
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_kgj8g"]
animation = &"initial_display"
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_l80un"]
advance_mode = 2
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_xptat"]
switch_mode = 2
advance_mode = 2
[sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_l80un"]
states/ellipsis_loop/node = SubResource("AnimationNodeAnimation_s6lek")
states/ellipsis_loop/position = Vector2(630, 100)
states/initial_display/node = SubResource("AnimationNodeAnimation_kgj8g")
states/initial_display/position = Vector2(399, 100)
transitions = ["Start", "initial_display", SubResource("AnimationNodeStateMachineTransition_l80un"), "initial_display", "ellipsis_loop", SubResource("AnimationNodeStateMachineTransition_xptat")]
[node name="Game" type="Node"]
script = ExtResource("1_qnjlk")
world_scene = ExtResource("2_s6lek")
start_scene = "uid://bctwol681jdk0"
[node name="Content" type="Node" parent="."]
unique_name_in_owner = true
[node name="LoadingScreen" type="Control" parent="."]
unique_name_in_owner = true
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = SubResource("Theme_s6lek")
script = ExtResource("3_kgj8g")
[node name="ColorRect" type="ColorRect" parent="LoadingScreen"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.0196078, 0.0431373, 0.0627451, 1)
[node name="MarginContainer" type="MarginContainer" parent="LoadingScreen"]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_top = -40.0
offset_right = 40.0
grow_vertical = 0
theme_override_constants/margin_left = 32
theme_override_constants/margin_bottom = 32
[node name="HBoxContainer" type="HBoxContainer" parent="LoadingScreen/MarginContainer"]
layout_mode = 2
[node name="Prompt" type="Label" parent="LoadingScreen/MarginContainer/HBoxContainer"]
layout_mode = 2
text = ">"
[node name="Blinker" type="Label" parent="LoadingScreen/MarginContainer/HBoxContainer"]
visible = false
layout_mode = 2
text = "_"
[node name="Loading" type="Label" parent="LoadingScreen/MarginContainer/HBoxContainer"]
layout_mode = 2
text = "UI_LOADING"
visible_characters = 0
visible_characters_behavior = 1
visible_ratio = 0.0
[node name="Ellipsis" type="Label" parent="LoadingScreen/MarginContainer/HBoxContainer"]
layout_mode = 2
text = "..."
visible_characters = 0
visible_characters_behavior = 1
visible_ratio = 0.0
[node name="LoaderAnimation" type="AnimationPlayer" parent="LoadingScreen"]
libraries = {
&"": SubResource("AnimationLibrary_kgj8g")
}
[node name="LoaderTree" type="AnimationTree" parent="LoadingScreen/LoaderAnimation"]
unique_name_in_owner = true
root_node = NodePath("%LoaderTree/../..")
tree_root = SubResource("AnimationNodeStateMachine_l80un")
anim_player = NodePath("..")
[connection signal="visibility_changed" from="LoadingScreen" to="LoadingScreen" method="_on_visibility_changed"]

View File

@ -15,6 +15,8 @@ var hold_to_sneak: bool
var enable_screen_shake: bool
var enable_head_bob: bool
var enable_debug_menu: bool
func _init() -> void:
ProjectSettings.settings_changed.connect(_read_settings)
@ -39,6 +41,8 @@ func _read_settings() -> void:
)
enable_head_bob = ProjectSettings.get_setting("game/config/accessibility/enable_head_bob")
enable_debug_menu = ProjectSettings.get_setting_with_override("game/debug/enable_debug_menu")
func _load_audio_bus_override() -> void:
# Load override audio bus file

14
src/game/level.gd Normal file
View File

@ -0,0 +1,14 @@
class_name Level extends Node3D
## A level with associated metadata
const SAVE_PATH_FMT := "user://{0}.state.res"
## Internal ID string
@export var id: String
## Human-readable name
@export var pretty_name: String
func get_save_path() -> String:
return SAVE_PATH_FMT.format([id])

1
src/game/level.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://68r4ht5ut1ct

View File

@ -0,0 +1,9 @@
extends Control
@onready var loader_tree: AnimationTree = %LoaderTree
@onready var state_machine: AnimationNodeStateMachinePlayback = loader_tree["parameters/playback"]
func _on_visibility_changed() -> void:
if state_machine:
state_machine.start("initial_display", true)

View File

@ -0,0 +1 @@
uid://ctf1if4ly6nun

View File

@ -4,14 +4,12 @@ const PITCH_LIMIT := deg_to_rad(85.0)
const FOCUS_SENSITIVITY := 0.2
const FOCUS_ACCELERATION := 8
@onready var player: Player = owner
@onready var _target := Vector2(rotation.x, rotation.y)
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED and player.look_enabled:
if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED and Player.instance.look_enabled:
camera_motion((event as InputEventMouseMotion).relative)
elif event is InputEventMouseButton:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
@ -21,7 +19,7 @@ func camera_motion(motion: Vector2) -> void:
var x_sensitivity: float = Game.settings.mouse_sensitivity_x
var y_sensitivity: float = Game.settings.mouse_sensitivity_y
var invert_pitch: bool = Game.settings.invert_pitch
if player.firing:
if Player.instance.firing:
# Focus movement when firing
# Game mechanic, should not be user-configurable.
x_sensitivity = FOCUS_SENSITIVITY
@ -42,7 +40,7 @@ func reset_pitch(tween_duration: float) -> void:
func _physics_process(delta: float) -> void:
var mouse_accel: float = Game.settings.mouse_acceleration
if player.firing:
if Player.instance.firing:
mouse_accel = FOCUS_ACCELERATION
var weight := 1 - exp(-mouse_accel * delta)

View File

@ -6,7 +6,6 @@ const VELOCITY_FACTOR := 2.0
var _on_right_foot := false
@onready var player: Player = owner
@onready var left_foot: FootController = %LeftFoot
@onready var right_foot: FootController = %RightFoot
@ -16,12 +15,12 @@ var _on_right_foot := false
func play_footstep() -> void:
if player.sneaking:
if Player.instance.sneaking:
return
var foot := right_foot if _on_right_foot else left_foot
var relative_speed := player.velocity.length() - MUTE_VELOCITY
var relative_speed := Player.instance.velocity.length() - MUTE_VELOCITY
if relative_speed < 0:
return

View File

@ -16,13 +16,11 @@ var timescale: float:
get:
return self["parameters/timescale/scale"]
@onready var player: Player = owner
func _process(delta: float) -> void:
var speed := player.velocity.length()
var speed := Player.instance.velocity.length()
var weight := 1 - exp(-BLEND_ACCELERATION * delta)
if player.is_on_floor():
if Player.instance.is_on_floor():
var timescale_target := speed * VELOCITY_TIMESCALE_FACTOR
timescale = lerpf(timescale, timescale_target, weight)
else:
@ -30,7 +28,7 @@ func _process(delta: float) -> void:
if Game.settings.enable_head_bob:
var blend_target := 0.0
if player.is_on_floor():
if Player.instance.is_on_floor():
blend_target = speed * VELOCITY_BLEND_FACTOR
blend = lerpf(blend, blend_target, weight)

View File

@ -2,6 +2,9 @@ class_name Player extends CharacterBody3D
#region Exported Properties
@export_category("HAX!!")
@export var godmode := false
@export_category("Status")
@export var dead := false
@export var movement_enabled := true
@ -75,7 +78,7 @@ static var instance: Player
func _ready() -> void:
Game.manager.milestone_reached.connect(_on_milestone)
World.instance.manager.milestone_reached.connect(_on_milestone)
instance = self
@ -145,7 +148,7 @@ func toggle_crouch() -> void:
## Get fuckign grabbed, idiot!
## Begin grab death sequence animation.
func get_grabbed() -> void:
if dead: # No double-grabsies
if dead or godmode: # No double-grabsies
return
movement_enabled = false
@ -174,7 +177,7 @@ func _on_milestone(milestone: Milestone) -> void:
func _signal_death() -> void:
# Called from the death animation
Game.manager.on_player_death()
World.instance.manager.on_player_death()
#endregion
@ -258,4 +261,18 @@ func _physics_process(delta: float) -> void:
move_and_slide()
#endregion
#region Persistence
func serialize() -> Dictionary:
return {
"inventory": inventory,
}
func deserialize(state: Dictionary) -> void:
@warning_ignore("unsafe_cast")
inventory.assign(state["inventory"] as Dictionary)
#endregion

View File

@ -592,7 +592,7 @@ _data = {
[sub_resource type="PlaneMesh" id="PlaneMesh_p6grl"]
[node name="Player" type="CharacterBody3D"]
[node name="Player" type="CharacterBody3D" groups=["Persistent"]]
collision_layer = 8
collision_mask = 33
script = ExtResource("1_npueo")

View File

@ -1,5 +1,8 @@
extends Node3D
@export var start_open := false
@export_category("Editor Tools")
@export var debug_open: bool:
set(value):
open()
@ -15,6 +18,32 @@ extends Node3D
@onready var nav_link: NavigationLink3D = %NavLink
func _ready() -> void:
_deferred_init.call_deferred()
func _deferred_init() -> void:
if start_open:
_instant_open()
func _instant_open() -> void:
nav_link.enabled = true
animation.play("open")
animation.advance(100)
func _instant_close() -> void:
nav_link.enabled = false
animation.play("open")
animation.advance(0)
animation.stop()
func is_open() -> bool:
return nav_link.enabled
func open() -> void:
nav_link.enabled = true
animation.play("open")
@ -27,3 +56,11 @@ func close() -> void:
# TODO bespoke close anim?
animation.play_backwards("open")
nav_link.enabled = false
func serialize() -> Dictionary:
return {"open": is_open()}
func deserialize(state: Dictionary) -> void:
start_open = state["open"]

View File

@ -281,7 +281,7 @@ _data = {
&"spray": SubResource("Animation_88qrs")
}
[node name="Bulkhead" instance=ExtResource("1_77udb")]
[node name="Bulkhead" groups=["Persistent"] instance=ExtResource("1_77udb")]
script = ExtResource("2_hknvo")
[node name="Frame" parent="." index="0"]

View File

@ -2,6 +2,15 @@ extends Node3D
@export var threshold := 100.0
@onready var spot_light_3d: SpotLight3D = %SpotLight3D
@onready var gunk_body: GunkBody = %GunkBody
func _ready() -> void:
_deferred_init.call_deferred()
func _deferred_init() -> void:
gunk_body.trigger_recompute()
func _on_clear_total_updated(clear_total: float) -> void:

View File

@ -116,6 +116,7 @@ libraries = {
autoplay = "flicker"
[node name="GunkBody" parent="MeshInstance3D" instance=ExtResource("6_3gl0p")]
unique_name_in_owner = true
mask_dim = 32
source_gunk_material = ExtResource("8_3gl0p")

View File

@ -8,6 +8,10 @@ signal activated
func _ready() -> void:
_deferred_init.call_deferred()
func _deferred_init() -> void:
if enabled:
enable()
else:
@ -40,3 +44,11 @@ func _activate() -> void:
func _on_interactive_selected() -> void:
if enabled:
interactive.enabled = _has_item()
func serialize() -> Dictionary:
return {"enabled": enabled}
func deserialize(state: Dictionary) -> void:
enabled = state["enabled"]

View File

@ -9,7 +9,7 @@ size = Vector3(0.475, 0.65, 0.2)
[sub_resource type="BoxShape3D" id="BoxShape3D_5bfyo"]
size = Vector3(0.475, 0.65, 0.2)
[node name="RetinalScanner" type="Node3D"]
[node name="RetinalScanner" type="Node3D" groups=["Persistent"]]
script = ExtResource("1_c71b5")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]

View File

@ -22,6 +22,10 @@ const CLEAN_THRESHOLD := 1400
func _ready() -> void:
interactive.label = label
_deferred_init.call_deferred()
func _deferred_init() -> void:
if clean:
gunk_body.clear_all()
_on_clean()
@ -85,3 +89,15 @@ func _on_gunk_body_clear_total_updated(clear_total: float) -> void:
func _on_action_delay_timeout() -> void:
activated.emit()
func serialize() -> Dictionary:
return {
"clean": clean,
"enabled": enabled,
}
func deserialize(state: Dictionary) -> void:
clean = state["clean"]
enabled = state["enabled"]

View File

@ -189,7 +189,7 @@ size = Vector3(0.475, 0.65, 0.2)
[sub_resource type="SphereShape3D" id="SphereShape3D_mxsyy"]
radius = 3.0
[node name="WallSwitch" instance=ExtResource("2_whafo")]
[node name="WallSwitch" groups=["Persistent"] instance=ExtResource("2_whafo")]
script = ExtResource("2_kfvqd")
enabled = true
label = "INTERACTIVE_SWITCH_LABEL"

View File

@ -11,8 +11,7 @@ uniform float time_pixellation = 30.0;
uniform float emission_strength = 0.05;
// Used ONLY by the gunk, does not affect the gunk mask.
uniform vec2 uv_scale = vec2(1.0);
uniform float aspect_ratio = 1.0;
uniform float time_scale = 1.0;
@ -29,9 +28,8 @@ float hardstep(float value) {
}
void fragment() {
vec2 aspect_ratio = vec2(SCREEN_PIXEL_SIZE.y / SCREEN_PIXEL_SIZE.x, 1.0);
vec2 scaled_pixellation = pixellation * aspect_ratio;
vec2 local_uv = floor(UV * uv_scale * scaled_pixellation) / scaled_pixellation;
vec2 scaled_pixellation = pixellation * vec2(1.0, aspect_ratio);
vec2 local_uv = floor(UV * scaled_pixellation) / scaled_pixellation;
float local_time = floor(TIME * time_scale * time_pixellation) / time_pixellation;
// swirl

View File

@ -0,0 +1,76 @@
[gd_scene load_steps=12 format=3 uid="uid://brknr57xc2cp0"]
[ext_resource type="Texture2D" uid="uid://b7ds08rj0yk7j" path="res://assets/ui/corpo_logo/corpo_logo_128_bg.png" id="1_1oh6t"]
[ext_resource type="Texture2D" uid="uid://dixpjnlaj86x2" path="res://assets/ui/corpo_logo/corpo_logo_128.png" id="1_p2l3a"]
[ext_resource type="Shader" uid="uid://dnytoirugot2e" path="res://src/shaders/canvas_grunk.gdshader" id="2_q367f"]
[ext_resource type="FastNoiseLite" uid="uid://cnlvdtx68giv6" path="res://assets/materials/gunk_noise.tres" id="3_8o5hc"]
[ext_resource type="PackedScene" uid="uid://b6dx0ovy15g5o" path="res://src/effects/grunk_2d/grunk_2d.tscn" id="5_xrtbx"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_8o5hc"]
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_7v5ka"]
width = 256
height = 256
seamless = true
seamless_blend_skirt = 0.5
noise = ExtResource("3_8o5hc")
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_d72jk"]
width = 256
height = 256
seamless = true
seamless_blend_skirt = 0.5
noise = ExtResource("3_8o5hc")
[sub_resource type="FastNoiseLite" id="FastNoiseLite_qsvii"]
frequency = 0.0006
fractal_type = 2
fractal_octaves = 4
fractal_gain = 0.667
domain_warp_type = 2
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_pgbvb"]
width = 1024
height = 1024
noise = SubResource("FastNoiseLite_qsvii")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_t8g1i"]
resource_local_to_scene = true
shader = ExtResource("2_q367f")
shader_parameter/color_1 = Color(0, 0, 0, 1)
shader_parameter/color_2 = Color(0.137255, 0.984314, 0.34902, 1)
shader_parameter/emission_color = Color(0.486275, 1, 0.25098, 1)
shader_parameter/pixellation = 32.0
shader_parameter/time_pixellation = 60.0
shader_parameter/emission_strength = 0.05
shader_parameter/aspect_ratio = 1.0
shader_parameter/time_scale = 1.0
shader_parameter/gunk_noise = SubResource("NoiseTexture3D_7v5ka")
shader_parameter/gunk_normal_map = SubResource("NoiseTexture3D_d72jk")
shader_parameter/mask_progress = 1.0
shader_parameter/mask_noise = SubResource("NoiseTexture2D_pgbvb")
[node name="SaveIcon" type="MarginContainer"]
offset_right = 40.0
offset_bottom = 40.0
[node name="IconBG" type="TextureRect" parent="."]
layout_mode = 2
size_flags_horizontal = 4
size_flags_vertical = 4
texture = ExtResource("1_1oh6t")
stretch_mode = 2
[node name="SaveIcon" type="TextureRect" parent="."]
clip_children = 2
texture_filter = 3
material = SubResource("ShaderMaterial_8o5hc")
layout_mode = 2
size_flags_horizontal = 4
size_flags_vertical = 4
texture = ExtResource("1_p2l3a")
stretch_mode = 2
[node name="Grunk2D" parent="SaveIcon" instance=ExtResource("5_xrtbx")]
material = SubResource("ShaderMaterial_t8g1i")
layout_mode = 1

View File

@ -10,15 +10,15 @@ const TANK_WARNING_BUFFER_PCT := 0.1
func _ready() -> void:
Game.manager.grunk_collected.connect(on_grunk_collected)
Game.manager.grunk_emptied.connect(on_grunk_emptied)
World.instance.manager.grunk_collected.connect(on_grunk_collected)
World.instance.manager.grunk_emptied.connect(on_grunk_emptied)
counter.pivot_offset = Vector2(0, counter.size.y)
func on_grunk_collected(delta: float) -> void:
counter.text = str(int(clampf(Game.manager.grunk_tank, 0.0, Game.manager.grunk_tank_limit)))
counter.text = str(int(clampf(World.instance.manager.grunk_tank, 0.0, World.instance.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)
var buffer := remap(World.instance.manager.get_tank_fill_pct(), 1 - TANK_WARNING_BUFFER_PCT, 1, 0, 1)
counter.modulate = Color.WHITE.lerp(Color.RED, clampf(buffer, 0, 1))

View File

@ -23,22 +23,22 @@ var _base_rumble := 0.0
func _ready() -> void:
Game.manager.grunk_collected.connect(on_grunk_collected)
Game.manager.grunk_emptied.connect(on_grunk_emptied)
World.instance.manager.grunk_collected.connect(on_grunk_collected)
World.instance.manager.grunk_emptied.connect(on_grunk_emptied)
func get_target_rotation() -> float:
return remap(
Game.manager.grunk_tank,
World.instance.manager.grunk_tank,
0,
Game.manager.grunk_tank_limit,
World.instance.manager.grunk_tank_limit,
NEEDLE_ANGLE_MIN,
NEEDLE_ANGLE_MAX
)
func on_grunk_collected(_delta: float) -> void:
var buffer := remap(Game.manager.get_tank_fill_pct(), 1 - TANK_WARNING_BUFFER_PCT, 1, 0, 1)
var buffer := remap(World.instance.manager.get_tank_fill_pct(), 1 - TANK_WARNING_BUFFER_PCT, 1, 0, 1)
_base_rumble = BUFFER_RUMBLE_FACTOR * buffer

View File

@ -6,8 +6,8 @@ class_name PlayerHUD extends Control
func _ready() -> void:
Game.manager.alert_raised.connect(_on_raise_alert)
Game.manager.alert_cleared.connect(_on_clear_alert)
World.instance.manager.alert_raised.connect(_on_raise_alert)
World.instance.manager.alert_cleared.connect(_on_clear_alert)
func select_interactive(prop: Interactive) -> void:

View File

@ -344,10 +344,10 @@ anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -401.486
offset_top = -302.289
offset_right = -401.486
offset_bottom = -302.289
offset_left = -399.906
offset_top = -299.03
offset_right = -399.906
offset_bottom = -299.03
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("4_ud8na")

View File

@ -0,0 +1,31 @@
extends PanelContainer
@onready var god_mode: CheckButton = %GodMode
@onready var collect_grunk_input: SpinBox = %CollectGrunkInput
func _ready() -> void:
# Initialize controls
if Player.instance:
god_mode.set_pressed_no_signal(Player.instance.godmode)
func kill_player() -> void:
Player.instance.get_grabbed()
func save_game() -> void:
World.instance.save_progress()
func load_game() -> void:
World.instance.on_game_over()
func collect_grunk() -> void:
World.instance.manager.collect_grunk(collect_grunk_input.value)
collect_grunk_input.value = 0.0
func empty_tank() -> void:
World.instance.manager.empty_tank()

View File

@ -0,0 +1 @@
uid://cekwr6enxknpn

View File

@ -0,0 +1,97 @@
[gd_scene load_steps=4 format=3 uid="uid://dofr2ebmvnwxf"]
[ext_resource type="Theme" uid="uid://doq7ay6f7dgfo" path="res://src/ui/menus/menu.theme" id="1_spfxq"]
[ext_resource type="Script" uid="uid://cekwr6enxknpn" path="res://src/ui/menus/debug_menu/debug_menu.gd" id="2_p20ux"]
[ext_resource type="FontFile" uid="uid://qadtckvw0t3l" path="res://assets/fonts/fontawesome-free-6.7.2-desktop/otfs/Font Awesome 6 Free-Solid-900.otf" id="3_rw5h6"]
[node name="DebugMenu" type="PanelContainer"]
custom_minimum_size = Vector2(1000, 600)
theme = ExtResource("1_spfxq")
script = ExtResource("2_p20ux")
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
theme_override_constants/margin_left = 16
theme_override_constants/margin_top = 16
theme_override_constants/margin_right = 16
theme_override_constants/margin_bottom = 16
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
layout_mode = 2
[node name="Header" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="Heading" type="Label" parent="MarginContainer/VBoxContainer/Header"]
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = &"HeaderLarge"
text = "DEBUG MENU"
[node name="CloseButton" type="Button" parent="MarginContainer/VBoxContainer/Header"]
layout_mode = 2
theme_override_fonts/font = ExtResource("3_rw5h6")
theme_override_font_sizes/font_size = 50
text = ""
flat = true
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/margin_right = 12
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
alignment = 2
[node name="GodMode" type="CheckButton" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "God mode"
alignment = 2
[node name="KillPlayer" type="Button" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
text = "Kill player"
[node name="SaveButton" type="Button" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
text = "Save game"
[node name="LoadButton" type="Button" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
text = "Load game"
[node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
alignment = 2
[node name="CollectGrunkInput" type="SpinBox" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer2"]
unique_name_in_owner = true
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
max_value = 6.4e+07
alignment = 2
suffix = "g"
[node name="CollectGrunkButton" type="Button" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer2"]
layout_mode = 2
text = "Collect grunk"
[node name="EmptyTankButton" type="Button" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer2"]
layout_mode = 2
text = "Empty tank"
[connection signal="pressed" from="MarginContainer/VBoxContainer/Header/CloseButton" to="." method="queue_free"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer/KillPlayer" to="." method="kill_player"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer/SaveButton" to="." method="save_game"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer/LoadButton" to="." method="load_game"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer2/CollectGrunkButton" to="." method="collect_grunk"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer2/EmptyTankButton" to="." method="empty_tank"]

View File

@ -1,18 +1,19 @@
[gd_scene load_steps=13 format=3 uid="uid://c0uitm5cg88h1"]
[gd_scene load_steps=14 format=3 uid="uid://c0uitm5cg88h1"]
[ext_resource type="Shader" uid="uid://dnytoirugot2e" path="res://src/shaders/canvas_grunk.gdshader" id="1_28vyc"]
[ext_resource type="PackedScene" uid="uid://b6dx0ovy15g5o" path="res://src/effects/grunk_2d/grunk_2d.tscn" id="1_sv5d8"]
[ext_resource type="FastNoiseLite" uid="uid://cnlvdtx68giv6" path="res://assets/materials/gunk_noise.tres" id="2_qsvii"]
[ext_resource type="Theme" uid="uid://b07fevr214mmr" path="res://src/ui/hud/hud_theme.tres" id="3_a6m17"]
[ext_resource type="Script" uid="uid://cjs2fen6jo0g0" path="res://src/ui/rumbler.gd" id="4_sv5d8"]
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_et0xc"]
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_7v5ka"]
width = 256
height = 256
seamless = true
seamless_blend_skirt = 0.5
noise = ExtResource("2_qsvii")
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_a6m17"]
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_d72jk"]
width = 256
height = 256
seamless = true
@ -26,12 +27,13 @@ fractal_octaves = 4
fractal_gain = 0.667
domain_warp_type = 2
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_et0xc"]
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_pgbvb"]
width = 1024
height = 1024
noise = SubResource("FastNoiseLite_qsvii")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_qsvii"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_3jt4s"]
resource_local_to_scene = true
shader = ExtResource("1_28vyc")
shader_parameter/color_1 = Color(0, 0.03, 0.1, 1)
shader_parameter/color_2 = Color(0, 0.1, 0.3, 1)
@ -39,12 +41,51 @@ shader_parameter/emission_color = Color(0.25, 0.88, 1, 1)
shader_parameter/pixellation = 50.0
shader_parameter/time_pixellation = 30.0
shader_parameter/emission_strength = 0.0
shader_parameter/uv_scale = Vector2(1, 1)
shader_parameter/aspect_ratio = 0.5625
shader_parameter/time_scale = 0.2
shader_parameter/gunk_noise = SubResource("NoiseTexture3D_et0xc")
shader_parameter/gunk_normal_map = SubResource("NoiseTexture3D_a6m17")
shader_parameter/gunk_noise = SubResource("NoiseTexture3D_7v5ka")
shader_parameter/gunk_normal_map = SubResource("NoiseTexture3D_d72jk")
shader_parameter/mask_progress = 0.0
shader_parameter/mask_noise = SubResource("NoiseTexture2D_et0xc")
shader_parameter/mask_noise = SubResource("NoiseTexture2D_pgbvb")
[sub_resource type="Animation" id="Animation_et0xc"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Grunk2D:material:shader_parameter/mask_progress")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("Grunk2D/Rumbler:visible")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("Blackout:visible")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
[sub_resource type="Animation" id="Animation_qsvii"]
resource_name = "transition_in"
@ -52,7 +93,7 @@ length = 14.0
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Grunk:material:shader_parameter/mask_progress")
tracks/0/path = NodePath("Grunk2D:material:shader_parameter/mask_progress")
tracks/0/interp = 2
tracks/0/loop_wrap = false
tracks/0/keys = {
@ -64,7 +105,7 @@ tracks/0/keys = {
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("Grunk/Rumbler:visible")
tracks/1/path = NodePath("Grunk2D/Rumbler:visible")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
@ -100,45 +141,6 @@ tracks/3/keys = {
}]
}
[sub_resource type="Animation" id="Animation_et0xc"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Grunk:material:shader_parameter/mask_progress")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("Grunk/Rumbler:visible")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("Blackout:visible")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_a6m17"]
_data = {
&"RESET": SubResource("Animation_et0xc"),
@ -153,27 +155,20 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Grunk" type="ColorRect" parent="."]
clip_children = 2
material = SubResource("ShaderMaterial_qsvii")
[node name="Grunk2D" parent="." instance=ExtResource("1_sv5d8")]
material = SubResource("ShaderMaterial_3jt4s")
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="Rumbler" type="Control" parent="Grunk"]
[node name="Rumbler" type="Control" parent="Grunk2D"]
visible = false
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 3.04544
offset_top = -0.292352
offset_right = 3.04541
offset_bottom = -0.292358
offset_left = -7.49218
offset_top = -3.99316
offset_right = -7.49219
offset_bottom = -3.99316
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
@ -181,7 +176,7 @@ script = ExtResource("4_sv5d8")
intensity = 6.0
metadata/_custom_type_script = "uid://cjs2fen6jo0g0"
[node name="DeathMessage" type="Label" parent="Grunk/Rumbler"]
[node name="DeathMessage" type="Label" parent="Grunk2D/Rumbler"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5

View File

@ -1,7 +1,10 @@
extends Control
## Menu shown in-game when the user presses pause.
@export_category("Game Scenes")
@export_file("*.tscn") var title_scene: String
@export var settings_scene: PackedScene
@export var debug_scene: PackedScene
var _freeze_input := false
@ -10,10 +13,15 @@ var _freeze_input := false
@onready var end_game_confirm: Control = %EndGameConfirm
@onready var settings_container: Container = %SettingsContainer
@onready var debug_button: Button = %DebugButton
@onready var debug_container: MarginContainer = %DebugContainer
func _ready() -> void:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
Engine.time_scale = 1.0
if Game.settings.enable_debug_menu:
debug_button.show()
func _unhandled_key_input(event: InputEvent) -> void:
@ -63,5 +71,12 @@ func cancel_end_game() -> void:
func confirm_end_game() -> void:
print_debug("TODO")
#world.fade_to_title()
# TODO transition
Game.instance.queue_scene(title_scene)
func debug_menu() -> void:
var instance: Control = debug_scene.instantiate()
debug_container.add_child(instance)
instance.tree_exited.connect(_unhide)
_hide()

View File

@ -1,8 +1,9 @@
[gd_scene load_steps=4 format=3 uid="uid://byvjsvavbg5xe"]
[gd_scene load_steps=5 format=3 uid="uid://byvjsvavbg5xe"]
[ext_resource type="Theme" uid="uid://doq7ay6f7dgfo" path="res://src/ui/menus/menu.theme" id="1_b4t8b"]
[ext_resource type="Script" uid="uid://cllx5glqld8wn" path="res://src/ui/menus/pause_menu/pause_menu.gd" id="1_rd0j2"]
[ext_resource type="PackedScene" uid="uid://d3eaqw2rdurct" path="res://src/ui/menus/settings_menu/settings_menu.tscn" id="3_dowgp"]
[ext_resource type="PackedScene" uid="uid://dofr2ebmvnwxf" path="res://src/ui/menus/debug_menu/debug_menu.tscn" id="4_1bm4j"]
[node name="PauseMenu" type="Control"]
process_mode = 3
@ -14,7 +15,9 @@ grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_b4t8b")
script = ExtResource("1_rd0j2")
title_scene = "uid://bctwol681jdk0"
settings_scene = ExtResource("3_dowgp")
debug_scene = ExtResource("4_1bm4j")
[node name="Shade" type="ColorRect" parent="."]
layout_mode = 1
@ -85,6 +88,13 @@ layout_mode = 2
theme_type_variation = &"DangerButton"
text = "PAUSE_QUIT"
[node name="DebugButton" type="Button" parent="MenuList/VBoxContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 2
theme_override_colors/font_color = Color(0.14, 1, 0.355, 1)
text = "PAUSE_DEBUG"
[node name="SettingsContainer" type="MarginContainer" parent="."]
unique_name_in_owner = true
custom_minimum_size = Vector2(1000, 600)
@ -102,6 +112,23 @@ grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="DebugContainer" type="MarginContainer" parent="."]
unique_name_in_owner = true
custom_minimum_size = Vector2(1000, 600)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -485.0
offset_top = -300.0
offset_right = 515.0
offset_bottom = 300.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="QuitConfirm" type="CenterContainer" parent="."]
unique_name_in_owner = true
visible = false
@ -196,6 +223,7 @@ text = "UI_QUIT"
[connection signal="pressed" from="MenuList/VBoxContainer/SettingsButton" to="." method="settings"]
[connection signal="pressed" from="MenuList/VBoxContainer/EndGameButton" to="." method="end_game"]
[connection signal="pressed" from="MenuList/VBoxContainer/QuitButton" to="." method="quit"]
[connection signal="pressed" from="MenuList/VBoxContainer/DebugButton" to="." method="debug_menu"]
[connection signal="pressed" from="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/CancelButton" to="." method="cancel_quit"]
[connection signal="pressed" from="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/ConfirmQuitButton" to="." method="confirm_quit"]
[connection signal="pressed" from="EndGameConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/CancelButton" to="." method="cancel_end_game"]

View File

@ -0,0 +1,46 @@
extends Control
## Title screen!
# TODO is there a way to get this path without loading the level scene?
@export var save_path: String
@export_category("Game Scenes")
# TODO is there a way to get the appropriate level scene path here without loading the world scene?
@export_file("*.tscn") var level_scene: String
@export_file("*.tscn") var world_scene: String
@export var settings_scene: PackedScene
@onready var continue_button: Button = %Continue
@onready var settings_container: Control = %SettingsContainer
@onready var title_sfx: AudioStreamPlayer = %TitleSFX
@onready var title_drone: AudioStreamPlayer = %TitleDrone
func _ready() -> void:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
if FileAccess.file_exists(save_path):
continue_button.disabled = false
func continue_game() -> void:
LoadingTools.load_save(save_path)
func new_game() -> void:
LoadingTools.load_level(level_scene)
func show_settings() -> void:
var instance: Control = settings_scene.instantiate()
settings_container.add_child(instance)
func quit() -> void:
get_tree().root.propagate_notification(NOTIFICATION_WM_CLOSE_REQUEST)
func _exit_tree() -> void:
title_sfx.stop()
title_drone.stop()

View File

@ -0,0 +1 @@
uid://dysldhadfr4oj

Binary file not shown.

View File

@ -0,0 +1,833 @@
[gd_scene load_steps=20 format=3 uid="uid://bctwol681jdk0"]
[ext_resource type="Theme" uid="uid://dj2ij1b2yjicr" path="res://src/ui/menus/title_screen/title_screen.theme" id="1_3lcvc"]
[ext_resource type="PackedScene" uid="uid://b6dx0ovy15g5o" path="res://src/effects/grunk_2d/grunk_2d.tscn" id="2_7vchy"]
[ext_resource type="Script" uid="uid://dysldhadfr4oj" path="res://src/ui/menus/title_screen/title_screen.gd" id="2_rjwhj"]
[ext_resource type="Texture2D" uid="uid://buc0oy5col0et" path="res://assets/ui/title.png" id="2_uxv0r"]
[ext_resource type="PackedScene" uid="uid://d3eaqw2rdurct" path="res://src/ui/menus/settings_menu/settings_menu.tscn" id="3_wqn52"]
[ext_resource type="Shader" uid="uid://dnytoirugot2e" path="res://src/shaders/canvas_grunk.gdshader" id="3_yrys0"]
[ext_resource type="FastNoiseLite" uid="uid://cnlvdtx68giv6" path="res://assets/materials/gunk_noise.tres" id="4_cgiy0"]
[ext_resource type="Texture2D" uid="uid://dixpjnlaj86x2" path="res://assets/ui/corpo_logo/corpo_logo_128.png" id="5_yrys0"]
[ext_resource type="FontFile" uid="uid://bgy7odoob7xyl" path="res://assets/fonts/Silkscreen/Silkscreen-Bold.ttf" id="6_cgiy0"]
[ext_resource type="AudioStream" uid="uid://c838ofbu4bqrn" path="res://assets/sfx/computer_noise.wav" id="10_x67pi"]
[ext_resource type="AudioStream" uid="uid://dx4d8a3mgpws" path="res://assets/sfx/ambient/drone_loop.wav" id="11_gdkv4"]
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_7v5ka"]
width = 256
height = 256
seamless = true
seamless_blend_skirt = 0.5
noise = ExtResource("4_cgiy0")
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_d72jk"]
width = 256
height = 256
seamless = true
seamless_blend_skirt = 0.5
noise = ExtResource("4_cgiy0")
[sub_resource type="FastNoiseLite" id="FastNoiseLite_qsvii"]
frequency = 0.0006
fractal_type = 2
fractal_octaves = 4
fractal_gain = 0.667
domain_warp_type = 2
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_pgbvb"]
width = 1024
height = 1024
noise = SubResource("FastNoiseLite_qsvii")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_rjwhj"]
resource_local_to_scene = true
shader = ExtResource("3_yrys0")
shader_parameter/color_1 = Color(0, 0.03, 0.1, 1)
shader_parameter/color_2 = Color(0, 0.1, 0.3, 1)
shader_parameter/emission_color = Color(0.25, 0.88, 1, 1)
shader_parameter/pixellation = 30.0
shader_parameter/time_pixellation = 30.0
shader_parameter/emission_strength = 0.0
shader_parameter/aspect_ratio = 0.5625
shader_parameter/time_scale = 0.4
shader_parameter/gunk_noise = SubResource("NoiseTexture3D_7v5ka")
shader_parameter/gunk_normal_map = SubResource("NoiseTexture3D_d72jk")
shader_parameter/mask_progress = 0.0
shader_parameter/mask_noise = SubResource("NoiseTexture2D_pgbvb")
[sub_resource type="Animation" id="Animation_iv5x5"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("VBoxContainer/Loader/Done:visible_ratio")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("VBoxContainer/TitleContainer:custom_minimum_size")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(800, 0)]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("VBoxContainer/WarningMessage/LogoContainer:custom_minimum_size")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(128, 0)]
}
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line1/Warning:visible_ratio")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/4/type = "value"
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line1/Warning2:visible_ratio")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/5/type = "value"
tracks/5/imported = false
tracks/5/enabled = true
tracks/5/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line2/Warning3:visible_ratio")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/6/type = "value"
tracks/6/imported = false
tracks/6/enabled = true
tracks/6/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line1/Blinker:visible")
tracks/6/interp = 1
tracks/6/loop_wrap = true
tracks/6/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/7/type = "value"
tracks/7/imported = false
tracks/7/enabled = true
tracks/7/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line2/Blinker2:visible")
tracks/7/interp = 1
tracks/7/loop_wrap = true
tracks/7/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/8/type = "value"
tracks/8/imported = false
tracks/8/enabled = true
tracks/8/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line1:visible")
tracks/8/interp = 1
tracks/8/loop_wrap = true
tracks/8/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/9/type = "value"
tracks/9/imported = false
tracks/9/enabled = true
tracks/9/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line2:visible")
tracks/9/interp = 1
tracks/9/loop_wrap = true
tracks/9/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/10/type = "value"
tracks/10/imported = false
tracks/10/enabled = true
tracks/10/path = NodePath("VBoxContainer/BufferSpace:custom_minimum_size")
tracks/10/interp = 1
tracks/10/loop_wrap = true
tracks/10/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(1024, 0)]
}
tracks/11/type = "value"
tracks/11/imported = false
tracks/11/enabled = true
tracks/11/path = NodePath("../Grunk2D:material:shader_parameter/mask_progress")
tracks/11/interp = 1
tracks/11/loop_wrap = true
tracks/11/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/12/type = "value"
tracks/12/imported = false
tracks/12/enabled = true
tracks/12/path = NodePath("VBoxContainer/MarginContainer/MenuButtons/Continue:visible")
tracks/12/interp = 1
tracks/12/loop_wrap = true
tracks/12/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/13/type = "value"
tracks/13/imported = false
tracks/13/enabled = true
tracks/13/path = NodePath("VBoxContainer/MarginContainer/MenuButtons/NewGame:visible")
tracks/13/interp = 1
tracks/13/loop_wrap = true
tracks/13/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/14/type = "value"
tracks/14/imported = false
tracks/14/enabled = true
tracks/14/path = NodePath("VBoxContainer/MarginContainer/MenuButtons/Settings:visible")
tracks/14/interp = 1
tracks/14/loop_wrap = true
tracks/14/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/15/type = "value"
tracks/15/imported = false
tracks/15/enabled = true
tracks/15/path = NodePath("VBoxContainer/MarginContainer/MenuButtons/Quit:visible")
tracks/15/interp = 1
tracks/15/loop_wrap = true
tracks/15/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/16/type = "value"
tracks/16/imported = false
tracks/16/enabled = true
tracks/16/path = NodePath("VBoxContainer/MarginContainer:visible")
tracks/16/interp = 1
tracks/16/loop_wrap = true
tracks/16/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/17/type = "value"
tracks/17/imported = false
tracks/17/enabled = true
tracks/17/path = NodePath("VBoxContainer/TitleContainer:visible")
tracks/17/interp = 1
tracks/17/loop_wrap = true
tracks/17/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/18/type = "value"
tracks/18/imported = false
tracks/18/enabled = true
tracks/18/path = NodePath("VBoxContainer/BufferSpace:visible")
tracks/18/interp = 1
tracks/18/loop_wrap = true
tracks/18/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/19/type = "value"
tracks/19/imported = false
tracks/19/enabled = true
tracks/19/path = NodePath("VBoxContainer/WarningMessage:visible")
tracks/19/interp = 1
tracks/19/loop_wrap = true
tracks/19/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/20/type = "value"
tracks/20/imported = false
tracks/20/enabled = true
tracks/20/path = NodePath("TitleDrone:playing")
tracks/20/interp = 1
tracks/20/loop_wrap = true
tracks/20/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/21/type = "value"
tracks/21/imported = false
tracks/21/enabled = true
tracks/21/path = NodePath("TitleDrone:volume_db")
tracks/21/interp = 1
tracks/21/loop_wrap = true
tracks/21/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [-40.0]
}
[sub_resource type="Animation" id="Animation_uxv0r"]
resource_name = "display"
length = 10.0
step = 0.05
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("VBoxContainer/Loader/Done:visible_ratio")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [0.0, 1.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("VBoxContainer/TitleContainer:custom_minimum_size")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 6.15, 6.2, 6.25, 6.3, 6.35, 6.4, 6.45, 6.5, 6.55, 6.6, 6.65),
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
"update": 1,
"values": [Vector2(1024, 0), Vector2(1024, 48), Vector2(1024, 96), Vector2(1024, 144), Vector2(1024, 192), Vector2(1024, 240), Vector2(1024, 288), Vector2(1024, 336), Vector2(1024, 384), Vector2(1024, 432), Vector2(1024, 480), Vector2(1024, 528)]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("VBoxContainer/WarningMessage/LogoContainer:custom_minimum_size")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0, 1, 1.1, 1.2, 1.3),
"transitions": PackedFloat32Array(1, 1, 1, 1, 1),
"update": 1,
"values": [Vector2(128, 0), Vector2(128, 32), Vector2(128, 64), Vector2(128, 96), Vector2(128, 128)]
}
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line1/Warning:visible_ratio")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
"times": PackedFloat32Array(0, 1.35, 1.45),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 0,
"values": [0.0, 0.0, 1.0]
}
tracks/4/type = "value"
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line1/Warning2:visible_ratio")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/keys = {
"times": PackedFloat32Array(0, 2.35, 2.75),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 0,
"values": [0.0, 0.0, 1.0]
}
tracks/5/type = "value"
tracks/5/imported = false
tracks/5/enabled = true
tracks/5/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line2/Warning3:visible_ratio")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/keys = {
"times": PackedFloat32Array(0, 3.65, 4.15),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 0,
"values": [0.0, 0.0, 1.0]
}
tracks/6/type = "value"
tracks/6/imported = false
tracks/6/enabled = true
tracks/6/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line1/Blinker:visible")
tracks/6/interp = 1
tracks/6/loop_wrap = true
tracks/6/keys = {
"times": PackedFloat32Array(0, 1.45, 1.6, 1.75139, 1.9, 2.05139, 2.2, 2.35, 2.75, 2.90139, 3.05, 3.20139, 3.35, 3.5, 3.65),
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
"update": 1,
"values": [false, true, false, true, false, true, false, true, false, true, false, true, false, true, false]
}
tracks/7/type = "value"
tracks/7/imported = false
tracks/7/enabled = true
tracks/7/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line2/Blinker2:visible")
tracks/7/interp = 1
tracks/7/loop_wrap = true
tracks/7/keys = {
"times": PackedFloat32Array(0, 4.15, 4.3, 4.45139, 4.6, 4.75139, 4.9, 5.05, 5.2, 5.35139, 5.5, 5.65, 5.79861, 5.95, 6.09861),
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
"update": 1,
"values": [false, true, false, true, false, true, false, true, false, true, false, true, false, true, false]
}
tracks/8/type = "value"
tracks/8/imported = false
tracks/8/enabled = true
tracks/8/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line1:visible")
tracks/8/interp = 1
tracks/8/loop_wrap = true
tracks/8/keys = {
"times": PackedFloat32Array(0, 1.3),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [false, true]
}
tracks/9/type = "value"
tracks/9/imported = false
tracks/9/enabled = true
tracks/9/path = NodePath("VBoxContainer/WarningMessage/VBoxContainer/Line2:visible")
tracks/9/interp = 1
tracks/9/loop_wrap = true
tracks/9/keys = {
"times": PackedFloat32Array(0, 3.65),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [false, true]
}
tracks/10/type = "value"
tracks/10/imported = false
tracks/10/enabled = true
tracks/10/path = NodePath("VBoxContainer/BufferSpace:custom_minimum_size")
tracks/10/interp = 1
tracks/10/loop_wrap = true
tracks/10/keys = {
"times": PackedFloat32Array(0, 7.75, 7.8, 7.85, 7.9, 7.95, 8, 8.05, 8.1, 8.15, 8.2, 8.25, 8.3, 8.35, 8.4, 8.45, 8.5, 8.55, 8.6, 8.65, 8.7, 8.75, 8.8, 8.85),
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
"update": 1,
"values": [Vector2(1024, 0), Vector2(1024, 48), Vector2(1024, 96), Vector2(1024, 144), Vector2(1024, 192), Vector2(1024, 240), Vector2(1024, 288), Vector2(1024, 336), Vector2(1024, 384), Vector2(1024, 432), Vector2(1024, 480), Vector2(1024, 528), Vector2(1024, 576), Vector2(1024, 624), Vector2(1024, 672), Vector2(1024, 720), Vector2(1024, 768), Vector2(1024, 816), Vector2(1024, 864), Vector2(1024, 912), Vector2(1024, 960), Vector2(1024, 1008), Vector2(1024, 1056), Vector2(1024, 1104)]
}
tracks/11/type = "value"
tracks/11/imported = false
tracks/11/enabled = true
tracks/11/path = NodePath("../Grunk2D:material:shader_parameter/mask_progress")
tracks/11/interp = 1
tracks/11/loop_wrap = true
tracks/11/keys = {
"times": PackedFloat32Array(0, 6.15, 10),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 0,
"values": [0.0, 0.0, 1.0]
}
tracks/12/type = "value"
tracks/12/imported = false
tracks/12/enabled = true
tracks/12/path = NodePath("VBoxContainer/MarginContainer/MenuButtons/Continue:visible")
tracks/12/interp = 1
tracks/12/loop_wrap = true
tracks/12/keys = {
"times": PackedFloat32Array(0, 6.75),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [false, true]
}
tracks/13/type = "value"
tracks/13/imported = false
tracks/13/enabled = true
tracks/13/path = NodePath("VBoxContainer/MarginContainer/MenuButtons/NewGame:visible")
tracks/13/interp = 1
tracks/13/loop_wrap = true
tracks/13/keys = {
"times": PackedFloat32Array(0, 7),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [false, true]
}
tracks/14/type = "value"
tracks/14/imported = false
tracks/14/enabled = true
tracks/14/path = NodePath("VBoxContainer/MarginContainer/MenuButtons/Settings:visible")
tracks/14/interp = 1
tracks/14/loop_wrap = true
tracks/14/keys = {
"times": PackedFloat32Array(0, 7.25),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [false, true]
}
tracks/15/type = "value"
tracks/15/imported = false
tracks/15/enabled = true
tracks/15/path = NodePath("VBoxContainer/MarginContainer/MenuButtons/Quit:visible")
tracks/15/interp = 1
tracks/15/loop_wrap = true
tracks/15/keys = {
"times": PackedFloat32Array(0, 7.5),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [false, true]
}
tracks/16/type = "value"
tracks/16/imported = false
tracks/16/enabled = true
tracks/16/path = NodePath("VBoxContainer/MarginContainer:visible")
tracks/16/interp = 1
tracks/16/loop_wrap = true
tracks/16/keys = {
"times": PackedFloat32Array(0, 6.75),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [false, true]
}
tracks/17/type = "value"
tracks/17/imported = false
tracks/17/enabled = true
tracks/17/path = NodePath("VBoxContainer/TitleContainer:visible")
tracks/17/interp = 1
tracks/17/loop_wrap = true
tracks/17/keys = {
"times": PackedFloat32Array(0, 6.15),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [false, true]
}
tracks/18/type = "value"
tracks/18/imported = false
tracks/18/enabled = true
tracks/18/path = NodePath("VBoxContainer/BufferSpace:visible")
tracks/18/interp = 1
tracks/18/loop_wrap = true
tracks/18/keys = {
"times": PackedFloat32Array(0, 7.75),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [false, true]
}
tracks/19/type = "value"
tracks/19/imported = false
tracks/19/enabled = true
tracks/19/path = NodePath("VBoxContainer/WarningMessage:visible")
tracks/19/interp = 1
tracks/19/loop_wrap = true
tracks/19/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [false, true]
}
tracks/20/type = "audio"
tracks/20/imported = false
tracks/20/enabled = true
tracks/20/path = NodePath("TitleSFX")
tracks/20/interp = 1
tracks/20/loop_wrap = true
tracks/20/keys = {
"clips": [{
"end_offset": 0.0,
"start_offset": 0.0,
"stream": ExtResource("10_x67pi")
}],
"times": PackedFloat32Array(0)
}
tracks/20/use_blend = true
tracks/21/type = "value"
tracks/21/imported = false
tracks/21/enabled = true
tracks/21/path = NodePath("TitleDrone:playing")
tracks/21/interp = 1
tracks/21/loop_wrap = true
tracks/21/keys = {
"times": PackedFloat32Array(0, 6.15),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [false, true]
}
tracks/22/type = "value"
tracks/22/imported = false
tracks/22/enabled = true
tracks/22/path = NodePath("TitleDrone:volume_db")
tracks/22/interp = 1
tracks/22/loop_wrap = true
tracks/22/keys = {
"times": PackedFloat32Array(6.15, 10),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [-40.0, -10.0]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_3qdtd"]
_data = {
&"RESET": SubResource("Animation_iv5x5"),
&"display": SubResource("Animation_uxv0r")
}
[node name="TitleScreen" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("2_rjwhj")
save_path = "user://ghost_ship.state.res"
level_scene = "uid://bov4ok76woyc"
world_scene = "uid://884jqafhtrv0"
settings_scene = ExtResource("3_wqn52")
[node name="ColorRect" type="ColorRect" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.0196078, 0.0431373, 0.0627451, 1)
[node name="Grunk2D" parent="." instance=ExtResource("2_7vchy")]
material = SubResource("ShaderMaterial_rjwhj")
layout_mode = 1
[node name="TitleScreenContent" type="MarginContainer" parent="."]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_top = -73.0
offset_right = 440.0
grow_vertical = 0
theme = ExtResource("1_3lcvc")
theme_override_constants/margin_left = 32
theme_override_constants/margin_bottom = 32
[node name="VBoxContainer" type="VBoxContainer" parent="TitleScreenContent"]
layout_mode = 2
theme_override_constants/separation = 32
[node name="Loader" type="HBoxContainer" parent="TitleScreenContent/VBoxContainer"]
layout_mode = 2
[node name="Prompt" type="Label" parent="TitleScreenContent/VBoxContainer/Loader"]
layout_mode = 2
text = ">"
[node name="Loading" type="Label" parent="TitleScreenContent/VBoxContainer/Loader"]
layout_mode = 2
text = "UI_LOADING"
visible_characters_behavior = 1
[node name="Ellipsis" type="Label" parent="TitleScreenContent/VBoxContainer/Loader"]
layout_mode = 2
text = "... "
visible_characters_behavior = 1
[node name="Done" type="Label" parent="TitleScreenContent/VBoxContainer/Loader"]
layout_mode = 2
text = "Done!"
visible_characters = 0
visible_characters_behavior = 1
visible_ratio = 0.0
[node name="WarningMessage" type="HBoxContainer" parent="TitleScreenContent/VBoxContainer"]
visible = false
layout_mode = 2
theme_override_constants/separation = 32
[node name="LogoContainer" type="Control" parent="TitleScreenContent/VBoxContainer/WarningMessage"]
clip_contents = true
custom_minimum_size = Vector2(128, 0)
layout_mode = 2
size_flags_vertical = 0
[node name="Logo" type="TextureRect" parent="TitleScreenContent/VBoxContainer/WarningMessage/LogoContainer"]
modulate = Color(0.137255, 0.984314, 0.34902, 1)
layout_mode = 1
offset_right = 128.0
offset_bottom = 128.0
texture = ExtResource("5_yrys0")
[node name="VBoxContainer" type="VBoxContainer" parent="TitleScreenContent/VBoxContainer/WarningMessage"]
layout_mode = 2
theme_override_constants/separation = 16
alignment = 2
[node name="Line1" type="HBoxContainer" parent="TitleScreenContent/VBoxContainer/WarningMessage/VBoxContainer"]
visible = false
layout_mode = 2
[node name="Warning" type="Label" parent="TitleScreenContent/VBoxContainer/WarningMessage/VBoxContainer/Line1"]
layout_mode = 2
theme_override_fonts/font = ExtResource("6_cgiy0")
text = "!WARNING!"
visible_characters = 0
visible_ratio = 0.0
[node name="Warning2" type="Label" parent="TitleScreenContent/VBoxContainer/WarningMessage/VBoxContainer/Line1"]
layout_mode = 2
text = " This is a secure terminal system."
visible_characters = 0
visible_ratio = 0.0
[node name="Blinker" type="Label" parent="TitleScreenContent/VBoxContainer/WarningMessage/VBoxContainer/Line1"]
visible = false
layout_mode = 2
text = "_"
[node name="Line2" type="HBoxContainer" parent="TitleScreenContent/VBoxContainer/WarningMessage/VBoxContainer"]
visible = false
layout_mode = 2
[node name="Warning3" type="Label" parent="TitleScreenContent/VBoxContainer/WarningMessage/VBoxContainer/Line2"]
layout_mode = 2
text = "Unauthorized access is prohibited under penalty of"
visible_characters = 0
visible_ratio = 0.0
[node name="Blinker2" type="Label" parent="TitleScreenContent/VBoxContainer/WarningMessage/VBoxContainer/Line2"]
visible = false
layout_mode = 2
text = "_"
[node name="BufferSpace" type="Control" parent="TitleScreenContent/VBoxContainer"]
visible = false
custom_minimum_size = Vector2(1024, 0)
layout_mode = 2
[node name="TitleContainer" type="Control" parent="TitleScreenContent/VBoxContainer"]
visible = false
clip_contents = true
custom_minimum_size = Vector2(800, 0)
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="TitleScreenContent/VBoxContainer/TitleContainer"]
layout_mode = 1
offset_right = 968.0
offset_bottom = 432.0
theme_override_constants/margin_left = 64
[node name="Title" type="TextureRect" parent="TitleScreenContent/VBoxContainer/TitleContainer/MarginContainer"]
modulate = Color(0.137255, 0.984314, 0.34902, 1)
texture_filter = 3
layout_mode = 2
texture = ExtResource("2_uxv0r")
stretch_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="TitleScreenContent/VBoxContainer"]
visible = false
layout_mode = 2
theme_override_constants/margin_left = 64
[node name="MenuButtons" type="VBoxContainer" parent="TitleScreenContent/VBoxContainer/MarginContainer"]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
size_flags_horizontal = 0
theme_override_constants/separation = 32
[node name="Continue" type="Button" parent="TitleScreenContent/VBoxContainer/MarginContainer/MenuButtons"]
unique_name_in_owner = true
visible = false
layout_mode = 2
disabled = true
text = "Continue"
[node name="NewGame" type="Button" parent="TitleScreenContent/VBoxContainer/MarginContainer/MenuButtons"]
visible = false
layout_mode = 2
text = "New Game"
[node name="Settings" type="Button" parent="TitleScreenContent/VBoxContainer/MarginContainer/MenuButtons"]
visible = false
layout_mode = 2
text = "Settings"
[node name="Quit" type="Button" parent="TitleScreenContent/VBoxContainer/MarginContainer/MenuButtons"]
visible = false
layout_mode = 2
theme_type_variation = &"DangerButton"
text = "Quit"
[node name="AnimationPlayer" type="AnimationPlayer" parent="TitleScreenContent"]
libraries = {
&"": SubResource("AnimationLibrary_3qdtd")
}
autoplay = "display"
[node name="TitleSFX" type="AudioStreamPlayer" parent="TitleScreenContent"]
unique_name_in_owner = true
volume_db = -20.0
bus = &"SFX"
[node name="TitleDrone" type="AudioStreamPlayer" parent="TitleScreenContent"]
unique_name_in_owner = true
stream = ExtResource("11_gdkv4")
volume_db = -40.0
bus = &"Music"
[node name="SettingsContainer" type="Control" parent="."]
unique_name_in_owner = true
custom_minimum_size = Vector2(1000, 600)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -500.0
offset_right = 500.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[connection signal="pressed" from="TitleScreenContent/VBoxContainer/MarginContainer/MenuButtons/Continue" to="." method="continue_game"]
[connection signal="pressed" from="TitleScreenContent/VBoxContainer/MarginContainer/MenuButtons/NewGame" to="." method="new_game"]
[connection signal="pressed" from="TitleScreenContent/VBoxContainer/MarginContainer/MenuButtons/Settings" to="." method="show_settings"]
[connection signal="pressed" from="TitleScreenContent/VBoxContainer/MarginContainer/MenuButtons/Quit" to="." method="quit"]

22
src/util/loading_tools.gd Normal file
View File

@ -0,0 +1,22 @@
class_name LoadingTools
## Utilities for loading game states
const WORLD_SCENE := "res://src/world/world.tscn"
static func _load_world(level: PackedScene, save: SaveState = null) -> void:
var finish_load := func(world: World) -> void:
world.save_state = save
world.initial_level = level
Game.instance.queue_scene(WORLD_SCENE).then(finish_load)
static func load_level(level_path: String, save: SaveState = null) -> void:
var chain_load := func(level: PackedScene) -> void: LoadingTools._load_world(level, save)
Game.instance.queue_load(level_path).then(chain_load)
static func load_save(save_path: String) -> void:
var chain_load := func(save: SaveState) -> void: load_level(save.level_path, save)
Game.instance.queue_load(save_path).then(chain_load)

View File

@ -0,0 +1 @@
uid://b1hmb3efjqldu

View File

@ -10,7 +10,7 @@ class_name SetPlayerPriorityTarget extends ActionLeaf
func tick(_actor: Node, blackboard: Blackboard) -> int:
if Game.manager.alert_level >= alert_threshold and is_instance_valid(Player.instance):
if World.instance.manager.alert_level >= alert_threshold and is_instance_valid(Player.instance):
blackboard.set_value(blackboard_key, Player.instance)
return SUCCESS
return FAILURE

View File

@ -61,8 +61,7 @@ func _ready() -> void:
# Overlay mesh with gunk material
mesh_instance.material_overlay = mat_instance
if initial_mask:
mask_texture.texture = initial_mask
_deferred_init.call_deferred()
# Initialize meshtool
meshtool.create_from_surface(mesh_instance.mesh as ArrayMesh, 0)
@ -71,12 +70,10 @@ func _ready() -> void:
_thread.start(_async_compute_clear_total)
func _trigger_recompute_deferred() -> void:
_mutex.lock()
_mask_tx = mask_viewport.get_texture()
#_mask_img = mask_viewport.get_texture().get_image()
_mutex.unlock()
_semaphore.post()
func _deferred_init() -> void:
if initial_mask:
mask_texture.texture = initial_mask
mask_texture.visible = true
func _async_compute_clear_total() -> void:
@ -258,13 +255,13 @@ func _process(_delta: float) -> void:
_mutex.unlock()
var delta := new_total - _prev_clear_total
if abs(delta) > CLEAR_TOTAL_EPSILON:
clear_total_updated.emit(new_total)
# Do not fire signal on first compute after initialization
# This prevents the player from collecting the grunk from the initial mask.
if _prev_clear_total >= 0:
clear_total_updated.emit(new_total)
# XXX due to fp error, this will drift from the "true count" over time
# but it probably won't matter :shrug:
Game.manager.collect_grunk(delta)
World.instance.manager.collect_grunk(delta)
_prev_clear_total = new_total
# If paint_continuous wasn't called last frame, stop the current polyline.
@ -283,6 +280,23 @@ func _process(_delta: float) -> void:
_multiline_buffer = PackedVector2Array()
func trigger_recompute() -> void:
_mutex.lock()
_mask_tx = mask_viewport.get_texture()
_mutex.unlock()
_semaphore.post()
func _on_mask_painted() -> void:
# XXX any problem with posting each frame?
_trigger_recompute_deferred.call_deferred()
trigger_recompute.call_deferred()
func serialize() -> Dictionary:
var state := {"mask": mask_viewport.get_texture().get_image()}
return state
func deserialize(state: Dictionary) -> void:
@warning_ignore("unsafe_cast")
initial_mask = ImageTexture.create_from_image(state["mask"] as Image)

View File

@ -5,7 +5,7 @@
[ext_resource type="Script" uid="uid://bom5qysgfvap1" path="res://src/world/gunk_body/draw_controller.gd" id="2_kkcjw"]
[ext_resource type="Script" uid="uid://ba7480ara8eo" path="res://levels/sandbox/debug_draw.gd" id="3_m8wx4"]
[node name="GunkBody" type="StaticBody3D"]
[node name="GunkBody" type="StaticBody3D" groups=["Persistent"]]
collision_layer = 5
collision_mask = 0
script = ExtResource("1_qqbpr")

View File

@ -19,7 +19,7 @@ stream_1/stream = ExtResource("5_omayi")
stream_2/stream = ExtResource("6_yg8lg")
stream_3/stream = ExtResource("7_4kci5")
[node name="GrunkNodule" type="StaticBody3D"]
[node name="GrunkNodule" type="StaticBody3D" groups=["Persistent"]]
collision_layer = 36
collision_mask = 0
script = ExtResource("1_iyr82")

View File

@ -20,6 +20,10 @@ var _sustained_damage := 0.0
var _hit_this_frame := false
func _enter_tree() -> void:
add_to_group("Persistent", true)
## Called each frame this node takes a hit.
##
## Derived types should override `_hit()` as a lifecycle method.
@ -49,7 +53,7 @@ func _process(_delta: float) -> void:
## Destroy this node, with the player collecting the grunk value.
func collect() -> void:
Game.manager.collect_grunk(value)
World.instance.manager.collect_grunk(value)
destroy()
@ -57,7 +61,7 @@ func collect() -> void:
##
## Derived types should override `_destroy` as a lifecycle method.
func destroy() -> void:
Game.manager.collect_grunk(value)
World.instance.manager.collect_grunk(value)
var splatter := GrunkSplatter.build(splatter_scale * scale.x)
add_sibling(splatter)
splatter.global_position = global_position
@ -68,3 +72,13 @@ func destroy() -> void:
func _destroy() -> void:
pass # Implemented in derived type
func serialize() -> Dictionary:
# Nothing to serialize, but we need a placeholder value to show we haven't been destroyed.
return {}
func deserialize(_state: Dictionary) -> void:
# Nothing to deserialize, but we won't be freed!
pass

View File

@ -24,7 +24,7 @@ var _busy := false
func trigger() -> void:
if not _busy:
_busy = true
Game.manager.raise_alert(ALERT_DELTA)
World.instance.manager.raise_alert(ALERT_DELTA)
animation_player.play("trigger")
trigger_animation.play("trigger")
alarm_sfx.play()

View File

@ -243,7 +243,7 @@ _data = {
&"pulse": SubResource("Animation_vokcn")
}
[node name="GunkAlarm" type="StaticBody3D"]
[node name="GunkAlarm" type="StaticBody3D" groups=["Persistent"]]
collision_layer = 36
collision_mask = 0
script = ExtResource("1_piaxx")

View File

@ -99,7 +99,7 @@ _data = {
&"pulse": SubResource("Animation_eu6st")
}
[node name="GunkHeart" type="StaticBody3D"]
[node name="GunkHeart" type="StaticBody3D" groups=["Persistent"]]
collision_layer = 36
collision_mask = 0
script = ExtResource("1_ftym0")

View File

@ -53,7 +53,7 @@ _data = {
&"trigger": SubResource("Animation_htscg")
}
[node name="Listener" type="StaticBody3D"]
[node name="Listener" type="StaticBody3D" groups=["Persistent"]]
collision_layer = 36
collision_mask = 0
script = ExtResource("1_htscg")

View File

@ -111,7 +111,7 @@ _data = {
&"trigger": SubResource("Animation_rdv5j")
}
[node name="GunkRelay" type="StaticBody3D"]
[node name="GunkRelay" type="StaticBody3D" groups=["Persistent"]]
collision_layer = 36
collision_mask = 0
script = ExtResource("1_rdv5j")

View File

@ -13,7 +13,7 @@ emission_energy_multiplier = 0.0
[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_t1c4j"]
data = PackedVector3Array(0.7236, -0.4472, 0.5257, 0, -1, 0, -0.2764, -0.4472, 0.8506, 0, -1, 0, 0.7236, -0.4472, 0.5257, 0.7236, -0.4472, -0.5257, -0.2764, -0.4472, 0.8506, 0, -1, 0, -0.8944, -0.4472, 0, -0.8944, -0.4472, 0, 0, -1, 0, -0.2764, -0.4472, -0.8506, -0.2764, -0.4472, -0.8506, 0, -1, 0, 0.7236, -0.4472, -0.5257, 0.7236, -0.4472, -0.5257, 0.7236, -0.4472, 0.5257, 0.8944, 0.4472, 0, 0.7236, -0.4472, 0.5257, -0.2764, -0.4472, 0.8506, 0.2764, 0.4472, 0.8506, -0.2764, -0.4472, 0.8506, -0.8944, -0.4472, 0, -0.7236, 0.4472, 0.5257, -0.8944, -0.4472, 0, -0.2764, -0.4472, -0.8506, -0.7236, 0.4472, -0.5257, -0.2764, -0.4472, -0.8506, 0.7236, -0.4472, -0.5257, 0.2764, 0.4472, -0.8506, 0.8944, 0.4472, 0, 0.7236, -0.4472, 0.5257, 0.2764, 0.4472, 0.8506, 0.2764, 0.4472, 0.8506, -0.2764, -0.4472, 0.8506, -0.7236, 0.4472, 0.5257, -0.7236, 0.4472, 0.5257, -0.8944, -0.4472, 0, -0.7236, 0.4472, -0.5257, -0.7236, 0.4472, -0.5257, -0.2764, -0.4472, -0.8506, 0.2764, 0.4472, -0.8506, 0.2764, 0.4472, -0.8506, 0.7236, -0.4472, -0.5257, 0.8944, 0.4472, 0, 0.8944, 0.4472, 0, 0.2764, 0.4472, 0.8506, 0, 1, 0, 0.2764, 0.4472, 0.8506, -0.7236, 0.4472, 0.5257, 0, 1, 0, -0.7236, 0.4472, 0.5257, -0.7236, 0.4472, -0.5257, 0, 1, 0, -0.7236, 0.4472, -0.5257, 0.2764, 0.4472, -0.8506, 0, 1, 0, 0.2764, 0.4472, -0.8506, 0.8944, 0.4472, 0, 0, 1, 0)
[node name="GunkTrigger" type="StaticBody3D"]
[node name="GunkTrigger" type="StaticBody3D" groups=["Persistent"]]
collision_layer = 36
collision_mask = 0
script = ExtResource("1_t1c4j")

81
src/world/save_state.gd Normal file
View File

@ -0,0 +1,81 @@
class_name SaveState extends Resource
## Serializable container for gameplay state.
const CURRENT_VERSION := 0
const PERSISTENT_GROUP := "Persistent"
const SERIALIZE_METHOD := "serialize"
const DESERIALIZE_METHOD := "deserialize"
@export var save_version := CURRENT_VERSION
@export var level_path: String
@export var persistent_state: Dictionary[String, Dictionary] = {}
@export_group("WorldManager State")
@export var grunk_tank_limit: int
@export var mp3_player_unlocked: bool
@export var toothbrush_unlocked: bool
@export var stickers_unlocked: bool
@export var grunk_tank: float
@export var grunk_vault: float
@export var alert_level: int
static func node_key(node: Node, world: World) -> String:
return str(world.level_root.get_path_to(node))
func load_to_world(world: World) -> void:
if level_path != world.current_level_scene.resource_path:
push_warning(
"This save is for ",
level_path,
" but the loaded level is for ",
world.current_level_scene.resource_path
)
world.manager.grunk_tank_limit = grunk_tank_limit
world.manager.mp3_player_unlocked = mp3_player_unlocked
world.manager.toothbrush_unlocked = toothbrush_unlocked
world.manager.stickers_unlocked = stickers_unlocked
world.manager.grunk_tank = grunk_tank
world.manager.grunk_vault = grunk_vault
world.manager.alert_level = alert_level
var persistent := world.get_tree().get_nodes_in_group(PERSISTENT_GROUP)
for node: Node in persistent:
var key := SaveState.node_key(node, world)
if key in persistent_state:
# Node is in our persistent state, so load it with data.
Callable(node, DESERIALIZE_METHOD).call(persistent_state[key])
else:
# Node isn't in our persistent state, so it must have been destroyed.
node.queue_free()
static func serialize(world: World) -> SaveState:
var save := SaveState.new()
save.level_path = world.current_level_scene.resource_path
save.grunk_tank_limit = world.manager.grunk_tank_limit
save.mp3_player_unlocked = world.manager.mp3_player_unlocked
save.toothbrush_unlocked = world.manager.toothbrush_unlocked
save.stickers_unlocked = world.manager.stickers_unlocked
save.grunk_tank = world.manager.grunk_tank
save.grunk_vault = world.manager.grunk_vault
save.alert_level = world.manager.alert_level
# NOTE: I'm assuming that `persistent` will have the same order ever time the world is loaded.
# This may not be the case. If so, we need to find a different way to uniquely identify nodes.
var persistent := world.get_tree().get_nodes_in_group(PERSISTENT_GROUP)
for node: Node in persistent:
var key := SaveState.node_key(node, world)
var data: Dictionary = Callable(node, SERIALIZE_METHOD).call()
save.persistent_state[key] = data
return save

View File

@ -0,0 +1 @@
uid://dsyyk7muaabf5

View File

@ -0,0 +1,9 @@
[gd_resource type="Resource" script_class="SpookManager" load_steps=3 format=3 uid="uid://0i72bf8ip1lx"]
[ext_resource type="PackedScene" uid="uid://ehf5sg3ahvbf" path="res://src/world/grunk_beast/grunk_beast.tscn" id="1_8hd1x"]
[ext_resource type="Script" uid="uid://bsn026pxqwkbc" path="res://src/world/spook_manager/spook_manager.gd" id="2_01euv"]
[resource]
script = ExtResource("2_01euv")
grunkbeast_scene = ExtResource("1_8hd1x")
metadata/_custom_type_script = "uid://bsn026pxqwkbc"

View File

@ -7,12 +7,7 @@ const BEAST_GROUP := "GrunkBeast"
@export var grunkbeast_scene: PackedScene
var debug_set_alert_level: int:
set = _on_alert_raised
func _init() -> void:
Game.manager.alert_raised.connect(_on_alert_raised)
Game.manager.alert_cleared.connect(_on_alert_cleared)
set = on_alert_raised
func _spawn_beast_at_point(spawn_point: Node3D) -> void:
@ -26,7 +21,7 @@ func spawn_beast() -> void:
var spawn_point := SceneTools.pick_unseen_from_group(SPAWN_GROUP)
if not spawn_point:
print_debug("Couldn't find a hidden spawn point... Picking one at random.")
var nodes := Game.manager.get_tree().get_nodes_in_group(SPAWN_GROUP)
var nodes := World.instance.get_tree().get_nodes_in_group(SPAWN_GROUP)
if not nodes:
print_debug("Oh that's why. There aren't any spawn points. Complain to a developer.")
return
@ -38,14 +33,14 @@ func spawn_beast() -> void:
## Spawn beasts at _every_ spawn point the player can't see.
func spawn_many_beasts() -> void:
var nodes := Game.manager.get_tree().get_nodes_in_group(SPAWN_GROUP)
var nodes := World.instance.get_tree().get_nodes_in_group(SPAWN_GROUP)
for node: Node in nodes:
var target := node as Node3D
if is_instance_valid(target) and not SceneTools.player_can_see(target.global_position):
_spawn_beast_at_point(target)
func _on_alert_raised(new_level: int) -> void:
func on_alert_raised(new_level: int) -> void:
match new_level:
0:
# LEVEL 0: UNAWARE
@ -56,8 +51,11 @@ func _on_alert_raised(new_level: int) -> void:
# Set up meet-spook.
# Get closest MeetSpook point to player.
if Player.instance:
var closest := SceneTools.closest_in_group(Player.instance, MeetSpook.GROUP)
(closest as MeetSpook).prepare()
var closest := (
SceneTools.closest_in_group(Player.instance, MeetSpook.GROUP) as MeetSpook
)
if closest:
closest.prepare()
2:
# LEVEL 2: AGGRESSIVE
# Beast pursues player on sight.
@ -77,16 +75,16 @@ func _on_alert_raised(new_level: int) -> void:
pass # TODO
func _on_alert_cleared() -> void:
# Destroy all but one grunk beasts
var beasts := Game.manager.get_tree().get_nodes_in_group(BEAST_GROUP)
if not beasts:
return
func on_alert_cleared() -> void:
# Destroy all grunk beasts
for b: Node in World.instance.get_tree().get_nodes_in_group(BEAST_GROUP):
b.queue_free()
@warning_ignore("unsafe_cast")
var survivor := beasts.pick_random() as GrunkBeast
for beast: Node in beasts:
if beast != survivor:
beast.queue_free()
# And spawn one back in, if needed
if World.instance.manager.alert_level >= 2:
spawn_beast()
survivor.clear_aggro()
## Set the state of spooky effects after a level is initialized & any save state is loaded.
func after_level_init() -> void:
on_alert_cleared()

View File

@ -3,6 +3,9 @@ class_name World extends Node
@export var pause_enabled := true
@export var save_state: SaveState
@export var manager: WorldManager
@export var spook_manager: SpookManager
@export_category("Game Scenes")
@ -12,18 +15,23 @@ class_name World extends Node
@export var kill_screen_scene: PackedScene
var current_level: PackedScene
var current_level_scene: PackedScene
var current_level: Level
@onready var level_root: Node3D = %LevelRoot
@onready var ui_root: Control = %UIRoot
@onready var save_icon: MarginContainer = %SaveIcon
static var instance: World
func _ready() -> void:
World.instance = self
Game.manager.player_dead.connect(on_player_death)
load_level(initial_level)
manager.alert_raised.connect(spook_manager.on_alert_raised)
manager.alert_cleared.connect(spook_manager.on_alert_cleared)
manager.player_dead.connect(on_player_death)
if initial_level:
load_level(initial_level)
func _unhandled_input(event: InputEvent) -> void:
@ -45,8 +53,15 @@ func unpause() -> void:
func load_level(level: PackedScene) -> void:
for c: Node in level_root.get_children():
c.queue_free()
current_level = level
level_root.add_child(level.instantiate())
level_root.remove_child(c)
current_level_scene = level
print("Instantiating level from ", level.resource_path)
current_level = level.instantiate()
level_root.add_child(current_level)
if save_state:
save_state.load_to_world(self)
print("Done!")
spook_manager.after_level_init()
func on_player_death() -> void:
@ -55,7 +70,26 @@ func on_player_death() -> void:
ui_root.add_child(kill_screen)
func _reload_saved(save: SaveState) -> void:
save_state = save
load_level(current_level_scene)
func on_game_over() -> void:
# TODO: reload from last checkpoint
# in the mean time, just reload the level
load_level(current_level)
# reload the level from the last save
var save_path := current_level.get_save_path()
if FileAccess.file_exists(save_path):
Game.instance.queue_load(save_path).finally(_reload_saved)
else:
load_level(current_level_scene)
func save_progress() -> void:
print("Preparing save state...")
save_icon.show()
var save := SaveState.serialize(self)
var save_path := current_level.get_save_path()
print("Writing save to ", save_path)
ResourceSaver.save(save, save_path)
save_icon.hide()
print("Done!")

View File

@ -1,21 +1,18 @@
[gd_scene load_steps=8 format=3 uid="uid://884jqafhtrv0"]
[ext_resource type="Script" uid="uid://cgqmhtemibxc5" path="res://src/world/world.gd" id="1_1k4gi"]
[ext_resource type="Resource" uid="uid://tgac5tnfx56r" path="res://src/world/world_manager.tres" id="2_5kmgb"]
[ext_resource type="PackedScene" uid="uid://byvjsvavbg5xe" path="res://src/ui/menus/pause_menu/pause_menu.tscn" id="2_6fy3g"]
[ext_resource type="PackedScene" uid="uid://ehf5sg3ahvbf" path="res://src/world/grunk_beast/grunk_beast.tscn" id="2_43c6p"]
[ext_resource type="Script" uid="uid://bsn026pxqwkbc" path="res://src/world/spook_manager/spook_manager.gd" id="2_bsf3i"]
[ext_resource type="PackedScene" uid="uid://bov4ok76woyc" path="res://levels/ghost_ship/ghost_ship.tscn" id="2_jte2u"]
[ext_resource type="Resource" uid="uid://0i72bf8ip1lx" path="res://src/world/spook_manager.tres" id="3_l0av5"]
[ext_resource type="PackedScene" uid="uid://b8rv6dg4tgaeb" path="res://levels/mechanic_test/mechanic_test.tscn" id="4_5kmgb"]
[ext_resource type="PackedScene" uid="uid://c0uitm5cg88h1" path="res://src/ui/menus/kill_screen/kill_screen.tscn" id="6_l0av5"]
[sub_resource type="Resource" id="Resource_43c6p"]
script = ExtResource("2_bsf3i")
grunkbeast_scene = ExtResource("2_43c6p")
metadata/_custom_type_script = "uid://bsn026pxqwkbc"
[ext_resource type="PackedScene" uid="uid://brknr57xc2cp0" path="res://src/ui/elements/save_icon/save_icon.tscn" id="7_5kmgb"]
[node name="World" type="Node"]
script = ExtResource("1_1k4gi")
spook_manager = SubResource("Resource_43c6p")
initial_level = ExtResource("2_jte2u")
manager = ExtResource("2_5kmgb")
spook_manager = ExtResource("3_l0av5")
initial_level = ExtResource("4_5kmgb")
pause_scene = ExtResource("2_6fy3g")
kill_screen_scene = ExtResource("6_l0av5")
@ -31,3 +28,17 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="SaveIcon" parent="UIRoot" instance=ExtResource("7_5kmgb")]
unique_name_in_owner = true
visible = false
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_top = -140.0
offset_right = 140.0
offset_bottom = 0.0
grow_vertical = 0
theme_override_constants/margin_left = 32
theme_override_constants/margin_bottom = 32

View File

@ -32,3 +32,13 @@ func _on_interactive_activated() -> void:
Player.instance.add_item(item)
# TODO: animation, sfx on collect?
queue_free()
func serialize() -> Dictionary:
# Nothing to serialize, but we need a placeholder value to show we haven't been destroyed.
return {}
func deserialize(_state: Dictionary) -> void:
# Nothing to deserialize, but we won't be freed!
pass

View File

@ -6,7 +6,7 @@
[sub_resource type="SphereShape3D" id="SphereShape3D_0mein"]
radius = 0.25
[node name="WorldItem" type="MeshInstance3D"]
[node name="WorldItem" type="MeshInstance3D" groups=["Persistent"]]
script = ExtResource("1_sptcj")
[node name="Interactive" type="StaticBody3D" parent="."]

View File

@ -1,5 +1,5 @@
class_name GameManagerType extends Node
## Autoloaded singleton encapsulating game state.
class_name WorldManager extends Resource
## Autoloaded singleton encapsulating game world state.
# TODO a lot of this should really be a property of the world.
@ -59,6 +59,10 @@ var grunk_vault := 0.0
var alert_level := 0
func _init() -> void:
milestone_reached.connect(_on_milestone)
## Add to the player's grunk tank.
func collect_grunk(delta: float) -> void:
grunk_tank += delta
@ -83,6 +87,8 @@ func deposit_tank() -> void:
if alert_level >= CLEAR_LEVEL:
clear_alert()
World.instance.save_progress()
func is_tank_full() -> bool:
return grunk_tank >= grunk_tank_limit

View File

@ -1,10 +1,10 @@
[gd_scene load_steps=13 format=3 uid="uid://cnrtgmanw40ei"]
[gd_resource type="Resource" script_class="WorldManager" load_steps=13 format=3 uid="uid://tgac5tnfx56r"]
[ext_resource type="Script" uid="uid://c1i5gnht15x0e" path="res://src/game/game_manager.gd" id="1_08e4a"]
[ext_resource type="Script" uid="uid://ufxoxupdvyd5" path="res://src/game/milestone.gd" id="2_b83gw"]
[ext_resource type="Script" uid="uid://c1i5gnht15x0e" path="res://src/world/world_manager.gd" id="1_i77rl"]
[ext_resource type="Script" uid="uid://ufxoxupdvyd5" path="res://src/game/milestone.gd" id="2_c37ff"]
[sub_resource type="Resource" id="Resource_h4r54"]
script = ExtResource("2_b83gw")
script = ExtResource("2_c37ff")
name = "[no reward]"
message = "Check the MANUAL for SWEET TIPZ
from the GRUNKIN' MASTERS!"
@ -20,7 +20,7 @@ fiesta = false
metadata/_custom_type_script = "uid://ufxoxupdvyd5"
[sub_resource type="Resource" id="Resource_b83gw"]
script = ExtResource("2_b83gw")
script = ExtResource("2_c37ff")
name = "MP3 PLAYER"
message = "MP3 PLAYER DECRYPTED. Enjoy some
light music whilst you GRUNK."
@ -36,7 +36,7 @@ fiesta = false
metadata/_custom_type_script = "uid://ufxoxupdvyd5"
[sub_resource type="Resource" id="Resource_o6um0"]
script = ExtResource("2_b83gw")
script = ExtResource("2_c37ff")
name = "TOOTHBRUSH"
message = "TOOTHBRUSH DECRYPTED.
Enjoy teethbrushing in moderation!"
@ -52,7 +52,7 @@ fiesta = false
metadata/_custom_type_script = "uid://ufxoxupdvyd5"
[sub_resource type="Resource" id="Resource_vebb4"]
script = ExtResource("2_b83gw")
script = ExtResource("2_c37ff")
name = "BONUS TRACK"
message = "BONUS TRACK DELIVERED
[TODO]"
@ -68,7 +68,7 @@ fiesta = false
metadata/_custom_type_script = "uid://ufxoxupdvyd5"
[sub_resource type="Resource" id="Resource_aolyc"]
script = ExtResource("2_b83gw")
script = ExtResource("2_c37ff")
name = "WELLNESS SEMINAR"
message = "SEMINAR DECRYPTED.
The FUTURE of WELLNESS is TODAY!"
@ -84,7 +84,7 @@ fiesta = false
metadata/_custom_type_script = "uid://ufxoxupdvyd5"
[sub_resource type="Resource" id="Resource_36cpv"]
script = ExtResource("2_b83gw")
script = ExtResource("2_c37ff")
name = "EL TANK GRANDE"
message = "\"EL TANK GRANDE\" DECRYPTED.
GRUNK carrying capacity increased."
@ -100,7 +100,7 @@ fiesta = false
metadata/_custom_type_script = "uid://ufxoxupdvyd5"
[sub_resource type="Resource" id="Resource_pfnwj"]
script = ExtResource("2_b83gw")
script = ExtResource("2_c37ff")
name = "STICKER PACK"
message = "NOW PRINTING... You can use
STICKERS to EXPRESS your \"SELF\""
@ -116,7 +116,7 @@ fiesta = false
metadata/_custom_type_script = "uid://ufxoxupdvyd5"
[sub_resource type="Resource" id="Resource_p7jc6"]
script = ExtResource("2_b83gw")
script = ExtResource("2_c37ff")
name = "QUOTA"
message = "QUOTA SATISFIED.
FINE WORK GRUNKER !"
@ -132,7 +132,7 @@ fiesta = false
metadata/_custom_type_script = "uid://ufxoxupdvyd5"
[sub_resource type="Resource" id="Resource_2eyq5"]
script = ExtResource("2_b83gw")
script = ExtResource("2_c37ff")
name = "???"
message = "????? ?"
bonus_track = false
@ -147,7 +147,7 @@ fiesta = false
metadata/_custom_type_script = "uid://ufxoxupdvyd5"
[sub_resource type="Resource" id="Resource_2qxxw"]
script = ExtResource("2_b83gw")
script = ExtResource("2_c37ff")
name = "taco fiesta"
message = "ENJOY THE FIESTA GRUNKER !"
bonus_track = false
@ -161,9 +161,11 @@ mystery = false
fiesta = true
metadata/_custom_type_script = "uid://ufxoxupdvyd5"
[node name="GameManager" type="Node"]
script = ExtResource("1_08e4a")
vault_milestones = Dictionary[int, ExtResource("2_b83gw")]({
[resource]
script = ExtResource("1_i77rl")
grunk_tank_limit = 96000
grunk_quota = 2000000
vault_milestones = Dictionary[int, ExtResource("2_c37ff")]({
0: SubResource("Resource_h4r54"),
100000: SubResource("Resource_b83gw"),
200000: SubResource("Resource_o6um0"),
@ -180,5 +182,7 @@ vault_milestones = Dictionary[int, ExtResource("2_b83gw")]({
4200000: SubResource("Resource_vebb4"),
6400000: SubResource("Resource_2qxxw")
})
[connection signal="milestone_reached" from="." to="." method="_on_milestone"]
mp3_player_unlocked = false
toothbrush_unlocked = false
stickers_unlocked = false
metadata/_custom_type_script = "uid://c1i5gnht15x0e"