Compare commits

...

5 Commits

18 changed files with 391 additions and 9 deletions
+1
View File
@@ -34,6 +34,7 @@ Signal_Debugger="*res://addons/SignalVisualizer/Debugger/SignalDebugger.gd"
FightWorldAutoload="*res://prefabs/fight/fight_world_autoload.tscn"
FieldService="*res://scripts/CSharp/Common/Farming/FieldService.cs"
SaveGameManager="*res://scripts/CSharp/Common/Savegame/SaveGameManager.cs"
SettingsSaveController="*res://scripts/CSharp/Common/Savegame/SettingsSaveController.cs"
[dialogic]
+5 -2
View File
@@ -1,10 +1,13 @@
[gd_scene load_steps=2 format=3 uid="uid://bopv10dqm1knc"]
[gd_scene load_steps=3 format=3 uid="uid://bopv10dqm1knc"]
[ext_resource type="PackedScene" uid="uid://c6wnoif01ltld" path="res://scenes/Babushka_scene_startMenu.tscn" id="1_15ton"]
[ext_resource type="Script" uid="uid://bbp0dyddwdbl8" path="res://scripts/CSharp/Common/Savegame/WindowSettingsSync.cs" id="2_d3jfo"]
[node name="BabushkaSceneBootstrap" type="Node2D"]
[node name="BabushkaSceneStartMenu" parent="." instance=ExtResource("1_15ton")]
_sceneNamesToLoad = PackedStringArray("res://scenes/Babushka_scene_farm_outside_2d.tscn")
[node name="SceneParent" type="Node" parent="."]
[node name="WindowSettings" type="Node" parent="."]
script = ExtResource("2_d3jfo")
+5 -2
View File
@@ -1,4 +1,4 @@
[gd_scene load_steps=116 format=3 uid="uid://gigb28qk8t12"]
[gd_scene load_steps=117 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"]
@@ -38,7 +38,6 @@
[ext_resource type="Resource" uid="uid://d1uuxp1lp4aro" path="res://resources/items/tomato_seed.tres" id="35_64mdn"]
[ext_resource type="Texture2D" uid="uid://65e44yde224q" path="res://art/farm/Babushka_house_01.png" id="36_e5b7x"]
[ext_resource type="Resource" uid="uid://duq7tshxv6uhp" path="res://resources/items/beet_seed.tres" id="36_fv1t2"]
[ext_resource type="Texture2D" uid="uid://cyyxqmphcrjj" path="res://art/farm/farming/farmobjekte/tomaten/tomaten_template.png" id="36_l7ekk"]
[ext_resource type="AudioStream" uid="uid://cfqg50am0swb7" path="res://audio/Music/Farming_90BPM_69Bars_Loop.wav" id="37_8ey8m"]
[ext_resource type="AudioStream" uid="uid://dku1rq5cocisg" path="res://audio/Music/Farming_90BPM_69Bars.wav" id="37_di1ed"]
[ext_resource type="Shader" uid="uid://braevmqauoek7" path="res://shader/swaying_plant.gdshader" id="37_taxvr"]
@@ -79,6 +78,7 @@
[ext_resource type="Script" uid="uid://dih1b0opgc3f7" path="res://scripts/GdScript/dialogic_start_specific.gd" id="77_l7ekk"]
[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"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_wtdui"]
shader = ExtResource("13_7p0hq")
@@ -2509,6 +2509,9 @@ script = ExtResource("76_l7ekk")
_variableResource = ExtResource("77_xcwle")
_payloadToSet = "farmOutside"
[node name="SaveGameCheat" type="Node" parent="."]
script = ExtResource("79_065st")
[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"]
@@ -216,6 +216,9 @@ public partial class FieldBehaviour2D : Sprite2D
var sceneName = _sceneKeyProvider.Payload.AsString();
var id = SaveId + _fieldIndex.Payload.AsString();
string jsonPayload = SavegameService.GetSaveData(sceneName, id);
if (string.IsNullOrEmpty(jsonPayload))
return;
Dictionary<string, Variant> save = Json.ParseString(jsonPayload).AsGodotDictionary<string, Variant>();
if (save.Count > 0)
@@ -3,6 +3,7 @@ using System;
using Godot;
using System.Collections.Generic;
using System.Linq;
using Babushka.scripts.CSharp.Common.Savegame;
namespace Babushka.scripts.CSharp.Common.Inventory;
@@ -17,6 +18,9 @@ public partial class InventoryInstance : Node
[Signal]
public delegate void InventoryContentsChangedEventHandler();
private const string SCENE_NAME = "inventory";
private const string ID = "instace";
/// <summary>
/// The total amount of Inventoryslots in the inventory (empty and occupied).
/// </summary>
@@ -42,6 +46,19 @@ public partial class InventoryInstance : Node
}
}
public override void _EnterTree()
{
LoadFromSaveData();
InventoryContentsChanged += UpdateSaveData;
SlotAmountChanged += UpdateSaveData;
}
public override void _ExitTree()
{
InventoryContentsChanged -= UpdateSaveData;
SlotAmountChanged -= UpdateSaveData;
}
public InventoryActionResult AddItem(ItemInstance newItem)
{
var result = AddItemAndStackRecursive(newItem, 0);
@@ -156,4 +173,59 @@ public partial class InventoryInstance : Node
{
return items.All(HasItems);
}
#region SAVE AND LOAD
public void UpdateSaveData()
{
var saveData = new SaveData();
saveData.SceneName = SCENE_NAME;
saveData.Id = ID;
var payloadData = new Godot.Collections.Dictionary<string, Variant>();
for (int i = 0; i < _slots.Count; i++)
{
if (!_slots[i].IsEmpty())
{
string key = i.ToString();
string[] value = new string[2];
value[0] = _slots[i].itemInstance.blueprint.ResourcePath;
value[1] = _slots[i].itemInstance.amount.ToString();
payloadData.Add(key,value);
}
}
saveData.JsonPayload = Json.Stringify(payloadData, indent: "\t");
SavegameService.AppendSave(saveData);
}
public void LoadFromSaveData()
{
var sceneName = SCENE_NAME;
var id = ID;
string jsonPayload = SavegameService.GetSaveData(sceneName, id);
if (string.IsNullOrEmpty(jsonPayload))
return;
Godot.Collections.Dictionary<string, Variant> save = Json.ParseString(jsonPayload).AsGodotDictionary<string, Variant>();
if (save.Count > 0)
{
for (int i = 0; i < _slots.Count; i++)
{
if (save.TryGetValue(i.ToString(), out Variant inventoryItemData))
{
string[] savePayload = inventoryItemData.AsStringArray();
ItemResource resource = ResourceLoader.Load<ItemResource>(savePayload[0]);
int _amount = int.Parse(savePayload[1]);
ItemInstance instance = new ItemInstance { blueprint = resource, amount = _amount };
AddItem(instance);
}
}
}
}
#endregion
}
@@ -23,7 +23,7 @@ public partial class InventoryManager : Node
}
}
public InventoryInstance playerInventory = new InventoryInstance();
public InventoryInstance? playerInventory;
private int _currentSelectedSlotIndex = 0;
@@ -34,7 +34,9 @@ public partial class InventoryManager : Node
public override void _Ready()
{
playerInventory = new InventoryInstance();
playerInventory.SlotAmount = 37;
AddChild(playerInventory);
}
public InventoryActionResult CreateItem(
@@ -0,0 +1,15 @@
using Godot;
namespace Babushka.scripts.CSharp.Common.Savegame;
[Tool]
public partial class SaveCheats : Node
{
[ExportToolButton("ResetSave")] Callable _raiseAction => Callable.From(Reset);
public void Reset()
{
SavegameService.Reset();
}
}
@@ -0,0 +1 @@
uid://iquhbkr7pqeg
@@ -9,6 +9,15 @@ namespace Babushka.scripts.CSharp.Common.Savegame;
[Serializable]
public class SaveData
{
// for future use in case the structure changes.
public const int VERSION = 1;
public int version { get; set; } = VERSION;
public bool IsVersionValid()
{
return VERSION == version;
}
public string SceneName;
public string Id;
public string JsonPayload;
@@ -7,12 +7,15 @@ namespace Babushka.scripts.CSharp.Common.Savegame;
/// </summary>
public partial class SaveGameManager : Node
{
public static string USER_DATA_FILE_PATH = "user://save_data/userData.json";
public static SaveGameManager? Instance { get; private set; } = null!;
public override void _EnterTree()
{
Instance = this;
USER_DATA_FILE_PATH = ProjectSettings.GlobalizePath(USER_DATA_FILE_PATH);
SavegameService.SavePath = USER_DATA_FILE_PATH;
SavegameService.Load();
}
@@ -9,10 +9,11 @@ namespace Babushka.scripts.CSharp.Common.Savegame;
/// Handles the saving and loading of game states.
/// Holds the central SaveData object that serves as a temporary SaveFile.
/// Automatically writes to disk on every scene change.
/// Uses the Godot Json Serializer, which may implicitly convert integers to floats, so be careful when extending!
/// </summary>
public static class SavegameService
{
public static readonly string SavePath = "user://babushka_savegame.json";
public static string SavePath = "";
public static Dictionary<string, string> SaveDatas = new ();
@@ -72,8 +73,20 @@ public static class SavegameService
public static void Save()
{
string json = Json.Stringify(SaveDatas, indent: "\t");
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
file.StoreString(json);
GD.Print(SavePath);
// create cloud directory
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(SavePath) ?? "");
try
{
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
file.StoreString(json);
}
catch (Exception e)
{
GD.Print(e.Message);
}
}
/// <summary>
@@ -83,8 +96,15 @@ public static class SavegameService
{
try
{
if (!System.IO.File.Exists(SavePath))
{
SaveDatas = new();
return;
}
string saveDataJson = FileAccess.GetFileAsString(SavePath);
SaveDatas = Json.ParseString(saveDataJson).AsGodotDictionary<string, string>();
}
catch(Exception e)
{
@@ -94,4 +114,10 @@ public static class SavegameService
_loaded = true;
}
public static void Reset()
{
SaveDatas = new ();
Save();
}
}
@@ -0,0 +1,31 @@
namespace Babushka.scripts.CSharp.Common.Savegame;
/// <summary>
/// Data structure for device-specific settings that should be saved / loaded to disk.
/// Will not be synced across devices.
/// </summary>
public class SettingsData
{
/// <summary>
/// To be incremented (and migrated) on modification.
/// </summary>
public const int VERSION = 1;
public int version { get; set; } = VERSION;
public bool IsVersionValid()
{
return VERSION == version;
}
public double runtimeSeconds { get; set; }
public int windowSizeX { get; set; } = 800;
public int windowSizeY { get; set; } = 600;
public int windowPositionX { get; set; } = 100;
public int windowPositionY { get; set; } = 100;
public bool windowBorderless { get; set; }
public float volumeMaster { get; set; } = 1.0f;
public float volumeFx { get; set; } = 1.0f;
public float volumeMusic { get; set; } = 1.0f;
}
@@ -0,0 +1 @@
uid://ol8xw1ekx0c2
@@ -0,0 +1,108 @@
using System;
using System.Text.Json;
using Godot;
namespace Babushka.scripts.CSharp.Common.Savegame;
/// <summary>
/// Handles the saving and loading of local settings data.
/// </summary>
[GlobalClass]
public partial class SettingsSaveController : Node
{
public static string SETTINGS_FILE_PATH = "user://userSettings.json";
public static SettingsSaveController Instance;
public SettingsData? settings = new SettingsData();
public SettingsData settingsDefault = new SettingsData();
public event Action OnSettingsReloaded;
private bool _loadedData;
private DateTime _readyTime;
public bool LoadedData => _loadedData;
public override void _EnterTree()
{
SETTINGS_FILE_PATH = ProjectSettings.GlobalizePath(SETTINGS_FILE_PATH);
_readyTime = DateTime.Now;
Instance = this;
LoadSettings();
}
public override void _ExitTree()
{
if (settings != null)
settings.runtimeSeconds += (DateTime.Now - _readyTime).TotalSeconds;
SaveSettings();
Instance = null;
}
/// <summary>
/// Saves Settings Data onto disk.
/// </summary>
public void SaveSettings()
{
try
{
string jsonString = JsonSerializer.Serialize(
settings,
new JsonSerializerOptions() { WriteIndented = true}
);
System.IO.File.WriteAllText(SETTINGS_FILE_PATH, jsonString);
}
catch (Exception e)
{
GD.PrintErr("Error Saving Settings:", e);
Console.WriteLine(e);
}
}
/// <summary>
/// Loads Settings data from disk.
/// </summary>
public void LoadSettings()
{
_loadedData = false;
try
{
if (!System.IO.File.Exists(SETTINGS_FILE_PATH))
{
settings = new SettingsData();
}
else
{
string jsonString = System.IO.File.ReadAllText(SETTINGS_FILE_PATH);
SettingsData? loadedSettings = JsonSerializer.Deserialize<SettingsData>(jsonString);
if (loadedSettings != null && !loadedSettings.IsVersionValid())
{
_loadedData = false;
}
else
{
settings = loadedSettings;
_loadedData = true;
}
}
}
catch (Exception e)
{
GD.PrintErr("Loading Error:", e);
Console.WriteLine(e);
}
}
/// <summary>
/// Resets Settings to default.
/// </summary>
public void ResetSettings()
{
settings = JsonSerializer.Deserialize<SettingsData>(JsonSerializer.Serialize<SettingsData>(settingsDefault));
OnSettingsReloaded?.Invoke();
}
}
@@ -0,0 +1 @@
uid://cc7gnydmbcft7
@@ -0,0 +1,101 @@
using Godot;
namespace Babushka.scripts.CSharp.Common.Savegame;
/// <summary>
/// Tracks important window settings and communicates with the <see cref="SettingsSaveController"/> to save/load them.
/// </summary>
public partial class WindowSettingsSync : Node
{
private Window window;
public override void _Ready()
{
window = GetWindow();
window.SizeChanged += SaveWindowSize;
SyncSettings();
SettingsSaveController.Instance.OnSettingsReloaded += SyncSettings;
}
public override void _ExitTree()
{
SaveWindowPosition();
SaveWindowBorderless();
SaveWindowSize();
SettingsSaveController.Instance.SaveSettings();
}
/// <summary>
/// Tries to get previous settings from settings-savefile, if available.
/// </summary>
public void SyncSettings()
{
if (!SettingsSaveController.Instance.LoadedData)
{
SaveWindowPosition();
SaveWindowSize();
SaveWindowBorderless();
return;
}
SettingsData? settingsData = SettingsSaveController.Instance.settings;
if (settingsData != null)
{
window.Position = new Vector2I(settingsData.windowPositionX, settingsData.windowPositionY);
ValidateWindowPosition();
window.Size = new Vector2I(settingsData.windowSizeX, settingsData.windowSizeY);
window.Borderless = settingsData.windowBorderless;
}
}
private void ValidateWindowPosition()
{
bool validWindowPosition = false;
foreach (Rect2I displayRect in DisplayServer.GetDisplayCutouts())
{
if (displayRect.HasPoint(window.Position))
{
validWindowPosition = true;
break;
}
}
if (!validWindowPosition)
{
window.MoveToCenter();
SaveWindowPosition();
}
}
private void SaveWindowPosition()
{
SettingsData? settingsData = SettingsSaveController.Instance.settings;
if (settingsData != null)
{
settingsData.windowPositionX = window.Position.X;
settingsData.windowPositionY = window.Position.Y;
}
}
private void SaveWindowSize()
{
SettingsData? settingsData = SettingsSaveController.Instance.settings;
if (settingsData != null)
{
settingsData.windowSizeX = window.Size.X;
settingsData.windowSizeY = window.Size.Y;
}
}
private void SaveWindowBorderless()
{
SettingsData? settingsData = SettingsSaveController.Instance.settings;
if (settingsData != null)
settingsData.windowBorderless = window.Borderless;
}
}
@@ -0,0 +1 @@
uid://bbp0dyddwdbl8
+1
View File
@@ -34,6 +34,7 @@ public partial class SceneTransition : Node
public void Quit()
{
SettingsSaveController.Instance.SaveSettings();
GetTree().Quit();
}