From c695115a4a29ba73d51fc19ca5eb3f249001b513 Mon Sep 17 00:00:00 2001 From: kziolkowski Date: Tue, 25 Nov 2025 13:33:53 +0100 Subject: [PATCH] :feature: implemented saving and loading for ducks, :fire: removed save implementation from interactionArea --- prefabs/farm/animals/duck.tscn | 16 +++--- prefabs/interactions/interaction_area_2d.tscn | 4 +- project.godot | 4 ++ scenes/Babushka_scene_farm_outside_2d.tscn | 27 ++++++++- .../CharacterControls/InteractionArea2D.cs | 55 +----------------- .../Common/Savegame/SaveIDProviderTool.cs | 28 ++++++++++ .../Common/Savegame/SaveIDProviderTool.cs.uid | 1 + scripts/CSharp/Common/Temp/MVPDuck.cs | 56 ++++++++++++++++++- 8 files changed, 123 insertions(+), 68 deletions(-) create mode 100644 scripts/CSharp/Common/Savegame/SaveIDProviderTool.cs create mode 100644 scripts/CSharp/Common/Savegame/SaveIDProviderTool.cs.uid diff --git a/prefabs/farm/animals/duck.tscn b/prefabs/farm/animals/duck.tscn index d3c0ff5..b503a3d 100644 --- a/prefabs/farm/animals/duck.tscn +++ b/prefabs/farm/animals/duck.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=24 format=3 uid="uid://muuxxgvx33fp"] +[gd_scene load_steps=25 format=3 uid="uid://muuxxgvx33fp"] [ext_resource type="Script" uid="uid://7m1rt7agb6rm" path="res://scripts/CSharp/Common/Temp/MVPDuck.cs" id="1_54k4r"] [ext_resource type="Texture2D" uid="uid://hvchk6t0xe7j" path="res://art/animals/Ente.png" id="1_cgxhx"] +[ext_resource type="Resource" uid="uid://tt3d166mntmi" path="res://resources/low code/farming/var_sceneNameProvider.tres" id="2_fdf3t"] [ext_resource type="AudioStream" uid="uid://qv0aubjeyi0u" path="res://audio/sfx/Animals/SFX_Duck_Quack_01.wav" id="3_kjie1"] [ext_resource type="Script" uid="uid://cfnrd5k1k0gxw" path="res://scripts/CSharp/Common/AudioPlayer2D.cs" id="3_rdn2q"] [ext_resource type="AudioStream" uid="uid://da84l8e44scwh" path="res://audio/sfx/Animals/SFX_Duck_Quack_02.wav" id="4_54k4r"] @@ -138,10 +139,11 @@ radius = 200.0 [sub_resource type="ViewportTexture" id="ViewportTexture_4830j"] viewport_path = NodePath("SubViewport") -[node name="Duck" type="Node2D" node_paths=PackedStringArray("_animationPlayer")] +[node name="Duck" type="Node2D" node_paths=PackedStringArray("_animationPlayer") groups=["Saveable"]] z_index = 1 y_sort_enabled = true script = ExtResource("1_54k4r") +_sceneKeyProvider = ExtResource("2_fdf3t") _transferDelayMs = 1000 _animationPlayer = NodePath("AnimationPlayer") @@ -160,11 +162,11 @@ libraries = { &"": SubResource("AnimationLibrary_54k4r") } -[node name="InteractionArea" parent="." node_paths=PackedStringArray("_spritesToOutline") instance=ExtResource("15_uo3dh")] +[node name="duck interaction" parent="." node_paths=PackedStringArray("_spritesToOutline") instance=ExtResource("15_uo3dh")] position = Vector2(18, -250) _spritesToOutline = [NodePath("../Duck rendered")] -[node name="CollisionShape3D" parent="InteractionArea/Area2D" index="0"] +[node name="CollisionShape3D" parent="duck interaction/Area2D" index="0"] shape = SubResource("CircleShape2D_uo3dh") [node name="Duck rendered" type="Sprite2D" parent="."] @@ -321,7 +323,7 @@ offset = Vector2(40, 40) region_enabled = true region_rect = Rect2(246, 393, 111, 111) -[connection signal="Interacted" from="InteractionArea" to="." method="TransferToTargetAfterDelay"] -[connection signal="Interacted" from="InteractionArea" to="Audio/NakNak" method="PlayOneShot"] +[connection signal="Interacted" from="duck interaction" to="." method="TransferToTargetAfterDelay"] +[connection signal="Interacted" from="duck interaction" to="Audio/NakNak" method="PlayOneShot"] -[editable path="InteractionArea"] +[editable path="duck interaction"] diff --git a/prefabs/interactions/interaction_area_2d.tscn b/prefabs/interactions/interaction_area_2d.tscn index cf2871e..fb04b7f 100644 --- a/prefabs/interactions/interaction_area_2d.tscn +++ b/prefabs/interactions/interaction_area_2d.tscn @@ -1,7 +1,6 @@ -[gd_scene load_steps=7 format=3 uid="uid://cqc72e4hq6bcd"] +[gd_scene load_steps=6 format=3 uid="uid://cqc72e4hq6bcd"] [ext_resource type="Script" uid="uid://ckp413wrub5fm" path="res://scripts/CSharp/Common/CharacterControls/InteractionArea2D.cs" id="1_5ajrf"] -[ext_resource type="Resource" uid="uid://tt3d166mntmi" path="res://resources/low code/farming/var_sceneNameProvider.tres" id="2_o1drf"] [ext_resource type="Material" uid="uid://blch5kdhkbj75" path="res://art/materials/simple_interactable_outline.tres" id="2_qoey7"] [ext_resource type="Script" uid="uid://cp2q4k62sjo6h" path="res://scripts/CSharp/Common/CharacterControls/DetectableInteractionArea.cs" id="3_2wrrq"] @@ -14,7 +13,6 @@ default_font_size = 30 [node name="InteractionArea" type="Node2D" node_paths=PackedStringArray("_area", "_label")] script = ExtResource("1_5ajrf") -_sceneKeyProvider = ExtResource("2_o1drf") _area = NodePath("Area2D") _label = NodePath("Area2D/CanvasLayer/MarginContainer/Label") _outlineMaterial = ExtResource("2_qoey7") diff --git a/project.godot b/project.godot index 2658752..7d188a7 100644 --- a/project.godot +++ b/project.godot @@ -221,6 +221,10 @@ folder_colors={ "res://shader/": "pink" } +[global_group] + +Saveable="" + [input] move_left={ diff --git a/scenes/Babushka_scene_farm_outside_2d.tscn b/scenes/Babushka_scene_farm_outside_2d.tscn index f8e461e..91f359e 100644 --- a/scenes/Babushka_scene_farm_outside_2d.tscn +++ b/scenes/Babushka_scene_farm_outside_2d.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=117 format=3 uid="uid://gigb28qk8t12"] +[gd_scene load_steps=118 format=3 uid="uid://gigb28qk8t12"] [ext_resource type="PackedScene" uid="uid://c25udixd5m6l0" path="res://prefabs/characters/Player2D.tscn" id="1_7wfwe"] [ext_resource type="Texture2D" uid="uid://8sr11ex30n0m" path="res://art/mockups/Kenney_Backgrounds/Samples/uncolored_hills.png" id="2_7b2ri"] @@ -79,6 +79,7 @@ [ext_resource type="Resource" uid="uid://tt3d166mntmi" path="res://resources/low code/farming/var_sceneNameProvider.tres" id="77_xcwle"] [ext_resource type="PackedScene" uid="uid://b1d2e7ely6hyw" path="res://prefabs/farm/base_field.tscn" id="78_xcwle"] [ext_resource type="Script" uid="uid://iquhbkr7pqeg" path="res://scripts/CSharp/Common/Savegame/SaveCheats.cs" id="79_065st"] +[ext_resource type="Script" uid="uid://ca4s0algeij1h" path="res://scripts/CSharp/Common/Savegame/SaveIDProviderTool.cs" id="80_w1kgo"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_wtdui"] shader = ExtResource("13_7p0hq") @@ -1060,6 +1061,7 @@ position = Vector2(145.5, -224) shape = SubResource("RectangleShape2D_0sfl7") [node name="InteractionArea" parent="YSorted/Well" instance=ExtResource("27_klb81")] +metadata/SaveID = "b8f7b7fe-e057-4974-ba12-9134722998de" [node name="CollisionShape3D" parent="YSorted/Well/InteractionArea/Area2D" index="0"] position = Vector2(146, -130) @@ -1074,6 +1076,7 @@ _blueprint = ExtResource("28_ipqaa") [node name="PickupInteractionArea" parent="YSorted/CanGenericPickup" index="3" node_paths=PackedStringArray("_spritesToOutline")] _outlineMaterial = null _spritesToOutline = [] +metadata/SaveID = "0c006f5c-c472-4f89-908b-d8f34503ba37" [node name="CollisionShape3D" parent="YSorted/CanGenericPickup/PickupInteractionArea/Area2D" index="0"] shape = SubResource("CircleShape2D_2065p") @@ -1090,6 +1093,7 @@ _blueprint = ExtResource("28_6b2nr") [node name="PickupInteractionArea" parent="YSorted/RakeGenericPickup" index="3" node_paths=PackedStringArray("_spritesToOutline")] _outlineMaterial = null _spritesToOutline = [] +metadata/SaveID = "c148aa78-114b-4770-a040-8498483edb1d" [node name="CollisionShape3D" parent="YSorted/RakeGenericPickup/PickupInteractionArea/Area2D" index="0"] shape = SubResource("CircleShape2D_tm0yg") @@ -1105,6 +1109,7 @@ _blueprint = ExtResource("35_64mdn") [node name="PickupInteractionArea" parent="YSorted/SeedPickup" index="3" node_paths=PackedStringArray("_spritesToOutline")] _outlineMaterial = null _spritesToOutline = [] +metadata/SaveID = "ad152c51-3631-42c1-9aa4-4df896b35d8c" [node name="CollisionShape3D" parent="YSorted/SeedPickup/PickupInteractionArea/Area2D" index="0"] shape = SubResource("CircleShape2D_tm0yg") @@ -1122,6 +1127,7 @@ _blueprint = ExtResource("36_fv1t2") [node name="PickupInteractionArea" parent="YSorted/SeedPickup2" index="3" node_paths=PackedStringArray("_spritesToOutline")] _outlineMaterial = null _spritesToOutline = [] +metadata/SaveID = "09e115e7-1d21-485a-be3e-b3fff9c83e78" [node name="CollisionShape3D" parent="YSorted/SeedPickup2/PickupInteractionArea/Area2D" index="0"] shape = SubResource("CircleShape2D_tm0yg") @@ -1264,6 +1270,7 @@ polygon = PackedVector2Array(247.227, 43.5123, 44.7822, 43.5123, -87.2178, 45.12 [node name="EnterHouseInteraction" parent="YSorted/Farm visuals/Static" instance=ExtResource("27_klb81")] position = Vector2(5834, 2354) scale = Vector2(2.425, 2.425) +metadata/SaveID = "5a93071f-c1ab-4b4b-b74e-a6324d44ddf8" [node name="DoorSprite" type="Sprite2D" parent="YSorted/Farm visuals/Static/EnterHouseInteraction"] position = Vector2(0.412364, -33.1959) @@ -2231,8 +2238,11 @@ collision_mask = 4 position = Vector2(-106.663, 182.891) shape = SubResource("RectangleShape2D_ycj14") -[node name="InteractionArea" parent="YSorted/Blocker" instance=ExtResource("27_klb81")] +[node name="InteractionArea" parent="YSorted/Blocker" node_paths=PackedStringArray("_spritesToOutline") instance=ExtResource("27_klb81")] position = Vector2(11234, 1850) +_spritesToOutline = [NodePath("Fence Door")] +_id = 1 +metadata/SaveID = "6ee77256-42af-49c9-a3f2-cf167853f6fb" [node name="CollisionShape3D" parent="YSorted/Blocker/InteractionArea/Area2D" index="0"] shape = SubResource("CircleShape2D_l7ekk") @@ -2263,12 +2273,14 @@ z_index = 0 y_sort_enabled = false position = Vector2(4374, 2652) _penTarget = NodePath("../../pen/penSlot1") +metadata/SaveID = "348bd0e3-1da5-4f10-84ab-b0444e99d541" [node name="Duck3" parent="YSorted/ducks" node_paths=PackedStringArray("_penTarget") instance=ExtResource("62_i36hd")] z_index = 0 y_sort_enabled = false position = Vector2(9259, 3194) _penTarget = NodePath("../../pen/penSlot2") +metadata/SaveID = "94c8a740-2745-4162-91e7-66f36b8681e0" [node name="Duck4" parent="YSorted/ducks" node_paths=PackedStringArray("_penTarget") instance=ExtResource("62_i36hd")] z_index = 0 @@ -2277,6 +2289,7 @@ position = Vector2(13441, 3612) rotation = 3.14159 scale = Vector2(1, -1) _penTarget = NodePath("../../pen/penSlot3") +metadata/SaveID = "b3508312-eb61-4520-8349-e49e0e5328d3" [node name="Duck5" parent="YSorted/ducks" node_paths=PackedStringArray("_penTarget") instance=ExtResource("62_i36hd")] z_index = 0 @@ -2285,12 +2298,14 @@ position = Vector2(15330, 2487) rotation = 3.14159 scale = Vector2(1, -1) _penTarget = NodePath("../../pen/penSlot4") +metadata/SaveID = "b73895c2-6366-4c7e-b5e2-23f3dc9485f2" [node name="Duck6" parent="YSorted/ducks" node_paths=PackedStringArray("_penTarget") instance=ExtResource("62_i36hd")] z_index = 0 y_sort_enabled = false position = Vector2(232, 2862) _penTarget = NodePath("../../pen/penSlot5") +metadata/SaveID = "a963b9d2-862f-458b-be2c-9a54ec1bde90" [node name="Duck7" parent="YSorted/ducks" node_paths=PackedStringArray("_penTarget") instance=ExtResource("62_i36hd")] z_index = 0 @@ -2299,6 +2314,7 @@ position = Vector2(2409, 3958) rotation = 3.14159 scale = Vector2(1, -1) _penTarget = NodePath("../../pen/penSlot6") +metadata/SaveID = "748aff78-10eb-4a4e-bb6d-a8ee25d472d1" [node name="DialogicToggle" type="Node2D" parent="YSorted/ducks"] script = ExtResource("51_uxa2m") @@ -2348,6 +2364,7 @@ region_rect = Rect2(207, 1184, 149, 142) [node name="InteractionArea" parent="YSorted/trash/trashObject2" index="0" node_paths=PackedStringArray("_spritesToOutline")] position = Vector2(-9, -46) _spritesToOutline = [] +metadata/SaveID = "549bbcf4-ea57-4b8f-80b1-b13ca648559b" [node name="trashObject3" parent="YSorted/trash" instance=ExtResource("53_ycj14")] z_index = 0 @@ -2359,6 +2376,7 @@ region_rect = Rect2(400, 1053, 163, 141) [node name="InteractionArea" parent="YSorted/trash/trashObject3" index="0" node_paths=PackedStringArray("_spritesToOutline")] position = Vector2(-13, -53) _spritesToOutline = [] +metadata/SaveID = "29874314-50c1-4a21-9494-18f936d6e097" [node name="trashObject4" parent="YSorted/trash" instance=ExtResource("53_ycj14")] z_index = 0 @@ -2370,6 +2388,7 @@ region_rect = Rect2(1048, 1092, 348, 106) [node name="InteractionArea" parent="YSorted/trash/trashObject4" index="0" node_paths=PackedStringArray("_spritesToOutline")] position = Vector2(0, -59) _spritesToOutline = [] +metadata/SaveID = "7ccaa831-5526-40ed-8ca3-31ba2ad929a6" [node name="trashObject5" parent="YSorted/trash" instance=ExtResource("53_ycj14")] z_index = 0 @@ -2408,6 +2427,7 @@ region_rect = Rect2(1048, 1092, 348, 106) [node name="InteractionArea" parent="YSorted/trash/trashObject9" index="0" node_paths=PackedStringArray("_spritesToOutline")] position = Vector2(22.40873, 25.05658) _spritesToOutline = [] +metadata/SaveID = "7bf227d6-3844-41e9-a9cd-524052aced3b" [node name="CanvasLayer" parent="." instance=ExtResource("32_2nee2")] @@ -2525,6 +2545,9 @@ _payloadToSet = "farmOutside" [node name="SaveGameCheat" type="Node" parent="."] script = ExtResource("79_065st") +[node name="SaveIDProvider" type="Node" parent="."] +script = ExtResource("80_w1kgo") + [connection signal="FilledWateringCan" from="YSorted/Vesna" to="Audio/SFX/FillWater SFX2" method="PlayOneShot"] [connection signal="InteractedTool" from="YSorted/Well/InteractionArea" to="YSorted/Vesna" method="TryFillWateringCan"] [connection signal="SuccessfulPickUp" from="YSorted/CanGenericPickup" to="YSorted/Vesna" method="HandlePickUp"] diff --git a/scripts/CSharp/Common/CharacterControls/InteractionArea2D.cs b/scripts/CSharp/Common/CharacterControls/InteractionArea2D.cs index c683a05..df66172 100644 --- a/scripts/CSharp/Common/CharacterControls/InteractionArea2D.cs +++ b/scripts/CSharp/Common/CharacterControls/InteractionArea2D.cs @@ -1,19 +1,11 @@ using System.Linq; -using Babushka.scripts.CSharp.Common.Savegame; using Babushka.scripts.CSharp.Common.Services; -using Babushka.scripts.CSharp.Low_Code.Variables; using Godot; -using Godot.Collections; namespace Babushka.scripts.CSharp.Common.CharacterControls; -public partial class InteractionArea2D : Node2D, ISaveable +public partial class InteractionArea2D : Node2D { - [ExportGroup("Persistence")] - [Export] public VariableResource _sceneKeyProvider; - [Export] private string _saveId = ""; // todo: find good default / generated solution - //todo: rewire broken instances in scenes - [ExportGroup("Settings")] [Export] private Area2D _area; [Export] private Label _label; @@ -48,15 +40,6 @@ public partial class InteractionArea2D : Node2D, ISaveable { _backupMaterials = _spritesToOutline.Select(s => s.Material).ToArray(); } - - // bad solution for interaction areas, because they are all named the same. - // option (equally bad) 1: take grandparent's name (could be null though) - // option 2 (also bad): Write Identity Provider class that uses, checks and assigns GUIDs for this purpose. - if (string.IsNullOrEmpty(_saveId)) - { - _saveId = Name; - } - LoadFromSaveData(); } public void OnPlayerEntered(Node2D player) @@ -120,9 +103,7 @@ public partial class InteractionArea2D : Node2D, ISaveable sprite.Material = _backupMaterials[i]; } } - Interact(); - UpdateSaveData(); } } @@ -145,38 +126,4 @@ public partial class InteractionArea2D : Node2D, ISaveable _label.Hide(); } - #region SAVE AND LOAD - - public void UpdateSaveData() - { - var payloadData = new Dictionary - { - { "interaction_counter", _interactionCounter } - }; - - SavegameService.AppendDataToSave(_sceneKeyProvider.Payload.AsString(), _saveId, payloadData); - } - - public void LoadFromSaveData() - { - var sceneName = _sceneKeyProvider.Payload.AsString(); - var id = _saveId; - int counter = 0; - - Dictionary save = SavegameService.GetSaveData(sceneName, id); - if (save.Count > 0) - { - if (save.TryGetValue("interaction_counter", out Variant interactionCounterVar)) - { - counter = interactionCounterVar.AsInt32(); - } - } - - for (int i = 0; i < counter; i++) - { - Interact(); - } - } - - #endregion } \ No newline at end of file diff --git a/scripts/CSharp/Common/Savegame/SaveIDProviderTool.cs b/scripts/CSharp/Common/Savegame/SaveIDProviderTool.cs new file mode 100644 index 0000000..5ba38ed --- /dev/null +++ b/scripts/CSharp/Common/Savegame/SaveIDProviderTool.cs @@ -0,0 +1,28 @@ +using System; +using Godot; +using Godot.Collections; + +namespace Babushka.scripts.CSharp.Common.Savegame; + +[Tool] +public partial class SaveIDProviderTool : Node +{ + [ExportToolButton("Assign IDs")] private Callable assignIDs => Callable.From(AssignIDs); + + private void AssignIDs() + { + Array saveables = GetTree().GetNodesInGroup("Saveable"); + foreach (var node in saveables) + { + GD.Print($"Checking {node.Name}."); + GD.Print($"Node has Meta SaveID: {node.HasMeta("SaveID")} and it's: " + node.GetMeta("SaveID").AsString()); + if (!node.HasMeta("SaveID") || string.IsNullOrEmpty(node.GetMeta("SaveID").AsString())) + { + string saveID = Guid.NewGuid().ToString(); + node.SetMeta("SaveID", saveID); + GD.Print($"Setting Save ID for node {node.Name}: " + saveID); + } + + } + } +} \ No newline at end of file diff --git a/scripts/CSharp/Common/Savegame/SaveIDProviderTool.cs.uid b/scripts/CSharp/Common/Savegame/SaveIDProviderTool.cs.uid new file mode 100644 index 0000000..953d5d2 --- /dev/null +++ b/scripts/CSharp/Common/Savegame/SaveIDProviderTool.cs.uid @@ -0,0 +1 @@ +uid://ca4s0algeij1h diff --git a/scripts/CSharp/Common/Temp/MVPDuck.cs b/scripts/CSharp/Common/Temp/MVPDuck.cs index 7c7cf93..d0e8f69 100644 --- a/scripts/CSharp/Common/Temp/MVPDuck.cs +++ b/scripts/CSharp/Common/Temp/MVPDuck.cs @@ -1,13 +1,20 @@ using System.Threading.Tasks; +using Babushka.scripts.CSharp.Common.Savegame; +using Babushka.scripts.CSharp.Low_Code.Variables; using Godot; +using Godot.Collections; namespace Babushka.scripts.CSharp.Common.Temp; /// /// Temporary Duck behaviour to make sure we can use them in the first showcase /// -public partial class MVPDuck : Node2D +public partial class MVPDuck : Node2D, ISaveable { + [ExportGroup("Persistence")] + [Export] public VariableResource _sceneKeyProvider; + + [ExportGroup("Animation")] [Export] private Node2D _penTarget; [Export] private int _transferDelayMs; [Export] private AnimationPlayer _animationPlayer; @@ -17,6 +24,11 @@ public partial class MVPDuck : Node2D [Signal] public delegate void DuckCollectedEventHandler(); + public override void _Ready() + { + LoadFromSaveData(); + } + public void TransferToTargetAfterDelay() { if (!_collected) @@ -25,7 +37,6 @@ public partial class MVPDuck : Node2D PlayAnimation(); _collected = true; } - } private void PlayAnimation() @@ -40,7 +51,48 @@ public partial class MVPDuck : Node2D if(!_penTarget.Equals(null)) Position = _penTarget.GlobalPosition; EmitSignal(SignalName.DuckCollected); + UpdateSaveData(); } +#region SAVE AND LOAD + public void UpdateSaveData() + { + var payloadData = new Dictionary + { + { "globalPositionX", GlobalPosition.X }, + { "globalPositionY", GlobalPosition.Y }, + }; + + string id = GetMeta("SaveID").AsString(); + GD.Print($"Updating Save ID for object {Name}: {id}"); + SavegameService.AppendDataToSave(_sceneKeyProvider.Payload.AsString(), id, payloadData); + } + + public void LoadFromSaveData() + { + var sceneName = _sceneKeyProvider.Payload.AsString(); + string id = GetMeta("SaveID").AsString(); + GD.Print($"Loading Save ID for object {Name}: " + id); + + Dictionary save = SavegameService.GetSaveData(sceneName, id); + if (save.Count > 0) + { + float xPos = 0; + float yPos = 0; + if (save.TryGetValue("globalPositionX", out Variant xPosVar)) + { + xPos = xPosVar.AsSingle(); + } + + if (save.TryGetValue("globalPositionY", out Variant yPosVar)) + { + yPos = yPosVar.AsSingle(); + } + + GlobalPosition = new Vector2(xPos, yPos); + } + } + + #endregion } \ No newline at end of file