From 9c2fb7ab9c336b59a5bd158573b557c671438c19 Mon Sep 17 00:00:00 2001 From: SchimmelSpreu83 Date: Sun, 8 Mar 2026 00:26:24 +0100 Subject: [PATCH] Implement save/load system --- .gitignore | 1 + .../studio_light_framing_02.glb.import | 2 +- .../spot_light/studio_spot_light.glb.import | 2 +- .../studio_spot_light_framing_01.glb.import | 2 +- .../models/plush/godot_plush_v2.glb.import | 2 +- game/localization/localization.de.translation | Bin 713 -> 1115 bytes game/localization/localization.en.translation | Bin 705 -> 1108 bytes game/localization/localization.es.translation | Bin 616 -> 999 bytes game/localization/localization.ja.translation | Bin 618 -> 1003 bytes game/project.godot | 7 ++++ game/src/core/world/level/world_proxy.gd | 37 +++++++++++++++++ game/src/core/world/level/world_proxy.gd.uid | 1 + game/src/core/world/world.gd | 22 ++++++++-- game/src/game.gd | 4 +- .../systems/flashlight/flashlight_battery.gd | 6 ++- .../flashlight/flashlight_battery.tscn | 5 +++ .../systems/flashlight/flashlight_manager.gd | 13 +++++- .../systems/flashlight/flashlight_trigger.gd | 11 ++++- .../gameplay/systems/power_box/fuse_pickup.gd | 6 ++- .../systems/power_box/fuse_pickup.tscn | 5 +++ .../gameplay/systems/power_box/power_box.gd | 8 +++- .../systems/power_box/power_box_fuse.tscn | 5 +++ game/src/ui/main_menu/main_menu.gd | 29 +++++++++++-- game/src/ui/main_menu/main_menu.tscn | 17 +++++--- game/src/ui/main_menu/save_entry.gd | 7 ++-- game/src/worlds/exposition/exposition.tscn | 39 +++++++----------- 26 files changed, 176 insertions(+), 55 deletions(-) create mode 100644 game/src/core/world/level/world_proxy.gd create mode 100644 game/src/core/world/level/world_proxy.gd.uid diff --git a/.gitignore b/.gitignore index 45d0692..c03a024 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Godot 4+ specific ignores .godot/ +*.unwrap_cache /android/ # FMod ignores diff --git a/game/assets/models/electrics/spot_light/studio_light_framing_02.glb.import b/game/assets/models/electrics/spot_light/studio_light_framing_02.glb.import index 21336e9..ba32bbc 100644 --- a/game/assets/models/electrics/spot_light/studio_light_framing_02.glb.import +++ b/game/assets/models/electrics/spot_light/studio_light_framing_02.glb.import @@ -24,7 +24,7 @@ nodes/use_node_type_suffixes=true meshes/ensure_tangents=true meshes/generate_lods=true meshes/create_shadow_meshes=true -meshes/light_baking=1 +meshes/light_baking=2 meshes/lightmap_texel_size=0.2 meshes/force_disable_compression=false skins/use_named_skins=true diff --git a/game/assets/models/electrics/spot_light/studio_spot_light.glb.import b/game/assets/models/electrics/spot_light/studio_spot_light.glb.import index 05e69d7..01aa265 100644 --- a/game/assets/models/electrics/spot_light/studio_spot_light.glb.import +++ b/game/assets/models/electrics/spot_light/studio_spot_light.glb.import @@ -24,7 +24,7 @@ nodes/use_node_type_suffixes=true meshes/ensure_tangents=true meshes/generate_lods=true meshes/create_shadow_meshes=true -meshes/light_baking=1 +meshes/light_baking=2 meshes/lightmap_texel_size=0.2 meshes/force_disable_compression=false skins/use_named_skins=true diff --git a/game/assets/models/electrics/spot_light/studio_spot_light_framing_01.glb.import b/game/assets/models/electrics/spot_light/studio_spot_light_framing_01.glb.import index 503e0b3..dcf109e 100644 --- a/game/assets/models/electrics/spot_light/studio_spot_light_framing_01.glb.import +++ b/game/assets/models/electrics/spot_light/studio_spot_light_framing_01.glb.import @@ -24,7 +24,7 @@ nodes/use_node_type_suffixes=true meshes/ensure_tangents=true meshes/generate_lods=true meshes/create_shadow_meshes=true -meshes/light_baking=1 +meshes/light_baking=2 meshes/lightmap_texel_size=0.2 meshes/force_disable_compression=false skins/use_named_skins=true diff --git a/game/assets/models/plush/godot_plush_v2.glb.import b/game/assets/models/plush/godot_plush_v2.glb.import index 3eb8023..dac815b 100644 --- a/game/assets/models/plush/godot_plush_v2.glb.import +++ b/game/assets/models/plush/godot_plush_v2.glb.import @@ -24,7 +24,7 @@ nodes/use_node_type_suffixes=true meshes/ensure_tangents=true meshes/generate_lods=true meshes/create_shadow_meshes=true -meshes/light_baking=1 +meshes/light_baking=2 meshes/lightmap_texel_size=0.2 meshes/force_disable_compression=false skins/use_named_skins=true diff --git a/game/localization/localization.de.translation b/game/localization/localization.de.translation index 70a90b24af427e4dc3f45cb07a8215c605aa08f7..cd577d09fc589b0e48fdb87ff85d751f8e4cddba 100644 GIT binary patch literal 1115 zcma)+O-NKx6vxke_*JPXHBA;YG%~@IDHswl6=sg4PQ}b*qQ&&)%~hZ8&3ljcktMCd zO+-SAJ{GOYRT04m_-(-|~u?$f0r1Ij2e+m^H8t15Y|GV{oina7%TfvqwqnnO zWKZNADX%f8N11nJl{sFZ76Z0pDf;-I32oU@Ob3GiJ|v2)uW_us{cmTG>u{#J^3X$L zvHkO?s<4xeNm#6m+z7G@p+?7D@!B@@X-`7$-Q-hjKk7qB7m`4d2*n3IOa74O)4UPn z3PNQP*=((kwnj%tCm`Z;-`_ROpx%MdUi@C)r(jPui@tagWA{$pnM6(ZL5O_+Le0$= zsGAU)L&O{IK2lKv?Tc)d@GK+1))90KDHjchL*+=Vq8u0(#i;Gv0kb9`Xw1P z)lZ0ZZkvzq_oJpdDK7G>w$JFNy*TyN*Oj9-TDk*5@UpIH%P%)*WO~@IfORW5jYjitk z{*8-e#woNNo3~8?UI1qRcL2Y6N3vs2lfC~Wzb0PS`yn0m&Fwm7a&5EvU-kRHbbW0| zt?%#tNEG#p-cpY#J7%&>^LWd(P4!*>ul}Dtp+=DlFB;gu{TCfgF-vBRl55VqK>Wk3 zn&JO|k^iGVAt0>qX#G$5?|K~pf4LTcqTnkb&t^zDpI~@3s>;QwnaPh=AiRh3tOL#J| zfZhZkm1-(ns3uA!2N5Vinx7t&Qb{c_xEQrJlVS7W{8&9A(YIMVDd&D`wKA%`UZa%{ z9V%`A)uP$oA{QFM&`=cgpd2Ai9F8~#&+wgz#$s+{z+IwfYJopqn6%k|)+e;AAYtVxv<%9LqgOCut6pVdmn<|YkaLW}+AqdMoV|VES_*zSKrQw1eYuj$&s=>C*vqfH zF*O1=KQJ2Fhsn=FQ(zi*GvLKVzs^hs;8SglKya>k?|CrYK|S%}N^0IO^unic5uVmu z92x-A*#xhitH%91A^21`y$>(Hxax|w&(&$KqHLx~1^?NP>F*I`ZhRr)2)}siSIY-9o68=OY;`}yoNU*l6<8D2)?xoX>6F19?La-0|3Ukj3 zU&iM4AMRN6eS7lJM-?#VkpEWaz%~Qc!0zVQ;L(j4BQU!5amS)Rln=mn(R^qgefXdJ E01-s}UH||9 literal 705 zcmV;y0zUmxQ$s@n000005C8yN1ONc30ssIgwJ-f(Tm$_S00yCYNx;-mYg#~JU?3<1 z&?>e;LYJ6uOuz&+Go9{sK<$Jso!$rS{Z_J)qt%$SZ6}G}%S?zC97GEjS9TjurcRin zE4sPi6AFoumI7G-VgPUeIbI3!egM;5B}tkz=1MA_WKIOhQ}q2Z)BklzkHE5|J=z1U z%hb}a%#CJF1jA9x(U3gHl9K<-ukun5*wmB*Axpo>cC02SmKG#$JE~NB)%11v3Ap}~ z544*mqa#d3keXV!1lWP%^=tjguUk<8h6tdW&&iP1fd2#x5F|BFXjmz?+Yaa9=kNJ< z{sw=n=2@-^{IDN|1!U$}nxj;8bar%pmg%cxSRUC*n&qEjQl#+I{Vh{b2bux*ms7is zb$81CQFN`fGb-1H5Ui^il}o1RZq_r%G^vdSBSAS#Pg_kp`utG;fu}irkit<}05gR1 z!eRyrf>g{;$;yU;9ygGdIEe|KR9Yt36fY<*SqvIJ6+}iRVsw1a6p@%0%LoZKQL*2K z=_yG*LN=kFC398r;1G}m^YIL5D6-u|Ai7^41EQW6t_$~;ksL}|RXi9dMA>|p@c5au zEZnc`WW~jj4G1fs`=yb=^CJ_h-R}&CbKz!6WVnZAt$iOLs9Bj2o88N>@_Zmk+qAUm zda}b>p0$T&wgXG3@i1hNCKHfIn53jhIzR$LNL6|g08}NM2@JjE)~$aAqYQ{xs=^`v zz(a+?lPRycYc9A4(r-Y}>mr<5GsXeDyrLhE5hoM|icqri--`{d9X}7t6oN zG5Ha+$B?_R7cT#bZJeOJ7;Z{7bGSa&>DDUP#C~ki#X%R)Cp|e+rxW#djB9?GN~ia3 n89fKa`kp?<2A3bT50yqmp)yasr{uAsK$B5?!=hXgQd2`iDLhEp diff --git a/game/localization/localization.es.translation b/game/localization/localization.es.translation index a45bc77742c7c206310947349b7ae6ca3b34af31..21e6988f8b796bf53b2185d044121c93165ea918 100644 GIT binary patch literal 999 zcma)5O=uHA6rO0Bwy`a3&=_c`kxC8~tl%#yXb2?Ino5%tYpR6JW^9IRX4jqFs-@t? zi-@3tKZl;glgE1Ly?FH|7VW{K$AWm>`n|ZLIVd>r@xAxG_h#O_oh_A1ITo%cI2Pgn z_^RWHwpihfifc$^8J@6Zn9A5*ShEA`pH|~r_HXe&jDF;#0GBJD1pFBJM9D9;sg>DPwJbcN$VWGn(q0G%_=O-=o62DLM-vJQ7+a$N7aKMjY%`w zCo>Hk1ZdH?cf&Q%Ws^kr(=p;P_!;0haJnmYsmq=YZQ3^tQ~+v|P^t7qM#0nrLF;e6 ze;8SXzaOB!_Jpy|(OhK)wtf<~o?m=ihaU%M!n%Iw=~@eJ=R482()EQOn-A0Q>CAB; zvvuU`Z7|i+U3Fe>;>*`L`1Dl>>xp|eu7hbWf=KJ`P-K-c#SKal4`onnLi zn-dmT-&5zk-C5a4K-c?jpZu750-t&r26W!!!pKpbLp-@X|E>V0--AGB9|G2ZoGm>e zea>e2opga-Yngjc@UZ55;jv&b8T*#sjoiEfl2QhX??ZuQ{f4kvP&~E~uyTHZ6@4X4 zmTlJsmmVv)hSLuE_{Dz$WZ2^o literal 616 zcmV-u0+;<#Q$s@n000005C8!00{{R}0ssIgwJ-f(?*cUv09Kr5MgXy>HMp42_q{PO zmlDuHbv@^VUjNJK&A(OXzx~Uy%fD0ZMHj^OPtE&tF}zIe4to3wdUWJ%>$yU=!^_l005KXDAJ z6o1nXa)=|EsZldh`2kGBX+5w~Pn6Z9%(P8=v+xf9{hkjpmdA#NfPaQ$MvfPs67EAL zlch{(@GJOHze#DdgTQ~P^25Dss>+niRH+`Qe$L*e_|+FWMO*(5D`UQq_J=G%nrAzh z>cRX0mbEF0(nA;}m%E+ginTK4I(?8T%aKk?L)K687jVV6kb{zdqy=TA ziLNb`lV)4}%{szvX~n)aDcC)nwQ;o!mrE&a>3}M1;C8fhKmkXn0Xan&CL$sQ=Cm*z zP=Y~dEISh5GbtVl47`Q)u73ueX!wD3g_?v$SoD(q4`gP!%pX9X^Az{^Z2|lG=4u^s zPFBb%lsXcnI~O^h`aSu^H5|TAw+DTmet|)psR%ijj?I_rO9)p?d|Oes6XxoEOC3A# zIt=!+qEp*pr?#nrPHBo6Y=W`NF|2~tzZyQK|>&s)+kLP)>K(Go3RYY$FFIm{x9XlLm2LXMkx?pRJ7L9sel5Dwx0ip5|6vRwCj)p~Ie}+!!*(pwHhjlW z7MGk2_r|2z;fyLeTq$#jE7lLETftcxrfUZts=dJG%5bi8&l4@qb|KO>Ron2*rp?(9 z+-6`cb3YOWASmAxa%p=)WqHE&A>v5DB+y;s*v!n|dSEzKK5w%k1fF zyJi;BUVsL*dn+2ddu)>Ed^$!v4nGGR0*>^=PW9NckxlbvfjU6jB-HBzu`w`hhoJRW z-`|hkflue4o7oY?KE;dmIoSG1TzP!*VH185pbqQ$;nnBe7eMz!(DmgX>-V$pY0U{B z_wm5dYhbFSv+BJ5)aNhr@aa|vZ&J4|T?Fg-^g0jRmrn}NX+QJ~bba5*J$XFRDK<2? zK52vXId$Ia&6TwjbUp7z{zGOJKJ8@$(0Nk}qX%^k@zloow`DMW4+5>d4|o9-9q9`h zaCVm8$d>7~*0>J^4{JUUJ_{GqiEpK?*yS@IDP_3$E)rNFXbFdfmFcyR)k+Jj5-4G@ zLZ>OX^jX<6-L|k43ro3Y$?^S%Lsk*WV&UnVnMR_~D242gjahQ(*%MF4LKe<#UYNN1 QC>bvxVY{Eyzbd}@FK_7L>Hq)$ literal 618 zcmV-w0+szzQ$s@n000005C8!40{{S00ssIgwJ-f(^8!T@047{#M)0zzH82jJ8dXTb z?jyV&$lBxN9yo?w9TOPn<%xh>y5U~Qp=)jV+xFp6;bnd;I}sa~AoCRJ7v?&2RB02O z6iC!$MLPva08#*709AgY$bFqSe&KIg{-Udt>1vfVS?egtl1i^~pyj^K13ylB;s{nL z{-=L*jR;lP;_+kq0ZhSZyRXttoW)3+S(`Sr@Cyk2pAQ^hv(_gE$Osb~Jz{`PwvgFu zwjzPSpWsjZC?=7f0Y9pv0c|j=>S$CpbG34?`ZpU+(W^3ainjhCR@QhU?H^fww9axf zwVU|^EMul9N)I3xArs8q&XL7hDSM$l$O_AmPD{bo|Kca`qH#e-1%Zi+O3BUX#sZxZ zjt!3tkk3HQXygGcQ34XNR=@~PY#B6!D9w159Xd)Pkq!+G;7IvVCUShdMlNnhp7hgD zL{Jf}7Fo-`UDk(`fFbD16PJpq&bpt1>&HPK2i zKqgzZDWRat1xYkiM8c6b!97t~-_SMW%vD9n0ifeXhx1n2mx<2U1f* ELo6N~-2eap diff --git a/game/project.godot b/game/project.godot index 339c917..3117306 100644 --- a/game/project.godot +++ b/game/project.godot @@ -50,6 +50,13 @@ folder_colors={ "res://tools/": "gray" } +[importer_defaults] + +scene={ +&"materials/extract": 1, +&"meshes/light_baking": 2 +} + [input] ui_accept={ diff --git a/game/src/core/world/level/world_proxy.gd b/game/src/core/world/level/world_proxy.gd new file mode 100644 index 0000000..fb6cb2b --- /dev/null +++ b/game/src/core/world/level/world_proxy.gd @@ -0,0 +1,37 @@ +class_name WorldProxy +extends Node + +signal world_changed + +static var world_proxies: Array[WorldProxy] = [] + +var world: World: set = set_world, get = get_world + + +func _enter_tree() -> void: + if not world_proxies.has(self): + world_proxies.append(self) + + +func _exit_tree() -> void: + world_proxies.erase(self) + + +func set_world(new_world: World) -> void: + world = new_world + world_changed.emit(world) + + +func get_world() -> World: + return world + + +func has_world() -> bool: + return is_instance_valid(world) + + +func wait_for_world() -> World: + if not is_instance_valid(world): + await world_changed + + return get_world() diff --git a/game/src/core/world/level/world_proxy.gd.uid b/game/src/core/world/level/world_proxy.gd.uid new file mode 100644 index 0000000..3305c25 --- /dev/null +++ b/game/src/core/world/level/world_proxy.gd.uid @@ -0,0 +1 @@ +uid://qifiwdgyx6e diff --git a/game/src/core/world/world.gd b/game/src/core/world/world.gd index a9e4b79..419d1f9 100644 --- a/game/src/core/world/world.gd +++ b/game/src/core/world/world.gd @@ -2,8 +2,8 @@ class_name World extends Node signal loaded -signal change_world_request(new_world_path: String, save_previous: bool, load_from_save: bool) -signal world_unload_request(do_save: bool) +signal change_world_requested(new_world_path: String, save_previous: bool, load_from_save: bool) +signal world_unload_requested(do_save: bool) @export var level_streamers: Array[LevelStreamer] ## Reference to the player. @@ -33,9 +33,23 @@ func _ready() -> void: else: await initialize_default_world_state() + initialize_level_streamers() + initialize_world_proxies() + loaded.emit() +func initialize_level_streamers() -> void: + for level_streamer: LevelStreamer in level_streamers: + level_streamer.loading_finished.connect(initialize_world_proxies) + + +func initialize_world_proxies() -> void: + for world_object: WorldProxy in WorldProxy.world_proxies: + if is_ancestor_of(world_object): + world_object.set_world(self) + + func initialize_default_world_state() -> void: world_state = WorldState.new() @@ -109,11 +123,11 @@ func get_loaded_level_ids() -> Array[StringName]: func request_world_change(new_world_path: String, save_current: bool = true, load_from_save: bool = true) -> void: - change_world_request.emit(new_world_path, save_current, load_from_save) + change_world_requested.emit(new_world_path, save_current, load_from_save) func request_world_unload(do_save: bool = true) -> void: - world_unload_request.emit(do_save) + world_unload_requested.emit(do_save) func store_instance_state(key: String, value: Variant) -> void: diff --git a/game/src/game.gd b/game/src/game.gd index 095ab61..4a60a84 100644 --- a/game/src/game.gd +++ b/game/src/game.gd @@ -83,8 +83,8 @@ func load_world(world_path: String, save_previous: bool = true, load_from_save: if load_from_save: world.world_state = load_world_state(world_path) - world.change_world_request.connect(load_world) - world.world_unload_request.connect(_on_unload_world_request) + world.change_world_requested.connect(load_world) + world.world_unload_requested.connect(_on_unload_world_request) world_container.add_child.call_deferred(world) await world.loaded diff --git a/game/src/gameplay/systems/flashlight/flashlight_battery.gd b/game/src/gameplay/systems/flashlight/flashlight_battery.gd index 4b62e80..d47910b 100644 --- a/game/src/gameplay/systems/flashlight/flashlight_battery.gd +++ b/game/src/gameplay/systems/flashlight/flashlight_battery.gd @@ -2,14 +2,18 @@ class_name FlashlightBattery extends Node3D -@export var world: World @export var manager: FlashlightManager @export var refill_amount: float = 0.15 +var world: World + +@onready var world_proxy: WorldProxy = $WorldProxy @onready var pickup_interaction: PickupInteraction = $PickupInteraction func _ready() -> void: + world = await world_proxy.wait_for_world() + if InstancePersister.new(self, world).get_property(&"is_collected", false): queue_free() return diff --git a/game/src/gameplay/systems/flashlight/flashlight_battery.tscn b/game/src/gameplay/systems/flashlight/flashlight_battery.tscn index 53d3918..2dd1c8c 100644 --- a/game/src/gameplay/systems/flashlight/flashlight_battery.tscn +++ b/game/src/gameplay/systems/flashlight/flashlight_battery.tscn @@ -2,6 +2,7 @@ [ext_resource type="Script" uid="uid://nsxr1qkvivsf" path="res://src/gameplay/systems/flashlight/flashlight_battery.gd" id="1_p5331"] [ext_resource type="Script" uid="uid://ckdo7huqoleoi" path="res://src/core/interaction/interaction_types/pickup.gd" id="2_0r1sb"] +[ext_resource type="Script" uid="uid://qifiwdgyx6e" path="res://src/core/world/level/world_proxy.gd" id="2_3nps0"] [ext_resource type="Script" uid="uid://ch00l1e1rteyw" path="res://addons/controller_icons/objects/ControllerIconTexture.gd" id="3_3nps0"] [ext_resource type="PackedScene" uid="uid://2dre1e56emw6" path="res://src/core/interaction/interaction_area.tscn" id="3_ouj7b"] @@ -25,6 +26,10 @@ radius = 0.1 script = ExtResource("1_p5331") metadata/_custom_type_script = "uid://nsxr1qkvivsf" +[node name="WorldProxy" type="Node" parent="." unique_id=930661856] +script = ExtResource("2_3nps0") +metadata/_custom_type_script = "uid://qifiwdgyx6e" + [node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1939916981] mesh = SubResource("CylinderMesh_0r1sb") diff --git a/game/src/gameplay/systems/flashlight/flashlight_manager.gd b/game/src/gameplay/systems/flashlight/flashlight_manager.gd index 22b6372..7bf8193 100644 --- a/game/src/gameplay/systems/flashlight/flashlight_manager.gd +++ b/game/src/gameplay/systems/flashlight/flashlight_manager.gd @@ -10,7 +10,6 @@ enum DrainModes { TIMED, ## Flashlight looses power over time. } -@export var world: World @export var enabled: bool = true @export var initial_power: float = 1.0 @export var full_percentage: float = 0.9 ## At which point we say that the flashlight is full. @@ -20,13 +19,23 @@ enum DrainModes { ## How much power is lost each second. @export var timed_drain_amount: float = 0.015: set = set_timed_drain_amount +var world: World var power: float = 0.0 var is_flashlight_active: bool = false: set = set_flashlight_is_active +var _world_proxy: WorldProxy -@onready var _instance_persister := InstancePersister.new(self, world) +@onready var _instance_persister: InstancePersister + + +func _enter_tree() -> void: + _world_proxy = WorldProxy.new() + add_child(_world_proxy) func _ready() -> void: + world = await _world_proxy.wait_for_world() + _instance_persister= InstancePersister.new(self, world) + power = _instance_persister.get_property(&"power", initial_power) is_flashlight_active = _instance_persister.get_property(&"is_flashlight_active", is_flashlight_active) drain_mode = _instance_persister.get_property(&"drain_mode", drain_mode) diff --git a/game/src/gameplay/systems/flashlight/flashlight_trigger.gd b/game/src/gameplay/systems/flashlight/flashlight_trigger.gd index b6cf129..3342cbd 100644 --- a/game/src/gameplay/systems/flashlight/flashlight_trigger.gd +++ b/game/src/gameplay/systems/flashlight/flashlight_trigger.gd @@ -6,18 +6,27 @@ enum DrainModes { AMOUNT, ## Reduce the power by [member drain_value] amount. } -@export var world: World @export var manager: FlashlightManager @export var allowed_bodies: Array[Node3D] @export var trigger_once: bool = true +var world: World +var _world_proxy: WorldProxy + @export var drain_mode := DrainModes.SET @export var drain_value: float = 0.3 @export var change_manager_drain_mode: bool = false @export var manager_drain_mode := FlashlightManager.DrainModes.MANUAL +func _enter_tree() -> void: + _world_proxy = WorldProxy.new() + add_child(_world_proxy) + + func _ready() -> void: + world = await _world_proxy.wait_for_world() + if trigger_once and InstancePersister.new(self, world).get_property(&"is_triggered", false): queue_free() return diff --git a/game/src/gameplay/systems/power_box/fuse_pickup.gd b/game/src/gameplay/systems/power_box/fuse_pickup.gd index 5cb98d9..2acc872 100644 --- a/game/src/gameplay/systems/power_box/fuse_pickup.gd +++ b/game/src/gameplay/systems/power_box/fuse_pickup.gd @@ -3,12 +3,16 @@ extends Node3D @export var power_box: PowerBox -@export var world: World + +var world: World @onready var pickup_interaction: PickupInteraction = $PickupInteraction +@onready var world_proxy: WorldProxy = $WorldProxy func _ready() -> void: + world = await world_proxy.wait_for_world() + if InstancePersister.new(self, world).get_property(&"is_collected", false): queue_free() return diff --git a/game/src/gameplay/systems/power_box/fuse_pickup.tscn b/game/src/gameplay/systems/power_box/fuse_pickup.tscn index d47b7ad..e08c9ed 100644 --- a/game/src/gameplay/systems/power_box/fuse_pickup.tscn +++ b/game/src/gameplay/systems/power_box/fuse_pickup.tscn @@ -2,6 +2,7 @@ [ext_resource type="Script" uid="uid://dkao86mwhq1tm" path="res://src/gameplay/systems/power_box/fuse_pickup.gd" id="1_b733i"] [ext_resource type="Script" uid="uid://ckdo7huqoleoi" path="res://src/core/interaction/interaction_types/pickup.gd" id="2_of2nm"] +[ext_resource type="Script" uid="uid://qifiwdgyx6e" path="res://src/core/world/level/world_proxy.gd" id="2_p036h"] [ext_resource type="Script" uid="uid://ch00l1e1rteyw" path="res://addons/controller_icons/objects/ControllerIconTexture.gd" id="3_hqmg0"] [ext_resource type="PackedScene" uid="uid://2dre1e56emw6" path="res://src/core/interaction/interaction_area.tscn" id="4_3b6jw"] @@ -28,6 +29,10 @@ radius = 0.1 script = ExtResource("1_b733i") metadata/_custom_type_script = "uid://dkao86mwhq1tm" +[node name="WorldProxy" type="Node" parent="." unique_id=586279846] +script = ExtResource("2_p036h") +metadata/_custom_type_script = "uid://qifiwdgyx6e" + [node name="PickupInteraction" type="Node" parent="." unique_id=285803844] script = ExtResource("2_of2nm") hover_hint = "Press {prompt} to pickup" diff --git a/game/src/gameplay/systems/power_box/power_box.gd b/game/src/gameplay/systems/power_box/power_box.gd index a6fe56f..cbb8cb8 100644 --- a/game/src/gameplay/systems/power_box/power_box.gd +++ b/game/src/gameplay/systems/power_box/power_box.gd @@ -4,21 +4,25 @@ extends Node3D signal power_restored signal power_revoked -@export var world: World @export var can_interact: bool = true: set = set_can_interact @export var start_activated: bool = false @export var minigame: Node3D @export var requires_fuse: bool = true @export var interaction: PickupInteraction +var world: World var is_restored: bool = false var is_active: bool = false var has_completed_minigame: bool = false -@onready var _instance_persister := InstancePersister.new(self, world) +@onready var world_proxy: WorldProxy = $WorldProxy +@onready var _instance_persister: InstancePersister func _ready() -> void: + world = await world_proxy.wait_for_world() + + _instance_persister = InstancePersister.new(self, world) is_restored = _instance_persister.get_property(&"is_restored", is_restored) is_active = _instance_persister.get_property(&"is_active", start_activated) has_completed_minigame = _instance_persister.get_property(&"has_completed_minigame", has_completed_minigame) diff --git a/game/src/gameplay/systems/power_box/power_box_fuse.tscn b/game/src/gameplay/systems/power_box/power_box_fuse.tscn index b2d1661..d64ce29 100644 --- a/game/src/gameplay/systems/power_box/power_box_fuse.tscn +++ b/game/src/gameplay/systems/power_box/power_box_fuse.tscn @@ -2,6 +2,7 @@ [ext_resource type="Script" uid="uid://bpvawt7ate0ug" path="res://src/gameplay/systems/power_box/power_box.gd" id="1_m14vv"] [ext_resource type="Script" uid="uid://ckdo7huqoleoi" path="res://src/core/interaction/interaction_types/pickup.gd" id="2_35cpc"] +[ext_resource type="Script" uid="uid://qifiwdgyx6e" path="res://src/core/world/level/world_proxy.gd" id="2_v6qxf"] [ext_resource type="Script" uid="uid://ch00l1e1rteyw" path="res://addons/controller_icons/objects/ControllerIconTexture.gd" id="3_35cpc"] [ext_resource type="PackedScene" uid="uid://2dre1e56emw6" path="res://src/core/interaction/interaction_area.tscn" id="4_v6qxf"] @@ -23,6 +24,10 @@ script = ExtResource("1_m14vv") interaction = NodePath("PickupInteraction") metadata/_custom_type_script = "uid://bpvawt7ate0ug" +[node name="WorldProxy" type="Node" parent="." unique_id=1910031977] +script = ExtResource("2_v6qxf") +metadata/_custom_type_script = "uid://qifiwdgyx6e" + [node name="PickupInteraction" type="Node" parent="." unique_id=1698155134] script = ExtResource("2_35cpc") button_prompt = SubResource("Texture2D_35cpc") diff --git a/game/src/ui/main_menu/main_menu.gd b/game/src/ui/main_menu/main_menu.gd index c953b70..9a1c8d6 100644 --- a/game/src/ui/main_menu/main_menu.gd +++ b/game/src/ui/main_menu/main_menu.gd @@ -5,21 +5,22 @@ signal load_game_request(save_data: SaveData, slot_index: int) signal quit_request @onready var continue_button: Button = %ContinueButton +@onready var load_button: Button = %LoadButton @onready var new_game_button: Button = %NewGameButton @onready var quit_button: Button = %QuitButton +@onready var saves_scroll_container: ScrollContainer = $SavesScrollContainer @onready var saves_container: VBoxContainer = %SavesContainer func _ready() -> void: continue_button.pressed.connect(_on_continue_pressed) + load_button.pressed.connect(_on_load_pressed) new_game_button.pressed.connect(_on_new_game_pressed) quit_button.pressed.connect(_on_quit_pressed) continue_button.visible = has_save() - populate_save_entries() - func has_save() -> bool: var config := ConfigFile.new() @@ -33,7 +34,8 @@ func populate_save_entries() -> void: for save_slot: int in get_save_slots(): var save_path: String = "user://%s/save_data.tres" % save_slot - SaveEntryButton.construct_save_entry_button(saves_container, save_path) + var button := SaveEntryButton.construct_save_entry_button(saves_container, save_path, save_slot) + button.pressed.connect(_on_save_entry_pressed.bind(save_slot)) func get_save_slots() -> PackedInt32Array: @@ -78,6 +80,11 @@ func _on_continue_pressed() -> void: load_game_request.emit(save_data, slot) +func _on_load_pressed() -> void: + saves_scroll_container.show() + populate_save_entries() + + func _on_new_game_pressed() -> void: var save_data: SaveData = Game.INITIAL_SAVE_DATA.duplicate() var slot_index: int = get_unique_save_slot_index() @@ -92,3 +99,19 @@ func _on_new_game_pressed() -> void: func _on_quit_pressed() -> void: quit_request.emit() + + +func _on_save_entry_pressed(save_slot: int) -> void: + var config := ConfigFile.new() + config.load("user://settings.ini") + config.set_value("game", "last_slot", save_slot) + config.save("user://settings.ini") + + var save_path: String = get_save_path(save_slot) + + if not FileAccess.file_exists(save_path): + _on_new_game_pressed() + return + + var save_data: SaveData = load(save_path) + load_game_request.emit(save_data, save_slot) diff --git a/game/src/ui/main_menu/main_menu.tscn b/game/src/ui/main_menu/main_menu.tscn index 04b5da6..6251e08 100644 --- a/game/src/ui/main_menu/main_menu.tscn +++ b/game/src/ui/main_menu/main_menu.tscn @@ -41,6 +41,11 @@ unique_name_in_owner = true layout_mode = 2 text = "CONTINUE" +[node name="LoadButton" type="Button" parent="MainButtons" unique_id=972924585] +unique_name_in_owner = true +layout_mode = 2 +text = "LOAD" + [node name="NewGameButton" type="Button" parent="MainButtons" unique_id=91755502] unique_name_in_owner = true layout_mode = 2 @@ -51,7 +56,7 @@ unique_name_in_owner = true layout_mode = 2 text = "QUIT" -[node name="Saves" type="ScrollContainer" parent="." unique_id=1084479536] +[node name="SavesScrollContainer" type="ScrollContainer" parent="." unique_id=1084479536] visible = false layout_mode = 1 anchors_preset = -1 @@ -68,23 +73,23 @@ grow_vertical = 2 follow_focus = true metadata/_edit_use_anchors_ = true -[node name="SavesContainer" type="VBoxContainer" parent="Saves" unique_id=665774472] +[node name="SavesContainer" type="VBoxContainer" parent="SavesScrollContainer" unique_id=665774472] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 -[node name="Button" type="Button" parent="Saves/SavesContainer" unique_id=784511958] +[node name="Button" type="Button" parent="SavesScrollContainer/SavesContainer" unique_id=784511958] layout_mode = 2 text = "Save 1" -[node name="Button2" type="Button" parent="Saves/SavesContainer" unique_id=1926271686] +[node name="Button2" type="Button" parent="SavesScrollContainer/SavesContainer" unique_id=1926271686] layout_mode = 2 text = "Save 1" -[node name="Button3" type="Button" parent="Saves/SavesContainer" unique_id=929881066] +[node name="Button3" type="Button" parent="SavesScrollContainer/SavesContainer" unique_id=929881066] layout_mode = 2 text = "Save 1" -[node name="Button4" type="Button" parent="Saves/SavesContainer" unique_id=366833041] +[node name="Button4" type="Button" parent="SavesScrollContainer/SavesContainer" unique_id=366833041] layout_mode = 2 text = "Save 1" diff --git a/game/src/ui/main_menu/save_entry.gd b/game/src/ui/main_menu/save_entry.gd index 00ba2ae..0035c64 100644 --- a/game/src/ui/main_menu/save_entry.gd +++ b/game/src/ui/main_menu/save_entry.gd @@ -9,10 +9,10 @@ signal pressed @onready var timestamp: Label = %Timestamp -static func construct_save_entry_button(node: Node, save_path: String) -> SaveEntryButton: +static func construct_save_entry_button(node: Node, save_path: String, slot_index: int) -> SaveEntryButton: var entry: SaveEntryButton = load("uid://b3rf5k5u0nh5j").instantiate() node.add_child(entry) - entry.apply_save_data(save_path) + entry.apply_save_data(save_path, slot_index) return entry @@ -20,8 +20,7 @@ func _ready() -> void: button.pressed.connect(pressed.emit) -func apply_save_data(save_path: String) -> void: +func apply_save_data(save_path: String, slot_index: int) -> void: var modified_time: int = FileAccess.get_modified_time(save_path) timestamp.text = str("Timestamp:\n", Time.get_time_dict_from_unix_time(modified_time)) - var slot_index: int = save_path.split("/")[3].to_int() save_slot.text = str("Slot: ", slot_index) diff --git a/game/src/worlds/exposition/exposition.tscn b/game/src/worlds/exposition/exposition.tscn index d84cc3e..8d52b98 100644 --- a/game/src/worlds/exposition/exposition.tscn +++ b/game/src/worlds/exposition/exposition.tscn @@ -308,9 +308,8 @@ shadow_enabled = true transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0000005, 17) flashlight_manager = NodePath("FlashlightManager") -[node name="FlashlightManager" type="Node" parent="PlayerCharacter" index="6" unique_id=583526169 node_paths=PackedStringArray("world")] +[node name="FlashlightManager" type="Node" parent="PlayerCharacter" index="6" unique_id=583526169] script = ExtResource("2_whdyi") -world = NodePath("../..") metadata/_custom_type_script = "uid://s1vhbwkyewdg" [node name="EntranceHall" type="Node3D" parent="." index="5" unique_id=1518685491] @@ -599,46 +598,40 @@ material = ExtResource("4_8xmty") [node name="godot_plush V2" parent="." index="7" unique_id=1756431274 instance=ExtResource("8_qm8ha")] transform = Transform3D(-50, 0, 4.371139e-06, 0, 50, 0, -4.371139e-06, 0, -50, 0, 5.628097, 0) -[node name="godot_plush_01" parent="godot_plush V2" index="0" unique_id=1748171278] +[node name="godot_plush_01" parent="godot_plush V2" index="0" unique_id=983026135] visible = false -[node name="godot_plush_sitted" parent="godot_plush V2" index="1" unique_id=438565683] +[node name="godot_plush_sitted" parent="godot_plush V2" index="1" unique_id=1406071640] visible = false -[node name="godot_plush_02" parent="godot_plush V2" index="2" unique_id=1913873414] +[node name="godot_plush_02" parent="godot_plush V2" index="2" unique_id=815057431] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.0025618896, 0) [node name="Objects" type="Node3D" parent="." index="8" unique_id=249052389] -[node name="FlashlightBattery" parent="Objects" index="0" unique_id=1021450063 node_paths=PackedStringArray("world", "manager") instance=ExtResource("9_n68sy")] +[node name="FlashlightBattery" parent="Objects" index="0" unique_id=1021450063 node_paths=PackedStringArray("manager") instance=ExtResource("9_n68sy")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.39999998, 2.0499978, 5.8) -world = NodePath("../..") manager = NodePath("../../PlayerCharacter/FlashlightManager") -[node name="FlashlightBattery2" parent="Objects" index="1" unique_id=1280919267 node_paths=PackedStringArray("world", "manager") instance=ExtResource("9_n68sy")] +[node name="FlashlightBattery2" parent="Objects" index="1" unique_id=1280919267 node_paths=PackedStringArray("manager") instance=ExtResource("9_n68sy")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.19999997, 2.0499978, 5.8) -world = NodePath("../..") manager = NodePath("../../PlayerCharacter/FlashlightManager") -[node name="FlashlightBattery3" parent="Objects" index="2" unique_id=180006569 node_paths=PackedStringArray("world", "manager") instance=ExtResource("9_n68sy")] +[node name="FlashlightBattery3" parent="Objects" index="2" unique_id=180006569 node_paths=PackedStringArray("manager") instance=ExtResource("9_n68sy")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.9802322e-08, 2.0499978, 5.8) -world = NodePath("../..") manager = NodePath("../../PlayerCharacter/FlashlightManager") -[node name="FlashlightBattery4" parent="Objects" index="3" unique_id=2057837844 node_paths=PackedStringArray("world", "manager") instance=ExtResource("9_n68sy")] +[node name="FlashlightBattery4" parent="Objects" index="3" unique_id=2057837844 node_paths=PackedStringArray("manager") instance=ExtResource("9_n68sy")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.10000003, 2.0499978, 5.8) -world = NodePath("../..") manager = NodePath("../../PlayerCharacter/FlashlightManager") -[node name="FlashlightBattery5" parent="Objects" index="4" unique_id=1599096313 node_paths=PackedStringArray("world", "manager") instance=ExtResource("9_n68sy")] +[node name="FlashlightBattery5" parent="Objects" index="4" unique_id=1599096313 node_paths=PackedStringArray("manager") instance=ExtResource("9_n68sy")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.30000004, 2.0499978, 5.8) -world = NodePath("../..") manager = NodePath("../../PlayerCharacter/FlashlightManager") refill_amount = 1.0 -[node name="FlashlightBattery6" parent="Objects" index="5" unique_id=1386160339 node_paths=PackedStringArray("world", "manager") instance=ExtResource("9_n68sy")] +[node name="FlashlightBattery6" parent="Objects" index="5" unique_id=1386160339 node_paths=PackedStringArray("manager") instance=ExtResource("9_n68sy")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.50000006, 2.0499978, 5.8) -world = NodePath("../..") manager = NodePath("../../PlayerCharacter/FlashlightManager") refill_amount = 0.5 @@ -670,19 +663,16 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, -5) top_level = true fov = 65.0 -[node name="PowerBoxFuse" parent="Objects/LogicCluster" index="1" unique_id=1951712625 node_paths=PackedStringArray("world") instance=ExtResource("10_xp6ux")] +[node name="PowerBoxFuse" parent="Objects/LogicCluster" index="1" unique_id=1951712625 instance=ExtResource("10_xp6ux")] transform = Transform3D(0.96592575, 0, 0.25881898, 0, 1, 0, -0.25881898, 0, 0.96592575, -1.2618543, 2.5, -5.657638) -world = NodePath("../../..") -[node name="FusePickup" parent="Objects/LogicCluster" index="2" unique_id=38269817 node_paths=PackedStringArray("power_box", "world") instance=ExtResource("11_qm8ha")] +[node name="FusePickup" parent="Objects/LogicCluster" index="2" unique_id=38269817 node_paths=PackedStringArray("power_box") instance=ExtResource("11_qm8ha")] transform = Transform3D(0.9999998, 0, 0, 0, 1, 0, 0, 0, 0.9999998, -4.2999997, 2.0499978, -3.9999995) power_box = NodePath("../PowerBoxFuse") -world = NodePath("../../..") [node name="Triggers" type="Node3D" parent="." index="9" unique_id=1343544739] -[node name="FlashlightTrigger" parent="Triggers" index="0" unique_id=1379952044 node_paths=PackedStringArray("world", "manager", "allowed_bodies") instance=ExtResource("10_dy1qy")] -world = NodePath("../..") +[node name="FlashlightTrigger" parent="Triggers" index="0" unique_id=1379952044 node_paths=PackedStringArray("manager", "allowed_bodies") instance=ExtResource("10_dy1qy")] manager = NodePath("../../PlayerCharacter/FlashlightManager") allowed_bodies = [NodePath("../../PlayerCharacter")] change_manager_drain_mode = true @@ -692,9 +682,8 @@ manager_drain_mode = 1 transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13.200001, 2.4, 0) shape = SubResource("BoxShape3D_dy1qy") -[node name="FlashlightTrigger2" parent="Triggers" index="1" unique_id=275007123 node_paths=PackedStringArray("world", "manager", "allowed_bodies") instance=ExtResource("10_dy1qy")] +[node name="FlashlightTrigger2" parent="Triggers" index="1" unique_id=275007123 node_paths=PackedStringArray("manager", "allowed_bodies") instance=ExtResource("10_dy1qy")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -14, 0, -23) -world = NodePath("../..") manager = NodePath("../../PlayerCharacter/FlashlightManager") allowed_bodies = [NodePath("../../PlayerCharacter")] trigger_once = false