diff --git a/Classes/Events/EventDatabase.cs b/Classes/Events/EventDatabase.cs index 89a97a82..fea84d92 100644 --- a/Classes/Events/EventDatabase.cs +++ b/Classes/Events/EventDatabase.cs @@ -17,7 +17,7 @@ public class EventDatabase ["EVENT_EVENT1_OPTION1", "EVENT_EVENT1_OPTION2", "EVENT_EVENT1_OPTION3"], ["EVENT_EVENT1_OUTCOME1", "EVENT_EVENT1_OUTCOME2", "EVENT_EVENT1_OUTCOME3"], [ - (self, node) => + (self, _) => { int randIndex = StageProducer.GlobalRng.RandiRange( 0, @@ -33,7 +33,7 @@ public class EventDatabase localizedName ); }, - (self, node) => + (self, _) => { int randIndex = StageProducer.GlobalRng.RandiRange( 0, @@ -49,12 +49,12 @@ public class EventDatabase localizedName ); }, - (self, node) => + (self, _) => { string stolenMoney = (StageProducer.PlayerStats.Money / 2).ToString(); StageProducer.PlayerStats.Money /= 2; - self.OutcomeDescriptions[2] = self.OutcomeDescriptions[0] = string.Format( + self.OutcomeDescriptions[2] = string.Format( TranslationServer.Translate("EVENT_EVENT1_OUTCOME3"), stolenMoney ); @@ -176,18 +176,18 @@ public class EventDatabase ["EVENT_EVENT3_OPTION1", "EVENT_EVENT3_OPTION2", "EVENT_EVENT3_OPTION3"], ["EVENT_EVENT3_OUTCOME1", "EVENT_EVENT3_OUTCOME2", "EVENT_EVENT3_OUTCOME3"], [ - (self, node) => + (_, _) => { StageProducer.PlayerStats.CurrentHealth = Math.Min( StageProducer.PlayerStats.CurrentHealth + 10, StageProducer.PlayerStats.MaxHealth ); }, - (self, node) => + (_, _) => { StageProducer.PlayerStats.MaxComboBar -= 5; }, - (self, node) => + (_, _) => { StageProducer.PlayerStats.Money -= 30; StageProducer.PlayerStats.AddNote(Scribe.NoteDictionary[3]); diff --git a/Classes/MidiMaestro/MidiMaestro.cs b/Classes/MidiMaestro/MidiMaestro.cs index 2c7bfa8f..be5d6c33 100644 --- a/Classes/MidiMaestro/MidiMaestro.cs +++ b/Classes/MidiMaestro/MidiMaestro.cs @@ -15,25 +15,12 @@ public partial class MidiMaestro : Resource private readonly NoteInfo[] _leftNotes; private readonly NoteInfo[] _rightNotes; - //private MidiFile strippedSong; /** * Constructor loads resource file and populates lane note arrays with NoteInfo * A string file path to a valid songMap .tres file */ public MidiMaestro(NoteChart savedChart) { - /*if (!OS.HasFeature("editor")) - { - filePath = OS.GetExecutablePath().GetBaseDir() + "/" + filePath; - } - - if (!FileAccess.FileExists(filePath)) - { - GD.PushError("ERROR: Unable to load level songMap resource file: " + filePath); - } - - NoteChart savedChart = ResourceLoader.Load(filePath);*/ - if (savedChart != null) { _upNotes = savedChart.GetLane(ArrowType.Up).ToArray(); diff --git a/Globals/SaveSystem.cs b/Globals/Configkeeper.cs similarity index 79% rename from Globals/SaveSystem.cs rename to Globals/Configkeeper.cs index 8e478d50..85e635bd 100644 --- a/Globals/SaveSystem.cs +++ b/Globals/Configkeeper.cs @@ -7,7 +7,7 @@ /** * SaveSystem: Manages FileI/O of configs and save files. */ -public static class SaveSystem +public static class Configkeeper { #region Config private const string UserConfigPath = "user://Options.cfg"; @@ -31,8 +31,6 @@ public static class SaveSystem private const bool DefaultTypeIsArrow = false; private const bool DefaultVerticalScroll = true; private const bool DefaultHighCon = false; - private const bool DefaultFirstTime = true; - private const bool DefaultHasWon = false; public enum ConfigSettings { @@ -53,8 +51,6 @@ public enum ConfigSettings LanguageKey, TypeIsArrow, HighContrast, - FirstTime, - HasWon, VerticalScroll, } @@ -81,8 +77,6 @@ private static void InitConfig() UpdateConfig(ConfigSettings.LanguageKey, DefaultLanguage); UpdateConfig(ConfigSettings.TypeIsArrow, DefaultTypeIsArrow); UpdateConfig(ConfigSettings.HighContrast, DefaultHighCon); - UpdateConfig(ConfigSettings.FirstTime, DefaultFirstTime); - UpdateConfig(ConfigSettings.HasWon, DefaultHasWon); UpdateConfig(ConfigSettings.VerticalScroll, DefaultVerticalScroll); } @@ -151,12 +145,6 @@ public static void UpdateConfig(ConfigSettings setting, Variant value) case ConfigSettings.HighContrast: _curConfigData.SetValue("Options", "HighContrast", value); break; - case ConfigSettings.FirstTime: - _curConfigData.SetValue("Game", "FirstTime", value); - break; - case ConfigSettings.HasWon: - _curConfigData.SetValue("Game", "HasWon", value); - break; default: GD.PushError("SaveSystem.UpdateConfig: Invalid config setting passed. " + setting); break; @@ -298,10 +286,6 @@ public static Variant GetConfigValue(ConfigSettings setting) return _curConfigData.GetValue("Options", "VerticalScroll", DefaultVerticalScroll); case ConfigSettings.HighContrast: return _curConfigData.GetValue("Options", "HighContrast", DefaultHighCon); - case ConfigSettings.FirstTime: - return _curConfigData.GetValue("Game", "FirstTime", DefaultFirstTime); - case ConfigSettings.HasWon: - return _curConfigData.GetValue("Game", "HasWon", DefaultHasWon); default: GD.PushError("Invalid config setting passed. " + setting); return float.MinValue; @@ -399,110 +383,4 @@ public static void ClearConfig() InitConfig(); } #endregion - - #region Save - - private const string UserSavePath = "user://MidnighRiff.save"; - - public class SaveFile - { - public ulong RngSeed { get; init; } - public ulong RngState { get; init; } - public int LastRoomIdx { get; init; } - public int Area { get; init; } - - public int Money { get; init; } - public int[] NoteIds { get; init; } - public int[] RelicIds { get; init; } - public int[] BattlePool { get; init; } - public int[] EventPool { get; init; } - public int PlayerHealth { get; init; } - public int Shortcuts { get; init; } - public int PlayerMaxCombo { get; init; } - - public SaveFile( - ulong rngSeed, - ulong rngState, - int lastRoomIdx, - int[] noteIds, - int[] relicIds, - int[] battlePool, - int[] eventPool, - int playerHealth, - int area, - int money, - int shortcuts, - int playerMaxCombo - ) - { - RngSeed = rngSeed; - RngState = rngState; - LastRoomIdx = lastRoomIdx; - NoteIds = noteIds; - RelicIds = relicIds; - BattlePool = battlePool ?? []; - EventPool = eventPool ?? []; - PlayerHealth = playerHealth; - Area = area; - Money = money; - Shortcuts = shortcuts; - PlayerMaxCombo = playerMaxCombo; - } - } - - public static void SaveGame() - { - int[] relicIds = StageProducer.PlayerStats.CurRelics.Select(r => r.Id).ToArray(); - int[] noteIds = StageProducer.PlayerStats.CurNotes.Select(r => r.Id).ToArray(); - SaveFile sv = new SaveFile( - StageProducer.GlobalRng.Seed, - StageProducer.GlobalRng.State, - StageProducer.CurRoom, - noteIds, - relicIds, - StageProducer.BattlePool?.ToArray(), - EventScene.EventPool?.ToArray(), - StageProducer.PlayerStats.CurrentHealth, - StageProducer.CurLevel.Id, - StageProducer.PlayerStats.Money, - StageProducer.PlayerStats.Shortcuts, - StageProducer.PlayerStats.MaxComboBar - ); - string json = JsonSerializer.Serialize(sv); - - FileAccess file = FileAccess.Open(UserSavePath, FileAccess.ModeFlags.Write); - - file.StoreLine(json); - file.Close(); - } - - /** - * Returns null if invalid save or save 404's. - */ - public static SaveFile LoadGame() - { - if (!FileAccess.FileExists(UserSavePath)) - return null; - FileAccess file = FileAccess.Open(UserSavePath, FileAccess.ModeFlags.Read); - string json = file.GetAsText(); - - file.Close(); - SaveFile sv; - try - { - sv = JsonSerializer.Deserialize(json); - } - catch (JsonException) - { - GD.PushWarning("Cannot deserialize save file, returning null."); - return null; - } - return sv; - } - - public static void ClearSave() - { - DirAccess.RemoveAbsolute(UserSavePath); - } - #endregion } diff --git a/Globals/Configkeeper.cs.uid b/Globals/Configkeeper.cs.uid new file mode 100644 index 00000000..55fcc578 --- /dev/null +++ b/Globals/Configkeeper.cs.uid @@ -0,0 +1 @@ +uid://bbkyrplc17j0w diff --git a/Globals/Savekeeper.cs b/Globals/Savekeeper.cs new file mode 100644 index 00000000..68cf96fe --- /dev/null +++ b/Globals/Savekeeper.cs @@ -0,0 +1,281 @@ +using System; +using System.Collections.Generic; +using Godot; +using FileAccess = Godot.FileAccess; + +/// +/// v1 of a drag and drop save system.

+/// Does not work with Godot resources for safer handling.

+/// Assumptions:

+/// T needs ToString() and T.TryParse(string value, out T result) -> bool

+/// Objects need to implement their own Serialize and Deserialize

+///
+public partial class Savekeeper : Node +{ + private const string SaveFileDirectory = "user://saves"; + private const string SaveFileExtension = ".save"; + private const string DefaultSaveFileName = "MidnightRiff"; + + private const char Delimiter = '|'; + + public delegate void SavingHandler(); + + /// + /// Event that a save is underway. Subscribe to serialize data before writing. + /// + public static event SavingHandler Saving; + + public delegate void GameSavedHandler(); + public static event GameSavedHandler GameSaved; + + public delegate void LoadedHandler(); + public static event LoadedHandler Loaded; + + public override void _EnterTree() + { + DirAccess.MakeDirAbsolute(SaveFileDirectory); + } + + public static Dictionary GameSaveObjects { get; private set; } = + new Dictionary(); + + private static void HandlePrevSave() + { + return; //Placeholder to do things, e.g. make a backup + } + + public static void RecordSave(string savePath = DefaultSaveFileName + SaveFileExtension) + { + Saving?.Invoke(); + Callable.From(() => SaveToFile(savePath)).CallDeferred(); + } + + public static bool SaveToFile(string savePath = DefaultSaveFileName + SaveFileExtension) + { + if (string.IsNullOrEmpty(savePath)) + return false; + FileAccess file = FileAccess.Open( + SaveFileDirectory + "/" + savePath, + FileAccess.ModeFlags.Write + ); + if (file == null) + return false; + + foreach ((string key, string value) in GameSaveObjects) + file.StoreLine(key + Delimiter + value); + file.Close(); + + GameSaved?.Invoke(); + return true; + } + + public static bool LoadFromFile(string savePath = DefaultSaveFileName + SaveFileExtension) + { + if (string.IsNullOrEmpty(savePath)) + return false; + FileAccess file = FileAccess.Open( + SaveFileDirectory + "/" + savePath, + FileAccess.ModeFlags.Read + ); + if (file == null) + return false; + + HandlePrevSave(); + + while (file.GetPosition() < file.GetLength()) + { + string line = file.GetLine().Trim(); + int idx = line.IndexOf(Delimiter); + if (idx == -1) + continue; + + string key = line.Substring(0, idx); + string value = SanitizeSaveString(line.Substring(idx)); + if (value == null) + continue; + + GameSaveObjects[key] = value; + } + + Loaded?.Invoke(); + return true; + } + + private static string SanitizeSaveString(string saveString) + { + if (string.IsNullOrEmpty(saveString)) + return null; + saveString = saveString.Trim(); + if (saveString.Length == 0 || saveString[0] != Delimiter) + return null; + return saveString.Substring(1); + } + + const string InvalidFormatString = "InvalidString"; + + //Ex: Position.X|45.8| + public static string Format(string valName, object value) + { + if (value == null) + return ""; + if (value is string s && !s.IsValidFileName()) + return valName + Delimiter + InvalidFormatString + Delimiter; + + return valName + Delimiter + value + Delimiter; + } + + private const string ArrayDelimiter = "*"; + + //Ex: ValidIds|[12*1*4*5]| + public static string FormatArray(string valName, T[] value) + { + if (value == null) + return ""; + String retString = valName + Delimiter + '['; + + foreach (object o in value) + { + if (o is string s && !s.IsValidFileName()) + { + retString += InvalidFormatString + ArrayDelimiter; + continue; + } + retString += o.ToString() + ArrayDelimiter; + } + retString += "]" + Delimiter; + return retString; + } + + public readonly struct ParseResult( + T value, + bool success, + int nextIdx, + string message = "Success!" + ) + { + public T Value { get; init; } = value; + public bool Success { get; init; } = success; + public int NextIdx { get; init; } = nextIdx; + public string Message { get; init; } = message; + } + + public delegate bool TryParseHandler(string value, out T result); //https://stackoverflow.com/questions/2961656/generic-tryparse + + public static bool StringParse(string value, out string result) + { + result = value; + return true; + } + + public static ParseResult Parse( + string valName, + string saveString, + int startIdx, + TryParseHandler handler + ) + { + if (string.IsNullOrEmpty(valName) || string.IsNullOrEmpty(saveString)) + return new ParseResult( + default, + false, + startIdx, + $"String was empty! {valName} {saveString}" + ); + + ParseResult success = ParseToSubStringValue(saveString, valName, startIdx); + if (!success.Success) + return new ParseResult(default, false, startIdx, success.Message); + string value = success.Value; + int finalIdx = success.NextIdx; + + if (handler(value, out var result)) + return new ParseResult(result, true, finalIdx); + + return new ParseResult( + default, + false, + startIdx, + $"Unable to parse from: \"{value}\" to type: {typeof(T)}" + ); + } + + public static ParseResult ParseArray( + string valName, + string saveString, + int startIdx, + TryParseHandler handler + ) + { + if (string.IsNullOrEmpty(valName) || string.IsNullOrEmpty(saveString)) + return new ParseResult( + default, + false, + startIdx, + $"String was empty! {valName} {saveString}" + ); + + ParseResult success = ParseToSubStringValue(saveString, valName, startIdx); + if (!success.Success) + return new ParseResult(default, false, startIdx, success.Message); + string value = success.Value; + int finalIdx = success.NextIdx; + + string[] values = value.Replace("[", "").Replace("]", "").Split(ArrayDelimiter); + List list = new List(); + foreach (String s in values) + { + if (string.IsNullOrEmpty(s)) + continue; + if (!handler(s, out var result)) + return new ParseResult( + list.ToArray(), + false, + finalIdx, + $"Unable to parse from: \"{s}\" to type: {typeof(T)}. Returning any successful values." + ); + + list.Add(result); + } + + return new ParseResult(list.ToArray(), true, finalIdx); + } + + private static ParseResult ParseToSubStringValue( + string saveString, + string valName, + int startIdx + ) + { + int nextIdx = saveString.IndexOf(valName, startIdx, StringComparison.Ordinal); + if (nextIdx == -1) + return new ParseResult(default, false, startIdx, $"Name not found! {valName}"); + + nextIdx += valName.Length + 1; + int finalIdx = saveString.IndexOf(Delimiter, nextIdx); + if (finalIdx == -1) + return new ParseResult( + default, + false, + startIdx, + $"No final delimiter found around value! \n String received: {saveString}, from position {nextIdx}" + ); + string value = saveString.Substring(nextIdx, finalIdx - nextIdx); + + return new ParseResult(value, true, finalIdx); + } + + #region Project Specific + + public const string DefaultRunSaveHeader = "CurrentGame"; + + public static void ClearRun() + { + if (GameSaveObjects.ContainsKey(DefaultRunSaveHeader)) + { + GameSaveObjects.Remove(DefaultRunSaveHeader); + SaveToFile(); + } + } + + #endregion +} diff --git a/Globals/Savekeeper.cs.uid b/Globals/Savekeeper.cs.uid new file mode 100644 index 00000000..18d0030f --- /dev/null +++ b/Globals/Savekeeper.cs.uid @@ -0,0 +1 @@ +uid://ckw1qew68cetv diff --git a/Globals/Scribe.cs b/Globals/Scribe.cs index 7cab15df..80f0a6d2 100644 --- a/Globals/Scribe.cs +++ b/Globals/Scribe.cs @@ -715,7 +715,7 @@ e is BattleDirector.Harbinger.OnDamageInstanceArgs dmgArgs ), }; - private static string DefaultNoteChartPath = "Audio/songMaps/"; + private static string DefaultNoteChartPath = "res://Audio/songMaps/"; public static readonly SongTemplate[] SongDictionary = new[] //Generalize and make pools for areas/room types { @@ -800,13 +800,13 @@ e is BattleDirector.Harbinger.OnDamageInstanceArgs dmgArgs ResourceLoader.Load(DefaultNoteChartPath + "FrostWaltz.tres") ), new SongTemplate( // 16 - name: "Astrorat", - enemyScenePath: [P_Astrorat.LoadPath], + "Astrorat", + [P_Astrorat.LoadPath], ResourceLoader.Load(DefaultNoteChartPath + "Astrorat.tres") ), new SongTemplate( // 17 - name: "CatGirl", - enemyScenePath: [P_Midriff.LoadPath], + "CatGirl", + [P_Midriff.LoadPath], ResourceLoader.Load(DefaultNoteChartPath + "Jammin' Forest.tres") ), }; diff --git a/Globals/StageProducer.cs b/Globals/StageProducer.cs index b0a3e6e0..62f8a742 100644 --- a/Globals/StageProducer.cs +++ b/Globals/StageProducer.cs @@ -16,7 +16,7 @@ public partial class StageProducer : Node public static readonly RandomNumberGenerator GlobalRng = new(); public static MapLevels CurLevel { get; private set; } - public static List BattlePool { get; private set; } + public static List BattlePool { get; private set; } = []; public static MapGrid Map { get; private set; } = new(); private Stages _curStage = Stages.Title; @@ -34,6 +34,11 @@ public partial class StageProducer : Node #region Initialization public override void _EnterTree() { + Savekeeper.Saving += SerializeRun; + Savekeeper.Saving += SerializePersist; + Savekeeper.LoadFromFile(); + DeserializePersist(); + InitFromCfg(); LiveInstance = this; @@ -48,22 +53,22 @@ public override void _EnterTree() public void InitFromCfg() { OptionsMenu.ChangeVolume( - SaveSystem.GetConfigValue(SaveSystem.ConfigSettings.Volume).As() + Configkeeper.GetConfigValue(Configkeeper.ConfigSettings.Volume).As() ); TranslationServer.SetLocale( - SaveSystem.GetConfigValue(SaveSystem.ConfigSettings.LanguageKey).As() + Configkeeper.GetConfigValue(Configkeeper.ConfigSettings.LanguageKey).As() ); ContrastFilter = GD.Load("res://Globals/ContrastFilter/ContrastFilter.tscn") .Instantiate(); - ContrastFilter.Visible = SaveSystem - .GetConfigValue(SaveSystem.ConfigSettings.HighContrast) + ContrastFilter.Visible = Configkeeper + .GetConfigValue(Configkeeper.ConfigSettings.HighContrast) .AsBool(); GetTree().Root.CallDeferred("add_child", ContrastFilter); - InputHandler.UseArrows = SaveSystem - .GetConfigValue(SaveSystem.ConfigSettings.TypeIsArrow) + InputHandler.UseArrows = Configkeeper + .GetConfigValue(Configkeeper.ConfigSettings.TypeIsArrow) .AsBool(); - BattleDirector.VerticalScroll = SaveSystem - .GetConfigValue(SaveSystem.ConfigSettings.VerticalScroll) + BattleDirector.VerticalScroll = Configkeeper + .GetConfigValue(Configkeeper.ConfigSettings.VerticalScroll) .AsBool(); } @@ -77,7 +82,7 @@ private void GenerateMapConsistent() private void StartNewGame() { GlobalRng.Randomize(); - if ((bool)SaveSystem.GetConfigValue(SaveSystem.ConfigSettings.FirstTime)) + if (GetPersistantVal(PersistKeys.TutorialDone) == 0) CurLevel = MapLevels.GetLevelFromId(0); else CurLevel = MapLevels.GetLevelFromId(1); @@ -86,8 +91,8 @@ private void StartNewGame() PlayerStats = new PlayerStats(); CurRoom = Map.GetRooms()[0].Idx; - BattlePool = null; - EventScene.EventPool = null; + BattlePool = []; + EventScene.EventPool = []; Scribe.InitRelicPools(); IsInitialized = true; MapGrid.ForceEliteBattles = false; @@ -95,36 +100,12 @@ private void StartNewGame() private bool LoadGame() { - SaveSystem.SaveFile sv = SaveSystem.LoadGame(); - if (sv == null) + if (!DeserializeRun()) { GD.PushWarning("Can't load game, either file 404 or invalid file."); return false; } - GlobalRng.Seed = sv.RngSeed; - CurLevel = MapLevels.GetLevelFromId(sv.Area); - BattlePool = sv.BattlePool.ToList(); - EventScene.EventPool = sv.EventPool.ToList(); - GenerateMapConsistent(); - GlobalRng.State = sv.RngState; - CurRoom = sv.LastRoomIdx; - - Scribe.InitRelicPools(); - PlayerStats = new PlayerStats(); - PlayerStats.CurNotes = []; - foreach (int noteId in sv.NoteIds) - { - PlayerStats.AddNote(Scribe.NoteDictionary[noteId]); - } - foreach (int relicId in sv.RelicIds) - { - PlayerStats.AddRelic(Scribe.RelicDictionary[relicId]); - } - PlayerStats.CurrentHealth = sv.PlayerHealth; - PlayerStats.Money = sv.Money; - PlayerStats.Shortcuts = sv.Shortcuts; - PlayerStats.MaxComboBar = sv.PlayerMaxCombo; IsInitialized = true; return true; } @@ -360,13 +341,282 @@ public static bool IsMoreLevels() public void ProgressLevels() { - GD.Print(CurLevel.Id); CurLevel = CurLevel.GetNextLevel(); Map = new(); GenerateMapConsistent(); CurRoom = Map.GetRooms()[0].Idx; - BattlePool = null; + BattlePool = []; + } + + #endregion + + #region Persistent Data + private const string PersistenceHeader = "PersistVals"; + + public enum PersistKeys + { + TutorialDone = 0, + HasWon = 1, + } //Relative order needs to be preserved between versions. + + private static int[] PersistantValues { get; set; } = [0, 0]; //Dumb and hacky right now. Literally doing this to avoid bool spam for now. + private const string PersistentIntValName = "PersistInts"; + + public static int GetPersistantVal(PersistKeys key) + { + return PersistantValues[(int)key]; + } + + public static void UpdatePersistantValues(PersistKeys key, int newVal) + { + PersistantValues[(int)key] = newVal; + SerializePersist(); + Savekeeper.SaveToFile(); + } + + private static void SerializePersist() + { + string saveString = ""; + saveString += Savekeeper.FormatArray(PersistentIntValName, PersistantValues); + Savekeeper.GameSaveObjects[PersistenceHeader] = saveString; + } + + private void DeserializePersist() + { + if (!Savekeeper.GameSaveObjects.TryGetValue(PersistenceHeader, out var loadPers)) + { + GD.PushWarning("Savekeeper does not contain persistence key!"); + return; + } + + int idx = 0; + var success = Savekeeper.ParseArray(PersistentIntValName, loadPers, idx, int.TryParse); + if (success.Success) + { + int[] tempVals = success.Value; + for (int i = 0; i < tempVals.Length && i < PersistantValues.Length; i++) //Manually update to safeguard against saves breaking when values are added. + PersistantValues[i] = tempVals[i]; + return; + } + GD.PushWarning( + $"Error deserializing persistent values: {loadPers} Error: {success.Message}" + ); + } + #endregion + + #region Saving + + enum RunSaveValues + { //Maintain in order of needing to be saved & loaded + RngSeed, + Area, + BattlePool, + EventPool, + RngState, + LastRoomIdx, + NoteIds, + RelicIds, + PlayerHealth, + Money, + Shortcuts, + PlayerMaxCombo, + } + + private void SerializeRun() + { + if (!IsInitialized) + return; + string saveString = ""; + saveString += + Savekeeper.Format(RunSaveValues.RngSeed.ToString(), GlobalRng.Seed) + + Savekeeper.Format(RunSaveValues.Area.ToString(), CurLevel.Id) + + Savekeeper.FormatArray(RunSaveValues.BattlePool.ToString(), BattlePool.ToArray()) + + Savekeeper.FormatArray( + RunSaveValues.EventPool.ToString(), + EventScene.EventPool.ToArray() + ) + + Savekeeper.Format(RunSaveValues.RngState.ToString(), GlobalRng.State) + + Savekeeper.Format(RunSaveValues.LastRoomIdx.ToString(), CurRoom) + + Savekeeper.FormatArray( + RunSaveValues.NoteIds.ToString(), + PlayerStats.CurNotes.Select(r => r.Id).ToArray() + ) + + Savekeeper.FormatArray( + RunSaveValues.RelicIds.ToString(), + PlayerStats.CurRelics.Select(r => r.Id).ToArray() + ) + + Savekeeper.Format(RunSaveValues.PlayerHealth.ToString(), PlayerStats.CurrentHealth) + + Savekeeper.Format(RunSaveValues.Money.ToString(), PlayerStats.Money) + + Savekeeper.Format(RunSaveValues.Shortcuts.ToString(), PlayerStats.Shortcuts) + + Savekeeper.Format(RunSaveValues.PlayerMaxCombo.ToString(), PlayerStats.MaxComboBar); + + Savekeeper.GameSaveObjects[Savekeeper.DefaultRunSaveHeader] = saveString; + } + + private bool DeserializeRun() //TODO: This is really verbose and bad. + { + if (!Savekeeper.GameSaveObjects.ContainsKey(Savekeeper.DefaultRunSaveHeader)) + return false; + int idx = 0; + string loadRun = Savekeeper.GameSaveObjects[Savekeeper.DefaultRunSaveHeader]; + + var ulongSuccess = Savekeeper.Parse( + RunSaveValues.RngSeed.ToString(), + loadRun, + idx, + ulong.TryParse + ); + if (!ulongSuccess.Success) + return false; + GlobalRng.Seed = ulongSuccess.Value; + idx = ulongSuccess.NextIdx; + + var intSuccess = Savekeeper.Parse( + RunSaveValues.Area.ToString(), + loadRun, + idx, + int.TryParse + ); + if (!intSuccess.Success) + return false; + CurLevel = MapLevels.GetLevelFromId(intSuccess.Value); + idx = intSuccess.NextIdx; + + var bPoolSuccess = Savekeeper.ParseArray( + RunSaveValues.BattlePool.ToString(), + loadRun, + idx, + int.TryParse + ); + if (bPoolSuccess.Success) + { + BattlePool = bPoolSuccess.Value.ToList(); + idx = bPoolSuccess.NextIdx; + } + else + { + GD.PushWarning("Could not parse battle pool!"); + BattlePool = []; + } + + var ePoolSuccess = Savekeeper.ParseArray( + RunSaveValues.EventPool.ToString(), + loadRun, + idx, + int.TryParse + ); + if (ePoolSuccess.Success) + { + EventScene.EventPool = ePoolSuccess.Value.ToList(); + idx = ePoolSuccess.NextIdx; + } + else + { + GD.PushWarning("Could not parse event pool!"); + EventScene.EventPool = []; + } + + GenerateMapConsistent(); + + ulongSuccess = Savekeeper.Parse( + RunSaveValues.RngState.ToString(), + loadRun, + idx, + ulong.TryParse + ); + if (!ulongSuccess.Success) + return false; + GlobalRng.State = ulongSuccess.Value; + idx = ulongSuccess.NextIdx; + + intSuccess = Savekeeper.Parse( + RunSaveValues.LastRoomIdx.ToString(), + loadRun, + idx, + int.TryParse + ); + if (!intSuccess.Success) + return false; + CurRoom = intSuccess.Value; + idx = intSuccess.NextIdx; + + Scribe.InitRelicPools(); + PlayerStats = new PlayerStats(); + PlayerStats.CurNotes = []; + + var noteSuccess = Savekeeper.ParseArray( + RunSaveValues.NoteIds.ToString(), + loadRun, + idx, + int.TryParse + ); + if (!noteSuccess.Success) + return false; + foreach (int noteId in noteSuccess.Value) + { + PlayerStats.AddNote(Scribe.NoteDictionary[noteId]); + } + idx = noteSuccess.NextIdx; + + var relicSuccess = Savekeeper.ParseArray( + RunSaveValues.RelicIds.ToString(), + loadRun, + idx, + int.TryParse + ); + if (!relicSuccess.Success) + return false; + foreach (int relicId in relicSuccess.Value) + { + PlayerStats.AddRelic(Scribe.RelicDictionary[relicId]); + } + idx = relicSuccess.NextIdx; + + intSuccess = Savekeeper.Parse( + RunSaveValues.PlayerHealth.ToString(), + loadRun, + idx, + int.TryParse + ); + if (!intSuccess.Success) + return false; + PlayerStats.CurrentHealth = intSuccess.Value; + idx = intSuccess.NextIdx; + + intSuccess = Savekeeper.Parse( + RunSaveValues.Money.ToString(), + loadRun, + idx, + int.TryParse + ); + if (!intSuccess.Success) + return false; + PlayerStats.Money = intSuccess.Value; + idx = intSuccess.NextIdx; + + intSuccess = Savekeeper.Parse( + RunSaveValues.Shortcuts.ToString(), + loadRun, + idx, + int.TryParse + ); + if (!intSuccess.Success) + return false; + PlayerStats.Shortcuts = intSuccess.Value; + idx = intSuccess.NextIdx; + + intSuccess = Savekeeper.Parse( + RunSaveValues.PlayerMaxCombo.ToString(), + loadRun, + idx, + int.TryParse + ); + if (!intSuccess.Success) + return false; + PlayerStats.MaxComboBar = intSuccess.Value; + + return true; } #endregion diff --git a/Scenes/BattleDirector/Scripts/BattleDirector.cs b/Scenes/BattleDirector/Scripts/BattleDirector.cs index 19b14d52..1040f01e 100644 --- a/Scenes/BattleDirector/Scripts/BattleDirector.cs +++ b/Scenes/BattleDirector/Scripts/BattleDirector.cs @@ -238,7 +238,6 @@ public override void _UnhandledInput(InputEvent @event) { if (@event is InputEventKey eventKey && eventKey.Pressed && !eventKey.Echo) { - return; if (eventKey.Keycode == Key.Key0) { DebugKillEnemy(); @@ -389,7 +388,7 @@ private void OnBattleLost() return; } Audio.StreamPaused = true; - SaveSystem.ClearSave(); + Savekeeper.ClearRun(); AddChild(GD.Load(EndScreen.LoadPath).Instantiate()); ProcessMode = ProcessModeEnum.Disabled; } diff --git a/Scenes/BattleDirector/Scripts/NotePlacementBar.cs b/Scenes/BattleDirector/Scripts/NotePlacementBar.cs index d69e5314..94c9ccff 100644 --- a/Scenes/BattleDirector/Scripts/NotePlacementBar.cs +++ b/Scenes/BattleDirector/Scripts/NotePlacementBar.cs @@ -230,8 +230,8 @@ public Note NotePlaced() { if (!CanPlaceNote()) GD.PushWarning("Note is attempting placement without a full bar!"); - string inputType = SaveSystem - .GetConfigValue(SaveSystem.ConfigSettings.InputType) + string inputType = Configkeeper + .GetConfigValue(Configkeeper.ConfigSettings.InputType) .ToString(); Note placedNote = GetNote(Input.IsActionPressed(inputType + "_secondaryPlacement")); CurrentBarValue -= placedNote.CostModifier * MaxValue; diff --git a/Scenes/BattleDirector/Tutorial/Toriel.cs b/Scenes/BattleDirector/Tutorial/Toriel.cs index 1ab5d190..d175c80a 100644 --- a/Scenes/BattleDirector/Tutorial/Toriel.cs +++ b/Scenes/BattleDirector/Tutorial/Toriel.cs @@ -58,7 +58,9 @@ public override void _Process(double delta) _nextButton?.GrabFocus(); } UpdateInputSprites(); - string scheme = SaveSystem.GetConfigValue(SaveSystem.ConfigSettings.InputType).As(); + string scheme = Configkeeper + .GetConfigValue(Configkeeper.ConfigSettings.InputType) + .As(); if (_waitingForPlace && Input.IsActionPressed(scheme + "_arrowRight")) { GetViewport().SetInputAsHandled(); @@ -68,7 +70,9 @@ public override void _Process(double delta) private void UpdateInputSprites() { - string prefix = SaveSystem.GetConfigValue(SaveSystem.ConfigSettings.InputType).ToString(); + string prefix = Configkeeper + .GetConfigValue(Configkeeper.ConfigSettings.InputType) + .ToString(); _inputSprites[0].Texture = GD.Load( ControlSettings.GetTextureForInput(prefix + "_arrowUp") ); diff --git a/Scenes/CustomSong/CustomScore.cs b/Scenes/CustomSong/CustomScore.cs index 83a1c8af..99773c1c 100644 --- a/Scenes/CustomSong/CustomScore.cs +++ b/Scenes/CustomSong/CustomScore.cs @@ -28,9 +28,12 @@ private enum ScoringVals public delegate void FinishedHandler(); public event FinishedHandler Finished; + private BattleDirector.Harbinger.NoteHitHandler _noteHitListener; + private BattleDirector.Harbinger.NotePlacedHandler _notePlacedListener; + public void ListenToDirector() { - BattleDirector.Harbinger.Instance.NoteHit += e => + _noteHitListener = e => { if (e is not BattleDirector.Harbinger.NoteHitArgs nArgs) return; @@ -44,10 +47,19 @@ public void ListenToDirector() break; } }; - BattleDirector.Harbinger.Instance.NotePlaced += _ => + _notePlacedListener = _ => { score[(int)ScoringVals.NotesPlaced] += 1; }; + + BattleDirector.Harbinger.Instance.NotePlaced += _notePlacedListener; + BattleDirector.Harbinger.Instance.NoteHit += _noteHitListener; + } + + public override void _ExitTree() + { + BattleDirector.Harbinger.Instance.NotePlaced -= _notePlacedListener; + BattleDirector.Harbinger.Instance.NoteHit -= _noteHitListener; } public CustomScore ShowResults(BattleDirector battleDirector, float enemyPercent) diff --git a/Scenes/EventScene/EventScene.cs b/Scenes/EventScene/EventScene.cs index 287995c2..2de1aa6e 100644 --- a/Scenes/EventScene/EventScene.cs +++ b/Scenes/EventScene/EventScene.cs @@ -26,7 +26,7 @@ public partial class EventScene : Node [Export] private MarginContainer _continueContainer; - public static List EventPool; + public static List EventPool = []; private static readonly Theme ButtonTheme = GD.Load( "res://Scenes/UI/Assets/GeneralTheme.tres" diff --git a/Scenes/Maps/Scripts/Cartographer.cs b/Scenes/Maps/Scripts/Cartographer.cs index 66e421d4..00dddba2 100644 --- a/Scenes/Maps/Scripts/Cartographer.cs +++ b/Scenes/Maps/Scripts/Cartographer.cs @@ -38,7 +38,7 @@ public partial class Cartographer : Node2D public override void _Ready() { DrawMap(); - SaveSystem.SaveGame(); + Savekeeper.RecordSave(); if ( StageProducer.GetCurRoom().Type == Stages.Boss && StageProducer.GetCurRoom().Children.Length == 0 @@ -74,10 +74,10 @@ public override void _Process(double delta) public override void _EnterTree() { BgAudioPlayer.LiveInstance.PlayLevelMusic(); - if (!SaveSystem.GetConfigValue(SaveSystem.ConfigSettings.FirstTime).AsBool()) + if (StageProducer.GetPersistantVal(StageProducer.PersistKeys.TutorialDone) == 1) return; BattleDirector.VerticalScroll = true; - SaveSystem.UpdateConfig(SaveSystem.ConfigSettings.VerticalScroll, true); + Configkeeper.UpdateConfig(Configkeeper.ConfigSettings.VerticalScroll, true); } private Vector2 GetPosition(int x, int y) diff --git a/Scenes/NoteManager/Assets/NoteWIthOutline.png b/Scenes/NoteManager/Assets/NoteWithOutline.png similarity index 100% rename from Scenes/NoteManager/Assets/NoteWIthOutline.png rename to Scenes/NoteManager/Assets/NoteWithOutline.png diff --git a/Scenes/NoteManager/Assets/NoteWIthOutline.png.import b/Scenes/NoteManager/Assets/NoteWithOutline.png.import similarity index 69% rename from Scenes/NoteManager/Assets/NoteWIthOutline.png.import rename to Scenes/NoteManager/Assets/NoteWithOutline.png.import index e9bef490..caa50296 100644 --- a/Scenes/NoteManager/Assets/NoteWIthOutline.png.import +++ b/Scenes/NoteManager/Assets/NoteWithOutline.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://coav3xvksq4jy" -path="res://.godot/imported/NoteWIthOutline.png-00b7213af7b9958e4dcd747d842ce073.ctex" +path="res://.godot/imported/NoteWithOutline.png-fd6e84a72270a62aa6b38d41c0180c0f.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://Scenes/NoteManager/Assets/NoteWIthOutline.png" -dest_files=["res://.godot/imported/NoteWIthOutline.png-00b7213af7b9958e4dcd747d842ce073.ctex"] +source_file="res://Scenes/NoteManager/Assets/NoteWithOutline.png" +dest_files=["res://.godot/imported/NoteWithOutline.png-fd6e84a72270a62aa6b38d41c0180c0f.ctex"] [params] diff --git a/Scenes/NoteManager/Scripts/InputHandler.cs b/Scenes/NoteManager/Scripts/InputHandler.cs index c61dfdb2..9d927e89 100644 --- a/Scenes/NoteManager/Scripts/InputHandler.cs +++ b/Scenes/NoteManager/Scripts/InputHandler.cs @@ -65,10 +65,12 @@ public override void _Ready() public override void _Process(double delta) { //TODO: Add change control scheme signal, so we don't query each frame. - string scheme = SaveSystem.GetConfigValue(SaveSystem.ConfigSettings.InputType).As(); + string scheme = Configkeeper + .GetConfigValue(Configkeeper.ConfigSettings.InputType) + .As(); if (Input.GetConnectedJoypads().Count <= 0 && scheme == "CONTROLLER") { - SaveSystem.UpdateConfig(SaveSystem.ConfigSettings.InputType, "WASD"); + Configkeeper.UpdateConfig(Configkeeper.ConfigSettings.InputType, "WASD"); } if (BattleDirector.PlayerDisabled) @@ -92,7 +94,7 @@ public override void _UnhandledInput(InputEvent @event) { if (@event is InputEventJoypadButton) { //Force Controller if controller was pressed - SaveSystem.UpdateConfig(SaveSystem.ConfigSettings.InputType, "CONTROLLER"); + Configkeeper.UpdateConfig(Configkeeper.ConfigSettings.InputType, "CONTROLLER"); } } @@ -127,35 +129,16 @@ private void UpdateArrowSprites() { if (!UseArrows) return; - Arrows[(int)ArrowType.Left].Node.Texture = GD.Load( - ArrowFolderPath + "New_Arrow.png" - ); - Arrows[(int)ArrowType.Left].Node.Outline.Texture = GD.Load( - ArrowFolderPath + "Arrow_Outline.png" - ); - Arrows[(int)ArrowType.Left].Node.RotationDegrees = 180f; + Texture2D arrowTexture = GD.Load(ArrowFolderPath + "New_Arrow.png"); + Texture2D outlineTexture = GD.Load(ArrowFolderPath + "Arrow_Outline.png"); + float[] rotations = [270f, 90f, 180f]; //Up, down, left - Arrows[(int)ArrowType.Up].Node.Texture = GD.Load( - ArrowFolderPath + "New_Arrow.png" - ); - Arrows[(int)ArrowType.Up].Node.Outline.Texture = GD.Load( - ArrowFolderPath + "Arrow_Outline.png" - ); - Arrows[(int)ArrowType.Up].Node.RotationDegrees = 270f; - - Arrows[(int)ArrowType.Down].Node.Texture = GD.Load( - ArrowFolderPath + "New_Arrow.png" - ); - Arrows[(int)ArrowType.Down].Node.Outline.Texture = GD.Load( - ArrowFolderPath + "Arrow_Outline.png" - ); - Arrows[(int)ArrowType.Down].Node.RotationDegrees = 90f; - - Arrows[(int)ArrowType.Right].Node.Texture = GD.Load( - ArrowFolderPath + "New_Arrow.png" - ); - Arrows[(int)ArrowType.Right].Node.Outline.Texture = GD.Load( - ArrowFolderPath + "Arrow_Outline.png" - ); + for (int i = 0; i < Arrows.Length; i++) + { + Arrows[i].Node.Texture = arrowTexture; + Arrows[i].Node.Outline.Texture = outlineTexture; + if (i < rotations.Length) + Arrows[i].Node.RotationDegrees = rotations[i]; + } } } diff --git a/Scenes/Puppets/Enemies/BossBlood/P_BossBlood.cs b/Scenes/Puppets/Enemies/BossBlood/P_BossBlood.cs index 9198c3b9..bd4748c1 100644 --- a/Scenes/Puppets/Enemies/BossBlood/P_BossBlood.cs +++ b/Scenes/Puppets/Enemies/BossBlood/P_BossBlood.cs @@ -37,7 +37,7 @@ public override void _Ready() this, BattleEffectTrigger.OnDamageInstance, 1, - (e, eff, val) => + (e, _, _) => { if (e is not BattleDirector.Harbinger.OnDamageInstanceArgs dArgs) return; diff --git a/Scenes/Puppets/Enemies/CyberFox/P_CyberFox.cs b/Scenes/Puppets/Enemies/CyberFox/P_CyberFox.cs index 985adf80..d1099374 100644 --- a/Scenes/Puppets/Enemies/CyberFox/P_CyberFox.cs +++ b/Scenes/Puppets/Enemies/CyberFox/P_CyberFox.cs @@ -36,7 +36,7 @@ public override void _Ready() this, BattleEffectTrigger.OnLoop, 1, - (e, eff, val) => + (e, _, _) => { e.BD.AddStatus(Targetting.First, StatusEffect.Dodge, 1); _effectNode.TriggerGlitch(1f); diff --git a/Scenes/Puppets/Enemies/Effigy/P_Effigy.cs b/Scenes/Puppets/Enemies/Effigy/P_Effigy.cs index fb510cd6..8cd0e024 100644 --- a/Scenes/Puppets/Enemies/Effigy/P_Effigy.cs +++ b/Scenes/Puppets/Enemies/Effigy/P_Effigy.cs @@ -20,7 +20,7 @@ public override void _Ready() this, BattleEffectTrigger.OnDamageInstance, 1, - (e, eff, val) => + (e, _, _) => { if (e is not BattleDirector.Harbinger.OnDamageInstanceArgs dArgs) return; @@ -35,7 +35,7 @@ public override void _Ready() this, BattleEffectTrigger.OnBattleEnd, 1, - (e, eff, val) => + (e, _, _) => { e.BD.DealDamage(Targetting.Player, e.BD.Player.GetCurrentHealth() - 1, null); } @@ -44,7 +44,7 @@ public override void _Ready() this, BattleEffectTrigger.OnBattleStart, 1, - (e, eff, val) => + (e, _, _) => { _tutorialInstance = Toriel.AttachNewToriel(e.BD); _tutorialInstance.BossDialogue(); diff --git a/Scenes/Puppets/Enemies/Keythulu/P_Keythulu.cs b/Scenes/Puppets/Enemies/Keythulu/P_Keythulu.cs index fb91b3ac..1f721d92 100644 --- a/Scenes/Puppets/Enemies/Keythulu/P_Keythulu.cs +++ b/Scenes/Puppets/Enemies/Keythulu/P_Keythulu.cs @@ -42,7 +42,7 @@ public override void _Ready() this, BattleEffectTrigger.OnLoop, 3, - (e, eff, val) => + (_, _, _) => { _effectSprite.Position = Vector2.Zero; _effectSprite.Visible = true; @@ -63,7 +63,7 @@ public override void _Ready() this, BattleEffectTrigger.OnDamageInstance, 1, - (e, eff, val) => + (e, _, _) => { if ( StageProducer.Config.RoomType == Stages.Custom @@ -76,7 +76,7 @@ public override void _Ready() ) { SteamWhisperer.PopAchievement("actTwoComp"); - SaveSystem.UpdateConfig(SaveSystem.ConfigSettings.HasWon, true); + StageProducer.UpdatePersistantValues(StageProducer.PersistKeys.HasWon, 1); } } ), diff --git a/Scenes/Puppets/Enemies/Strawman/P_Strawman.cs b/Scenes/Puppets/Enemies/Strawman/P_Strawman.cs index cfb99b03..7e58022e 100644 --- a/Scenes/Puppets/Enemies/Strawman/P_Strawman.cs +++ b/Scenes/Puppets/Enemies/Strawman/P_Strawman.cs @@ -22,7 +22,7 @@ public override void _Ready() this, BattleEffectTrigger.OnBattleStart, 1, - (e, eff, val) => + (e, _, _) => { _tutorialInstance = Toriel.AttachNewToriel(e.BD); _tutorialInstance.IntroDialogue(); @@ -32,7 +32,7 @@ public override void _Ready() this, BattleEffectTrigger.OnDamageInstance, 2, - (e, eff, val) => + (e, eff, _) => { if (e is not BattleDirector.Harbinger.OnDamageInstanceArgs dArgs) return; @@ -53,7 +53,7 @@ public override void _Ready() this, BattleEffectTrigger.OnLoop, 1, - (e, eff, val) => + (e, _, _) => { if (e is not BattleDirector.Harbinger.LoopEventArgs lArgs) return; @@ -67,7 +67,7 @@ public override void _Ready() this, BattleEffectTrigger.NoteHit, 1, - (e, eff, val) => + (e, eff, _) => { if (eff.Value == 0) return; @@ -83,7 +83,7 @@ public override void _Ready() this, BattleEffectTrigger.NotePlaced, 1, - (e, eff, val) => + (_, _, _) => { _tutorialInstance.CallDeferred(nameof(_tutorialInstance.OnPlaceDialogue1)); } @@ -92,7 +92,7 @@ public override void _Ready() this, BattleEffectTrigger.OnDamageInstance, 1, - (e, eff, val) => + (e, _, _) => { if (e is not BattleDirector.Harbinger.OnDamageInstanceArgs dArgs) return; @@ -101,7 +101,10 @@ public override void _Ready() && dArgs.Dmg.Target.GetCurrentHealth() <= dArgs.Dmg.Damage ) { - SaveSystem.UpdateConfig(SaveSystem.ConfigSettings.FirstTime, false); + StageProducer.UpdatePersistantValues( + StageProducer.PersistKeys.TutorialDone, + 1 + ); SteamWhisperer.PopAchievement("tutorial"); } } diff --git a/Scenes/Puppets/Enemies/Turtle/P_Turtle.cs b/Scenes/Puppets/Enemies/Turtle/P_Turtle.cs index 47ad8089..70ebe332 100644 --- a/Scenes/Puppets/Enemies/Turtle/P_Turtle.cs +++ b/Scenes/Puppets/Enemies/Turtle/P_Turtle.cs @@ -26,12 +26,12 @@ public override void _Ready() this, BattleEffectTrigger.OnLoop, 1, - (e, eff, val) => + (e, _, val) => { // take 1/4th of player's energy, and heal that amount int quarterEnergy = (int)e.BD.NPB.GetCurrentBarValue() / 4; e.BD.NPB.IncreaseCharge(-quarterEnergy); - this.Heal(quarterEnergy); + Heal(quarterEnergy); //gain block based on val e.BD.AddStatus(Targetting.First, StatusEffect.Block, val); diff --git a/Scenes/ShopScene/Scripts/ShopScene.cs b/Scenes/ShopScene/Scripts/ShopScene.cs index d66c9735..672d3e7c 100644 --- a/Scenes/ShopScene/Scripts/ShopScene.cs +++ b/Scenes/ShopScene/Scripts/ShopScene.cs @@ -343,7 +343,7 @@ private void RemoveNote() private bool _hasHealed; private const int HealCost = 50; - private int _healAmount = (StageProducer.PlayerStats.MaxHealth / 4); + private int HealAmount => (StageProducer.PlayerStats.MaxHealth / 4); private void UpdateHealButton() { @@ -366,7 +366,7 @@ private void TryHeal() StageProducer.PlayerStats.Money -= HealCost; _hasHealed = true; - _player.Heal(_healAmount); + _player.Heal(HealAmount); UpdateHealButton(); UpdateMoneyLabel(); } diff --git a/Scenes/UI/Options/HowToPlay.tscn b/Scenes/UI/Options/HowToPlay.tscn index 04ff7993..15652baf 100644 --- a/Scenes/UI/Options/HowToPlay.tscn +++ b/Scenes/UI/Options/HowToPlay.tscn @@ -7,7 +7,7 @@ [ext_resource type="Texture2D" uid="uid://cr82n7aojboaw" path="res://Scenes/UI/Assets/UI_CenterFrameLight.png" id="4_4vscx"] [ext_resource type="Texture2D" uid="uid://caw70lr5e1yiq" path="res://Classes/Notes/Assets/Note_PlayerDouble.png" id="4_m6low"] [ext_resource type="Texture2D" uid="uid://cdf3g3174du4r" path="res://Classes/Notes/Assets/Note_PlayerHeal.png" id="5_8kiq2"] -[ext_resource type="Texture2D" uid="uid://coav3xvksq4jy" path="res://Scenes/NoteManager/Assets/NoteWIthOutline.png" id="5_xqve7"] +[ext_resource type="Texture2D" uid="uid://coav3xvksq4jy" path="res://Scenes/NoteManager/Assets/NoteWithOutline.png" id="5_xqve7"] [ext_resource type="Texture2D" uid="uid://c3chrsxrulapd" path="res://Classes/Notes/Assets/Note_PlayerBasic.png" id="6_uonw3"] [ext_resource type="Texture2D" uid="uid://dg0lmu0pip4lr" path="res://Classes/Notes/Assets/Note_PlayerVampire.png" id="7_rbdrm"] diff --git a/Scenes/UI/Options/Scripts/HowToPlay.cs b/Scenes/UI/Options/Scripts/HowToPlay.cs index 9fae60c5..e8a0a455 100644 --- a/Scenes/UI/Options/Scripts/HowToPlay.cs +++ b/Scenes/UI/Options/Scripts/HowToPlay.cs @@ -46,7 +46,7 @@ public void ReturnToPrev() private void DoTutorial() { - SaveSystem.UpdateConfig(SaveSystem.ConfigSettings.FirstTime, true); + StageProducer.UpdatePersistantValues(StageProducer.PersistKeys.TutorialDone, 0); StageProducer.LiveInstance.TransitionStage(Stages.Map); } diff --git a/Scenes/UI/Options/Scripts/LanguageSelection.cs b/Scenes/UI/Options/Scripts/LanguageSelection.cs index 5e14c224..e300ccd0 100644 --- a/Scenes/UI/Options/Scripts/LanguageSelection.cs +++ b/Scenes/UI/Options/Scripts/LanguageSelection.cs @@ -25,7 +25,7 @@ public void OnLanguageSelected(int index) private void ChangeLanguage(string language) { TranslationServer.SetLocale(language); - SaveSystem.UpdateConfig(SaveSystem.ConfigSettings.LanguageKey, language); + Configkeeper.UpdateConfig(Configkeeper.ConfigSettings.LanguageKey, language); } private void PresetDropdown(string language) diff --git a/Scenes/UI/Options/Scripts/OptionsMenu.cs b/Scenes/UI/Options/Scripts/OptionsMenu.cs index 3e8905dd..021b28f1 100644 --- a/Scenes/UI/Options/Scripts/OptionsMenu.cs +++ b/Scenes/UI/Options/Scripts/OptionsMenu.cs @@ -44,8 +44,8 @@ public override void _Ready() AudioServer.GetBusVolumeDb(AudioServer.GetBusIndex("Master")) ); - _highContrastToggle.ButtonPressed = SaveSystem - .GetConfigValue(SaveSystem.ConfigSettings.HighContrast) + _highContrastToggle.ButtonPressed = Configkeeper + .GetConfigValue(Configkeeper.ConfigSettings.HighContrast) .AsBool(); _volumeSlider.DragEnded += VolumeChanged; @@ -58,7 +58,7 @@ public override void _Ready() _titleScreenOptions.Visible = !StageProducer.IsInitialized - && !SaveSystem.GetConfigValue(SaveSystem.ConfigSettings.FirstTime).AsBool(); + && StageProducer.GetPersistantVal(StageProducer.PersistKeys.TutorialDone) == 1; _noteSpriteToggle.ButtonPressed = InputHandler.UseArrows; _noteSpriteToggle.Toggled += ArrowSpritesToggled; _verticalScrollToggle.ButtonPressed = BattleDirector.VerticalScroll; @@ -118,7 +118,7 @@ private void VolumeChanged(bool valueChanged) if (!valueChanged) return; ChangeVolume((float)_volumeSlider.Value); - SaveSystem.UpdateConfig(SaveSystem.ConfigSettings.Volume, _volumeSlider.Value); + Configkeeper.UpdateConfig(Configkeeper.ConfigSettings.Volume, _volumeSlider.Value); } public static void ChangeVolume(double value) @@ -132,19 +132,19 @@ public static void ChangeVolume(double value) private void ArrowSpritesToggled(bool value) { InputHandler.UseArrows = value; - SaveSystem.UpdateConfig(SaveSystem.ConfigSettings.TypeIsArrow, value); + Configkeeper.UpdateConfig(Configkeeper.ConfigSettings.TypeIsArrow, value); } private void VerticalScrollToggled(bool value) { BattleDirector.VerticalScroll = value; - SaveSystem.UpdateConfig(SaveSystem.ConfigSettings.VerticalScroll, value); + Configkeeper.UpdateConfig(Configkeeper.ConfigSettings.VerticalScroll, value); } private void HighContrastChanged(bool toggled) { StageProducer.ContrastFilter.Visible = toggled; - SaveSystem.UpdateConfig(SaveSystem.ConfigSettings.HighContrast, toggled); + Configkeeper.UpdateConfig(Configkeeper.ConfigSettings.HighContrast, toggled); } private void OpenHowToPlay() diff --git a/Scenes/UI/Remapping/ControlSettings.cs b/Scenes/UI/Remapping/ControlSettings.cs index 5ae9746c..44e9f265 100644 --- a/Scenes/UI/Remapping/ControlSettings.cs +++ b/Scenes/UI/Remapping/ControlSettings.cs @@ -151,7 +151,7 @@ public override void _Ready() } _remapTabs.CurrentTab = - SaveSystem.GetConfigValue(SaveSystem.ConfigSettings.InputType).ToString() + Configkeeper.GetConfigValue(Configkeeper.ConfigSettings.InputType).ToString() == KeyboardPrefix ? 0 : 1; @@ -251,8 +251,8 @@ private void NoNullFocus() /// private void ChangeInputType() { - SaveSystem.UpdateConfig( - SaveSystem.ConfigSettings.InputType, + Configkeeper.UpdateConfig( + Configkeeper.ConfigSettings.InputType, _remapTabs.CurrentTab == 0 ? KeyboardPrefix : JoyPrefix ); _remapDescription.Text = Tr(_remapTabs.CurrentTab == 0 ? _keyboardRemap : _controllerRemap); @@ -392,46 +392,49 @@ private void HandleRemapInput(InputEvent @event) /// private static readonly Dictionary< string, - (SaveSystem.ConfigSettings keyboard, SaveSystem.ConfigSettings controller) + (Configkeeper.ConfigSettings keyboard, Configkeeper.ConfigSettings controller) > ConfigMap = new() { { "_arrowUp", - (SaveSystem.ConfigSettings.InputKeyboardUp, SaveSystem.ConfigSettings.InputControllerUp) + ( + Configkeeper.ConfigSettings.InputKeyboardUp, + Configkeeper.ConfigSettings.InputControllerUp + ) }, { "_arrowDown", ( - SaveSystem.ConfigSettings.InputKeyboardDown, - SaveSystem.ConfigSettings.InputControllerDown + Configkeeper.ConfigSettings.InputKeyboardDown, + Configkeeper.ConfigSettings.InputControllerDown ) }, { "_arrowLeft", ( - SaveSystem.ConfigSettings.InputKeyboardLeft, - SaveSystem.ConfigSettings.InputControllerLeft + Configkeeper.ConfigSettings.InputKeyboardLeft, + Configkeeper.ConfigSettings.InputControllerLeft ) }, { "_arrowRight", ( - SaveSystem.ConfigSettings.InputKeyboardRight, - SaveSystem.ConfigSettings.InputControllerRight + Configkeeper.ConfigSettings.InputKeyboardRight, + Configkeeper.ConfigSettings.InputControllerRight ) }, { "_secondaryPlacement", ( - SaveSystem.ConfigSettings.InputKeyboardSecondary, - SaveSystem.ConfigSettings.InputControllerSecondary + Configkeeper.ConfigSettings.InputKeyboardSecondary, + Configkeeper.ConfigSettings.InputControllerSecondary ) }, { "_inventory", ( - SaveSystem.ConfigSettings.InputKeyboardInventory, - SaveSystem.ConfigSettings.InputControllerInventory + Configkeeper.ConfigSettings.InputKeyboardInventory, + Configkeeper.ConfigSettings.InputControllerInventory ) }, }; @@ -474,7 +477,7 @@ private void SaveKeyInput(string button, InputEvent key) return; var config = key is InputEventKey ? configPair.keyboard : configPair.controller; - SaveSystem.UpdateConfig(config, keycode); + Configkeeper.UpdateConfig(config, keycode); } /// diff --git a/Scenes/UI/TitleScreen/Scripts/TitleScreen.cs b/Scenes/UI/TitleScreen/Scripts/TitleScreen.cs index b6eca91a..d666b943 100644 --- a/Scenes/UI/TitleScreen/Scripts/TitleScreen.cs +++ b/Scenes/UI/TitleScreen/Scripts/TitleScreen.cs @@ -30,8 +30,8 @@ public override void _UnhandledInput(InputEvent @event) if (eventKey.Keycode == Key.Key0) { SteamWhisperer.ResetAll(); - SaveSystem.ClearSave(); - SaveSystem.ClearConfig(); + Savekeeper.ClearRun(); + Configkeeper.ClearConfig(); StageProducer.LiveInstance.InitFromCfg(); } } @@ -48,8 +48,8 @@ public override void _Ready() { if (StageProducer.LiveInstance.LastStage == Stages.Custom) OpenCustomSelection(); - _customSelectionButton.Visible = (bool) - SaveSystem.GetConfigValue(SaveSystem.ConfigSettings.HasWon); + _customSelectionButton.Visible = + StageProducer.GetPersistantVal(StageProducer.PersistKeys.HasWon) == 1; } public override void _Process(double delta) diff --git a/Scenes/UI/TitleScreen/TitleScreenEffects.tscn b/Scenes/UI/TitleScreen/TitleScreenEffects.tscn index 670edb8d..0c883ce0 100644 --- a/Scenes/UI/TitleScreen/TitleScreenEffects.tscn +++ b/Scenes/UI/TitleScreen/TitleScreenEffects.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=4 format=3 uid="uid://dkaxidh7xlvfc"] [ext_resource type="Texture2D" uid="uid://iqbqsiyjd3uq" path="res://Scenes/UI/TitleScreen/Assets/2D_Shadow_Map.webp" id="1_07rxh"] -[ext_resource type="Texture2D" uid="uid://coav3xvksq4jy" path="res://Scenes/NoteManager/Assets/NoteWIthOutline.png" id="2_ltons"] +[ext_resource type="Texture2D" uid="uid://coav3xvksq4jy" path="res://Scenes/NoteManager/Assets/NoteWithOutline.png" id="2_ltons"] [sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_4croe"] particle_flag_disable_z = true diff --git a/project.godot b/project.godot index bd90939a..06d5742c 100644 --- a/project.godot +++ b/project.godot @@ -24,6 +24,7 @@ buses/default_bus_layout="" [autoload] +Savekeeper="*res://Globals/Savekeeper.cs" StageProducer="*res://Globals/StageProducer.cs" TimeKeeper="*res://Globals/TimeKeeper.cs" Scribe="*res://Globals/Scribe.cs"