From e68f0a8dcf6ee9f56e41179b38da95e6c3380558 Mon Sep 17 00:00:00 2001 From: Omar Akermi Date: Wed, 14 May 2025 22:56:17 +0200 Subject: [PATCH 01/10] quest patches fix --- S1API/Internal/Patches/QuestPatches.cs | 81 ++++++----- S1API/Items/ItemDefinition.cs | 190 +++++++++++++++++-------- 2 files changed, 171 insertions(+), 100 deletions(-) diff --git a/S1API/Internal/Patches/QuestPatches.cs b/S1API/Internal/Patches/QuestPatches.cs index bb964b04..cf44676e 100644 --- a/S1API/Internal/Patches/QuestPatches.cs +++ b/S1API/Internal/Patches/QuestPatches.cs @@ -1,13 +1,13 @@ -#if (IL2CPPMELON || IL2CPPBEPINEX) +#if (IL2CPPMELON) using S1Loaders = Il2CppScheduleOne.Persistence.Loaders; using S1Datas = Il2CppScheduleOne.Persistence.Datas; using S1Quests = Il2CppScheduleOne.Quests; +using S1Persistence = Il2CppScheduleOne.Persistence; #elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX) using S1Loaders = ScheduleOne.Persistence.Loaders; using S1Datas = ScheduleOne.Persistence.Datas; using S1Quests = ScheduleOne.Quests; #endif - #if (IL2CPPMELON || IL2CPPBEPINEX) using Il2CppSystem.Collections.Generic; #elif (MONOMELON || MONOBEPINEX) @@ -18,64 +18,78 @@ using System.IO; using System.Linq; using HarmonyLib; +using MelonLoader; using Newtonsoft.Json; -using S1API.Internal.Abstraction; using S1API.Internal.Utils; using S1API.Quests; using UnityEngine; +using ISaveable = S1API.Internal.Abstraction.ISaveable; namespace S1API.Internal.Patches { /// - /// INTERNAL: All patches related to quests. + /// INTERNAL: Contains patches specific to quest handling and modification. /// [HarmonyPatch] internal class QuestPatches { /// - /// Patching performed when all quests are saved. + /// Invoked after all quests are saved. + /// Ensures that modded quest data is correctly saved to a designated folder. /// - /// Instance of the quest manager. - /// Path to the base Quest folder. - /// List of extra saveable data. The game uses this for cleanup later. - [HarmonyPatch(typeof(S1Quests.QuestManager), "WriteData")] + /// The path to the primary save folder where quest data will be stored. + [HarmonyPatch(typeof(S1Persistence.SaveManager), nameof(S1Persistence.SaveManager.Save), typeof(string))] [HarmonyPostfix] - private static void QuestManagerWriteData(S1Quests.QuestManager __instance, string parentFolderPath, ref List __result) + private static void SaveManager_Save_Postfix(string saveFolderPath) { - string questsPath = Path.Combine(parentFolderPath, "Quests"); + try + { + string moddedQuestsPath = Path.Combine(saveFolderPath, "..\\Players\\ModdedQuests"); + + if (!Directory.Exists(moddedQuestsPath)) + Directory.CreateDirectory(moddedQuestsPath); + + foreach (Quest quest in QuestManager.Quests) + { + + List dummy = new(); + quest.SaveInternal(moddedQuestsPath, ref dummy); - foreach (Quest quest in QuestManager.Quests) - quest.SaveInternal(questsPath, ref __result); + } + + } + catch (Exception ex) + { + return; + } } + /// - /// Patching performed for when all quests are loaded. + /// Patching performed for when all quests are loaded from the modded quests directory. /// - /// Instance of the quest loader. - /// Path to the base Quest folder. + /// Instance of the quest loader responsible for loading quests. + /// Path to the base Quest folder where quests are located. [HarmonyPatch(typeof(S1Loaders.QuestsLoader), "Load")] [HarmonyPostfix] private static void QuestsLoaderLoad(S1Loaders.QuestsLoader __instance, string mainPath) { - // Make sure we have a quests directory (fresh saves don't at this point in runtime) - if (!Directory.Exists(mainPath)) - return; + string moddedQuestsPath = Path.Combine(mainPath, "..\\Players\\ModdedQuests"); - string[] questDirectories = Directory.GetDirectories(mainPath) + string[] questDirectories = Directory.GetDirectories(moddedQuestsPath) .Select(Path.GetFileName) .Where(directory => directory != null && directory.StartsWith("Quest_")) .ToArray()!; - foreach (string questDirectory in questDirectories) { - string baseQuestPath = Path.Combine(mainPath, questDirectory); + string baseQuestPath = Path.Combine(moddedQuestsPath, questDirectory); __instance.TryLoadFile(baseQuestPath, out string questDataText); if (questDataText == null) continue; S1Datas.QuestData baseQuestData = JsonUtility.FromJson(questDataText); - string questDirectoryPath = Path.Combine(mainPath, questDirectory); + string questDirectoryPath = Path.Combine(moddedQuestsPath, questDirectory); string questDataPath = Path.Combine(questDirectoryPath, "QuestData"); if (!__instance.TryLoadFile(questDataPath, out string questText)) continue; @@ -93,26 +107,11 @@ private static void QuestsLoaderLoad(S1Loaders.QuestsLoader __instance, string m } } + /// - /// Patching performed for when stale files are deleted. + /// Executes custom initialization logic whenever a quest starts. /// - /// Instance of the quest manager. - /// Path to the base Quest folder. - [HarmonyPatch(typeof(S1Quests.QuestManager), "DeleteUnapprovedFiles")] - [HarmonyPostfix] - private static void QuestManagerDeleteUnapprovedFiles(S1Quests.QuestManager __instance, string parentFolderPath) - { - string questFolder = Path.Combine(parentFolderPath, "Quests"); - string?[] existingQuests = QuestManager.Quests.Select(quest => quest.SaveFolder).ToArray(); - - string[] unapprovedQuestDirectories = Directory.GetDirectories(questFolder) - .Where(directory => directory.StartsWith("Quest_") && !existingQuests.Contains(directory)) - .ToArray(); - - foreach (string unapprovedQuestDirectory in unapprovedQuestDirectories) - Directory.Delete(unapprovedQuestDirectory, true); - } - + /// The instance of the quest that is starting. [HarmonyPatch(typeof(S1Quests.Quest), "Start")] [HarmonyPrefix] private static void QuestStart(S1Quests.Quest __instance) => diff --git a/S1API/Items/ItemDefinition.cs b/S1API/Items/ItemDefinition.cs index 2ac08b66..a35b0062 100644 --- a/S1API/Items/ItemDefinition.cs +++ b/S1API/Items/ItemDefinition.cs @@ -1,118 +1,190 @@ -#if (IL2CPPMELON || IL2CPPBEPINEX) +#if (IL2CPPMELON) using S1ItemFramework = Il2CppScheduleOne.ItemFramework; #elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX) using S1ItemFramework = ScheduleOne.ItemFramework; #endif +using UnityEngine; using S1API.Internal.Abstraction; namespace S1API.Items { /// /// Represents an item definition in-game. - /// NOTE: A definition is "what" the item is. For example, "This is a `Soda`". - /// Any instanced items in the game will be a instead. + /// Use this class to read and create new item definitions dynamically. /// public class ItemDefinition : IGUIDReference { /// - /// INTERNAL: A reference to the item definition in the game. + /// INTERNAL: A reference to the native game item definition. /// - internal readonly S1ItemFramework.ItemDefinition S1ItemDefinition; + internal S1ItemFramework.ItemDefinition S1ItemDefinition { get; } /// - /// Creates a new item definition from the game item definition instance. + /// INTERNAL: Wraps an existing native item definition. /// - /// - internal ItemDefinition(S1ItemFramework.ItemDefinition s1ItemDefinition) => - S1ItemDefinition = s1ItemDefinition; + internal ItemDefinition(S1ItemFramework.ItemDefinition definition) + { + S1ItemDefinition = definition; + } /// - /// INTERNAL: Gets an item definition from a GUID. + /// The unique ID of this item. /// - /// The GUID to look for - /// The applicable item definition, if found. - internal static ItemDefinition GetFromGUID(string guid) => - ItemManager.GetItemDefinition(guid); + public string ID + { + get => S1ItemDefinition.ID; + set => S1ItemDefinition.ID = value; + } /// - /// Performs an equals check on the game item definition instance. + /// The display name for this item. /// - /// The item definition you want to compare against. - /// Whether the item definitions are the same or not. - public override bool Equals(object? obj) => - obj is ItemDefinition other && S1ItemDefinition == other.S1ItemDefinition; + public string Name + { + get => S1ItemDefinition.Name; + set => S1ItemDefinition.Name = value; + } /// - /// Snags the hash code from the game instance versus this instance. + /// A short description for this item. /// - /// The game intance hash code - public override int GetHashCode() => - S1ItemDefinition?.GetHashCode() ?? 0; + public string Description + { + get => S1ItemDefinition.Description; + set => S1ItemDefinition.Description = value; + } /// - /// Performs an == check on the game item definition instance. + /// Stack limit for this item (max quantity per slot). /// - /// The first item definition to compare. - /// The second item definition to compare. - /// Whether the item definitions are the same or not. - public static bool operator ==(ItemDefinition? left, ItemDefinition? right) + public int StackLimit { - if (ReferenceEquals(left, right)) return true; - return left?.S1ItemDefinition == right?.S1ItemDefinition; + get => S1ItemDefinition.StackLimit; + set => S1ItemDefinition.StackLimit = value; } /// - /// Performs an != check on the game item definition instance. + /// The category for inventory sorting. /// - /// The first item definition to compare. - /// The second item definition to compare. - /// Whether the item definitions are different or not. - public static bool operator !=(ItemDefinition left, ItemDefinition right) => - !(left == right); + public ItemCategory Category + { + get => (ItemCategory)S1ItemDefinition.Category; + set => S1ItemDefinition.Category = (S1ItemFramework.EItemCategory)value; + } /// - /// The unique identifier assigned to this item definition. + /// The icon for this item. /// - public virtual string GUID => - S1ItemDefinition.ID; + public Sprite Icon + { + get => S1ItemDefinition.Icon; + set => S1ItemDefinition.Icon = value; + } /// - /// The unique identifier assigned to this item definition. + /// Whether this item is available in the demo version of the game. /// - public string ID => - S1ItemDefinition.ID; + public bool AvailableInDemo + { + get => S1ItemDefinition.AvailableInDemo; + set => S1ItemDefinition.AvailableInDemo = value; + } /// - /// The display name for this item. + /// Legal status of the item (e.g., illegal drugs). + /// + public LegalStatus LegalStatus + { + get => (LegalStatus)S1ItemDefinition.legalStatus; + set => S1ItemDefinition.legalStatus = (S1ItemFramework.ELegalStatus)value; + } + + + /// + /// The color of the label shown in UI. + /// + public Color LabelDisplayColor + { + get => S1ItemDefinition.LabelDisplayColor; + set => S1ItemDefinition.LabelDisplayColor = value; + } + + /// + /// Any keywords used to filter/search this item. /// - public string Name => - S1ItemDefinition.Name; + public string[] Keywords + { + get => S1ItemDefinition.Keywords; + set => S1ItemDefinition.Keywords = value; + } /// - /// The description used for this item. + /// Creates a new item instance with the specified quantity. /// - public string Description => - S1ItemDefinition.Description; + public virtual ItemInstance CreateInstance(int quantity = 1) + { + var inst = S1ItemDefinition.GetDefaultInstance(quantity); + return new ItemInstance(inst); + } + + /// + /// Gets the globally unique identifier (GUID) of the item, which is equivalent to the ID. + /// + public string GUID => ID; /// - /// The category this item is assigned to. + /// Determines whether the specified object is equal to the current object. /// - public ItemCategory Category => - (ItemCategory)S1ItemDefinition.Category; + /// The object to compare with the current object. + /// true if the specified object is an and has the same S1ItemDefinition; otherwise, false. + public override bool Equals(object? obj) => + obj is ItemDefinition other && S1ItemDefinition == other.S1ItemDefinition; /// - /// The stack limit for this item. + /// Serves as the default hash function. /// - public int StackLimit => - S1ItemDefinition.StackLimit; + /// A hash code for the current object based on S1ItemDefinition. + public override int GetHashCode() => S1ItemDefinition.GetHashCode(); /// - /// Creates an instance of this item from the definition. + /// Determines whether two instances are equal. /// - /// How many of the item the instance will have. - /// A new item instance within the game. - public virtual ItemInstance CreateInstance(int quantity = 1) => - new ItemInstance(S1ItemDefinition.GetDefaultInstance(quantity)); + /// The first to compare. + /// The second to compare. + /// true if both instances are equal or have the same S1ItemDefinition; otherwise, false. + public static bool operator ==(ItemDefinition? a, ItemDefinition? b) + { + if (ReferenceEquals(a, b)) + return true; + if (a is null || b is null) + return false; + return ReferenceEquals(a.S1ItemDefinition, b.S1ItemDefinition); + } + /// + /// Determines whether two instances are not equal. + /// + /// The first to compare. + /// The second to compare. + /// true if the instances are not equal; otherwise, false. + public static bool operator !=(ItemDefinition? a, ItemDefinition? b) + { + if (ReferenceEquals(a, b)) + return false; + if (a is null || b is null) + return true; + return !ReferenceEquals(a.S1ItemDefinition, b.S1ItemDefinition); + } + + } + + /// + /// Represents the legal status of an item (e.g., legal or illegal). + /// + public enum LegalStatus + { + Legal, + Illegal, + // More if needed } } From 78bec8b55204724f8d0060d78228d2b1f1a1ff5c Mon Sep 17 00:00:00 2001 From: Omar Akermi Date: Wed, 14 May 2025 22:59:34 +0200 Subject: [PATCH 02/10] fix for mono --- S1API/Internal/Patches/QuestPatches.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/S1API/Internal/Patches/QuestPatches.cs b/S1API/Internal/Patches/QuestPatches.cs index cf44676e..4d0b633f 100644 --- a/S1API/Internal/Patches/QuestPatches.cs +++ b/S1API/Internal/Patches/QuestPatches.cs @@ -7,6 +7,8 @@ using S1Loaders = ScheduleOne.Persistence.Loaders; using S1Datas = ScheduleOne.Persistence.Datas; using S1Quests = ScheduleOne.Quests; +using S1Persistence = ScheduleOne.Persistence; + #endif #if (IL2CPPMELON || IL2CPPBEPINEX) using Il2CppSystem.Collections.Generic; @@ -18,7 +20,6 @@ using System.IO; using System.Linq; using HarmonyLib; -using MelonLoader; using Newtonsoft.Json; using S1API.Internal.Utils; using S1API.Quests; @@ -51,8 +52,11 @@ private static void SaveManager_Save_Postfix(string saveFolderPath) foreach (Quest quest in QuestManager.Quests) { - +#if (IL2CPPMELON) List dummy = new(); +#else + List dummy = new List(); +#endif quest.SaveInternal(moddedQuestsPath, ref dummy); } From 41d3b057def140539ab5e4e22da37e32cd85340a Mon Sep 17 00:00:00 2001 From: Omar Akermi Date: Thu, 15 May 2025 00:20:21 +0200 Subject: [PATCH 03/10] changes for max --- S1API/Internal/Patches/QuestPatches.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/S1API/Internal/Patches/QuestPatches.cs b/S1API/Internal/Patches/QuestPatches.cs index 4d0b633f..9c865667 100644 --- a/S1API/Internal/Patches/QuestPatches.cs +++ b/S1API/Internal/Patches/QuestPatches.cs @@ -52,11 +52,9 @@ private static void SaveManager_Save_Postfix(string saveFolderPath) foreach (Quest quest in QuestManager.Quests) { -#if (IL2CPPMELON) - List dummy = new(); -#else + List dummy = new List(); -#endif + quest.SaveInternal(moddedQuestsPath, ref dummy); } @@ -64,7 +62,7 @@ private static void SaveManager_Save_Postfix(string saveFolderPath) } catch (Exception ex) { - return; + throw new Exception("Failed during SaveManager_Save_Postfix execution.", ex); } } From fd280c411962737fa2669d32bd0af3c0e5516472 Mon Sep 17 00:00:00 2001 From: Omar Akermi Date: Thu, 15 May 2025 11:54:16 +0200 Subject: [PATCH 04/10] fix: using logger --- S1API/Internal/Patches/QuestPatches.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/S1API/Internal/Patches/QuestPatches.cs b/S1API/Internal/Patches/QuestPatches.cs index 9c865667..8d4fee8b 100644 --- a/S1API/Internal/Patches/QuestPatches.cs +++ b/S1API/Internal/Patches/QuestPatches.cs @@ -34,6 +34,8 @@ namespace S1API.Internal.Patches [HarmonyPatch] internal class QuestPatches { + protected static readonly Logging.Log Logger = new Logging.Log("QuestPatches"); + /// /// Invoked after all quests are saved. /// Ensures that modded quest data is correctly saved to a designated folder. @@ -62,7 +64,7 @@ private static void SaveManager_Save_Postfix(string saveFolderPath) } catch (Exception ex) { - throw new Exception("Failed during SaveManager_Save_Postfix execution.", ex); + Logger.Error("Failed during SaveManager_Save_Postfix execution."); } } From e7df9c06dcf8ba6661de09a9f8e615707096019c Mon Sep 17 00:00:00 2001 From: Omar Akermi Date: Thu, 15 May 2025 12:35:19 +0200 Subject: [PATCH 05/10] change exception handling --- S1API/Internal/Patches/QuestPatches.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/S1API/Internal/Patches/QuestPatches.cs b/S1API/Internal/Patches/QuestPatches.cs index 8d4fee8b..51147a2a 100644 --- a/S1API/Internal/Patches/QuestPatches.cs +++ b/S1API/Internal/Patches/QuestPatches.cs @@ -64,7 +64,8 @@ private static void SaveManager_Save_Postfix(string saveFolderPath) } catch (Exception ex) { - Logger.Error("Failed during SaveManager_Save_Postfix execution."); + Logger.Error("Failed during SaveManager_Save_Postfix execution.\n" + ex); + throw; } } From f115cdd33e3cb1625333b656e90ab7fb0dbad1b8 Mon Sep 17 00:00:00 2001 From: Omar Akermi Date: Fri, 16 May 2025 14:31:41 +0200 Subject: [PATCH 06/10] fix: save under Modded/Quests --- S1API/Internal/Patches/QuestPatches.cs | 44 +++++++++++++++++++------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/S1API/Internal/Patches/QuestPatches.cs b/S1API/Internal/Patches/QuestPatches.cs index 51147a2a..5d98854e 100644 --- a/S1API/Internal/Patches/QuestPatches.cs +++ b/S1API/Internal/Patches/QuestPatches.cs @@ -3,6 +3,7 @@ using S1Datas = Il2CppScheduleOne.Persistence.Datas; using S1Quests = Il2CppScheduleOne.Quests; using S1Persistence = Il2CppScheduleOne.Persistence; +using Il2CppScheduleOne.DevUtilities; #elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX) using S1Loaders = ScheduleOne.Persistence.Loaders; using S1Datas = ScheduleOne.Persistence.Datas; @@ -47,25 +48,38 @@ private static void SaveManager_Save_Postfix(string saveFolderPath) { try { - string moddedQuestsPath = Path.Combine(saveFolderPath, "..\\Players\\ModdedQuests"); + var saveManager = Singleton.Instance; - if (!Directory.Exists(moddedQuestsPath)) - Directory.CreateDirectory(moddedQuestsPath); + string[] approved = { + "Modded", + Path.Combine("Modded", "Quests") + }; - foreach (Quest quest in QuestManager.Quests) + foreach (var path in approved) { + if (!saveManager.ApprovedBaseLevelPaths.Contains(path)) + saveManager.ApprovedBaseLevelPaths.Add(path); + } - List dummy = new List(); - - quest.SaveInternal(moddedQuestsPath, ref dummy); + // ✅ Create the directory structure + string questsPath = Path.Combine(saveFolderPath, "Modded", "Quests"); + Directory.CreateDirectory(questsPath); + // ✅ Save only non-vanilla modded quests + foreach (Quest quest in QuestManager.Quests) + { + if (!quest.GetType().Namespace.StartsWith("ScheduleOne")) + { + List dummy = new(); + quest.SaveInternal(questsPath, ref dummy); + } } + Logger.Msg($"[S1API] ✅ Saved modded quests to: {questsPath}"); } catch (Exception ex) { - Logger.Error("Failed during SaveManager_Save_Postfix execution.\n" + ex); - throw; + Logger.Error("[S1API] ❌ Failed to save modded quests:\n" + ex); } } @@ -79,12 +93,20 @@ private static void SaveManager_Save_Postfix(string saveFolderPath) [HarmonyPostfix] private static void QuestsLoaderLoad(S1Loaders.QuestsLoader __instance, string mainPath) { - string moddedQuestsPath = Path.Combine(mainPath, "..\\Players\\ModdedQuests"); + string moddedQuestsPath = Path.Combine(mainPath, "..//Modded", "Quests"); + + if (!Directory.Exists(moddedQuestsPath)) + { + Logger.Warning("[S1API] No Modded/Quests folder found: " + moddedQuestsPath); + return; + } + + Logger.Msg("[S1API] ✅ Loading modded quests from: " + moddedQuestsPath); string[] questDirectories = Directory.GetDirectories(moddedQuestsPath) .Select(Path.GetFileName) .Where(directory => directory != null && directory.StartsWith("Quest_")) - .ToArray()!; + .ToArray(); foreach (string questDirectory in questDirectories) { string baseQuestPath = Path.Combine(moddedQuestsPath, questDirectory); From 25daf9400c03dc6fc86b8a609b5e2ab70c14575f Mon Sep 17 00:00:00 2001 From: Omar Akermi Date: Fri, 16 May 2025 14:51:41 +0200 Subject: [PATCH 07/10] fix: load path --- S1API/Internal/Patches/QuestPatches.cs | 35 +++++++++++++++++--------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/S1API/Internal/Patches/QuestPatches.cs b/S1API/Internal/Patches/QuestPatches.cs index 5d98854e..ca14985b 100644 --- a/S1API/Internal/Patches/QuestPatches.cs +++ b/S1API/Internal/Patches/QuestPatches.cs @@ -9,7 +9,7 @@ using S1Datas = ScheduleOne.Persistence.Datas; using S1Quests = ScheduleOne.Quests; using S1Persistence = ScheduleOne.Persistence; - +using ScheduleOne.DevUtilities; #endif #if (IL2CPPMELON || IL2CPPBEPINEX) using Il2CppSystem.Collections.Generic; @@ -30,18 +30,23 @@ namespace S1API.Internal.Patches { /// - /// INTERNAL: Contains patches specific to quest handling and modification. + /// INTERNAL: Contains patches related to quest processing and custom modifications. /// [HarmonyPatch] internal class QuestPatches { + /// + /// Provides a centralized logging mechanism to capture and output messages, warnings, + /// and errors during runtime, using underlying logging frameworks like BepInEx or MelonLoader. + /// protected static readonly Logging.Log Logger = new Logging.Log("QuestPatches"); /// - /// Invoked after all quests are saved. - /// Ensures that modded quest data is correctly saved to a designated folder. + /// Executes additional logic after quests are saved by the SaveManager. + /// Ensures that directories for modded quests are properly created and that + /// only non-vanilla modded quests are saved into the specified folder. /// - /// The path to the primary save folder where quest data will be stored. + /// The path to the save folder where quests are being stored. [HarmonyPatch(typeof(S1Persistence.SaveManager), nameof(S1Persistence.SaveManager.Save), typeof(string))] [HarmonyPostfix] private static void SaveManager_Save_Postfix(string saveFolderPath) @@ -70,7 +75,7 @@ private static void SaveManager_Save_Postfix(string saveFolderPath) { if (!quest.GetType().Namespace.StartsWith("ScheduleOne")) { - List dummy = new(); + List dummy = new List(); quest.SaveInternal(questsPath, ref dummy); } } @@ -85,15 +90,19 @@ private static void SaveManager_Save_Postfix(string saveFolderPath) /// - /// Patching performed for when all quests are loaded from the modded quests directory. + /// Invoked after all base quests are loaded to handle modded quest loading. + /// Loads modded quests from a specific "Modded/Quests" directory and integrates them into the game. /// - /// Instance of the quest loader responsible for loading quests. - /// Path to the base Quest folder where quests are located. + /// The quest loader instance responsible for managing quest load operations. + /// The path to the primary quest directory in the base game. [HarmonyPatch(typeof(S1Loaders.QuestsLoader), "Load")] [HarmonyPostfix] private static void QuestsLoaderLoad(S1Loaders.QuestsLoader __instance, string mainPath) { - string moddedQuestsPath = Path.Combine(mainPath, "..//Modded", "Quests"); + string moddedQuestsPath = Path.Combine( + Singleton.Instance.LoadedGameFolderPath, + "Modded", "Quests" + ); if (!Directory.Exists(moddedQuestsPath)) { @@ -107,6 +116,7 @@ private static void QuestsLoaderLoad(S1Loaders.QuestsLoader __instance, string m .Select(Path.GetFileName) .Where(directory => directory != null && directory.StartsWith("Quest_")) .ToArray(); + foreach (string questDirectory in questDirectories) { string baseQuestPath = Path.Combine(moddedQuestsPath, questDirectory); @@ -136,9 +146,10 @@ private static void QuestsLoaderLoad(S1Loaders.QuestsLoader __instance, string m /// - /// Executes custom initialization logic whenever a quest starts. + /// Executes logic prior to the start of a quest. + /// Ensures that linked modded quest data is properly initialized. /// - /// The instance of the quest that is starting. + /// The instance of the quest that is being started. [HarmonyPatch(typeof(S1Quests.Quest), "Start")] [HarmonyPrefix] private static void QuestStart(S1Quests.Quest __instance) => From eaeb3390f6cfe33b7341f7004f25967dd0878ffd Mon Sep 17 00:00:00 2001 From: Omar Akermi Date: Fri, 16 May 2025 14:53:54 +0200 Subject: [PATCH 08/10] fix: no singleton usage savemanager.instance --- S1API/Internal/Patches/QuestPatches.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/S1API/Internal/Patches/QuestPatches.cs b/S1API/Internal/Patches/QuestPatches.cs index ca14985b..af5fb96d 100644 --- a/S1API/Internal/Patches/QuestPatches.cs +++ b/S1API/Internal/Patches/QuestPatches.cs @@ -53,7 +53,7 @@ private static void SaveManager_Save_Postfix(string saveFolderPath) { try { - var saveManager = Singleton.Instance; + var saveManager = S1Persistence.SaveManager.Instance; string[] approved = { "Modded", @@ -80,7 +80,6 @@ private static void SaveManager_Save_Postfix(string saveFolderPath) } } - Logger.Msg($"[S1API] ✅ Saved modded quests to: {questsPath}"); } catch (Exception ex) { @@ -110,8 +109,6 @@ private static void QuestsLoaderLoad(S1Loaders.QuestsLoader __instance, string m return; } - Logger.Msg("[S1API] ✅ Loading modded quests from: " + moddedQuestsPath); - string[] questDirectories = Directory.GetDirectories(moddedQuestsPath) .Select(Path.GetFileName) .Where(directory => directory != null && directory.StartsWith("Quest_")) From e84c6581d55d5c238960d5bb1df1385d205e15e4 Mon Sep 17 00:00:00 2001 From: Omar Akermi Date: Fri, 16 May 2025 14:55:42 +0200 Subject: [PATCH 09/10] fix: singelton in load --- S1API/Internal/Patches/QuestPatches.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/S1API/Internal/Patches/QuestPatches.cs b/S1API/Internal/Patches/QuestPatches.cs index af5fb96d..eb985cfb 100644 --- a/S1API/Internal/Patches/QuestPatches.cs +++ b/S1API/Internal/Patches/QuestPatches.cs @@ -99,7 +99,7 @@ private static void SaveManager_Save_Postfix(string saveFolderPath) private static void QuestsLoaderLoad(S1Loaders.QuestsLoader __instance, string mainPath) { string moddedQuestsPath = Path.Combine( - Singleton.Instance.LoadedGameFolderPath, + S1Persistence.LoadManager.Instance.LoadedGameFolderPath, "Modded", "Quests" ); From 84bd747058617b963ac34bbc8d75dee21f7c413a Mon Sep 17 00:00:00 2001 From: Omar Akermi Date: Sat, 17 May 2025 11:09:10 +0200 Subject: [PATCH 10/10] fix: directory creation instead of log --- S1API/Internal/Patches/QuestPatches.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/S1API/Internal/Patches/QuestPatches.cs b/S1API/Internal/Patches/QuestPatches.cs index eb985cfb..189019f5 100644 --- a/S1API/Internal/Patches/QuestPatches.cs +++ b/S1API/Internal/Patches/QuestPatches.cs @@ -3,13 +3,11 @@ using S1Datas = Il2CppScheduleOne.Persistence.Datas; using S1Quests = Il2CppScheduleOne.Quests; using S1Persistence = Il2CppScheduleOne.Persistence; -using Il2CppScheduleOne.DevUtilities; #elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX) using S1Loaders = ScheduleOne.Persistence.Loaders; using S1Datas = ScheduleOne.Persistence.Datas; using S1Quests = ScheduleOne.Quests; using S1Persistence = ScheduleOne.Persistence; -using ScheduleOne.DevUtilities; #endif #if (IL2CPPMELON || IL2CPPBEPINEX) using Il2CppSystem.Collections.Generic; @@ -105,7 +103,7 @@ private static void QuestsLoaderLoad(S1Loaders.QuestsLoader __instance, string m if (!Directory.Exists(moddedQuestsPath)) { - Logger.Warning("[S1API] No Modded/Quests folder found: " + moddedQuestsPath); + Directory.CreateDirectory(moddedQuestsPath); return; }