From 046b9ff55c38a4fcd7b18efbb7bf5e0317082e65 Mon Sep 17 00:00:00 2001 From: Jarod Spangler Date: Fri, 20 Jun 2025 17:08:26 -0700 Subject: [PATCH 1/3] Refined saves (#248) * Transition to New Save System * Move Persistent Values to StageProducer Moved HasWon and FirstTime(renamed TutorialDone) to StageProducer * Code Cleanup and Tweaks Removed some commented code Added _ to specify ignored parameters --- Classes/Events/EventDatabase.cs | 14 +- Classes/MidiMaestro/MidiMaestro.cs | 13 - Globals/{SaveSystem.cs => Configkeeper.cs} | 124 +------ Globals/Configkeeper.cs.uid | 1 + Globals/Savekeeper.cs | 281 +++++++++++++++ Globals/Savekeeper.cs.uid | 1 + Globals/Scribe.cs | 10 +- Globals/StageProducer.cs | 328 +++++++++++++++--- .../BattleDirector/Scripts/BattleDirector.cs | 3 +- .../Scripts/NotePlacementBar.cs | 4 +- Scenes/BattleDirector/Tutorial/Toriel.cs | 8 +- Scenes/CustomSong/CustomScore.cs | 16 +- Scenes/EventScene/EventScene.cs | 2 +- Scenes/Maps/Scripts/Cartographer.cs | 6 +- ...oteWIthOutline.png => NoteWithOutline.png} | Bin ....png.import => NoteWithOutline.png.import} | 6 +- Scenes/NoteManager/Scripts/InputHandler.cs | 47 +-- .../Puppets/Enemies/BossBlood/P_BossBlood.cs | 2 +- Scenes/Puppets/Enemies/CyberFox/P_CyberFox.cs | 2 +- Scenes/Puppets/Enemies/Effigy/P_Effigy.cs | 6 +- Scenes/Puppets/Enemies/Keythulu/P_Keythulu.cs | 6 +- Scenes/Puppets/Enemies/Strawman/P_Strawman.cs | 17 +- Scenes/Puppets/Enemies/Turtle/P_Turtle.cs | 4 +- Scenes/ShopScene/Scripts/ShopScene.cs | 4 +- Scenes/UI/Options/HowToPlay.tscn | 2 +- Scenes/UI/Options/Scripts/HowToPlay.cs | 2 +- .../UI/Options/Scripts/LanguageSelection.cs | 2 +- Scenes/UI/Options/Scripts/OptionsMenu.cs | 14 +- Scenes/UI/Remapping/ControlSettings.cs | 35 +- Scenes/UI/TitleScreen/Scripts/TitleScreen.cs | 8 +- Scenes/UI/TitleScreen/TitleScreenEffects.tscn | 2 +- project.godot | 1 + 32 files changed, 687 insertions(+), 284 deletions(-) rename Globals/{SaveSystem.cs => Configkeeper.cs} (79%) create mode 100644 Globals/Configkeeper.cs.uid create mode 100644 Globals/Savekeeper.cs create mode 100644 Globals/Savekeeper.cs.uid rename Scenes/NoteManager/Assets/{NoteWIthOutline.png => NoteWithOutline.png} (100%) rename Scenes/NoteManager/Assets/{NoteWIthOutline.png.import => NoteWithOutline.png.import} (69%) 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" From b1b596b4b125ae89a0bb411ec648069e5f460585 Mon Sep 17 00:00:00 2001 From: Connor Lowe <74088480+cornerloan@users.noreply.github.com> Date: Fri, 20 Jun 2025 19:26:51 -0700 Subject: [PATCH 2/3] Simple Enemy Descriptions (#249) * Initial implementation The enemy descriptions class should be working but the box is small so it doesn't look good. Also the icons need to be different than just the note icon. Such as making the loop icon for loop type effects. * Finished except for temp icons I don't have it set up for every BattleEffectTrigger, so as we use more triggers we need to edit GetTriggerIcon() in EnemyDescriptions.cs * Updated temp arts * Minor Refinements Removed Description on Note class to build translation key. Made description for enemy effect an optional parameter. Adjusted translations for accuracy. Removed effect description from Strawman to not compete screen space with arrow input guides. Better set up description box in controls. --------- Co-authored-by: LifeHckr --- Globals/Translations/Translations.csv | 20 ++++- .../BattleDirector/Assets/BattleEndSymbol.png | Bin 0 -> 131 bytes .../Assets/BattleEndSymbol.png.import | 34 ++++++++ .../Assets/BattleStartSymbol.png | Bin 0 -> 688 bytes .../Assets/BattleStartSymbol.png.import | 34 ++++++++ .../Assets/DamageInstanceSymbol.png | Bin 0 -> 700 bytes .../Assets/DamageInstanceSymbol.png.import | 34 ++++++++ Scenes/BattleDirector/Assets/LoopSymbol.png | Bin 0 -> 630 bytes .../Assets/LoopSymbol.png.import | 34 ++++++++ Scenes/BattleDirector/BattleScene.tscn | 25 ++++-- Scenes/BattleDirector/EnemyDescriptions.tscn | 50 +++++++++++ .../BattleDirector/Scripts/BattleDirector.cs | 5 ++ .../Scripts/EnemyDescriptions.cs | 78 ++++++++++++++++++ .../Scripts/EnemyDescriptions.cs.uid | 1 + Scenes/BattleDirector/Tutorial/Toriel.cs | 2 + .../Puppets/Enemies/BossBlood/P_BossBlood.cs | 3 +- Scenes/Puppets/Enemies/CyberFox/P_CyberFox.cs | 3 +- Scenes/Puppets/Enemies/Effigy/P_Effigy.cs | 6 +- Scenes/Puppets/Enemies/EnemyEffect.cs | 5 +- .../Enemies/Holograeme/P_Holograeme.cs | 6 +- Scenes/Puppets/Enemies/Keythulu/P_Keythulu.cs | 7 +- Scenes/Puppets/Enemies/LWS/P_LWS.cs | 3 +- .../Puppets/Enemies/Parasifly/P_Parasifly.cs | 3 +- Scenes/Puppets/Enemies/TheGWS/P_TheGWS.cs | 3 +- Scenes/Puppets/Enemies/Turtle/P_Turtle.cs | 3 +- 25 files changed, 339 insertions(+), 20 deletions(-) create mode 100644 Scenes/BattleDirector/Assets/BattleEndSymbol.png create mode 100644 Scenes/BattleDirector/Assets/BattleEndSymbol.png.import create mode 100644 Scenes/BattleDirector/Assets/BattleStartSymbol.png create mode 100644 Scenes/BattleDirector/Assets/BattleStartSymbol.png.import create mode 100644 Scenes/BattleDirector/Assets/DamageInstanceSymbol.png create mode 100644 Scenes/BattleDirector/Assets/DamageInstanceSymbol.png.import create mode 100644 Scenes/BattleDirector/Assets/LoopSymbol.png create mode 100644 Scenes/BattleDirector/Assets/LoopSymbol.png.import create mode 100644 Scenes/BattleDirector/EnemyDescriptions.tscn create mode 100644 Scenes/BattleDirector/Scripts/EnemyDescriptions.cs create mode 100644 Scenes/BattleDirector/Scripts/EnemyDescriptions.cs.uid diff --git a/Globals/Translations/Translations.csv b/Globals/Translations/Translations.csv index 47d68594..58738268 100644 --- a/Globals/Translations/Translations.csv +++ b/Globals/Translations/Translations.csv @@ -206,4 +206,22 @@ CREDITS_ADDITIONAL_ART,Art,额外美术 CREDITS_ADDITIONAL_HELP,Additional Help,额外协助 CREDITS_OTHER_PLAYTESTERS,All our other playtesters,以及所有其他测试玩家 CREDITS_SPECIAL_THANKS,Special Thanks,特别鸣谢 -CREDITS_THANKS_PLAYERS,And to you the player!,以及屏幕前的你! \ No newline at end of file +CREDITS_THANKS_PLAYERS,And to you the player!,以及屏幕前的你! +BOSSBLOOD_EFFECT1,"Heal and convert a basic note into a Blood note.","每循环治疗 点生命,并将一个普通音符转化为血之音符。" +CYBERFOX_EFFECT1,Gains one dodge.,获得一次闪避。 +EFFIGY_EFFECT1,Only takes 1 damage at a time.,每次只受到1点伤害。 +EFFIGY_EFFECT2,Sets player's health to 1 if defeated.,被击败时将玩家的生命值设为1。 +HOLOGRAEME_EFFECT1,Player only plays every other loop.,玩家每隔一轮行动一次。 +HOLOGRAEME_EFFECT2,"Holograeme only has 1 health, and is immune to relic damage.","全息格莱姆仅有1点生命值,且免疫遗物伤害。" +KEYTHULU_EFFECT1,"Every loop mindcrush ticks down by one, when it reaches zero, the player is defeated.","每次循环,心灵碾压计数会减1。当计数归零时,玩家即被击败。" +LWS_EFFECT1,Converts an empty note into a Lesser Wolf Snake note.,将一个基础音符转化为次级狼蛇音符。 +PARASIFLY_EFFECT1,All enemies gains block the first time fatal damage is dealt.,首次受到致命伤害时,所有敌人获得格挡。 +STRAWMAN_EFFECT1,Player cannot take fatal damage.,玩家不会受到致命伤害。 +GWS_EFFECT1,Adds Greater Wolf Snake notes after the first loop.,首次循环后,添加高级狼蛇音符。 +TURTLE_EFFECT1,Gains one block and drains some of player's charge to heal.,获得一层格挡,并消耗玩家部分充能以进行治疗。 +GWS_NOTE_DESCRIPTION,"If timed badly, deals high damage to the player. Damage increases each loop.","若时机不佳,则对玩家造成高额伤害,且伤害随每次循环递增。" +PARASIFLY_NOTE_DESCRIPTION,Parasifly gains block if timed badly.,若时机不佳,寄生蝇获得格挡。 +BOSSBLOOD_NOTE_DESCRIPTION,Boss Blood heals if timed badly.,若时机不佳,首领之血会进行治疗。 +SPIDER_NOTE_DESCRIPTION,Player gets poisoned if timed badly.,若时机不佳,玩家会中毒。 +LWS_NOTE_DESCRIPTION,"If timed badly, deals damage to the player. Damage increases each loop.","若时机不佳,则对玩家造成伤害,且伤害随每次循环递增。" +MUSHROOM_NOTE_DESCRIPTION,Player gets poisoned if timed badly.,若时机不佳,玩家会中毒。 \ No newline at end of file diff --git a/Scenes/BattleDirector/Assets/BattleEndSymbol.png b/Scenes/BattleDirector/Assets/BattleEndSymbol.png new file mode 100644 index 0000000000000000000000000000000000000000..a4beda98b042254ffe1d58671f8da8dc9812828f GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}ww^AIArY-_ z&n*;X5MXc(T(h9-+kB6Zvj><&+>7J?KD;lmDVi+}m_q%Tkx1WXd! Wm&T&;*rfL~i0|p@=d#Wzp$PzKAt|r` literal 0 HcmV?d00001 diff --git a/Scenes/BattleDirector/Assets/BattleEndSymbol.png.import b/Scenes/BattleDirector/Assets/BattleEndSymbol.png.import new file mode 100644 index 00000000..0ca42b8d --- /dev/null +++ b/Scenes/BattleDirector/Assets/BattleEndSymbol.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bmw2tkpmv22ew" +path="res://.godot/imported/BattleEndSymbol.png-5c282cbc73fc3f3efd9a51295ca4bc9d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Scenes/BattleDirector/Assets/BattleEndSymbol.png" +dest_files=["res://.godot/imported/BattleEndSymbol.png-5c282cbc73fc3f3efd9a51295ca4bc9d.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/Scenes/BattleDirector/Assets/BattleStartSymbol.png b/Scenes/BattleDirector/Assets/BattleStartSymbol.png new file mode 100644 index 0000000000000000000000000000000000000000..84a88910c36cf42747776e5ac64d578b1a3f60eb GIT binary patch literal 688 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=Dh+L6~vJ#O${~4egmB5hW46K32*3xq68y`AMmI6}bgK)eHls47YguJQ{>uF6ifOi{PD zbs{}UK3djZt>nqvW6s4qD1-ZCERRDRmN*N_31y=g{ z<>lpi<;HsXMd|v6mX?v90`zGE;%B09k2gXakl<5wp<;IRwdJb`TMuUx6%m z$bf?OUvW XwIM}mqrAyNP_prK^>bP0l+XkK)068O literal 0 HcmV?d00001 diff --git a/Scenes/BattleDirector/Assets/BattleStartSymbol.png.import b/Scenes/BattleDirector/Assets/BattleStartSymbol.png.import new file mode 100644 index 00000000..8bf06276 --- /dev/null +++ b/Scenes/BattleDirector/Assets/BattleStartSymbol.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c5r2idc5x21ro" +path="res://.godot/imported/BattleStartSymbol.png-a8cb230df43bb6721a2442dfafc1c8b6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Scenes/BattleDirector/Assets/BattleStartSymbol.png" +dest_files=["res://.godot/imported/BattleStartSymbol.png-a8cb230df43bb6721a2442dfafc1c8b6.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/Scenes/BattleDirector/Assets/DamageInstanceSymbol.png b/Scenes/BattleDirector/Assets/DamageInstanceSymbol.png new file mode 100644 index 0000000000000000000000000000000000000000..506c22395784f942293ac3ddae996fc48e263b85 GIT binary patch literal 700 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=Dh+L6~vJ#O${~4egmB5hW46K32*3xq68y`AMmI6}bgK)eHls47YguJQ{>uF6ifOi{PD zbs{}UK3djZt>nqvW6s4qD1-ZCERRDRmN*N_31y=g{ z<>lpi<;HsXMd|v6mX?v90`zGE;%B09k2gXakl<5wp<;IRwdJb`TMuUx6%m z$bfLw>YfYXG<;+u=y z81Ge=*L@0WaXY86;8OR6Q<85RVs>rbqql6@JH?>5jeQw)P4n(Nn(A%G_h;#xFoxca j&`;h;J+5szX4(}@Nljs*BF-y8DUiX_)z4*}Q$iB}k_Gps literal 0 HcmV?d00001 diff --git a/Scenes/BattleDirector/Assets/DamageInstanceSymbol.png.import b/Scenes/BattleDirector/Assets/DamageInstanceSymbol.png.import new file mode 100644 index 00000000..a6a8bb92 --- /dev/null +++ b/Scenes/BattleDirector/Assets/DamageInstanceSymbol.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dosjbiqlrb3cr" +path="res://.godot/imported/DamageInstanceSymbol.png-9cd27c768e2ba03d6c47f5ac093aa1d8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Scenes/BattleDirector/Assets/DamageInstanceSymbol.png" +dest_files=["res://.godot/imported/DamageInstanceSymbol.png-9cd27c768e2ba03d6c47f5ac093aa1d8.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/Scenes/BattleDirector/Assets/LoopSymbol.png b/Scenes/BattleDirector/Assets/LoopSymbol.png new file mode 100644 index 0000000000000000000000000000000000000000..8f1265e31c520e58c727b4a1dc4fe10f81299392 GIT binary patch literal 630 zcmV-+0*U>JP)EX>4Tx04R}tkv&MmKpe$iQ?()$K?{mFWT>6&q9Tr^ibb$c+6t{Ym|XfHG-*gu zTpR`0f`cE6RR9%C|}6A ztZ?4qtX68Qbx;1nU_o2XaGmBD5?DkMDTt6!!v-p_5T#Wk#YCF+;~xG|$DbmXOs)+u za?GO&6_Voz|AXJ%n#HL}Hz^ngx?gPjV;l(V0?oQ@e;?a+^91le16NwxUu^)hpQP8@ zTKEVU*aj}H+nT%wT zL`sys=JD>{&ffk#)9UXBrZsZA%>mmh00023NklGD|K-GNH7QF37#_S_*Q4+7$!P=!kZ4mbdO0P81QH&WG0 Q2><{907*qoM6N<$f`1PiF8}}l literal 0 HcmV?d00001 diff --git a/Scenes/BattleDirector/Assets/LoopSymbol.png.import b/Scenes/BattleDirector/Assets/LoopSymbol.png.import new file mode 100644 index 00000000..a7522f00 --- /dev/null +++ b/Scenes/BattleDirector/Assets/LoopSymbol.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://r7oc2aa06s24" +path="res://.godot/imported/LoopSymbol.png-4d5a564a81d7d7d2248da6ec1ceb0061.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Scenes/BattleDirector/Assets/LoopSymbol.png" +dest_files=["res://.godot/imported/LoopSymbol.png-4d5a564a81d7d7d2248da6ec1ceb0061.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/Scenes/BattleDirector/BattleScene.tscn b/Scenes/BattleDirector/BattleScene.tscn index 2e60464c..3dea38a2 100644 --- a/Scenes/BattleDirector/BattleScene.tscn +++ b/Scenes/BattleDirector/BattleScene.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=13 format=3 uid="uid://b0mrgr7h0ty1y"] +[gd_scene load_steps=14 format=3 uid="uid://b0mrgr7h0ty1y"] [ext_resource type="Script" uid="uid://bttu0wmy2fp64" path="res://Scenes/BattleDirector/Scripts/BattleDirector.cs" id="1_jmdo1"] [ext_resource type="Script" uid="uid://pl57giqyhckb" path="res://Scenes/UI/Scripts/MenuModule.cs" id="2_ka0ws"] @@ -10,6 +10,7 @@ [ext_resource type="Script" uid="uid://cp6t6haqyef7o" path="res://Scenes/AreaBasedBackground.cs" id="7_6k2qj"] [ext_resource type="Texture2D" uid="uid://dbjotl0v1ymia" path="res://SharedAssets/BattleFrame1.png" id="7_klvil"] [ext_resource type="Theme" uid="uid://d37e3tpsbxwak" path="res://Scenes/UI/Assets/GeneralTheme.tres" id="8_62qim"] +[ext_resource type="PackedScene" uid="uid://bejjkejyeffek" path="res://Scenes/BattleDirector/EnemyDescriptions.tscn" id="11_gbbxr"] [sub_resource type="Gradient" id="Gradient_8uy3a"] offsets = PackedFloat32Array(0, 0.766234, 1) @@ -20,7 +21,7 @@ gradient = SubResource("Gradient_8uy3a") fill_from = Vector2(1, 0) fill_to = Vector2(0.738532, 1) -[node name="ProtoBattleDirector" type="Node2D" node_paths=PackedStringArray("PuppetMarkers", "_countdownLabel", "CD", "CM", "DW", "NPB", "Audio", "FocusedButton")] +[node name="ProtoBattleDirector" type="Node2D" node_paths=PackedStringArray("PuppetMarkers", "_countdownLabel", "CD", "CM", "DW", "NPB", "Audio", "Descriptions", "FocusedButton")] process_mode = 1 script = ExtResource("1_jmdo1") PuppetMarkers = [NodePath("PlayerMarker"), NodePath("Enemy1Marker"), NodePath("Enemy2Marker"), NodePath("Enemy3Marker")] @@ -30,6 +31,7 @@ CM = NodePath("VPContainer") DW = NodePath("3D/SubViewport/3DWizard") NPB = NodePath("NotePlacementBar") Audio = NodePath("AudioStreamPlayer") +Descriptions = NodePath("EnemyDescriptions") FocusedButton = NodePath("StartButton") metadata/_edit_lock_ = true @@ -100,10 +102,10 @@ offset_bottom = 360.0 texture = ExtResource("7_klvil") [node name="StartButton" type="Button" parent="."] -offset_left = 241.0 -offset_top = 230.0 -offset_right = 443.0 -offset_bottom = 267.0 +offset_left = 190.0 +offset_top = 200.0 +offset_right = 450.0 +offset_bottom = 248.0 theme = ExtResource("8_62qim") text = "BATTLE_ROOM_BEGIN_BUTTON" @@ -142,3 +144,14 @@ theme_override_constants/shadow_offset_y = 3 theme_override_constants/shadow_outline_size = 3 theme_override_font_sizes/font_size = 64 text = "5" + +[node name="EnemyDescriptions" parent="." instance=ExtResource("11_gbbxr")] +anchors_preset = 0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 320.0 +offset_top = 183.0 +offset_right = 320.0 +offset_bottom = 183.0 +grow_horizontal = 1 +grow_vertical = 1 diff --git a/Scenes/BattleDirector/EnemyDescriptions.tscn b/Scenes/BattleDirector/EnemyDescriptions.tscn new file mode 100644 index 00000000..31a82b3f --- /dev/null +++ b/Scenes/BattleDirector/EnemyDescriptions.tscn @@ -0,0 +1,50 @@ +[gd_scene load_steps=3 format=3 uid="uid://bejjkejyeffek"] + +[ext_resource type="Script" uid="uid://da8no3g3kbob7" path="res://Scenes/BattleDirector/Scripts/EnemyDescriptions.cs" id="1_r3tcc"] +[ext_resource type="Texture2D" uid="uid://djd6iw2g84bba" path="res://Scenes/UI/Assets/UI_CenterFrame.png" id="2_mg2uj"] + +[node name="EnemyDescriptions" type="Control" node_paths=PackedStringArray("DescriptionsContainer")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("1_r3tcc") +DescriptionsContainer = NodePath("Center/MarginContainer/MarginContainer/VBoxContainer") + +[node name="Center" type="CenterContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + +[node name="MarginContainer" type="MarginContainer" parent="Center"] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/margin_top = 230 + +[node name="UiCenterFrame" type="NinePatchRect" parent="Center/MarginContainer"] +custom_minimum_size = Vector2(280, 80) +layout_mode = 2 +texture = ExtResource("2_mg2uj") +patch_margin_left = 12 +patch_margin_top = 12 +patch_margin_right = 12 +patch_margin_bottom = 12 + +[node name="MarginContainer" type="MarginContainer" parent="Center/MarginContainer"] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 6 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 6 + +[node name="VBoxContainer" type="VBoxContainer" parent="Center/MarginContainer/MarginContainer"] +layout_mode = 2 +mouse_filter = 2 diff --git a/Scenes/BattleDirector/Scripts/BattleDirector.cs b/Scenes/BattleDirector/Scripts/BattleDirector.cs index 1040f01e..b4fe2929 100644 --- a/Scenes/BattleDirector/Scripts/BattleDirector.cs +++ b/Scenes/BattleDirector/Scripts/BattleDirector.cs @@ -34,6 +34,9 @@ public partial class BattleDirector : Node2D [Export] private AudioStreamPlayer Audio; + [Export] + public EnemyDescriptions Descriptions; + [Export] public Button FocusedButton; //Initial start button @@ -139,6 +142,7 @@ public override void _Ready() { FocusedButton.QueueFree(); FocusedButton = null; + Descriptions.QueueFree(); StartCountdown(); }; @@ -201,6 +205,7 @@ private void InitEnemies() _enemies[i] = enemy; AddEnemyEffects(enemy); } + Descriptions.Setup(_enemies[0]); } public override void _Process(double delta) diff --git a/Scenes/BattleDirector/Scripts/EnemyDescriptions.cs b/Scenes/BattleDirector/Scripts/EnemyDescriptions.cs new file mode 100644 index 00000000..880ac793 --- /dev/null +++ b/Scenes/BattleDirector/Scripts/EnemyDescriptions.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using FunkEngine; +using Godot; + +public partial class EnemyDescriptions : Control +{ + [Export] + private VBoxContainer DescriptionsContainer; + + private const string _loopIconPath = "res://Scenes/BattleDirector/Assets/LoopSymbol.png"; + private const string _damageInstanceIconPath = + "res://Scenes/BattleDirector/Assets/DamageInstanceSymbol.png"; + private const string _battleStartIconPath = + "res://Scenes/BattleDirector/Assets/BattleStartSymbol.png"; + private const string _battleEndIconPath = + "res://Scenes/BattleDirector/Assets/BattleEndSymbol.png"; + + private bool _isVisible = false; + private const string TranslationKeySuffix = "_NOTE_DESCRIPTION"; + + public void Setup(EnemyPuppet enemy) + { + if (enemy.InitialNote.Amount > 0) + { + string desc = NoteDescBuilder(Scribe.NoteDictionary[enemy.InitialNote.NoteId].Name); + AddDescriptionRow(Scribe.NoteDictionary[enemy.InitialNote.NoteId].Texture, desc); + _isVisible = true; + } + + foreach (var effect in enemy.GetBattleEvents()) + { + if (effect.Description == null) + continue; + Texture2D icon = GetTriggerIcon(effect.GetTrigger()); + AddDescriptionRow(icon, effect.Description); + _isVisible = true; + } + + Visible = _isVisible; + } + + private void AddDescriptionRow(Texture2D iconTexture, string text) + { + HBoxContainer hbox = new HBoxContainer(); + + TextureRect icon = new TextureRect(); + icon.Texture = iconTexture; + icon.StretchMode = TextureRect.StretchModeEnum.Keep; + + Label desc = new Label(); + desc.Text = text; + desc.SizeFlagsHorizontal = SizeFlags.ExpandFill; + desc.AutowrapMode = TextServer.AutowrapMode.WordSmart; + + hbox.AddChild(icon); + hbox.AddChild(desc); + DescriptionsContainer.AddChild(hbox); + } + + private string NoteDescBuilder(string noteName) + { + return noteName.ToUpper() + TranslationKeySuffix; + } + + private Texture2D GetTriggerIcon(BattleEffectTrigger trigger) + { + //TODO: add more as we get more enemy effect triggers + return trigger switch + { + BattleEffectTrigger.OnLoop => GD.Load(_loopIconPath), + BattleEffectTrigger.OnDamageInstance => GD.Load(_damageInstanceIconPath), + BattleEffectTrigger.OnBattleStart => GD.Load(_battleStartIconPath), + BattleEffectTrigger.OnBattleEnd => GD.Load(_battleEndIconPath), + _ => null, + }; + } +} diff --git a/Scenes/BattleDirector/Scripts/EnemyDescriptions.cs.uid b/Scenes/BattleDirector/Scripts/EnemyDescriptions.cs.uid new file mode 100644 index 00000000..3a488050 --- /dev/null +++ b/Scenes/BattleDirector/Scripts/EnemyDescriptions.cs.uid @@ -0,0 +1 @@ +uid://da8no3g3kbob7 diff --git a/Scenes/BattleDirector/Tutorial/Toriel.cs b/Scenes/BattleDirector/Tutorial/Toriel.cs index d175c80a..bc4ed1ce 100644 --- a/Scenes/BattleDirector/Tutorial/Toriel.cs +++ b/Scenes/BattleDirector/Tutorial/Toriel.cs @@ -341,6 +341,7 @@ public void OnPlaceDialogue4() public void BossDialogue() { _dialogueBox.Visible = true; + _currentDirector.Descriptions.Visible = false; GetTree().SetPause(true); _currentDirector.FocusedButton.Visible = false; _dialogueLabel.Text = Tr("TUTORIAL_BOSS"); @@ -355,6 +356,7 @@ public void BossDialogueReady() GetTree().SetPause(false); _currentDirector.FocusedButton.Visible = true; _currentDirector.FocusedButton.GrabFocus(); + _currentDirector.Descriptions.Visible = true; } #endregion } diff --git a/Scenes/Puppets/Enemies/BossBlood/P_BossBlood.cs b/Scenes/Puppets/Enemies/BossBlood/P_BossBlood.cs index bd4748c1..0fc7033e 100644 --- a/Scenes/Puppets/Enemies/BossBlood/P_BossBlood.cs +++ b/Scenes/Puppets/Enemies/BossBlood/P_BossBlood.cs @@ -31,7 +31,8 @@ public override void _Ready() { eff.Owner.Heal(val); e.BD.RandApplyNote(eff.Owner, 14, 1); - } + }, + "BOSSBLOOD_EFFECT1" ), new EnemyEffect( this, diff --git a/Scenes/Puppets/Enemies/CyberFox/P_CyberFox.cs b/Scenes/Puppets/Enemies/CyberFox/P_CyberFox.cs index d1099374..ea08af13 100644 --- a/Scenes/Puppets/Enemies/CyberFox/P_CyberFox.cs +++ b/Scenes/Puppets/Enemies/CyberFox/P_CyberFox.cs @@ -40,7 +40,8 @@ public override void _Ready() { e.BD.AddStatus(Targetting.First, StatusEffect.Dodge, 1); _effectNode.TriggerGlitch(1f); - } + }, + "CYBERFOX_EFFECT1" ), }; } diff --git a/Scenes/Puppets/Enemies/Effigy/P_Effigy.cs b/Scenes/Puppets/Enemies/Effigy/P_Effigy.cs index 8cd0e024..1944bae1 100644 --- a/Scenes/Puppets/Enemies/Effigy/P_Effigy.cs +++ b/Scenes/Puppets/Enemies/Effigy/P_Effigy.cs @@ -29,7 +29,8 @@ public override void _Ready() dArgs.Dmg.ModifyDamage(-dArgs.Dmg.Damage + 1); if (dArgs.Dmg.Source != null) dArgs.Dmg.Source.TakeDamage(new DamageInstance(1, null, dArgs.Dmg.Source)); - } + }, + "EFFIGY_EFFECT1" ), new EnemyEffect( this, @@ -38,7 +39,8 @@ public override void _Ready() (e, _, _) => { e.BD.DealDamage(Targetting.Player, e.BD.Player.GetCurrentHealth() - 1, null); - } + }, + "EFFIGY_EFFECT2" ), new EnemyEffect( this, diff --git a/Scenes/Puppets/Enemies/EnemyEffect.cs b/Scenes/Puppets/Enemies/EnemyEffect.cs index 8fb1bc1d..6bd8724f 100644 --- a/Scenes/Puppets/Enemies/EnemyEffect.cs +++ b/Scenes/Puppets/Enemies/EnemyEffect.cs @@ -8,12 +8,14 @@ public class EnemyEffect : IBattleEvent private int _baseValue; public int Value; private Action _onEnemyEffect; + public string Description { get; private set; } public EnemyEffect( EnemyPuppet owner, BattleEffectTrigger trigger, int val, - Action onEnemyEffect + Action onEnemyEffect, + string description = null ) { Owner = owner; @@ -21,6 +23,7 @@ Action onEnemyEffect Value = _baseValue; Trigger = trigger; _onEnemyEffect = onEnemyEffect; + Description = description; } public void OnTrigger(BattleEventArgs e) diff --git a/Scenes/Puppets/Enemies/Holograeme/P_Holograeme.cs b/Scenes/Puppets/Enemies/Holograeme/P_Holograeme.cs index 468906b9..988b630c 100644 --- a/Scenes/Puppets/Enemies/Holograeme/P_Holograeme.cs +++ b/Scenes/Puppets/Enemies/Holograeme/P_Holograeme.cs @@ -46,7 +46,8 @@ public override void _Ready() BattleDirector.PlayerDisabled = true; BattleDirector.VerticalScrollRotation = 0f; e.BD.AddStatus(Targetting.Player, StatusEffect.Disable); - } + }, + "HOLOGRAEME_EFFECT1" ), new EnemyEffect( this, @@ -98,7 +99,8 @@ e is not BattleDirector.Harbinger.OnDamageInstanceArgs dArgs { dArgs.Dmg.ModifyDamage(0, 0); } - } + }, + "HOLOGRAEME_EFFECT2" ), }; } diff --git a/Scenes/Puppets/Enemies/Keythulu/P_Keythulu.cs b/Scenes/Puppets/Enemies/Keythulu/P_Keythulu.cs index 1f721d92..b22e5c0e 100644 --- a/Scenes/Puppets/Enemies/Keythulu/P_Keythulu.cs +++ b/Scenes/Puppets/Enemies/Keythulu/P_Keythulu.cs @@ -27,16 +27,19 @@ public override void _Ready() enemTween.SetLoops(); enemTween.Play(); + const int effect1Val = 6; + BattleEvents = new EnemyEffect[] { new EnemyEffect( this, BattleEffectTrigger.OnBattleStart, - 6, + effect1Val, (e, eff, val) => { e.BD.AddStatus(Targetting.Player, StatusEffect.MindCrush, val); - } + }, + "KEYTHULU_EFFECT1" ), new EnemyEffect( this, diff --git a/Scenes/Puppets/Enemies/LWS/P_LWS.cs b/Scenes/Puppets/Enemies/LWS/P_LWS.cs index 4da24ce9..25264cf8 100644 --- a/Scenes/Puppets/Enemies/LWS/P_LWS.cs +++ b/Scenes/Puppets/Enemies/LWS/P_LWS.cs @@ -30,7 +30,8 @@ public override void _Ready() (e, eff, val) => { e.BD.RandApplyNote(eff.Owner, InitialNote.NoteId, val); - } + }, + "LWS_EFFECT1" ), }; } diff --git a/Scenes/Puppets/Enemies/Parasifly/P_Parasifly.cs b/Scenes/Puppets/Enemies/Parasifly/P_Parasifly.cs index bf27cee0..e3d42576 100644 --- a/Scenes/Puppets/Enemies/Parasifly/P_Parasifly.cs +++ b/Scenes/Puppets/Enemies/Parasifly/P_Parasifly.cs @@ -47,7 +47,8 @@ public override void _Ready() return; e.BD.AddStatus(Targetting.All, StatusEffect.Block, val); eff.Value = 0; - } + }, + "PARASIFLY_EFFECT1" ), }; } diff --git a/Scenes/Puppets/Enemies/TheGWS/P_TheGWS.cs b/Scenes/Puppets/Enemies/TheGWS/P_TheGWS.cs index b119fdbd..ee4830fd 100644 --- a/Scenes/Puppets/Enemies/TheGWS/P_TheGWS.cs +++ b/Scenes/Puppets/Enemies/TheGWS/P_TheGWS.cs @@ -54,7 +54,8 @@ public override void _Ready() eff.Owner ); eff.Value = 0; - } + }, + "GWS_EFFECT1" ), }; } diff --git a/Scenes/Puppets/Enemies/Turtle/P_Turtle.cs b/Scenes/Puppets/Enemies/Turtle/P_Turtle.cs index 70ebe332..e2c992bf 100644 --- a/Scenes/Puppets/Enemies/Turtle/P_Turtle.cs +++ b/Scenes/Puppets/Enemies/Turtle/P_Turtle.cs @@ -35,7 +35,8 @@ public override void _Ready() //gain block based on val e.BD.AddStatus(Targetting.First, StatusEffect.Block, val); - } + }, + "TURTLE_EFFECT1" ), }; } From c85439f76a8d210bf4f95ba1b7974542de75f99e Mon Sep 17 00:00:00 2001 From: LifeHckr Date: Mon, 30 Jun 2025 22:07:38 -0700 Subject: [PATCH 3/3] Prepare for patch PR Updated a few enemy descriptions. Removed debug buttons. --- Globals/Translations/Translations.csv | 8 ++++---- Scenes/BattleDirector/Scripts/BattleDirector.cs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Globals/Translations/Translations.csv b/Globals/Translations/Translations.csv index 58738268..2160ce13 100644 --- a/Globals/Translations/Translations.csv +++ b/Globals/Translations/Translations.csv @@ -207,17 +207,17 @@ CREDITS_ADDITIONAL_HELP,Additional Help,额外协助 CREDITS_OTHER_PLAYTESTERS,All our other playtesters,以及所有其他测试玩家 CREDITS_SPECIAL_THANKS,Special Thanks,特别鸣谢 CREDITS_THANKS_PLAYERS,And to you the player!,以及屏幕前的你! -BOSSBLOOD_EFFECT1,"Heal and convert a basic note into a Blood note.","每循环治疗 点生命,并将一个普通音符转化为血之音符。" +BOSSBLOOD_EFFECT1,"Heal and convert an empty note into a Boss Blood note.","每循环治疗 点生命,并将一个普通音符转化为血之音符。" CYBERFOX_EFFECT1,Gains one dodge.,获得一次闪避。 -EFFIGY_EFFECT1,Only takes 1 damage at a time.,每次只受到1点伤害。 +EFFIGY_EFFECT1,"Only takes 1 damage at a time, and reflects 1 damage to the player.",每次仅受到1点伤害,并向玩家反弹1点伤害。 EFFIGY_EFFECT2,Sets player's health to 1 if defeated.,被击败时将玩家的生命值设为1。 -HOLOGRAEME_EFFECT1,Player only plays every other loop.,玩家每隔一轮行动一次。 +HOLOGRAEME_EFFECT1,"Player only plays every other loop with notes hidden. Learn the pattern!",玩家每隔一次循环才能进行游戏,且期间音符会被隐藏。记住这个模式! HOLOGRAEME_EFFECT2,"Holograeme only has 1 health, and is immune to relic damage.","全息格莱姆仅有1点生命值,且免疫遗物伤害。" KEYTHULU_EFFECT1,"Every loop mindcrush ticks down by one, when it reaches zero, the player is defeated.","每次循环,心灵碾压计数会减1。当计数归零时,玩家即被击败。" LWS_EFFECT1,Converts an empty note into a Lesser Wolf Snake note.,将一个基础音符转化为次级狼蛇音符。 PARASIFLY_EFFECT1,All enemies gains block the first time fatal damage is dealt.,首次受到致命伤害时,所有敌人获得格挡。 STRAWMAN_EFFECT1,Player cannot take fatal damage.,玩家不会受到致命伤害。 -GWS_EFFECT1,Adds Greater Wolf Snake notes after the first loop.,首次循环后,添加高级狼蛇音符。 +GWS_EFFECT1,Adds Great Wolf Snake notes after the first loop.,首次循环后,添加高级狼蛇音符。 TURTLE_EFFECT1,Gains one block and drains some of player's charge to heal.,获得一层格挡,并消耗玩家部分充能以进行治疗。 GWS_NOTE_DESCRIPTION,"If timed badly, deals high damage to the player. Damage increases each loop.","若时机不佳,则对玩家造成高额伤害,且伤害随每次循环递增。" PARASIFLY_NOTE_DESCRIPTION,Parasifly gains block if timed badly.,若时机不佳,寄生蝇获得格挡。 diff --git a/Scenes/BattleDirector/Scripts/BattleDirector.cs b/Scenes/BattleDirector/Scripts/BattleDirector.cs index b4fe2929..032b3f2f 100644 --- a/Scenes/BattleDirector/Scripts/BattleDirector.cs +++ b/Scenes/BattleDirector/Scripts/BattleDirector.cs @@ -241,6 +241,7 @@ private void UpdateBeat(Beat beat) #region Input&Timing public override void _UnhandledInput(InputEvent @event) { + return; if (@event is InputEventKey eventKey && eventKey.Pressed && !eventKey.Echo) { if (eventKey.Keycode == Key.Key0)