From d6cdf5b2ebb826c2105cd8b4d506aa3f348cf598 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Tue, 25 Mar 2025 22:03:18 -0400 Subject: [PATCH 01/20] Initial save game UI work (WIP) --- save_load/assets/folder-open-hover.png | Bin 0 -> 887 bytes save_load/assets/folder-open-hover.png.import | 34 ++++++ save_load/assets/folder-open-normal.png | Bin 0 -> 842 bytes .../assets/folder-open-normal.png.import | 34 ++++++ save_load/assets/folder-open-pressed.png | Bin 0 -> 771 bytes .../assets/folder-open-pressed.png.import | 34 ++++++ save_load/assets/save-hover.png | Bin 0 -> 869 bytes save_load/assets/save-hover.png.import | 34 ++++++ save_load/assets/save-normal.png | Bin 0 -> 855 bytes save_load/assets/save-normal.png.import | 34 ++++++ save_load/assets/save-pressed.png | Bin 0 -> 773 bytes save_load/assets/save-pressed.png.import | 34 ++++++ save_load/assets/trash-hover.png | Bin 0 -> 860 bytes save_load/assets/trash-hover.png.import | 34 ++++++ save_load/assets/trash-normal.png | Bin 0 -> 826 bytes save_load/assets/trash-normal.png.import | 34 ++++++ save_load/assets/trash-pressed.png | Bin 0 -> 768 bytes save_load/assets/trash-pressed.png.import | 34 ++++++ save_load/autoloads/save_game_manager.gd | 22 ++-- .../components/save_level_data_component.gd | 82 ++++++++------ .../save_file_highlight_panel_theme.tres | 4 + .../resources/save_file_panel_theme.tres | 8 ++ save_load/ui/save_file.gd | 63 +++++++++++ save_load/ui/save_file.gd.uid | 1 + save_load/ui/save_file.tscn | 100 ++++++++++++++++++ save_load/ui/save_files_list.gd | 29 +++++ save_load/ui/save_files_list.gd.uid | 1 + save_load/ui/save_load_ui.tscn | 50 +++++++++ 28 files changed, 624 insertions(+), 42 deletions(-) create mode 100644 save_load/assets/folder-open-hover.png create mode 100644 save_load/assets/folder-open-hover.png.import create mode 100644 save_load/assets/folder-open-normal.png create mode 100644 save_load/assets/folder-open-normal.png.import create mode 100644 save_load/assets/folder-open-pressed.png create mode 100644 save_load/assets/folder-open-pressed.png.import create mode 100644 save_load/assets/save-hover.png create mode 100644 save_load/assets/save-hover.png.import create mode 100644 save_load/assets/save-normal.png create mode 100644 save_load/assets/save-normal.png.import create mode 100644 save_load/assets/save-pressed.png create mode 100644 save_load/assets/save-pressed.png.import create mode 100644 save_load/assets/trash-hover.png create mode 100644 save_load/assets/trash-hover.png.import create mode 100644 save_load/assets/trash-normal.png create mode 100644 save_load/assets/trash-normal.png.import create mode 100644 save_load/assets/trash-pressed.png create mode 100644 save_load/assets/trash-pressed.png.import create mode 100644 save_load/resources/save_file_highlight_panel_theme.tres create mode 100644 save_load/resources/save_file_panel_theme.tres create mode 100644 save_load/ui/save_file.gd create mode 100644 save_load/ui/save_file.gd.uid create mode 100644 save_load/ui/save_file.tscn create mode 100644 save_load/ui/save_files_list.gd create mode 100644 save_load/ui/save_files_list.gd.uid create mode 100644 save_load/ui/save_load_ui.tscn diff --git a/save_load/assets/folder-open-hover.png b/save_load/assets/folder-open-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..06ead5b436be70d30aad0287095b259781d4dfd8 GIT binary patch literal 887 zcmV--1Bm>IP)@$Gi#A0>i;|F9^s;%1 zK*%Tx!d4kf1=7Ol1(FJjpoU0<(kDpEX>sq+^>*&eeVcpF%y0QW4}b2L|LgOdThXi- zM)5FqVH3u+uakHYPbT)qhYvjAO*ZMZ&hb_xgZO`Em-6gDT$ChOTUv>IKByAt-Z zxKmMn)wZV?rtndYpjB*0*rz%Igrc0mb{uc<#3Wu8LuD9|T+<>|J6BQ8h++Ez5uFr6 zKYshK_!jTuHAVURe39(07LI7YG>+Tx9KILB{ey!XEa;rZa8L|;2M0ME&ZS3w)9|*)X`89E?sTY-^VrWLq0guXQns>o74ShAS5jGKP!r4EFTtMZ2H6 zS`2Fx<(cl&HHOtVjC~mb!d4s=!)3kHJl(3z~grY2mh7-dCeo4*DYKM--ad+b6&`<$ux~YYIrqgtNFtI1X>lvix zjS1TyIMEGcvwpx-rjXN$a<+vv_mpc(v7XV{ec# zjNpO9$q_|)B7-Py(R_K;MI?8%c&pE$;j~KCztq3}S9?g+Gk7Os$gdUUx%MDi&&Idr zw){-eHJPgIMl$EwIJZl0PL7WbA#J*ME%AXf{@L$2Y**c^UCaVAd N002ovPDHLkV1jx|tgZk6 literal 0 HcmV?d00001 diff --git a/save_load/assets/folder-open-hover.png.import b/save_load/assets/folder-open-hover.png.import new file mode 100644 index 0000000..f68715f --- /dev/null +++ b/save_load/assets/folder-open-hover.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://by4w5ll3le7g6" +path="res://.godot/imported/folder-open-hover.png-a1ef3a807e674ab8f8806dada553ab61.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://save_load/assets/folder-open-hover.png" +dest_files=["res://.godot/imported/folder-open-hover.png-a1ef3a807e674ab8f8806dada553ab61.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=1 diff --git a/save_load/assets/folder-open-normal.png b/save_load/assets/folder-open-normal.png new file mode 100644 index 0000000000000000000000000000000000000000..2a3f3a3713dff4e4d4a74191412229db6c0435c8 GIT binary patch literal 842 zcmV-Q1GW5#P)1v3Or=n=v_zt^g;0@EVGu$Df)qkQv?zmUr|nFrHbK-PT114)Hc=4-89@-Z z35={rf-(sd3X7l=EEj2knZ7N&6T{_<&i|b8-Oc$9hk5Szch0#NLXuP=>8zwaNn=V* z+Zg4aiglACvS<(iurB7lX{G5&+GVs)4=0Rp6tfqd9}jF=$j(z&%MFIfKj- zr~%g`HRTL41Uir-&?4Zvq)50=l5J2)yMz3x%HYylfYZRL(D-&>5AZT)kOAmlpf?Nc z?-B4CC=QvLV<96Hu*~co@Z4eAkh7f3k-j^|%9k2sLL2mS^P&RS2GrzH&z!eOQkj`er5vu3N`W4r(?dXj2B2Tk+H`WV1K8(zDsBbNrvw>z@mzt_oOVf7 zsd(}rP+1_5rNFvW3eQQ$*d$P6X20@yS5*UibM?$fk_%M>`yHn4d;$jA6=!@j*>FAK z*e3$q_8>HOI>vqiBdI`!^$Fc zYgwBXq@)tyn8Va-W+NVWaYu0dk(MV9#W~gcP;laf>NLUaePeq_V{|X=deJnHETpf!5$4~>6{qaU0_ctL@^KCrjD?(WOe$HR z;LHJcJUVH(AV0*`iwH34nA9j~HPGs?JvFn>9_3@pLZKRk>~~CB!~CUfrN;%Q4(J9p zx;81XKTJ|N@CB$~Y3MglYi8sBJ!xi>z!jE`x?%x|?)M}W0gr(L1rGQEXfm@u;heh($ literal 0 HcmV?d00001 diff --git a/save_load/assets/folder-open-normal.png.import b/save_load/assets/folder-open-normal.png.import new file mode 100644 index 0000000..23f63a6 --- /dev/null +++ b/save_load/assets/folder-open-normal.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cmq51cgasug81" +path="res://.godot/imported/folder-open-normal.png-f7f228a37723c0515dbd7272838372e1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://save_load/assets/folder-open-normal.png" +dest_files=["res://.godot/imported/folder-open-normal.png-f7f228a37723c0515dbd7272838372e1.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=1 diff --git a/save_load/assets/folder-open-pressed.png b/save_load/assets/folder-open-pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..448ad270e0db1bf87a488907ece0ba9f10de7040 GIT binary patch literal 771 zcmV+e1N{7nP)_C z(EKE1!$6U>7J7@7c$SejPCyrSWeHgi&?X#Do|zeBKIp@YK!@N&A=l6h9{d(dusm_A zYH1F3V{Cza0hZ!+mXPoZ*K2U!i@1kjfi(4YR_wqR;GklO;bZ^=Y&s$-jy(%(^~LbhXdzO5+T({krh zA-4qbL^u@KI+;kZV(R^)UpTG*|7Boc=dg;HuwoA!5iU_*pDu+S=It zH_MO2U4yP#EM&}UKK9DhT>nA-Oj$D00!}Z^ zMzqriK|U{x7svE3puER8xs$NMYu6&%O1%#Bv8(Wp>xC;tFH-DVBLSR+ODTQa-fzE! zFS0AM)w=z_#C*W!WW#&Z|D#U1dSyQikvn~B^ijN(7c_%vO?LQ=3V0c#iyiKDxTCsX zXVtRZ$7Tb3a52!A7?YC-)*k8H`z%+*elyq~{{Tke^4RO9zbF6z002ovPDHLkV1j~- BZ+ZX# literal 0 HcmV?d00001 diff --git a/save_load/assets/folder-open-pressed.png.import b/save_load/assets/folder-open-pressed.png.import new file mode 100644 index 0000000..3e826ff --- /dev/null +++ b/save_load/assets/folder-open-pressed.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://blyryo60jydgi" +path="res://.godot/imported/folder-open-pressed.png-b69b4af3e2256bb46d3464e4d48a50c0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://save_load/assets/folder-open-pressed.png" +dest_files=["res://.godot/imported/folder-open-pressed.png-b69b4af3e2256bb46d3464e4d48a50c0.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=1 diff --git a/save_load/assets/save-hover.png b/save_load/assets/save-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..04fdbf9a9e255b05b6ce7ceea0bf4a57a4cf53c0 GIT binary patch literal 869 zcmV-r1DgDaP)?xHXurl2CI=(UMTL^3QQ5N(+lwJoV-gi1dl{{S;YgrKst zoh3EUFQrXpL{y-)u!T;%Y;niBKIT5})%ScOc;GJX+&TAtzjJ=fnR8>LixgXcT3|m= z0xV^IWD*zz>QTBUtWwMedooB5As)C&<2IpVA`UFKDE!Qe$9p8of$?@hfu?{=)d;*n ziUySKb5*>I^HRz;UDNKq;a02VgJoF~sa4U=KmYt9ZxaCb-h2*8F;PL!>Cg-o^Wr03HLI(^Y^zq_`6?zvn_$8}M5#&$?qsQHuAQ zwP1m{e+npzoBVxy0mko5lpdVQLT7>A1q8kaILAM?`I<_xwxG>qAL(#QenBvk?LffV7J}98;NW@|%GhKuN~865yevXb_S{Jjd5BsWf9;v+tY}m}ypdN%hWb z5}8v9*y7=Lnc2*8+KSuZCTBK8oJ743z*>*M&q5fy1JM9XX93ZW2_Pskvu8kK77!28 zK@{h@prlhVhwe)PLQ*5}2q=pH(QIaqiEx_cxK5ZWEwDKYId*M`u|J7pwH~`cF6^=a)yae8u*?hhij8#g+oU2aKP?pMJP*Tkz=k}H|sXT5( zn@ddD!fZ9rE$LEFJ|O7~a5Lt7MZhgdr-J|>X&ul3SdyFwbV^zu!U?nj8zXKMdffBN z^L;(g5(0qZ9^M47$IR-?tPa?lcDs>I$3g(G-NS1)v!S#aY7l7mmBS$bDEIKjWAe&F z0PxMjtMl*<$1RbUI}^VJwr23oOBw@iyZ4L0Ige~#!85U>lR5PCCql@?kAVI~$o09d ztgt>{W&+N*&>5E|fU{1v@TG*AeFP2xpJEIj0}h(mSWx|dnGL&Q?JDraGvqUH1*kT& zks$j4Gn)dgNxJTm)V8!7@F5>2v*Zm;nqQ2%XSJnV2!QXlZ9{;%`YPVZs9#J2nZK9{ zNs`JWz4PcfR1CZTc4o-z26`pkPZ-~qf;PFHW0&V!w@Iqx&u`YE6=wc(Ky}K+?|UoI zfA5;vs5u)e0iFUiD@6Go;Ha6+rd-^f1&#oB+|h(=HXVPJ$9I$9e4@oQAQixx0xLbq hzni@-Gy5+&{sxYa>JtA8VvYa+002ovPDHLkV1n6gg*X5J literal 0 HcmV?d00001 diff --git a/save_load/assets/save-normal.png.import b/save_load/assets/save-normal.png.import new file mode 100644 index 0000000..cb6ff20 --- /dev/null +++ b/save_load/assets/save-normal.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ja8bc1h5x85o" +path="res://.godot/imported/save-normal.png-126e9be66da8efc0f0eb84b8cc03c6ff.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://save_load/assets/save-normal.png" +dest_files=["res://.godot/imported/save-normal.png-126e9be66da8efc0f0eb84b8cc03c6ff.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=1 diff --git a/save_load/assets/save-pressed.png b/save_load/assets/save-pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..28f2cdd3021eefe536b64312fe67a9ca97c525ce GIT binary patch literal 773 zcmV+g1N!`lP)cxXUZlbh;sGwdHTW`_&10yP4#9oyiyejBX5K;?0*uOxKBIMwY zsyDS*gxW^DSPLR37zNWqTBG%li9BBBC2y0LWD5Dh5;FVdz4`9$?Ci`!Ir@MUFb6CG zD@|&D<(vJ~h&8|%@TZAJYi$Wg)MEVe78_m{i7`XQJCo=$uxx535d$s*ol4&{Fb@2X z{|A6!<+)_YS+1nN3Ahc!eSLA@0dN{f2fa9>Fn=R{;s1~}N?}n=KOrOTG!vb`W<@?O zO3g%CMtWK4E6qegBd}Ic_(^M!1_ptA1rmdP0@m;dI9Y+jeJ_bv1Rp#T^11yrkeC3v z0!So*hXgoH(!mMqZIw{XvHLA9As5F^xe&-Vz`%T+BzCwEcnRbpCh*u{Ca!5=z=gnf z@&1fs0MoD}4mjkX^EK!w;er=HuO%n3?!;{ZZ32x(tU-fvP?Ga)B3l3^fmf2eM~bjV zi7*%dW-A;UW`H9#zB@3Vh>E~)$kem|T(w5voGcBO8qnD&2t?t;>wvpJhl5UsR6=?z z>_S7p21ULMJd^fY>F)t9Ss-v+k!69s;?x7cK2LSSi(?iDY*%C{*^Nq=5n0)B*aCrW zMYa%9)@^~nw}5!y$I-Ay>@A^jLQEjzy(Ej%0eVQRYyX9!a^LFpje7@ z#Z;**(rO=Akw~0%(8&VlBS=jnJ5GND4g;S;JUb5@l3!E#z#Om(xB+}o`ac8L<(Z7t zd|(l{3EYw(wbc^?K18u(nvu|y{NlZ|c1l@|fL3i=Z_rll#G3>CifNkiE2dVW3wW#8 zIU56>0y}*QdnndI?>X-8jlf39Id&_3PoP*TEMW7RwOVfdf8a+~A%PL^RKz9qTP?;< zQhujQTxOzReA$d>#(U8V+>w+pXTa!Rz&o+YzuE8)^z?%)bdEGf00000NkvXXu0mjf D;R05P literal 0 HcmV?d00001 diff --git a/save_load/assets/save-pressed.png.import b/save_load/assets/save-pressed.png.import new file mode 100644 index 0000000..8583721 --- /dev/null +++ b/save_load/assets/save-pressed.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://crqgyft4gfilt" +path="res://.godot/imported/save-pressed.png-49f5851a0548d3847310651a4ef477c2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://save_load/assets/save-pressed.png" +dest_files=["res://.godot/imported/save-pressed.png-49f5851a0548d3847310651a4ef477c2.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=1 diff --git a/save_load/assets/trash-hover.png b/save_load/assets/trash-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..a75b5e77b3640b88a22dcba55b76dc5f878a10d6 GIT binary patch literal 860 zcmV-i1Ec(jP)#Z3P?ALdQaF z3>J2mQY|h

w)6@y--_XyRASVkdB(v%9k&*$=#_b|3R*-hbYE^Zqlt1|gdTdw?O} zAh1qT?JMvScqU*v%=|_O!&d=gz%`&J;4r^{8^E}LpFsx51qD!mabOfMLoA>P90iz$ z=dpRff&%a=#LBBYv0uRJAS)209gom>tGn@id6Bq`X88p5L3>&gCZUW8#w-p`7P?M}2uuDGA0KWq;VK4mxa9tt41UwNi zZ?u*7)xe^Hp;`hSCb7g8Sipl0aSK=@;KyHeKnlJn#u-R3OjL7!UN&A;$QlZTe1ZT} zb2r>7aXkY7>TYy8kO$-@a0Gbgbx4<|xg9`RzsZ`Ym_bZAL6x_LzyCTcH8u zfDH-kKpRotTlvm`q>7o-oW64)saoJPr)LPYQVgI4d~yVkGCt(VgPeAOdWtvO#$jA-2|I)br46o$qq{q71x^Dy mfz2yq_($Lk_6NLK9fdy}0>xs){Ko140000X-U$cq_#W&+*WopSa0J?DLwCsL{Ac13JEcs09rHjNo!C? zi4>F2K?iRJ828ja3hZ3PJ6ulyuGePV%r1GR;6vb|$C))tp@yM#0P6r!l1VB8cYu?? zh9X6hgspPrM^#>0}<`JmG ztpQ2Lf?o0%n3OaeAv+9A2C~zEZrmEE#9h>1RRJ*J(CKyk{Nj+CNO1O0)+yAJF)#BJ z>XcN7Ig+R0{q=qU=*R*<3$P`J7iep6cj{*}uVxl@XY6M*uU-&#XVnx^0|OB7DHK41 z%|lh^ps-h<+`thi`#fWn1_n_1F94JqxCYi!sA&OIn}@RAg}vsWI5kl3c_?fSN?Zf= zF61tsF(NL2usZmWG0%GriUJU49vWTseI6Kd$c?e~7r@apb5K+feRZsIS3_irl13yg z1}k7+#JPQe%wnKZ43WF$xc`ewwl!kr-s}?rGphjSd>mtD&w>3}%|THsUEaN*=v$iw zpRFec0G(+8%sP0bIs$N|*-RV&Qx4t%N!RPpg>L}8o;G12hwKL4g#ercYVkil7DU+< z=@np)nZ1wWi3e&Nx)+5y48RWH1<*~tGz%Otv$;4mfSG*&P62Z{8-F^?c;8go9;DN9 z9aCmm(tSzYK8528WEap6>;<;f$m1V@xBU47o>#&C20aO_8DkH?LI3~&07*qoM6N<$ Ef}~Dq4*&oF literal 0 HcmV?d00001 diff --git a/save_load/assets/trash-normal.png.import b/save_load/assets/trash-normal.png.import new file mode 100644 index 0000000..37967e7 --- /dev/null +++ b/save_load/assets/trash-normal.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dvp5yeoqw36yt" +path="res://.godot/imported/trash-normal.png-9a70475e0bfa21e155c8f068d078dd58.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://save_load/assets/trash-normal.png" +dest_files=["res://.godot/imported/trash-normal.png-9a70475e0bfa21e155c8f068d078dd58.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=1 diff --git a/save_load/assets/trash-pressed.png b/save_load/assets/trash-pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..3a8c56e4f8dd6dae21ee0b83fbeacb3c1383225a GIT binary patch literal 768 zcmV+b1ONPqP) zz#{MtPqYh|2eyG;%C-&6sj+gW2N@KPwz<5OuV-9&$r2N36se>Ha0#e7T#%}Yqf~-v z=&Zoo09Fj;F91g)yz_Yikh0ARaKljkIdI)z%z?R3VQ2_o2*3f20S%z-Fs~1~wUy3e zsE1xjTO&|~)t-`iPpbl~m)QN+0cL=$61vY8FcT;wHQ+MvSU`CXcrWlrfK%#u4d_^| zo{4-NxG(T;0xy7X{mk7!S3r8zql;V<^yw;H|JeurD6>Rq+#>*K?w?9maU*NS>=Qos zD0DhT0fZ$05b6M=wLlG+RY7NRnwwQJnJo~dM?%oNN|+>?h8Hw_7ouq}VeBPApNF;w z^*0HVtO_!dG1o8Aeyb_Qo_Fm+6OMVAf>;7DnoxFE9P|3$)m#7=0rq>yKnK*^nbWAR z%`9$C>on?XFNm8n(S>>i2G9e(>Hrj}9!ji(;<7+kU<-sMX{=db0L}jbKv>`y7`jkt z0VLH!uF0qlvWbDb>!G+hD0K|v9mwM*WrQt&xHxD#<{8&PIsi8H(96j0U%;}^Z&|(5 z$y(JxI{G0Tl=(aHbcd27lQ*-aLFzXe=g;@elyQ+h9 zM>^kof$?=p#pKlE0l;Wh0G|b3Jx2hNn{C(tSQB_>fIB%Te;4^)Nz#cRW{~5+#~6Tb z8O(ot0%c{%(p}&L@X2PtXB6ja%#$=Lazw>~Z{;_ELut)A void: if event.is_action_pressed("quick_save"): - save_game() + quick_save() if event.is_action_pressed("quick_load"): - load_game() + quick_load() -func save_game() -> void: +func quick_save() -> void: var save_level_data_component: SaveLevelDataComponent = get_tree().get_first_node_in_group("save_level_data_component") - if save_level_data_component == null: push_error("Could not find SaveLevelDataComponent node in level") return - save_level_data_component.save_game() + save_level_data_component.quick_save_game() game_saved.emit() -func load_game() -> void: - EntityManager.reset_world.emit() - +func quick_load() -> void: var save_level_data_component: SaveLevelDataComponent = get_tree().get_first_node_in_group("save_level_data_component") - if save_level_data_component == null: push_error("Could not find SaveLevelDataComponent node in level") return - save_level_data_component.load_game() + # TODO: Don't reset world if quicksave not found + EntityManager.reset_world.emit() + save_level_data_component.quick_load_game() game_loaded.emit() diff --git a/save_load/components/save_level_data_component.gd b/save_load/components/save_level_data_component.gd index f7e8f5b..3f9280e 100644 --- a/save_load/components/save_level_data_component.gd +++ b/save_load/components/save_level_data_component.gd @@ -10,21 +10,61 @@ extends Node ## * Linux: ~/.local/share/godot/app_userdata/[project_name][br] @export var save_game_data_path: String = "user://game_data/" @export var save_file_name: String = "save_%s_game_data.tres" +@export var quicksave_file_name: String = "quicksave_%s_game_data.tres" var level_scene_name: String var game_data_resource: SaveGameDataResource -var level_save_file_name: String -var save_game_file_path: String +#var level_save_file_name: String +#var save_game_file_path: String func _ready() -> void: add_to_group("save_level_data_component") level_scene_name = get_parent().name - level_save_file_name = save_file_name % level_scene_name - save_game_file_path = save_game_data_path + level_save_file_name + +func list_saves() -> Array[SaveFileDetailsResource]: # TODO: Update hints + var save_files: Array[SaveFileDetailsResource] = [] + if !DirAccess.dir_exists_absolute(save_game_data_path): + return save_files + + for filename: String in ResourceLoader.list_directory(save_game_data_path): + if !filename.begins_with("save_"): continue + if !filename.ends_with(".tres"): continue # Screenshots, etc + + var _save_path: String = save_game_data_path + filename + var _save_icon: String = filename.replace(".tres", ".png") + var _save_resource: SaveFileDetailsResource = SaveFileDetailsResource.new() + var _loaded_file: FileAccess = FileAccess.open(_save_path, FileAccess.READ) + + _save_resource.filename = filename + _save_resource.date_created = Time.get_datetime_string_from_unix_time(FileAccess.get_modified_time(_save_path)) + _save_resource.filesize = _loaded_file.get_length() + + if FileAccess.file_exists(save_game_data_path + _save_icon): + _save_resource.save_icon = _save_icon + + save_files.append(_save_resource) + + return save_files + +func load_game() -> void: + _load_game_resource(save_file_name % level_scene_name) + +func quick_load_game() -> void: + # TODO: Change this to a dynamic quick save file + # This might be left as 01 (01 always the last quicksave) + _load_game_resource(quicksave_file_name % "01") + +func quick_save_game() -> void: + # TODO: Change this to a dynamic quick save file + _save_game_as_resource(quicksave_file_name % "01") + +func save_game() -> void: + _save_game_as_resource(save_file_name % level_scene_name) + func save_node_data() -> void: var nodes: Array = get_tree().get_nodes_in_group("save_data_component") @@ -38,17 +78,21 @@ func save_node_data() -> void: var save_final_resource: Node3DDataResource = save_data_resource.duplicate() game_data_resource.save_data_nodes.append(save_final_resource) -func save_game() -> void: + + +func _save_game_as_resource(resource_filename: String) -> void: if !DirAccess.dir_exists_absolute(save_game_data_path): DirAccess.make_dir_absolute(save_game_data_path) + var save_game_file_path: String = save_game_data_path + resource_filename save_node_data() var result: int = ResourceSaver.save(game_data_resource, save_game_file_path) if result != OK: printerr("Failed to save game: ", result) -func load_game() -> void: +func _load_game_resource(resource_filename: String) -> void: + var save_game_file_path: String = save_game_data_path + resource_filename if !FileAccess.file_exists(save_game_file_path): printerr("Failed to load save. File does not exist: ", save_game_file_path) return @@ -62,29 +106,3 @@ func load_game() -> void: for resource: Resource in game_data_resource.save_data_nodes: if resource is Node3DDataResource: (resource as Node3DDataResource)._load_data(root_node) - - -func list_saves() -> Array[SaveFileDetailsResource]: # TODO: Update hints - var save_files: Array[SaveFileDetailsResource] = [] - if !DirAccess.dir_exists_absolute(save_game_data_path): - return save_files - - for filename: String in ResourceLoader.list_directory(save_game_data_path): - if !filename.begins_with("save_"): continue - if !filename.ends_with(".tres"): continue # Screenshots, etc - - var _save_path: String = save_game_data_path + filename - var _save_icon: String = filename.replace(".tres", ".png") - var _save_resource: SaveFileDetailsResource = SaveFileDetailsResource.new() - var _loaded_file: FileAccess = FileAccess.open(_save_path, FileAccess.READ) - - _save_resource.filename = filename - _save_resource.date_created = Time.get_datetime_string_from_unix_time(FileAccess.get_modified_time(_save_path)) - _save_resource.filesize = _loaded_file.get_length() - - if FileAccess.file_exists(save_game_data_path + _save_icon): - _save_resource.save_icon = _save_icon - - save_files.append(_save_resource) - - return save_files diff --git a/save_load/resources/save_file_highlight_panel_theme.tres b/save_load/resources/save_file_highlight_panel_theme.tres new file mode 100644 index 0000000..083bbaa --- /dev/null +++ b/save_load/resources/save_file_highlight_panel_theme.tres @@ -0,0 +1,4 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://bwm315lqbbb87"] + +[resource] +bg_color = Color(0.728173, 0.579132, 0.164487, 1) diff --git a/save_load/resources/save_file_panel_theme.tres b/save_load/resources/save_file_panel_theme.tres new file mode 100644 index 0000000..7835864 --- /dev/null +++ b/save_load/resources/save_file_panel_theme.tres @@ -0,0 +1,8 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://biousyggn7iua"] + +[resource] +content_margin_left = 5.0 +content_margin_top = 5.0 +content_margin_right = 5.0 +content_margin_bottom = 5.0 +bg_color = Color(0, 0.65098, 0.886275, 0) diff --git a/save_load/ui/save_file.gd b/save_load/ui/save_file.gd new file mode 100644 index 0000000..976b6f8 --- /dev/null +++ b/save_load/ui/save_file.gd @@ -0,0 +1,63 @@ +class_name SaveFilePanel +extends Panel + + +@export var save_panel_highlight: StyleBoxFlat +@export var save_panel_normal: StyleBoxFlat + +@export_group("Node Exports") +@export var save_name_label: Label +@export var save_date_label: Label +@export var save_icon: TextureRect +@export var save_button: TextureButton +@export var load_button: TextureButton +@export var delete_button: TextureButton + +var save_file_details: SaveFileDetailsResource + + +func _ready() -> void: + mouse_entered.connect(_on_mouse_entered) + mouse_exited.connect(_on_mouse_exited) + + delete_button.pressed.connect(_on_delete_button_pressed) + save_button.pressed.connect(_on_save_button_pressed) + load_button.pressed.connect(_on_load_button_pressed) + + +func initialize(_resource: SaveFileDetailsResource) -> void: + save_file_details = _resource + set_save_date() + set_save_icon() + set_save_name() + +func set_save_date() -> void: + save_date_label.text = save_file_details.date_created + +func set_save_icon() -> void: + save_icon.texture = load(save_file_details.save_icon) + +func set_save_name() -> void: + # TODO: Remove extension from name + save_name_label.text = save_file_details.filename + + + +func _on_delete_button_pressed() -> void: + SaveGameManager.delete_save_file.emit(save_file_details.filename) + print("DELETING: ", save_file_details.filename) + +func _on_load_button_pressed() -> void: + SaveGameManager.load_save_file.emit(save_file_details.filename) + print("LOADING: ", save_file_details.filename) + +func _on_save_button_pressed() -> void: + SaveGameManager.create_save_file.emit(save_file_details.filename) + print("SAVING: ", save_file_details.filename) + + +func _on_mouse_entered() -> void: + set("theme_override_styles/panel", save_panel_highlight) + +func _on_mouse_exited() -> void: + set("theme_override_styles/panel", save_panel_normal) diff --git a/save_load/ui/save_file.gd.uid b/save_load/ui/save_file.gd.uid new file mode 100644 index 0000000..276bc14 --- /dev/null +++ b/save_load/ui/save_file.gd.uid @@ -0,0 +1 @@ +uid://dcfdyua5gwpw4 diff --git a/save_load/ui/save_file.tscn b/save_load/ui/save_file.tscn new file mode 100644 index 0000000..b3fcaa3 --- /dev/null +++ b/save_load/ui/save_file.tscn @@ -0,0 +1,100 @@ +[gd_scene load_steps=14 format=3 uid="uid://bb7poutsn4ex2"] + +[ext_resource type="Texture2D" uid="uid://dknv7amroftm8" path="res://assets/icon.svg" id="1_714lu"] +[ext_resource type="StyleBox" uid="uid://biousyggn7iua" path="res://save_load/resources/save_file_panel_theme.tres" id="1_cqw77"] +[ext_resource type="Texture2D" uid="uid://cmq51cgasug81" path="res://save_load/assets/folder-open-normal.png" id="1_k6haa"] +[ext_resource type="Script" uid="uid://dcfdyua5gwpw4" path="res://save_load/ui/save_file.gd" id="2_5g2eu"] +[ext_resource type="Texture2D" uid="uid://blyryo60jydgi" path="res://save_load/assets/folder-open-pressed.png" id="2_714lu"] +[ext_resource type="Texture2D" uid="uid://ja8bc1h5x85o" path="res://save_load/assets/save-normal.png" id="2_jgxci"] +[ext_resource type="Texture2D" uid="uid://crqgyft4gfilt" path="res://save_load/assets/save-pressed.png" id="3_cqw77"] +[ext_resource type="StyleBox" uid="uid://bwm315lqbbb87" path="res://save_load/resources/save_file_highlight_panel_theme.tres" id="3_om23c"] +[ext_resource type="Texture2D" uid="uid://by4w5ll3le7g6" path="res://save_load/assets/folder-open-hover.png" id="3_ubfnn"] +[ext_resource type="Texture2D" uid="uid://o3l0j53mgkan" path="res://save_load/assets/save-hover.png" id="4_5g2eu"] +[ext_resource type="Texture2D" uid="uid://dvp5yeoqw36yt" path="res://save_load/assets/trash-normal.png" id="4_c2bnc"] +[ext_resource type="Texture2D" uid="uid://brwa8yljyrlgy" path="res://save_load/assets/trash-pressed.png" id="5_jgxci"] +[ext_resource type="Texture2D" uid="uid://cmrtuy0i5qc01" path="res://save_load/assets/trash-hover.png" id="6_cqw77"] + +[node name="SaveFilePanel" type="Panel" node_paths=PackedStringArray("save_name_label", "save_date_label", "save_icon", "save_button", "load_button", "delete_button")] +custom_minimum_size = Vector2(420, 60) +offset_right = 420.0 +offset_bottom = 60.0 +theme_override_styles/panel = ExtResource("1_cqw77") +script = ExtResource("2_5g2eu") +save_panel_highlight = ExtResource("3_om23c") +save_panel_normal = ExtResource("1_cqw77") +save_name_label = NodePath("HBoxContainer/NameDate/SaveName") +save_date_label = NodePath("HBoxContainer/NameDate/SaveDate") +save_icon = NodePath("HBoxContainer/SaveFileIcon") +save_button = NodePath("HBoxContainer/Actions/SaveButton") +load_button = NodePath("HBoxContainer/Actions/LoadButton") +delete_button = NodePath("HBoxContainer/Actions/DeleteButton") + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +custom_minimum_size = Vector2(400, 0) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -200.0 +offset_top = -20.5 +offset_right = 200.0 +offset_bottom = 20.5 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 0 + +[node name="SaveFileIcon" type="TextureRect" parent="HBoxContainer"] +layout_mode = 2 +texture = ExtResource("1_714lu") +expand_mode = 3 + +[node name="NameDate" type="VBoxContainer" parent="HBoxContainer"] +layout_mode = 2 +size_flags_vertical = 8 + +[node name="SaveName" type="Label" parent="HBoxContainer/NameDate"] +layout_mode = 2 +text = "Save name #1" + +[node name="SaveDate" type="Label" parent="HBoxContainer/NameDate"] +layout_mode = 2 +theme_override_font_sizes/font_size = 10 +text = "2025/03/20 13:43:12" + +[node name="Actions" type="HBoxContainer" parent="HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 10 + +[node name="SaveButton" type="TextureButton" parent="HBoxContainer/Actions"] +clip_contents = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "Load Save" +texture_normal = ExtResource("2_jgxci") +texture_pressed = ExtResource("3_cqw77") +texture_hover = ExtResource("4_5g2eu") +ignore_texture_size = true +stretch_mode = 5 + +[node name="LoadButton" type="TextureButton" parent="HBoxContainer/Actions"] +clip_contents = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "Load Save" +texture_normal = ExtResource("1_k6haa") +texture_pressed = ExtResource("2_714lu") +texture_hover = ExtResource("3_ubfnn") +ignore_texture_size = true +stretch_mode = 5 + +[node name="DeleteButton" type="TextureButton" parent="HBoxContainer/Actions"] +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "Delete Save" +texture_normal = ExtResource("4_c2bnc") +texture_pressed = ExtResource("5_jgxci") +texture_hover = ExtResource("6_cqw77") +ignore_texture_size = true +stretch_mode = 5 diff --git a/save_load/ui/save_files_list.gd b/save_load/ui/save_files_list.gd new file mode 100644 index 0000000..382eb31 --- /dev/null +++ b/save_load/ui/save_files_list.gd @@ -0,0 +1,29 @@ +extends VBoxContainer + + +@export var save_file_scene: PackedScene + + +func _ready() -> void: + SaveGameManager.refresh_saves_list.connect(_on_refresh_saves_list) + refresh_saves_list() + +## Clear the SaveFilesList node of all saves and load most recent saves +func refresh_saves_list() -> void: + _clear_save_files_list() + + var save_level_data_component: SaveLevelDataComponent = get_tree().get_first_node_in_group("save_level_data_component") + var save_files: Array[SaveFileDetailsResource] = save_level_data_component.list_saves() + + for save_resource: SaveFileDetailsResource in save_files: + var _save_file: SaveFilePanel = save_file_scene.instantiate() + _save_file.initialize(save_resource) + add_child(_save_file) + + +func _clear_save_files_list() -> void: + for _panel: SaveFilePanel in get_children(): + _panel.queue_free() + +func _on_refresh_saves_list() -> void: + refresh_saves_list() diff --git a/save_load/ui/save_files_list.gd.uid b/save_load/ui/save_files_list.gd.uid new file mode 100644 index 0000000..ba9e57b --- /dev/null +++ b/save_load/ui/save_files_list.gd.uid @@ -0,0 +1 @@ +uid://cqabj86bq8whn diff --git a/save_load/ui/save_load_ui.tscn b/save_load/ui/save_load_ui.tscn new file mode 100644 index 0000000..2250372 --- /dev/null +++ b/save_load/ui/save_load_ui.tscn @@ -0,0 +1,50 @@ +[gd_scene load_steps=3 format=3 uid="uid://dauchkhmnyk7n"] + +[ext_resource type="Script" uid="uid://cqabj86bq8whn" path="res://save_load/ui/save_files_list.gd" id="1_t4pkj"] +[ext_resource type="PackedScene" uid="uid://bb7poutsn4ex2" path="res://save_load/ui/save_file.tscn" id="1_tqtxm"] + +[node name="SaveLoadUI" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 6 +size_flags_vertical = 4 + +[node name="Panel" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="MarginContainer" type="MarginContainer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="Panel/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "Load Save" +horizontal_alignment = 1 + +[node name="SaveFilesList" type="VBoxContainer" parent="Panel/MarginContainer/VBoxContainer"] +clip_contents = true +layout_mode = 2 +size_flags_horizontal = 4 +script = ExtResource("1_t4pkj") +save_file_scene = ExtResource("1_tqtxm") From b3284bc93346db1f0fe1bf0c7c83da1b01e125f3 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Tue, 25 Mar 2025 22:16:34 -0400 Subject: [PATCH 02/20] Creating basic autosave option (unused) --- save_load/autoloads/save_game_manager.gd | 1 + .../components/save_level_data_component.gd | 25 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/save_load/autoloads/save_game_manager.gd b/save_load/autoloads/save_game_manager.gd index 61400d0..f9ec5fa 100644 --- a/save_load/autoloads/save_game_manager.gd +++ b/save_load/autoloads/save_game_manager.gd @@ -10,6 +10,7 @@ extends Node signal game_saved signal game_loaded signal refresh_saves_list +signal create_auto_save_file signal create_save_file(filename: String) signal delete_save_file(filename: String) signal load_save_file(filename: String) diff --git a/save_load/components/save_level_data_component.gd b/save_load/components/save_level_data_component.gd index 3f9280e..b07feca 100644 --- a/save_load/components/save_level_data_component.gd +++ b/save_load/components/save_level_data_component.gd @@ -9,20 +9,22 @@ extends Node ## * macOS: ~/Library/Application Support/Godot/app_userdata/[project_name][br] ## * Linux: ~/.local/share/godot/app_userdata/[project_name][br] @export var save_game_data_path: String = "user://game_data/" -@export var save_file_name: String = "save_%s_game_data.tres" -@export var quicksave_file_name: String = "quicksave_%s_game_data.tres" +@export var max_auto_saves: int = 5 + +@export_group("Save File Names") +@export var save_file_name: String = "save_%s_game_data.tres" ## %s is the level name +@export var quicksave_file_name: String = "quicksave_game_data.tres" +@export var autosave_file_name: String = "autosave_%s_game_data.tres" ## %s is the save number (probably 01) var level_scene_name: String var game_data_resource: SaveGameDataResource -#var level_save_file_name: String -#var save_game_file_path: String func _ready() -> void: add_to_group("save_level_data_component") level_scene_name = get_parent().name - + SaveGameManager.create_auto_save_file.connect(_on_create_auto_save_file) func list_saves() -> Array[SaveFileDetailsResource]: # TODO: Update hints @@ -54,13 +56,10 @@ func load_game() -> void: _load_game_resource(save_file_name % level_scene_name) func quick_load_game() -> void: - # TODO: Change this to a dynamic quick save file - # This might be left as 01 (01 always the last quicksave) - _load_game_resource(quicksave_file_name % "01") + _load_game_resource(quicksave_file_name) func quick_save_game() -> void: - # TODO: Change this to a dynamic quick save file - _save_game_as_resource(quicksave_file_name % "01") + _save_game_as_resource(quicksave_file_name) func save_game() -> void: _save_game_as_resource(save_file_name % level_scene_name) @@ -79,7 +78,6 @@ func save_node_data() -> void: game_data_resource.save_data_nodes.append(save_final_resource) - func _save_game_as_resource(resource_filename: String) -> void: if !DirAccess.dir_exists_absolute(save_game_data_path): DirAccess.make_dir_absolute(save_game_data_path) @@ -106,3 +104,8 @@ func _load_game_resource(resource_filename: String) -> void: for resource: Resource in game_data_resource.save_data_nodes: if resource is Node3DDataResource: (resource as Node3DDataResource)._load_data(root_node) + + +func _on_create_auto_save_file() -> void: + # TODO: Check max number of autosaves, increment existing autosaves, etc + _save_game_as_resource(autosave_file_name % "01") From f81080eedc9dad4f6f812e175e5e096204b06e06 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 09:54:59 -0400 Subject: [PATCH 03/20] Using signal in SaveGameManager for quick saves and loads --- save_load/autoloads/save_game_manager.gd | 14 ++++++++++---- scenes/ui/menus/pause_menu.gd | 4 ++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/save_load/autoloads/save_game_manager.gd b/save_load/autoloads/save_game_manager.gd index f9ec5fa..f2fa944 100644 --- a/save_load/autoloads/save_game_manager.gd +++ b/save_load/autoloads/save_game_manager.gd @@ -14,16 +14,22 @@ signal create_auto_save_file signal create_save_file(filename: String) signal delete_save_file(filename: String) signal load_save_file(filename: String) +signal quick_save +signal quick_load +func _ready() -> void: + quick_load.connect(quick_load_game) + quick_save.connect(quick_load_game) + func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("quick_save"): - quick_save() + quick_save_game() if event.is_action_pressed("quick_load"): - quick_load() + quick_load_game() -func quick_save() -> void: +func quick_save_game() -> void: var save_level_data_component: SaveLevelDataComponent = get_tree().get_first_node_in_group("save_level_data_component") if save_level_data_component == null: push_error("Could not find SaveLevelDataComponent node in level") @@ -32,7 +38,7 @@ func quick_save() -> void: save_level_data_component.quick_save_game() game_saved.emit() -func quick_load() -> void: +func quick_load_game() -> void: var save_level_data_component: SaveLevelDataComponent = get_tree().get_first_node_in_group("save_level_data_component") if save_level_data_component == null: push_error("Could not find SaveLevelDataComponent node in level") diff --git a/scenes/ui/menus/pause_menu.gd b/scenes/ui/menus/pause_menu.gd index 97a1dee..5060614 100644 --- a/scenes/ui/menus/pause_menu.gd +++ b/scenes/ui/menus/pause_menu.gd @@ -24,11 +24,11 @@ func _on_resume_button_pressed() -> void: hide_menu() func _on_quick_load_pressed() -> void: - SaveGameManager.load_game() + SaveGameManager.quick_load.emit() hide_menu() func _on_quick_save_pressed() -> void: - SaveGameManager.save_game() + SaveGameManager.quick_save.emit() hide_menu() func _on_settings_button_pressed() -> void: From 6498e4ad3572ce076f5173719279753caad6c14e Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 09:56:01 -0400 Subject: [PATCH 04/20] Adding save name and removing icon from save file resource --- save_load/components/save_level_data_component.gd | 11 ++++++++++- save_load/resources/save_file_details_resource.gd | 1 + save_load/resources/save_game_data_resource.gd | 4 +--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/save_load/components/save_level_data_component.gd b/save_load/components/save_level_data_component.gd index b07feca..48075f7 100644 --- a/save_load/components/save_level_data_component.gd +++ b/save_load/components/save_level_data_component.gd @@ -39,10 +39,19 @@ func list_saves() -> Array[SaveFileDetailsResource]: # TODO: Update hints var _save_path: String = save_game_data_path + filename var _save_icon: String = filename.replace(".tres", ".png") var _save_resource: SaveFileDetailsResource = SaveFileDetailsResource.new() - var _loaded_file: FileAccess = FileAccess.open(_save_path, FileAccess.READ) + + # TODO: Reconsider loading the save to get the name + # This could be a large save making opening each one slow + # Should work out a better method for getting the save name (filename != save name) + var _save_file: SaveGameDataResource = ResourceLoader.load(_save_path) + _save_resource.save_name = _save_file.save_name _save_resource.filename = filename _save_resource.date_created = Time.get_datetime_string_from_unix_time(FileAccess.get_modified_time(_save_path)) + + ## In 4.5, this can probably be replaced with FileAccess.get_size(_save_path) + ## This should reduce the need for opening the file here + var _loaded_file: FileAccess = FileAccess.open(_save_path, FileAccess.READ) _save_resource.filesize = _loaded_file.get_length() if FileAccess.file_exists(save_game_data_path + _save_icon): diff --git a/save_load/resources/save_file_details_resource.gd b/save_load/resources/save_file_details_resource.gd index e56bc20..dd90d45 100644 --- a/save_load/resources/save_file_details_resource.gd +++ b/save_load/resources/save_file_details_resource.gd @@ -4,6 +4,7 @@ class_name SaveFileDetailsResource extends Node +@export var save_name: String = "My Save" @export var filename: String = "" @export var date_created: String = "" @export var filesize: int = 0 diff --git a/save_load/resources/save_game_data_resource.gd b/save_load/resources/save_game_data_resource.gd index 6836bd1..44210ff 100644 --- a/save_load/resources/save_game_data_resource.gd +++ b/save_load/resources/save_game_data_resource.gd @@ -7,6 +7,4 @@ extends Resource @export var game_version: String = ProjectSettings.get_setting("application/config/version") @export var save_data_nodes: Array[Node3DDataResource] -## Path to the screenshot/icon[br] -## This should generally be in the following format: e.g. user://game_data/save_my_save_name_screenshot.png -@export var save_icon: String +@export var save_name: String From 57b81c3604590349d3106a738a604aed27bde6a7 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 10:55:07 -0400 Subject: [PATCH 05/20] Moving saving/loading management into SaveGameManager --- save_load/autoloads/save_game_manager.gd | 115 ++++++++++++++++-- .../components/save_level_data_component.gd | 86 +------------ 2 files changed, 111 insertions(+), 90 deletions(-) diff --git a/save_load/autoloads/save_game_manager.gd b/save_load/autoloads/save_game_manager.gd index f2fa944..9c25816 100644 --- a/save_load/autoloads/save_game_manager.gd +++ b/save_load/autoloads/save_game_manager.gd @@ -18,9 +18,18 @@ signal quick_save signal quick_load +var _game_data_resource: SaveGameDataResource +var _save_level_data_component: SaveLevelDataComponent ## Contains the save paths and filenames + + func _ready() -> void: quick_load.connect(quick_load_game) quick_save.connect(quick_load_game) + create_save_file.connect(_save_game_as_resource) + load_save_file.connect(_load_game_resource) + + _load_save_level_data_component() + func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("quick_save"): @@ -29,22 +38,108 @@ func _unhandled_input(event: InputEvent) -> void: quick_load_game() +func list_saves() -> Array[SaveFileDetailsResource]: + var save_files: Array[SaveFileDetailsResource] = [] + if not _load_save_level_data_component(): + return save_files + + var save_game_data_path: String = _save_level_data_component.save_game_data_path + if !DirAccess.dir_exists_absolute(save_game_data_path): + return save_files + + for filename: String in ResourceLoader.list_directory(save_game_data_path): + if !filename.begins_with("save_"): continue + if !filename.ends_with(".tres"): continue # Screenshots, etc + + var _save_path: String = save_game_data_path + filename + var _save_icon: String = filename.replace(".tres", ".png") + var _save_resource: SaveFileDetailsResource = SaveFileDetailsResource.new() + + # TODO: Reconsider loading the save to get the name + # This could be a large save making opening each one slow + # Should work out a better method for getting the save name (filename != save name) + var _save_file: SaveGameDataResource = ResourceLoader.load(_save_path) + _save_resource.save_name = _save_file.save_name + + _save_resource.filename = filename + _save_resource.date_created = Time.get_datetime_string_from_unix_time(FileAccess.get_modified_time(_save_path)) + + ## In 4.5, this can probably be replaced with FileAccess.get_size(_save_path) + ## This should reduce the need for opening the file here + var _loaded_file: FileAccess = FileAccess.open(_save_path, FileAccess.READ) + _save_resource.filesize = _loaded_file.get_length() + + if FileAccess.file_exists(save_game_data_path + _save_icon): + _save_resource.save_icon = _save_icon + + save_files.append(_save_resource) + + return save_files + func quick_save_game() -> void: - var save_level_data_component: SaveLevelDataComponent = get_tree().get_first_node_in_group("save_level_data_component") - if save_level_data_component == null: - push_error("Could not find SaveLevelDataComponent node in level") - return + if not _load_save_level_data_component(): return - save_level_data_component.quick_save_game() + _save_game_as_resource(_save_level_data_component.quicksave_file_name) game_saved.emit() func quick_load_game() -> void: - var save_level_data_component: SaveLevelDataComponent = get_tree().get_first_node_in_group("save_level_data_component") - if save_level_data_component == null: - push_error("Could not find SaveLevelDataComponent node in level") - return + if not _load_save_level_data_component(): return # TODO: Don't reset world if quicksave not found EntityManager.reset_world.emit() - save_level_data_component.quick_load_game() + _load_game_resource(_save_level_data_component.quicksave_file_name) game_loaded.emit() + + +## Find the SaveLevelDataComponent within the level which stores the save paths and filenames +func _load_save_level_data_component() -> bool: + _save_level_data_component = get_tree().get_first_node_in_group("save_level_data_component") + if _save_level_data_component == null: + push_error("Could not find SaveLevelDataComponent node in level") + return false + + return true + +func _load_game_resource(resource_filename: String) -> void: + if not _load_save_level_data_component(): return + var save_game_file_path: String = _save_level_data_component.save_game_data_path + resource_filename + if !FileAccess.file_exists(save_game_file_path): + printerr("Failed to load save. File does not exist: ", save_game_file_path) + return + + _game_data_resource = ResourceLoader.load(save_game_file_path) + if _game_data_resource == null: + printerr("Failed to load save. Unknown format? ", save_game_file_path) + return + + var root_node: Window = get_tree().root + for resource: Resource in _game_data_resource.save_data_nodes: + if resource is Node3DDataResource: + (resource as Node3DDataResource)._load_data(root_node) + +func _save_game_as_resource(resource_filename: String) -> void: + if not _load_save_level_data_component(): return + + if !DirAccess.dir_exists_absolute(_save_level_data_component.save_game_data_path): + DirAccess.make_dir_absolute(_save_level_data_component.save_game_data_path) + + var save_game_file_path: String = _save_level_data_component.save_game_data_path + resource_filename + _save_node_data() + + var result: int = ResourceSaver.save(_game_data_resource, save_game_file_path) + if result != OK: + printerr("Failed to save game: ", result) + +## Save the properties defined on the SaveDataComponents attached to various nodes (such as Block) +func _save_node_data() -> void: + var nodes: Array = get_tree().get_nodes_in_group("save_data_component") + + if nodes == null: return + + _game_data_resource = SaveGameDataResource.new() + for node: Node in nodes: + if node is SaveDataComponent: + @warning_ignore("unsafe_method_access") + var save_data_resource: Node3DDataResource = node._save_data() + var save_final_resource: Node3DDataResource = save_data_resource.duplicate() + _game_data_resource.save_data_nodes.append(save_final_resource) diff --git a/save_load/components/save_level_data_component.gd b/save_load/components/save_level_data_component.gd index 48075f7..550e504 100644 --- a/save_load/components/save_level_data_component.gd +++ b/save_load/components/save_level_data_component.gd @@ -27,94 +27,20 @@ func _ready() -> void: SaveGameManager.create_auto_save_file.connect(_on_create_auto_save_file) -func list_saves() -> Array[SaveFileDetailsResource]: # TODO: Update hints - var save_files: Array[SaveFileDetailsResource] = [] - if !DirAccess.dir_exists_absolute(save_game_data_path): - return save_files - - for filename: String in ResourceLoader.list_directory(save_game_data_path): - if !filename.begins_with("save_"): continue - if !filename.ends_with(".tres"): continue # Screenshots, etc - - var _save_path: String = save_game_data_path + filename - var _save_icon: String = filename.replace(".tres", ".png") - var _save_resource: SaveFileDetailsResource = SaveFileDetailsResource.new() - - # TODO: Reconsider loading the save to get the name - # This could be a large save making opening each one slow - # Should work out a better method for getting the save name (filename != save name) - var _save_file: SaveGameDataResource = ResourceLoader.load(_save_path) - _save_resource.save_name = _save_file.save_name - - _save_resource.filename = filename - _save_resource.date_created = Time.get_datetime_string_from_unix_time(FileAccess.get_modified_time(_save_path)) - - ## In 4.5, this can probably be replaced with FileAccess.get_size(_save_path) - ## This should reduce the need for opening the file here - var _loaded_file: FileAccess = FileAccess.open(_save_path, FileAccess.READ) - _save_resource.filesize = _loaded_file.get_length() - - if FileAccess.file_exists(save_game_data_path + _save_icon): - _save_resource.save_icon = _save_icon - - save_files.append(_save_resource) - - return save_files - func load_game() -> void: - _load_game_resource(save_file_name % level_scene_name) + SaveGameManager.load_save_file.emit(save_file_name % level_scene_name) func quick_load_game() -> void: - _load_game_resource(quicksave_file_name) + SaveGameManager.quick_load.emit() func quick_save_game() -> void: - _save_game_as_resource(quicksave_file_name) + SaveGameManager.quick_save.emit() func save_game() -> void: - _save_game_as_resource(save_file_name % level_scene_name) - -func save_node_data() -> void: - var nodes: Array = get_tree().get_nodes_in_group("save_data_component") - - if nodes == null: return - - game_data_resource = SaveGameDataResource.new() - for node: Node in nodes: - if node is SaveDataComponent: - @warning_ignore("unsafe_method_access") - var save_data_resource: Node3DDataResource = node._save_data() - var save_final_resource: Node3DDataResource = save_data_resource.duplicate() - game_data_resource.save_data_nodes.append(save_final_resource) - - -func _save_game_as_resource(resource_filename: String) -> void: - if !DirAccess.dir_exists_absolute(save_game_data_path): - DirAccess.make_dir_absolute(save_game_data_path) - - var save_game_file_path: String = save_game_data_path + resource_filename - save_node_data() - - var result: int = ResourceSaver.save(game_data_resource, save_game_file_path) - if result != OK: - printerr("Failed to save game: ", result) - -func _load_game_resource(resource_filename: String) -> void: - var save_game_file_path: String = save_game_data_path + resource_filename - if !FileAccess.file_exists(save_game_file_path): - printerr("Failed to load save. File does not exist: ", save_game_file_path) - return - - game_data_resource = ResourceLoader.load(save_game_file_path) - if game_data_resource == null: - printerr("Failed to load save. Unknown format? ", save_game_file_path) - return - - var root_node: Window = get_tree().root - for resource: Resource in game_data_resource.save_data_nodes: - if resource is Node3DDataResource: - (resource as Node3DDataResource)._load_data(root_node) + SaveGameManager.create_save_file.emit(save_file_name % level_scene_name) func _on_create_auto_save_file() -> void: # TODO: Check max number of autosaves, increment existing autosaves, etc - _save_game_as_resource(autosave_file_name % "01") + #_save_game_as_resource(autosave_file_name % "01") + pass From 8aab061d36461e39f5f8a42b878afa69f55048e0 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 10:57:40 -0400 Subject: [PATCH 06/20] Displaying Save/Load UI from Pause Menu --- save_load/autoloads/save_game_manager.gd | 2 ++ save_load/ui/save_files_list.gd | 3 +-- save_load/ui/save_files_list.tscn | 10 ++++++++ save_load/ui/save_load_ui.tscn | 11 +++------ scenes/ui/menus/pause_menu.gd | 16 ++++++------- scenes/ui/menus/pause_menu.tscn | 30 ++++++++++++------------ scenes/ui/ui.gd | 16 +++++++++++++ scenes/ui/ui.tscn | 6 ++++- 8 files changed, 60 insertions(+), 34 deletions(-) create mode 100644 save_load/ui/save_files_list.tscn diff --git a/save_load/autoloads/save_game_manager.gd b/save_load/autoloads/save_game_manager.gd index 9c25816..107743d 100644 --- a/save_load/autoloads/save_game_manager.gd +++ b/save_load/autoloads/save_game_manager.gd @@ -16,6 +16,8 @@ signal delete_save_file(filename: String) signal load_save_file(filename: String) signal quick_save signal quick_load +signal open_save_list_ui +signal close_save_list_ui var _game_data_resource: SaveGameDataResource diff --git a/save_load/ui/save_files_list.gd b/save_load/ui/save_files_list.gd index 382eb31..0135c48 100644 --- a/save_load/ui/save_files_list.gd +++ b/save_load/ui/save_files_list.gd @@ -12,8 +12,7 @@ func _ready() -> void: func refresh_saves_list() -> void: _clear_save_files_list() - var save_level_data_component: SaveLevelDataComponent = get_tree().get_first_node_in_group("save_level_data_component") - var save_files: Array[SaveFileDetailsResource] = save_level_data_component.list_saves() + var save_files: Array[SaveFileDetailsResource] = SaveGameManager.list_saves() for save_resource: SaveFileDetailsResource in save_files: var _save_file: SaveFilePanel = save_file_scene.instantiate() diff --git a/save_load/ui/save_files_list.tscn b/save_load/ui/save_files_list.tscn new file mode 100644 index 0000000..136ebdf --- /dev/null +++ b/save_load/ui/save_files_list.tscn @@ -0,0 +1,10 @@ +[gd_scene load_steps=3 format=3 uid="uid://cyxieflejsggu"] + +[ext_resource type="Script" uid="uid://cqabj86bq8whn" path="res://save_load/ui/save_files_list.gd" id="1_sh1p7"] +[ext_resource type="PackedScene" uid="uid://bb7poutsn4ex2" path="res://save_load/ui/save_file.tscn" id="2_kb5u8"] + +[node name="SaveFilesList" type="VBoxContainer"] +clip_contents = true +size_flags_horizontal = 4 +script = ExtResource("1_sh1p7") +save_file_scene = ExtResource("2_kb5u8") diff --git a/save_load/ui/save_load_ui.tscn b/save_load/ui/save_load_ui.tscn index 2250372..429c896 100644 --- a/save_load/ui/save_load_ui.tscn +++ b/save_load/ui/save_load_ui.tscn @@ -1,7 +1,6 @@ -[gd_scene load_steps=3 format=3 uid="uid://dauchkhmnyk7n"] +[gd_scene load_steps=2 format=3 uid="uid://dauchkhmnyk7n"] -[ext_resource type="Script" uid="uid://cqabj86bq8whn" path="res://save_load/ui/save_files_list.gd" id="1_t4pkj"] -[ext_resource type="PackedScene" uid="uid://bb7poutsn4ex2" path="res://save_load/ui/save_file.tscn" id="1_tqtxm"] +[ext_resource type="PackedScene" uid="uid://cyxieflejsggu" path="res://save_load/ui/save_files_list.tscn" id="1_tqtxm"] [node name="SaveLoadUI" type="Control"] layout_mode = 3 @@ -42,9 +41,5 @@ theme_override_font_sizes/font_size = 40 text = "Load Save" horizontal_alignment = 1 -[node name="SaveFilesList" type="VBoxContainer" parent="Panel/MarginContainer/VBoxContainer"] -clip_contents = true +[node name="SaveFilesList" parent="Panel/MarginContainer/VBoxContainer" instance=ExtResource("1_tqtxm")] layout_mode = 2 -size_flags_horizontal = 4 -script = ExtResource("1_t4pkj") -save_file_scene = ExtResource("1_tqtxm") diff --git a/scenes/ui/menus/pause_menu.gd b/scenes/ui/menus/pause_menu.gd index 5060614..c95c6cc 100644 --- a/scenes/ui/menus/pause_menu.gd +++ b/scenes/ui/menus/pause_menu.gd @@ -23,13 +23,13 @@ func _on_exit_game_button_pressed() -> void: func _on_resume_button_pressed() -> void: hide_menu() -func _on_quick_load_pressed() -> void: - SaveGameManager.quick_load.emit() - hide_menu() - -func _on_quick_save_pressed() -> void: - SaveGameManager.quick_save.emit() - hide_menu() - func _on_settings_button_pressed() -> void: SignalManager.open_settings_menu.emit() + + +func _on_load_game_pressed() -> void: + SaveGameManager.open_save_list_ui.emit() + +func _on_save_game_pressed() -> void: + # TODO + SaveGameManager.open_save_list_ui.emit() diff --git a/scenes/ui/menus/pause_menu.tscn b/scenes/ui/menus/pause_menu.tscn index 4e357f3..949adb3 100644 --- a/scenes/ui/menus/pause_menu.tscn +++ b/scenes/ui/menus/pause_menu.tscn @@ -36,45 +36,45 @@ theme_override_constants/margin_top = 10 theme_override_constants/margin_right = 10 theme_override_constants/margin_bottom = 10 -[node name="CenterContainer" type="VBoxContainer" parent="Background/MarginContainer"] +[node name="MenuContainer" type="VBoxContainer" parent="Background/MarginContainer"] layout_mode = 2 size_flags_horizontal = 4 theme_override_constants/separation = 20 -[node name="Title" type="Label" parent="Background/MarginContainer/CenterContainer"] +[node name="Title" type="Label" parent="Background/MarginContainer/MenuContainer"] layout_mode = 2 theme_override_font_sizes/font_size = 40 text = "Paused" horizontal_alignment = 1 vertical_alignment = 1 -[node name="ButtonsContainer" type="VBoxContainer" parent="Background/MarginContainer/CenterContainer"] +[node name="ButtonsContainer" type="VBoxContainer" parent="Background/MarginContainer/MenuContainer"] layout_mode = 2 size_flags_vertical = 6 theme_override_constants/separation = 20 -[node name="ResumeButton" type="Button" parent="Background/MarginContainer/CenterContainer/ButtonsContainer"] +[node name="ResumeButton" type="Button" parent="Background/MarginContainer/MenuContainer/ButtonsContainer"] layout_mode = 2 text = "Resume" -[node name="QuickSave" type="Button" parent="Background/MarginContainer/CenterContainer/ButtonsContainer"] +[node name="SaveGame" type="Button" parent="Background/MarginContainer/MenuContainer/ButtonsContainer"] layout_mode = 2 -text = "Quick Save" +text = "Save Game" -[node name="QuickLoad" type="Button" parent="Background/MarginContainer/CenterContainer/ButtonsContainer"] +[node name="LoadGame" type="Button" parent="Background/MarginContainer/MenuContainer/ButtonsContainer"] layout_mode = 2 -text = "Quick Load" +text = "Load Game" -[node name="SettingsButton" type="Button" parent="Background/MarginContainer/CenterContainer/ButtonsContainer"] +[node name="SettingsButton" type="Button" parent="Background/MarginContainer/MenuContainer/ButtonsContainer"] layout_mode = 2 text = "Settings" -[node name="ExitGameButton" type="Button" parent="Background/MarginContainer/CenterContainer/ButtonsContainer"] +[node name="ExitGameButton" type="Button" parent="Background/MarginContainer/MenuContainer/ButtonsContainer"] layout_mode = 2 text = "Exit Game" -[connection signal="pressed" from="Background/MarginContainer/CenterContainer/ButtonsContainer/ResumeButton" to="." method="_on_resume_button_pressed"] -[connection signal="pressed" from="Background/MarginContainer/CenterContainer/ButtonsContainer/QuickSave" to="." method="_on_quick_save_pressed"] -[connection signal="pressed" from="Background/MarginContainer/CenterContainer/ButtonsContainer/QuickLoad" to="." method="_on_quick_load_pressed"] -[connection signal="pressed" from="Background/MarginContainer/CenterContainer/ButtonsContainer/SettingsButton" to="." method="_on_settings_button_pressed"] -[connection signal="pressed" from="Background/MarginContainer/CenterContainer/ButtonsContainer/ExitGameButton" to="." method="_on_exit_game_button_pressed"] +[connection signal="pressed" from="Background/MarginContainer/MenuContainer/ButtonsContainer/ResumeButton" to="." method="_on_resume_button_pressed"] +[connection signal="pressed" from="Background/MarginContainer/MenuContainer/ButtonsContainer/SaveGame" to="." method="_on_save_game_pressed"] +[connection signal="pressed" from="Background/MarginContainer/MenuContainer/ButtonsContainer/LoadGame" to="." method="_on_load_game_pressed"] +[connection signal="pressed" from="Background/MarginContainer/MenuContainer/ButtonsContainer/SettingsButton" to="." method="_on_settings_button_pressed"] +[connection signal="pressed" from="Background/MarginContainer/MenuContainer/ButtonsContainer/ExitGameButton" to="." method="_on_exit_game_button_pressed"] diff --git a/scenes/ui/ui.gd b/scenes/ui/ui.gd index 1f201a8..4f330eb 100644 --- a/scenes/ui/ui.gd +++ b/scenes/ui/ui.gd @@ -6,6 +6,7 @@ extends CanvasLayer @onready var inventory: Inventory = $Inventory @onready var quick_slots: MarginContainer = $QuickSlots @onready var pause_menu: PauseMenu = $PauseMenu +@onready var save_load_ui: Control = $SaveLoadUI @onready var settings_menu: SettingsMenu = $SettingsMenu @onready var waila: Waila = $Waila @@ -16,6 +17,8 @@ func _ready() -> void: SignalManager.close_settings_menu.connect(_on_close_settings_menu) SignalManager.open_settings_menu.connect(_on_open_settings_menu) SignalManager.resume_game.connect(_on_resume_game) + SaveGameManager.close_save_list_ui.connect(_on_close_save_list_ui) + SaveGameManager.open_save_list_ui.connect(_on_open_save_list_ui) _on_resume_game() @@ -24,6 +27,7 @@ func hide_menus() -> void: pause_menu.visible = false settings_menu.visible = false inventory.visible = false + save_load_ui.visible = false func hide_ui_elements() -> void: crosshair.visible = false @@ -34,6 +38,9 @@ func hide_ui_elements() -> void: func _on_close_pause_menu() -> void: SignalManager.resume_game.emit() +func _on_close_save_list_ui() -> void: + SignalManager.open_settings_menu.emit() + func _on_close_settings_menu() -> void: SignalManager.resume_game.emit() @@ -46,6 +53,15 @@ func _on_open_pause_menu() -> void: get_tree().paused = true +func _on_open_save_list_ui() -> void: + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + + hide_ui_elements() + hide_menus() + save_load_ui.visible = true + + get_tree().paused = true + func _on_open_settings_menu() -> void: Input.mouse_mode = Input.MOUSE_MODE_VISIBLE diff --git a/scenes/ui/ui.tscn b/scenes/ui/ui.tscn index c69a58d..a6f3d5e 100644 --- a/scenes/ui/ui.tscn +++ b/scenes/ui/ui.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=3 uid="uid://c7fj7wla8bd70"] +[gd_scene load_steps=9 format=3 uid="uid://c7fj7wla8bd70"] [ext_resource type="Script" uid="uid://bslimr2y4lnvq" path="res://scenes/ui/ui.gd" id="1_aac20"] [ext_resource type="PackedScene" uid="uid://dvogu3djluqsn" path="res://scenes/ui/waila.tscn" id="1_u7n8c"] @@ -7,6 +7,7 @@ [ext_resource type="PackedScene" uid="uid://bopvfwcgnnawg" path="res://scenes/ui/menus/pause_menu.tscn" id="6_7vp6q"] [ext_resource type="Resource" uid="uid://blfp6tiir282o" path="res://resources/inventory/player_inventory_testing.tres" id="6_pfayw"] [ext_resource type="PackedScene" uid="uid://4bdgwwx27m71" path="res://scenes/ui/menus/settings_menu.tscn" id="7_7vp6q"] +[ext_resource type="PackedScene" uid="uid://dauchkhmnyk7n" path="res://save_load/ui/save_load_ui.tscn" id="8_jcn1r"] [node name="UI" type="CanvasLayer"] script = ExtResource("1_aac20") @@ -41,3 +42,6 @@ visible = false [node name="SettingsMenu" parent="." instance=ExtResource("7_7vp6q")] visible = false + +[node name="SaveLoadUI" parent="." instance=ExtResource("8_jcn1r")] +visible = false From 81592b036854aca0853ad1a461c51ce068880803 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 10:58:30 -0400 Subject: [PATCH 07/20] Removing unnused inventory scene from UI for now --- scenes/ui/ui.gd | 2 -- scenes/ui/ui.tscn | 8 +------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/scenes/ui/ui.gd b/scenes/ui/ui.gd index 4f330eb..ea9b1db 100644 --- a/scenes/ui/ui.gd +++ b/scenes/ui/ui.gd @@ -3,7 +3,6 @@ extends CanvasLayer @onready var crosshair: CenterContainer = $Crosshair -@onready var inventory: Inventory = $Inventory @onready var quick_slots: MarginContainer = $QuickSlots @onready var pause_menu: PauseMenu = $PauseMenu @onready var save_load_ui: Control = $SaveLoadUI @@ -26,7 +25,6 @@ func _ready() -> void: func hide_menus() -> void: pause_menu.visible = false settings_menu.visible = false - inventory.visible = false save_load_ui.visible = false func hide_ui_elements() -> void: diff --git a/scenes/ui/ui.tscn b/scenes/ui/ui.tscn index a6f3d5e..36fb119 100644 --- a/scenes/ui/ui.tscn +++ b/scenes/ui/ui.tscn @@ -1,11 +1,9 @@ -[gd_scene load_steps=9 format=3 uid="uid://c7fj7wla8bd70"] +[gd_scene load_steps=7 format=3 uid="uid://c7fj7wla8bd70"] [ext_resource type="Script" uid="uid://bslimr2y4lnvq" path="res://scenes/ui/ui.gd" id="1_aac20"] [ext_resource type="PackedScene" uid="uid://dvogu3djluqsn" path="res://scenes/ui/waila.tscn" id="1_u7n8c"] [ext_resource type="PackedScene" uid="uid://cbiygbgpfk220" path="res://scenes/ui/quick_slots.tscn" id="4_g5kmx"] -[ext_resource type="PackedScene" uid="uid://dcr25y1lw4wjp" path="res://scenes/ui/inventory/inventory.tscn" id="5_0dwhk"] [ext_resource type="PackedScene" uid="uid://bopvfwcgnnawg" path="res://scenes/ui/menus/pause_menu.tscn" id="6_7vp6q"] -[ext_resource type="Resource" uid="uid://blfp6tiir282o" path="res://resources/inventory/player_inventory_testing.tres" id="6_pfayw"] [ext_resource type="PackedScene" uid="uid://4bdgwwx27m71" path="res://scenes/ui/menus/settings_menu.tscn" id="7_7vp6q"] [ext_resource type="PackedScene" uid="uid://dauchkhmnyk7n" path="res://save_load/ui/save_load_ui.tscn" id="8_jcn1r"] @@ -33,10 +31,6 @@ mouse_filter = 2 [node name="QuickSlots" parent="." instance=ExtResource("4_g5kmx")] -[node name="Inventory" parent="." instance=ExtResource("5_0dwhk")] -visible = false -inventory_resource = ExtResource("6_pfayw") - [node name="PauseMenu" parent="." instance=ExtResource("6_7vp6q")] visible = false From 42e6aff24aca268d0df0a2b29f493dab3af0f06b Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 11:04:25 -0400 Subject: [PATCH 08/20] Set the save name to the filename if name doesn't exist --- save_load/ui/save_file.gd | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/save_load/ui/save_file.gd b/save_load/ui/save_file.gd index 976b6f8..35e0aec 100644 --- a/save_load/ui/save_file.gd +++ b/save_load/ui/save_file.gd @@ -38,8 +38,9 @@ func set_save_icon() -> void: save_icon.texture = load(save_file_details.save_icon) func set_save_name() -> void: - # TODO: Remove extension from name - save_name_label.text = save_file_details.filename + save_name_label.text = save_file_details.save_name + if save_file_details.save_name == null or save_file_details.save_name == "": + save_name_label.text = save_file_details.filename From 1d3516060020806f586c236b99f9671e97dbff33 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 11:09:13 -0400 Subject: [PATCH 09/20] Removing unnecessary load --- save_load/autoloads/save_game_manager.gd | 3 --- 1 file changed, 3 deletions(-) diff --git a/save_load/autoloads/save_game_manager.gd b/save_load/autoloads/save_game_manager.gd index 107743d..be26baf 100644 --- a/save_load/autoloads/save_game_manager.gd +++ b/save_load/autoloads/save_game_manager.gd @@ -30,9 +30,6 @@ func _ready() -> void: create_save_file.connect(_save_game_as_resource) load_save_file.connect(_load_game_resource) - _load_save_level_data_component() - - func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("quick_save"): quick_save_game() From 7e40ded4b2fcf8d482e2e47be886986c3b6127c9 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 11:15:58 -0400 Subject: [PATCH 10/20] Don't pause save/load menu ui --- save_load/ui/save_load_ui.tscn | 1 + 1 file changed, 1 insertion(+) diff --git a/save_load/ui/save_load_ui.tscn b/save_load/ui/save_load_ui.tscn index 429c896..4b1d13d 100644 --- a/save_load/ui/save_load_ui.tscn +++ b/save_load/ui/save_load_ui.tscn @@ -3,6 +3,7 @@ [ext_resource type="PackedScene" uid="uid://cyxieflejsggu" path="res://save_load/ui/save_files_list.tscn" id="1_tqtxm"] [node name="SaveLoadUI" type="Control"] +process_mode = 3 layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 From e62d24431d7503b60f0efda1da7dc7e59e01af93 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 11:45:42 -0400 Subject: [PATCH 11/20] Moving most logic and settings out of SaveDataComponent into SaveGameManager and SaveGameSettingsResource --- save_load/autoloads/save_game_manager.gd | 14 +++---- .../components/save_level_data_component.gd | 42 ++----------------- .../components/save_level_data_component.tscn | 4 +- .../resources/save_game_settings_resource.gd | 15 +++++++ .../save_game_settings_resource.gd.uid | 1 + .../save_game_settings_resource.tres | 12 ++++++ 6 files changed, 41 insertions(+), 47 deletions(-) create mode 100644 save_load/resources/save_game_settings_resource.gd create mode 100644 save_load/resources/save_game_settings_resource.gd.uid create mode 100644 save_load/resources/save_game_settings_resource.tres diff --git a/save_load/autoloads/save_game_manager.gd b/save_load/autoloads/save_game_manager.gd index be26baf..f698bfe 100644 --- a/save_load/autoloads/save_game_manager.gd +++ b/save_load/autoloads/save_game_manager.gd @@ -42,7 +42,7 @@ func list_saves() -> Array[SaveFileDetailsResource]: if not _load_save_level_data_component(): return save_files - var save_game_data_path: String = _save_level_data_component.save_game_data_path + var save_game_data_path: String = _save_level_data_component.settings.save_game_data_path if !DirAccess.dir_exists_absolute(save_game_data_path): return save_files @@ -78,7 +78,7 @@ func list_saves() -> Array[SaveFileDetailsResource]: func quick_save_game() -> void: if not _load_save_level_data_component(): return - _save_game_as_resource(_save_level_data_component.quicksave_file_name) + _save_game_as_resource(_save_level_data_component.settings.quicksave_file_name) game_saved.emit() func quick_load_game() -> void: @@ -86,7 +86,7 @@ func quick_load_game() -> void: # TODO: Don't reset world if quicksave not found EntityManager.reset_world.emit() - _load_game_resource(_save_level_data_component.quicksave_file_name) + _load_game_resource(_save_level_data_component.settings.quicksave_file_name) game_loaded.emit() @@ -101,7 +101,7 @@ func _load_save_level_data_component() -> bool: func _load_game_resource(resource_filename: String) -> void: if not _load_save_level_data_component(): return - var save_game_file_path: String = _save_level_data_component.save_game_data_path + resource_filename + var save_game_file_path: String = _save_level_data_component.settings.save_game_data_path + resource_filename if !FileAccess.file_exists(save_game_file_path): printerr("Failed to load save. File does not exist: ", save_game_file_path) return @@ -119,10 +119,10 @@ func _load_game_resource(resource_filename: String) -> void: func _save_game_as_resource(resource_filename: String) -> void: if not _load_save_level_data_component(): return - if !DirAccess.dir_exists_absolute(_save_level_data_component.save_game_data_path): - DirAccess.make_dir_absolute(_save_level_data_component.save_game_data_path) + if !DirAccess.dir_exists_absolute(_save_level_data_component.settings.save_game_data_path): + DirAccess.make_dir_absolute(_save_level_data_component.settings.save_game_data_path) - var save_game_file_path: String = _save_level_data_component.save_game_data_path + resource_filename + var save_game_file_path: String = _save_level_data_component.settings.save_game_data_path + resource_filename _save_node_data() var result: int = ResourceSaver.save(_game_data_resource, save_game_file_path) diff --git a/save_load/components/save_level_data_component.gd b/save_load/components/save_level_data_component.gd index 550e504..10ccc4b 100644 --- a/save_load/components/save_level_data_component.gd +++ b/save_load/components/save_level_data_component.gd @@ -1,46 +1,10 @@ -## Performs the actual saving and loading of data related to this level/scene -## Utilized by the SaveGameManager +## Provides an easy reference for save settings through an export rather than code +## Should be attached to the main world/level scene class_name SaveLevelDataComponent extends Node -## See documentation to where this path is: https://docs.godotengine.org/en/stable/tutorials/io/data_paths.html#accessing-persistent-user-data-user[br][br] -## Default Paths:[br] -## * Windows: %APPDATA%\Godot\app_userdata\[project_name][br] -## * macOS: ~/Library/Application Support/Godot/app_userdata/[project_name][br] -## * Linux: ~/.local/share/godot/app_userdata/[project_name][br] -@export var save_game_data_path: String = "user://game_data/" -@export var max_auto_saves: int = 5 - -@export_group("Save File Names") -@export var save_file_name: String = "save_%s_game_data.tres" ## %s is the level name -@export var quicksave_file_name: String = "quicksave_game_data.tres" -@export var autosave_file_name: String = "autosave_%s_game_data.tres" ## %s is the save number (probably 01) - -var level_scene_name: String -var game_data_resource: SaveGameDataResource +@export var settings: SaveGameSettings ## The SaveGameSettings resource func _ready() -> void: add_to_group("save_level_data_component") - level_scene_name = get_parent().name - - SaveGameManager.create_auto_save_file.connect(_on_create_auto_save_file) - - -func load_game() -> void: - SaveGameManager.load_save_file.emit(save_file_name % level_scene_name) - -func quick_load_game() -> void: - SaveGameManager.quick_load.emit() - -func quick_save_game() -> void: - SaveGameManager.quick_save.emit() - -func save_game() -> void: - SaveGameManager.create_save_file.emit(save_file_name % level_scene_name) - - -func _on_create_auto_save_file() -> void: - # TODO: Check max number of autosaves, increment existing autosaves, etc - #_save_game_as_resource(autosave_file_name % "01") - pass diff --git a/save_load/components/save_level_data_component.tscn b/save_load/components/save_level_data_component.tscn index ccdc1d0..570dc3a 100644 --- a/save_load/components/save_level_data_component.tscn +++ b/save_load/components/save_level_data_component.tscn @@ -1,6 +1,8 @@ -[gd_scene load_steps=2 format=3 uid="uid://c3pqilb6yh5kc"] +[gd_scene load_steps=3 format=3 uid="uid://c3pqilb6yh5kc"] [ext_resource type="Script" uid="uid://c7x2qvyu62230" path="res://save_load/components/save_level_data_component.gd" id="1_exguq"] +[ext_resource type="Resource" uid="uid://o32fooj1lxg7" path="res://save_load/resources/save_game_settings_resource.tres" id="2_rkr1f"] [node name="SaveLevelDataComponent" type="Node"] script = ExtResource("1_exguq") +settings = ExtResource("2_rkr1f") diff --git a/save_load/resources/save_game_settings_resource.gd b/save_load/resources/save_game_settings_resource.gd new file mode 100644 index 0000000..4aca6b5 --- /dev/null +++ b/save_load/resources/save_game_settings_resource.gd @@ -0,0 +1,15 @@ +class_name SaveGameSettings +extends Resource + + +## See documentation to where this path is: https://docs.godotengine.org/en/stable/tutorials/io/data_paths.html#accessing-persistent-user-data-user[br][br] +## Default Paths:[br] +## * Windows: %APPDATA%\Godot\app_userdata\[project_name][br] +## * macOS: ~/Library/Application Support/Godot/app_userdata/[project_name][br] +## * Linux: ~/.local/share/godot/app_userdata/[project_name][br] +@export var save_game_data_path: String = "user://game_data/" +@export var max_auto_saves: int = 5 + +@export var save_file_name: String = "save_game_data.tres" +@export var quicksave_file_name: String = "quicksave_game_data.tres" +@export var autosave_file_name: String = "autosave_%s_game_data.tres" ## %s is the save number (probably 01) diff --git a/save_load/resources/save_game_settings_resource.gd.uid b/save_load/resources/save_game_settings_resource.gd.uid new file mode 100644 index 0000000..7d1ede0 --- /dev/null +++ b/save_load/resources/save_game_settings_resource.gd.uid @@ -0,0 +1 @@ +uid://d0iptf06t7f47 diff --git a/save_load/resources/save_game_settings_resource.tres b/save_load/resources/save_game_settings_resource.tres new file mode 100644 index 0000000..0e1f12d --- /dev/null +++ b/save_load/resources/save_game_settings_resource.tres @@ -0,0 +1,12 @@ +[gd_resource type="Resource" script_class="SaveGameSettings" load_steps=2 format=3 uid="uid://o32fooj1lxg7"] + +[ext_resource type="Script" uid="uid://d0iptf06t7f47" path="res://save_load/resources/save_game_settings_resource.gd" id="1_o1tpj"] + +[resource] +script = ExtResource("1_o1tpj") +save_game_data_path = "user://game_data/" +max_auto_saves = 5 +save_file_name = "save_%s_game_data.tres" +quicksave_file_name = "quicksave_game_data.tres" +autosave_file_name = "autosave_%s_game_data.tres" +metadata/_custom_type_script = "uid://d0iptf06t7f47" From f736f36ec6c5f8e0bc3f548f451963063337e7ea Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 12:50:45 -0400 Subject: [PATCH 12/20] Updating README --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5c5d36e..2800bc3 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ Building a Minecraft clone of sorts to learn Godot. -This was initially based on Sable Spirit Studio's YT series but has evolved to be quite a bit different. Very little of the original code exists (outside `DBItems`). - ![Skyblock Screenshot](assets/skyblock-screenshot.png) @@ -24,3 +22,9 @@ This was initially based on Sable Spirit Studio's YT series but has evolved to b | Quick Load | `F6` | | Quick Save | `F5` | | Sprint/Run | `Shift` | + + +## Acknowledgements + +* [Sable Spirit Studios](https://www.youtube.com/@SableSpiritStudio) for the inspiration and ideas +* [Tabler](https://tabler.io) for UI icons From 038162f581caaea0bf44d13861243367aaaabdd4 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 13:15:01 -0400 Subject: [PATCH 13/20] Add Quick Save name and allow listing of Autosaves and Quicksaves --- save_load/autoloads/save_game_manager.gd | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/save_load/autoloads/save_game_manager.gd b/save_load/autoloads/save_game_manager.gd index f698bfe..d1da3f5 100644 --- a/save_load/autoloads/save_game_manager.gd +++ b/save_load/autoloads/save_game_manager.gd @@ -20,7 +20,7 @@ signal open_save_list_ui signal close_save_list_ui -var _game_data_resource: SaveGameDataResource +var _game_data_resource: SaveGameDataResource = SaveGameDataResource.new() var _save_level_data_component: SaveLevelDataComponent ## Contains the save paths and filenames @@ -37,7 +37,7 @@ func _unhandled_input(event: InputEvent) -> void: quick_load_game() -func list_saves() -> Array[SaveFileDetailsResource]: +func list_saves(include_quick_saves: bool = true, include_auto_saves: bool = true) -> Array[SaveFileDetailsResource]: var save_files: Array[SaveFileDetailsResource] = [] if not _load_save_level_data_component(): return save_files @@ -47,8 +47,10 @@ func list_saves() -> Array[SaveFileDetailsResource]: return save_files for filename: String in ResourceLoader.list_directory(save_game_data_path): - if !filename.begins_with("save_"): continue - if !filename.ends_with(".tres"): continue # Screenshots, etc + # TODO: Rework so the settings determine the file_name prepends + if filename.begins_with("quicksave_") and not include_quick_saves: continue + elif filename.begins_with("autosave_") and not include_auto_saves: continue + elif !filename.begins_with("save_") and !filename.begins_with("quicksave_") and !filename.begins_with("autosave_"): continue var _save_path: String = save_game_data_path + filename var _save_icon: String = filename.replace(".tres", ".png") @@ -78,6 +80,7 @@ func list_saves() -> Array[SaveFileDetailsResource]: func quick_save_game() -> void: if not _load_save_level_data_component(): return + _game_data_resource.save_name = "Quick Save" _save_game_as_resource(_save_level_data_component.settings.quicksave_file_name) game_saved.emit() @@ -135,7 +138,6 @@ func _save_node_data() -> void: if nodes == null: return - _game_data_resource = SaveGameDataResource.new() for node: Node in nodes: if node is SaveDataComponent: @warning_ignore("unsafe_method_access") From 0ede9891d91878518331c89673b8fe32d6758de0 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 15:12:12 -0400 Subject: [PATCH 14/20] Create save with just save name (not filename) --- save_load/autoloads/save_game_manager.gd | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/save_load/autoloads/save_game_manager.gd b/save_load/autoloads/save_game_manager.gd index d1da3f5..9d69135 100644 --- a/save_load/autoloads/save_game_manager.gd +++ b/save_load/autoloads/save_game_manager.gd @@ -11,7 +11,7 @@ signal game_saved signal game_loaded signal refresh_saves_list signal create_auto_save_file -signal create_save_file(filename: String) +signal create_save_file(save_name: String) signal delete_save_file(filename: String) signal load_save_file(filename: String) signal quick_save @@ -27,7 +27,7 @@ var _save_level_data_component: SaveLevelDataComponent ## Contains the save path func _ready() -> void: quick_load.connect(quick_load_game) quick_save.connect(quick_load_game) - create_save_file.connect(_save_game_as_resource) + create_save_file.connect(_on_save_game_as_resource) load_save_file.connect(_load_game_resource) func _unhandled_input(event: InputEvent) -> void: @@ -130,7 +130,7 @@ func _save_game_as_resource(resource_filename: String) -> void: var result: int = ResourceSaver.save(_game_data_resource, save_game_file_path) if result != OK: - printerr("Failed to save game: ", result) + printerr("Failed to save game (" , result, "): ", save_game_file_path) ## Save the properties defined on the SaveDataComponents attached to various nodes (such as Block) func _save_node_data() -> void: @@ -144,3 +144,14 @@ func _save_node_data() -> void: var save_data_resource: Node3DDataResource = node._save_data() var save_final_resource: Node3DDataResource = save_data_resource.duplicate() _game_data_resource.save_data_nodes.append(save_final_resource) + + +func _on_save_game_as_resource(save_name: String) -> void: + if not _load_save_level_data_component(): return + + var current_date: String = Time.get_datetime_string_from_system().replace(":", "") + var _filename: String = "save_" + current_date + ".tres" + + _game_data_resource.save_name = save_name + _save_game_as_resource(_filename) + game_saved.emit() From 675d56be4ca9ef7eb7667cd254935a9f8aca5241 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 15:14:45 -0400 Subject: [PATCH 15/20] Removing save button and added support for deleting save files --- save_load/autoloads/save_game_manager.gd | 9 +++ save_load/ui/save_file.gd | 23 ++++--- save_load/ui/save_file.tscn | 61 ++++++++++++----- save_load/ui/save_load_ui.gd | 36 ++++++++++ save_load/ui/save_load_ui.gd.uid | 1 + save_load/ui/save_load_ui.tscn | 85 +++++++++++++++++++++++- 6 files changed, 185 insertions(+), 30 deletions(-) create mode 100644 save_load/ui/save_load_ui.gd create mode 100644 save_load/ui/save_load_ui.gd.uid diff --git a/save_load/autoloads/save_game_manager.gd b/save_load/autoloads/save_game_manager.gd index 9d69135..4a52274 100644 --- a/save_load/autoloads/save_game_manager.gd +++ b/save_load/autoloads/save_game_manager.gd @@ -29,6 +29,7 @@ func _ready() -> void: quick_save.connect(quick_load_game) create_save_file.connect(_on_save_game_as_resource) load_save_file.connect(_load_game_resource) + delete_save_file.connect(_on_delete_save_file) func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("quick_save"): @@ -155,3 +156,11 @@ func _on_save_game_as_resource(save_name: String) -> void: _game_data_resource.save_name = save_name _save_game_as_resource(_filename) game_saved.emit() + +func _on_delete_save_file(filename: String) -> void: + if filename.length() < 1: + printerr("_on_delete_save_file(): Empty filename provided") + return + + var save_file_path: String = _save_level_data_component.settings.save_game_data_path + filename + DirAccess.remove_absolute(save_file_path) diff --git a/save_load/ui/save_file.gd b/save_load/ui/save_file.gd index 35e0aec..1c82756 100644 --- a/save_load/ui/save_file.gd +++ b/save_load/ui/save_file.gd @@ -9,9 +9,11 @@ extends Panel @export var save_name_label: Label @export var save_date_label: Label @export var save_icon: TextureRect -@export var save_button: TextureButton @export var load_button: TextureButton +@export var delete_confirm_ui: Control @export var delete_button: TextureButton +@export var delete_confirm_button: BaseButton +@export var delete_cancel_button: BaseButton var save_file_details: SaveFileDetailsResource @@ -21,9 +23,11 @@ func _ready() -> void: mouse_exited.connect(_on_mouse_exited) delete_button.pressed.connect(_on_delete_button_pressed) - save_button.pressed.connect(_on_save_button_pressed) load_button.pressed.connect(_on_load_button_pressed) + delete_confirm_button.pressed.connect(_on_delete_confirm_button_pressed) + delete_cancel_button.pressed.connect(_on_delete_cancel_button_pressed) + func initialize(_resource: SaveFileDetailsResource) -> void: save_file_details = _resource @@ -43,19 +47,18 @@ func set_save_name() -> void: save_name_label.text = save_file_details.filename - func _on_delete_button_pressed() -> void: + delete_confirm_ui.show() + +func _on_delete_confirm_button_pressed() -> void: SaveGameManager.delete_save_file.emit(save_file_details.filename) - print("DELETING: ", save_file_details.filename) + queue_free() + +func _on_delete_cancel_button_pressed() -> void: + delete_confirm_ui.hide() func _on_load_button_pressed() -> void: SaveGameManager.load_save_file.emit(save_file_details.filename) - print("LOADING: ", save_file_details.filename) - -func _on_save_button_pressed() -> void: - SaveGameManager.create_save_file.emit(save_file_details.filename) - print("SAVING: ", save_file_details.filename) - func _on_mouse_entered() -> void: set("theme_override_styles/panel", save_panel_highlight) diff --git a/save_load/ui/save_file.tscn b/save_load/ui/save_file.tscn index b3fcaa3..9be91ae 100644 --- a/save_load/ui/save_file.tscn +++ b/save_load/ui/save_file.tscn @@ -1,20 +1,17 @@ -[gd_scene load_steps=14 format=3 uid="uid://bb7poutsn4ex2"] +[gd_scene load_steps=11 format=3 uid="uid://bb7poutsn4ex2"] [ext_resource type="Texture2D" uid="uid://dknv7amroftm8" path="res://assets/icon.svg" id="1_714lu"] [ext_resource type="StyleBox" uid="uid://biousyggn7iua" path="res://save_load/resources/save_file_panel_theme.tres" id="1_cqw77"] [ext_resource type="Texture2D" uid="uid://cmq51cgasug81" path="res://save_load/assets/folder-open-normal.png" id="1_k6haa"] [ext_resource type="Script" uid="uid://dcfdyua5gwpw4" path="res://save_load/ui/save_file.gd" id="2_5g2eu"] [ext_resource type="Texture2D" uid="uid://blyryo60jydgi" path="res://save_load/assets/folder-open-pressed.png" id="2_714lu"] -[ext_resource type="Texture2D" uid="uid://ja8bc1h5x85o" path="res://save_load/assets/save-normal.png" id="2_jgxci"] -[ext_resource type="Texture2D" uid="uid://crqgyft4gfilt" path="res://save_load/assets/save-pressed.png" id="3_cqw77"] [ext_resource type="StyleBox" uid="uid://bwm315lqbbb87" path="res://save_load/resources/save_file_highlight_panel_theme.tres" id="3_om23c"] [ext_resource type="Texture2D" uid="uid://by4w5ll3le7g6" path="res://save_load/assets/folder-open-hover.png" id="3_ubfnn"] -[ext_resource type="Texture2D" uid="uid://o3l0j53mgkan" path="res://save_load/assets/save-hover.png" id="4_5g2eu"] [ext_resource type="Texture2D" uid="uid://dvp5yeoqw36yt" path="res://save_load/assets/trash-normal.png" id="4_c2bnc"] [ext_resource type="Texture2D" uid="uid://brwa8yljyrlgy" path="res://save_load/assets/trash-pressed.png" id="5_jgxci"] [ext_resource type="Texture2D" uid="uid://cmrtuy0i5qc01" path="res://save_load/assets/trash-hover.png" id="6_cqw77"] -[node name="SaveFilePanel" type="Panel" node_paths=PackedStringArray("save_name_label", "save_date_label", "save_icon", "save_button", "load_button", "delete_button")] +[node name="SaveFilePanel" type="Panel" node_paths=PackedStringArray("save_name_label", "save_date_label", "save_icon", "load_button", "delete_confirm_ui", "delete_button", "delete_confirm_button", "delete_cancel_button")] custom_minimum_size = Vector2(420, 60) offset_right = 420.0 offset_bottom = 60.0 @@ -25,9 +22,11 @@ save_panel_normal = ExtResource("1_cqw77") save_name_label = NodePath("HBoxContainer/NameDate/SaveName") save_date_label = NodePath("HBoxContainer/NameDate/SaveDate") save_icon = NodePath("HBoxContainer/SaveFileIcon") -save_button = NodePath("HBoxContainer/Actions/SaveButton") load_button = NodePath("HBoxContainer/Actions/LoadButton") +delete_confirm_ui = NodePath("ConfirmDeletePanel") delete_button = NodePath("HBoxContainer/Actions/DeleteButton") +delete_confirm_button = NodePath("ConfirmDeletePanel/VBoxContainer/HBoxContainer/ConfirmButton") +delete_cancel_button = NodePath("ConfirmDeletePanel/VBoxContainer/HBoxContainer/CancelButton") [node name="HBoxContainer" type="HBoxContainer" parent="."] custom_minimum_size = Vector2(400, 0) @@ -67,17 +66,6 @@ text = "2025/03/20 13:43:12" layout_mode = 2 size_flags_horizontal = 10 -[node name="SaveButton" type="TextureButton" parent="HBoxContainer/Actions"] -clip_contents = true -custom_minimum_size = Vector2(32, 32) -layout_mode = 2 -tooltip_text = "Load Save" -texture_normal = ExtResource("2_jgxci") -texture_pressed = ExtResource("3_cqw77") -texture_hover = ExtResource("4_5g2eu") -ignore_texture_size = true -stretch_mode = 5 - [node name="LoadButton" type="TextureButton" parent="HBoxContainer/Actions"] clip_contents = true custom_minimum_size = Vector2(32, 32) @@ -98,3 +86,42 @@ texture_pressed = ExtResource("5_jgxci") texture_hover = ExtResource("6_cqw77") ignore_texture_size = true stretch_mode = 5 + +[node name="ConfirmDeletePanel" type="Panel" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="ConfirmDeletePanel"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -61.5 +offset_top = -29.0 +offset_right = 61.5 +offset_bottom = 29.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Label" type="Label" parent="ConfirmDeletePanel/VBoxContainer"] +layout_mode = 2 +text = "Delete Save?" +horizontal_alignment = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="ConfirmDeletePanel/VBoxContainer"] +layout_mode = 2 + +[node name="ConfirmButton" type="Button" parent="ConfirmDeletePanel/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "Confirm" + +[node name="CancelButton" type="Button" parent="ConfirmDeletePanel/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "Cancel" diff --git a/save_load/ui/save_load_ui.gd b/save_load/ui/save_load_ui.gd new file mode 100644 index 0000000..11744cd --- /dev/null +++ b/save_load/ui/save_load_ui.gd @@ -0,0 +1,36 @@ +class_name SaveLoadUI +extends Control + + +@export var show_save_ui_button: BaseButton +@export var new_save_ui: Control +@export var save_name_input: LineEdit +@export var create_save_button: BaseButton +@export var create_save_cancel_button: BaseButton + + +func _ready() -> void: + show_save_ui_button.pressed.connect(_on_show_save_ui_button_pressed) + create_save_button.pressed.connect(_on_create_save_button_pressed) + create_save_cancel_button.pressed.connect(_on_create_save_cancel_button_pressed) + + new_save_ui.hide() + + +func _on_create_save_button_pressed() -> void: + var save_name: String = save_name_input.text + if save_name.strip_edges() == "": + save_name = Time.get_datetime_string_from_system().replace(":", "") + + SaveGameManager.create_save_file.emit(save_name) + SaveGameManager.refresh_saves_list.emit() + new_save_ui.hide() + SignalManager.resume_game.emit() + +func _on_create_save_cancel_button_pressed() -> void: + new_save_ui.hide() + +func _on_show_save_ui_button_pressed() -> void: + var current_date: String = Time.get_datetime_string_from_system() + save_name_input.text = current_date.replace(":", "") + new_save_ui.show() diff --git a/save_load/ui/save_load_ui.gd.uid b/save_load/ui/save_load_ui.gd.uid new file mode 100644 index 0000000..d3bf6a0 --- /dev/null +++ b/save_load/ui/save_load_ui.gd.uid @@ -0,0 +1 @@ +uid://b6831eygibii7 diff --git a/save_load/ui/save_load_ui.tscn b/save_load/ui/save_load_ui.tscn index 4b1d13d..0a20730 100644 --- a/save_load/ui/save_load_ui.tscn +++ b/save_load/ui/save_load_ui.tscn @@ -1,8 +1,13 @@ -[gd_scene load_steps=2 format=3 uid="uid://dauchkhmnyk7n"] +[gd_scene load_steps=7 format=3 uid="uid://dauchkhmnyk7n"] +[ext_resource type="Script" uid="uid://b6831eygibii7" path="res://save_load/ui/save_load_ui.gd" id="1_lo08d"] [ext_resource type="PackedScene" uid="uid://cyxieflejsggu" path="res://save_load/ui/save_files_list.tscn" id="1_tqtxm"] +[ext_resource type="PackedScene" uid="uid://bb7poutsn4ex2" path="res://save_load/ui/save_file.tscn" id="2_6uxbh"] +[ext_resource type="Texture2D" uid="uid://ja8bc1h5x85o" path="res://save_load/assets/save-normal.png" id="3_lo08d"] +[ext_resource type="Texture2D" uid="uid://crqgyft4gfilt" path="res://save_load/assets/save-pressed.png" id="4_md7la"] +[ext_resource type="Texture2D" uid="uid://o3l0j53mgkan" path="res://save_load/assets/save-hover.png" id="5_hmxxn"] -[node name="SaveLoadUI" type="Control"] +[node name="SaveLoadUI" type="Control" node_paths=PackedStringArray("show_save_ui_button", "new_save_ui", "save_name_input", "create_save_button", "create_save_cancel_button")] process_mode = 3 layout_mode = 3 anchors_preset = 15 @@ -12,6 +17,12 @@ grow_horizontal = 2 grow_vertical = 2 size_flags_horizontal = 6 size_flags_vertical = 4 +script = ExtResource("1_lo08d") +show_save_ui_button = NodePath("Panel/MarginContainer/VBoxContainer/BottomRow/SaveButton") +new_save_ui = NodePath("Panel/NewSaveUI") +save_name_input = NodePath("Panel/NewSaveUI/MarginContainer/VBoxContainer/SaveNameInput") +create_save_button = NodePath("Panel/NewSaveUI/MarginContainer/VBoxContainer/SaveButton") +create_save_cancel_button = NodePath("Panel/NewSaveUI/MarginContainer/VBoxContainer/CancelButton") [node name="Panel" type="Panel" parent="."] layout_mode = 1 @@ -39,8 +50,76 @@ layout_mode = 2 [node name="Label" type="Label" parent="Panel/MarginContainer/VBoxContainer"] layout_mode = 2 theme_override_font_sizes/font_size = 40 -text = "Load Save" +text = "Save/Load Game" horizontal_alignment = 1 [node name="SaveFilesList" parent="Panel/MarginContainer/VBoxContainer" instance=ExtResource("1_tqtxm")] layout_mode = 2 + +[node name="SaveFilePanel" parent="Panel/MarginContainer/VBoxContainer/SaveFilesList" instance=ExtResource("2_6uxbh")] +layout_mode = 2 + +[node name="BottomRow" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="SaveButton" type="TextureButton" parent="Panel/MarginContainer/VBoxContainer/BottomRow"] +clip_contents = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "New Save" +texture_normal = ExtResource("3_lo08d") +texture_pressed = ExtResource("4_md7la") +texture_hover = ExtResource("5_hmxxn") +ignore_texture_size = true +stretch_mode = 5 + +[node name="NewSaveUI" type="Panel" parent="Panel"] +custom_minimum_size = Vector2(450, 100) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="MarginContainer" type="MarginContainer" parent="Panel/NewSaveUI"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -205.0 +offset_top = -29.5 +offset_right = 205.0 +offset_bottom = 29.5 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="VBoxContainer" type="VBoxContainer" parent="Panel/NewSaveUI/MarginContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 + +[node name="SaveNameLabel" type="Label" parent="Panel/NewSaveUI/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 20 +text = "Create New Save" + +[node name="SaveNameInput" type="LineEdit" parent="Panel/NewSaveUI/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(400, 0) +layout_mode = 2 +placeholder_text = "New Save File Name" + +[node name="SaveButton" type="Button" parent="Panel/NewSaveUI/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Create Save" + +[node name="CancelButton" type="Button" parent="Panel/NewSaveUI/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Cancel" From 34159bcb8032775514a118657e84cc2cf45a97da Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 15:15:10 -0400 Subject: [PATCH 16/20] Formatting --- save_load/ui/save_files_list.gd | 1 + 1 file changed, 1 insertion(+) diff --git a/save_load/ui/save_files_list.gd b/save_load/ui/save_files_list.gd index 0135c48..d3f36de 100644 --- a/save_load/ui/save_files_list.gd +++ b/save_load/ui/save_files_list.gd @@ -8,6 +8,7 @@ func _ready() -> void: SaveGameManager.refresh_saves_list.connect(_on_refresh_saves_list) refresh_saves_list() + ## Clear the SaveFilesList node of all saves and load most recent saves func refresh_saves_list() -> void: _clear_save_files_list() From acab2a7b92e9b5e64db122385b0034b69efef064 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 15:18:56 -0400 Subject: [PATCH 17/20] Replace Save Game and Load Game buttons with Saves --- scenes/ui/menus/pause_menu.gd | 7 +------ scenes/ui/menus/pause_menu.tscn | 11 +++-------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/scenes/ui/menus/pause_menu.gd b/scenes/ui/menus/pause_menu.gd index c95c6cc..9be0c5a 100644 --- a/scenes/ui/menus/pause_menu.gd +++ b/scenes/ui/menus/pause_menu.gd @@ -26,10 +26,5 @@ func _on_resume_button_pressed() -> void: func _on_settings_button_pressed() -> void: SignalManager.open_settings_menu.emit() - -func _on_load_game_pressed() -> void: - SaveGameManager.open_save_list_ui.emit() - -func _on_save_game_pressed() -> void: - # TODO +func _on_saves_button_pressed() -> void: SaveGameManager.open_save_list_ui.emit() diff --git a/scenes/ui/menus/pause_menu.tscn b/scenes/ui/menus/pause_menu.tscn index 949adb3..9b7974f 100644 --- a/scenes/ui/menus/pause_menu.tscn +++ b/scenes/ui/menus/pause_menu.tscn @@ -57,13 +57,9 @@ theme_override_constants/separation = 20 layout_mode = 2 text = "Resume" -[node name="SaveGame" type="Button" parent="Background/MarginContainer/MenuContainer/ButtonsContainer"] +[node name="SavesButton" type="Button" parent="Background/MarginContainer/MenuContainer/ButtonsContainer"] layout_mode = 2 -text = "Save Game" - -[node name="LoadGame" type="Button" parent="Background/MarginContainer/MenuContainer/ButtonsContainer"] -layout_mode = 2 -text = "Load Game" +text = "Saves" [node name="SettingsButton" type="Button" parent="Background/MarginContainer/MenuContainer/ButtonsContainer"] layout_mode = 2 @@ -74,7 +70,6 @@ layout_mode = 2 text = "Exit Game" [connection signal="pressed" from="Background/MarginContainer/MenuContainer/ButtonsContainer/ResumeButton" to="." method="_on_resume_button_pressed"] -[connection signal="pressed" from="Background/MarginContainer/MenuContainer/ButtonsContainer/SaveGame" to="." method="_on_save_game_pressed"] -[connection signal="pressed" from="Background/MarginContainer/MenuContainer/ButtonsContainer/LoadGame" to="." method="_on_load_game_pressed"] +[connection signal="pressed" from="Background/MarginContainer/MenuContainer/ButtonsContainer/SavesButton" to="." method="_on_saves_button_pressed"] [connection signal="pressed" from="Background/MarginContainer/MenuContainer/ButtonsContainer/SettingsButton" to="." method="_on_settings_button_pressed"] [connection signal="pressed" from="Background/MarginContainer/MenuContainer/ButtonsContainer/ExitGameButton" to="." method="_on_exit_game_button_pressed"] From 136a0d304d14e623a327d2d4966295cc4a146d78 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 15:19:11 -0400 Subject: [PATCH 18/20] Hide NewSaveUI by default --- save_load/ui/save_load_ui.tscn | 1 + 1 file changed, 1 insertion(+) diff --git a/save_load/ui/save_load_ui.tscn b/save_load/ui/save_load_ui.tscn index 0a20730..207cc37 100644 --- a/save_load/ui/save_load_ui.tscn +++ b/save_load/ui/save_load_ui.tscn @@ -75,6 +75,7 @@ ignore_texture_size = true stretch_mode = 5 [node name="NewSaveUI" type="Panel" parent="Panel"] +visible = false custom_minimum_size = Vector2(450, 100) layout_mode = 1 anchors_preset = 15 From 096bbb434d96202469bb3ffe35d226d49f247cf9 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 15:57:52 -0400 Subject: [PATCH 19/20] Allow Scrolling of saves --- save_load/ui/save_load_ui.tscn | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/save_load/ui/save_load_ui.tscn b/save_load/ui/save_load_ui.tscn index 207cc37..4c5f2f8 100644 --- a/save_load/ui/save_load_ui.tscn +++ b/save_load/ui/save_load_ui.tscn @@ -53,10 +53,16 @@ theme_override_font_sizes/font_size = 40 text = "Save/Load Game" horizontal_alignment = 1 -[node name="SaveFilesList" parent="Panel/MarginContainer/VBoxContainer" instance=ExtResource("1_tqtxm")] +[node name="ScrollContainer" type="ScrollContainer" parent="Panel/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 500) layout_mode = 2 +horizontal_scroll_mode = 0 -[node name="SaveFilePanel" parent="Panel/MarginContainer/VBoxContainer/SaveFilesList" instance=ExtResource("2_6uxbh")] +[node name="SaveFilesList" parent="Panel/MarginContainer/VBoxContainer/ScrollContainer" instance=ExtResource("1_tqtxm")] +layout_mode = 2 +size_flags_horizontal = 6 + +[node name="SaveFilePanel" parent="Panel/MarginContainer/VBoxContainer/ScrollContainer/SaveFilesList" instance=ExtResource("2_6uxbh")] layout_mode = 2 [node name="BottomRow" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer"] From 7645b8827e8b07328c1bd71808949b72fcc8b889 Mon Sep 17 00:00:00 2001 From: Ryan Reed Date: Wed, 26 Mar 2025 16:07:51 -0400 Subject: [PATCH 20/20] Sorting saves list by date created, descending --- save_load/autoloads/save_game_manager.gd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/save_load/autoloads/save_game_manager.gd b/save_load/autoloads/save_game_manager.gd index 4a52274..2558117 100644 --- a/save_load/autoloads/save_game_manager.gd +++ b/save_load/autoloads/save_game_manager.gd @@ -76,6 +76,7 @@ func list_saves(include_quick_saves: bool = true, include_auto_saves: bool = tru save_files.append(_save_resource) + save_files.sort_custom(_custom_save_file_sort) return save_files func quick_save_game() -> void: @@ -94,6 +95,10 @@ func quick_load_game() -> void: game_loaded.emit() +## Sort the save files list by date created, descending +func _custom_save_file_sort(a: SaveFileDetailsResource, b: SaveFileDetailsResource) -> bool: + return a.date_created > b.date_created + ## Find the SaveLevelDataComponent within the level which stores the save paths and filenames func _load_save_level_data_component() -> bool: _save_level_data_component = get_tree().get_first_node_in_group("save_level_data_component")