diff --git a/Nautilus/Assets/Gadgets/CraftingGadget.cs b/Nautilus/Assets/Gadgets/CraftingGadget.cs index 30eea3e3..b8ae8229 100644 --- a/Nautilus/Assets/Gadgets/CraftingGadget.cs +++ b/Nautilus/Assets/Gadgets/CraftingGadget.cs @@ -104,7 +104,7 @@ protected internal override void Build() } else if (!prefab.TryGetGadget(out ScanningGadget scanningGadget) || !scanningGadget.IsBuildable) { - InternalLogger.Log($"Prefab '{prefab.Info.ClassID}' was not automatically registered into a crafting tree."); + InternalLogger.Debug($"Prefab '{prefab.Info.ClassID}' was not automatically registered into a crafting tree."); } if (CraftingTime >= 0f) diff --git a/Nautilus/Assets/Gadgets/ScanningGadget.cs b/Nautilus/Assets/Gadgets/ScanningGadget.cs index 367a66ad..49c0e4e1 100644 --- a/Nautilus/Assets/Gadgets/ScanningGadget.cs +++ b/Nautilus/Assets/Gadgets/ScanningGadget.cs @@ -381,17 +381,17 @@ protected internal override void Build() PDAHandler.AddEncyclopediaEntry(EncyclopediaEntryData); } - if (AnalysisTech is { }) - { - KnownTechHandler.SetAnalysisTechEntry(AnalysisTech); - } - if (CompoundTechsForUnlock is { Count: > 0 }) { KnownTechHandler.RemoveAllCurrentAnalysisTechEntry(prefab.Info.TechType); KnownTechHandler.SetCompoundUnlock(prefab.Info.TechType, CompoundTechsForUnlock); } + if (AnalysisTech is { }) + { + KnownTechHandler.SetAnalysisTechEntry(AnalysisTech); + } + if (ScannerEntryData is { }) { PDAHandler.AddCustomScannerEntry(ScannerEntryData); @@ -399,7 +399,7 @@ protected internal override void Build() if (RequiredForUnlock != TechType.None) { - KnownTechHandler.AddRequirementForUnlock(prefab.Info.TechType, RequiredForUnlock); + KnownTechHandler.SetAnalysisTechEntry(RequiredForUnlock, new TechType[] { prefab.Info.TechType }); } if (CompoundTechsForUnlock is { Count: > 0 } || RequiredForUnlock != TechType.None) diff --git a/Nautilus/Assets/ModPrefabCache.cs b/Nautilus/Assets/ModPrefabCache.cs index 933c546d..78412c3b 100644 --- a/Nautilus/Assets/ModPrefabCache.cs +++ b/Nautilus/Assets/ModPrefabCache.cs @@ -125,6 +125,7 @@ public void RemoveCachedPrefab(string classId) { if(!prefab.IsPrefab()) Destroy(prefab); + InternalLogger.Debug($"ModPrefabCache: removed prefab {classId}"); Entries.Remove(classId); } } diff --git a/Nautilus/Assets/ModPrefabRequest.cs b/Nautilus/Assets/ModPrefabRequest.cs index c3154ca5..84f1d28d 100644 --- a/Nautilus/Assets/ModPrefabRequest.cs +++ b/Nautilus/Assets/ModPrefabRequest.cs @@ -25,7 +25,7 @@ public ModPrefabRequest(PrefabInfo prefabInfo) private void Init() { - if (task != null) + if ((task != null && !Done) || (Done && TryGetPrefab(out _))) { return; } @@ -45,34 +45,37 @@ public object Current get { Init(); - return task; + return task.Current; } } public bool TryGetPrefab(out GameObject result) { - result = taskResult.Get(); - if (!Done) - { - Done = result; - } - return result != null; + return ModPrefabCache.TryGetPrefabFromCache(prefabInfo.ClassID, out result) && result != null; } public bool MoveNext() { Init(); - if (task == null) + if (!task.MoveNext()) { - return false; + Done = true; } - - return !TryGetPrefab(out _); + return !Done; } - public void Reset() {} + public void Reset() + { + Init(); + task.Reset(); + Done = false; + } public void Release() { + ModPrefabCache.RemovePrefabFromCache(prefabInfo.ClassID); + taskResult = null; + task = null; + Done = false; } } \ No newline at end of file diff --git a/Nautilus/Crafting/ModCraftTreeLinkingNode.cs b/Nautilus/Crafting/ModCraftTreeLinkingNode.cs index d1c84bb2..32e9700a 100644 --- a/Nautilus/Crafting/ModCraftTreeLinkingNode.cs +++ b/Nautilus/Crafting/ModCraftTreeLinkingNode.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Nautilus.Handlers; +using Nautilus.Utility; using UnityEngine; using UnityEngine.Assertions; @@ -83,16 +84,15 @@ public ModCraftTreeTab AddTabNode(string nameID) /// public ModCraftTreeTab GetTabNode(string nameID) { - foreach (ModCraftTreeTab node in ChildNodes) + foreach (var node in ChildNodes) { if (node == null) { continue; } - if (node.Name == nameID && node.Action == TreeAction.Expand) + if (node.Name == nameID && node.Action == TreeAction.Expand && node is ModCraftTreeTab tab) { - ModCraftTreeTab tab = node; return tab; } } @@ -107,16 +107,15 @@ public ModCraftTreeTab GetTabNode(string nameID) /// public ModCraftTreeCraft GetCraftingNode(TechType techType) { - foreach (ModCraftTreeNode node in ChildNodes) + foreach (var node in ChildNodes) { if (node == null) { continue; } - if (node.TechType == techType && node.Action == TreeAction.Craft) + if (node.TechType == techType && node.Action == TreeAction.Craft && node is ModCraftTreeCraft craftNode) { - ModCraftTreeCraft craftNode = (ModCraftTreeCraft)node; return craftNode; } } @@ -131,7 +130,7 @@ public ModCraftTreeCraft GetCraftingNode(TechType techType) /// public ModCraftTreeNode GetNode(string nameID) { - foreach (ModCraftTreeNode node in ChildNodes) + foreach (var node in ChildNodes) { if (node == null) { diff --git a/Nautilus/Handlers/CraftTreeHandler.cs b/Nautilus/Handlers/CraftTreeHandler.cs index c16a6ca4..e086faf7 100644 --- a/Nautilus/Handlers/CraftTreeHandler.cs +++ b/Nautilus/Handlers/CraftTreeHandler.cs @@ -1,14 +1,13 @@ +namespace Nautilus.Handlers; + using System.Linq; using Nautilus.Crafting; using Nautilus.Patchers; -using Nautilus.Utility; - -namespace Nautilus.Handlers; /// /// A handler class for creating and modifying crafting trees. /// -public static class CraftTreeHandler +public static class CraftTreeHandler { /// /// Adds a new crafting node to the root of the specified crafting tree, at the provided tab location. @@ -23,7 +22,7 @@ public static class CraftTreeHandler /// public static void AddCraftingNode(CraftTree.Type craftTree, TechType craftingItem, params string[] stepsToTab) { - if(CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) + if (CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) { root.AddCraftNode(craftingItem, stepsToTab.LastOrDefault()); return; @@ -40,7 +39,7 @@ public static void AddCraftingNode(CraftTree.Type craftTree, TechType craftingIt public static void AddCraftingNode(CraftTree.Type craftTree, TechType craftingItem) { - if(CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) + if (CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) { root.AddCraftNode(craftingItem); return; @@ -58,7 +57,7 @@ public static void AddCraftingNode(CraftTree.Type craftTree, TechType craftingIt /// The sprite of the tab. public static void AddTabNode(CraftTree.Type craftTree, string name, string displayName, Atlas.Sprite sprite) { - if(CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) + if (CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) { root.AddTabNode(name, displayName, sprite); return; @@ -76,7 +75,7 @@ public static void AddTabNode(CraftTree.Type craftTree, string name, string disp public static void AddTabNode(CraftTree.Type craftTree, string name, string displayName, UnityEngine.Sprite sprite) { - if(CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) + if (CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) { root.AddTabNode(name, displayName, sprite); return; @@ -99,7 +98,7 @@ public static void AddTabNode(CraftTree.Type craftTree, string name, string disp /// public static void AddTabNode(CraftTree.Type craftTree, string name, string displayName, Atlas.Sprite sprite, params string[] stepsToTab) { - if(CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) + if (CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) { root.AddTabNode(name, displayName, sprite, "English", stepsToTab.LastOrDefault()); return; @@ -122,7 +121,7 @@ public static void AddTabNode(CraftTree.Type craftTree, string name, string disp /// public static void AddTabNode(CraftTree.Type craftTree, string name, string displayName, UnityEngine.Sprite sprite, params string[] stepsToTab) { - if(CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) + if (CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) { root.AddTabNode(name, displayName, sprite, "English", stepsToTab.LastOrDefault()); return; @@ -188,15 +187,9 @@ public static void AddTabNode(CraftTree.Type craftTree, string name, string disp public static void RemoveNode(CraftTree.Type craftTree, params string[] stepsToNode) { - if(CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) + if (CraftTreePatcher.CustomTrees.TryGetValue(craftTree, out var root)) { - var tab = root.GetTabNode(stepsToNode); - if(tab == null) - { - root.GetNode(stepsToNode)?.RemoveNode(); - return; - } - tab.RemoveNode(); + root.GetNode(stepsToNode)?.RemoveNode(); return; } CraftTreePatcher.NodesToRemove.Add(new Node(stepsToNode, craftTree)); diff --git a/Nautilus/Handlers/Enums/Extensions/EnumExtensions_TechCategory.cs b/Nautilus/Handlers/Enums/Extensions/EnumExtensions_TechCategory.cs index 65473cf1..c43f5662 100644 --- a/Nautilus/Handlers/Enums/Extensions/EnumExtensions_TechCategory.cs +++ b/Nautilus/Handlers/Enums/Extensions/EnumExtensions_TechCategory.cs @@ -17,23 +17,22 @@ public static EnumBuilder WithPdaInfo(this EnumBuilder WithPdaInfo(this EnumBuilder bui { var techGroup = (TechGroup)builder; var name = builder.ToString(); + var fullName = "Group" + name; if (!string.IsNullOrEmpty(displayName)) { - LanguageHandler.SetLanguageLine("Group" + name, displayName, language); + LanguageHandler.SetLanguageLine(fullName, displayName, language); } - else if (string.IsNullOrEmpty(Language.main.Get("Group" + name))) + else if (string.IsNullOrEmpty(Language.main.Get(fullName))) { InternalLogger.Warn($"Display name was not specified and no existing language line has been found for TechGroup '{name}'."); } diff --git a/Nautilus/Handlers/KnownTechHandler.cs b/Nautilus/Handlers/KnownTechHandler.cs index afc0acb9..19dc13d2 100644 --- a/Nautilus/Handlers/KnownTechHandler.cs +++ b/Nautilus/Handlers/KnownTechHandler.cs @@ -24,6 +24,27 @@ private static void Reinitialize() public static void UnlockOnStart(TechType techType) { KnownTechPatcher.UnlockedAtStart.Add(techType); + + bool removed = false; + var removalList = new List(); + KnownTechPatcher.DefaultRemovalTechs.ForEach((x) => + { + if (x.Value.Remove(techType)) + { + removed = true; + InternalLogger.Debug($"Removed {techType.AsString()} from {x.Key}'s DefaultRemovalTechs"); + if (x.Value.Count > 0 ) + { + removalList.Add(x.Key); + } + } + }); + + foreach (string key in removalList) + KnownTechPatcher.DefaultRemovalTechs.Remove(key); + + if (removed) + InternalLogger.Debug($"Set {techType.AsString()} to Unlock On Start "); Reinitialize(); } @@ -48,7 +69,7 @@ public static void AddRequirementForUnlock(TechType blueprint, TechType requirem public static void SetHardLocked(TechType techType) { KnownTechPatcher.HardLocked.Add(techType); - KnownTechPatcher.UnlockedAtStart.Remove(techType); + RemoveDefaultUnlock(techType); Reinitialize(); } @@ -140,17 +161,13 @@ internal static void RemoveAnalysisSpecific(TechType targetTechType, List types)) - { - if (!types.Contains(targetTechType)) - { - types.Add(targetTechType); - } - } + if (KnownTechPatcher.AnalysisTech.TryGetValue(techType, out var analysisTech) && analysisTech.unlockTechTypes.Remove(targetTechType)) + InternalLogger.Debug($"Removed unlock for {targetTechType.AsString()} from {techType} that was added by another mod."); + + if (KnownTechPatcher.RemoveFromSpecificTechs.TryGetValue(techType, out HashSet types)) + types.Add(targetTechType); else - { - KnownTechPatcher.RemoveFromSpecificTechs[techType] = new List() { targetTechType }; - } + KnownTechPatcher.RemoveFromSpecificTechs[techType] = new HashSet() { targetTechType }; } Reinitialize(); @@ -158,6 +175,11 @@ internal static void RemoveAnalysisSpecific(TechType targetTechType, List before calling this method. /// /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked. @@ -198,6 +221,7 @@ public static void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerab /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed". /// If there is already an existing AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be /// added to the existing AnalysisTech entry unlocks. + /// ***Note: This will not remove any original unlock and if you need to do so you should use before calling this method. /// /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked. @@ -211,6 +235,7 @@ public static void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerab /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed". /// If there is already an existing AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be /// added to the existing AnalysisTech entry unlocks. + /// ***Note: This will not remove any original unlock and if you need to do so you should use before calling this method. /// /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked. @@ -224,6 +249,7 @@ public static void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerab /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed". /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be /// added to the existing AnalysisTech entry unlocks. + /// ***Note: This will not remove any original unlock and if you need to do so you should use before calling this method. /// /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked. @@ -237,6 +263,7 @@ public static void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerab /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed". /// If there is already an existing AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be /// added to the existing AnalysisTech entry unlocks. + /// ***Note: This will not remove any original unlock and if you need to do so you should use before calling this method. /// /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked. @@ -251,6 +278,7 @@ public static void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerab /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed". /// If there is already an existing AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be /// added to the existing AnalysisTech entry unlocks. + /// ***Note: This will not remove any original unlock and if you need to do so you should use before calling this method. /// /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked. @@ -265,6 +293,7 @@ public static void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerab /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed". /// If there is already an existing AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be /// added to the existing AnalysisTech entry unlocks. + /// ***Note: This will not remove any original unlock and if you need to do so you should use before calling this method. /// /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked. @@ -279,6 +308,7 @@ public static void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerab /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed". /// If there is already an existing AnalysisTech entry for a TechType, all the TechTypes in will be /// added to the existing AnalysisTech entry unlocks. + /// ***Note: This will not remove any original unlock and if you need to do so you should use before calling this method. /// /// The analysis tech entry to add. public static void SetAnalysisTechEntry(KnownTech.AnalysisTech analysisTech) @@ -290,6 +320,7 @@ public static void SetAnalysisTechEntry(KnownTech.AnalysisTech analysisTech) /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed". /// If there is already an existing AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be /// added to the existing AnalysisTech entry unlocks. + /// ***Note: This will not remove any original unlock and if you need to do so you should use before calling this method. /// /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked. @@ -303,7 +334,7 @@ public static void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerab /// /// Allows you to set up a custom Compound Unlock requiring multiple techtypes to be unlocked before 1 is. - /// ***Note: This will not remove any original unlock and if you need to do so you should use or + /// ***Note: This will not remove any original unlock and if you need to do so you should use before calling this method. /// /// /// @@ -344,10 +375,12 @@ public static void RemoveDefaultUnlock(TechType techType) { var modName = ReflectionHelper.CallingAssemblyByStackTrace().GetName().Name; if (!KnownTechPatcher.DefaultRemovalTechs.TryGetValue(modName, out var techTypes)) - techTypes = new List(); + techTypes = new HashSet(); techTypes.Add(techType); KnownTechPatcher.DefaultRemovalTechs[modName] = techTypes; + if (KnownTechPatcher.UnlockedAtStart.Remove(techType)) + InternalLogger.Debug($"Removed Default unlock for {techType} that was added by another mod."); Reinitialize(); } diff --git a/Nautilus/Handlers/PrefabHandler.cs b/Nautilus/Handlers/PrefabHandler.cs index f3f0f49f..c2665fed 100644 --- a/Nautilus/Handlers/PrefabHandler.cs +++ b/Nautilus/Handlers/PrefabHandler.cs @@ -26,19 +26,20 @@ internal static IEnumerator GetPrefabAsync(TaskResult gameObject, Pr yield break; } - yield return prefabFactory(gameObject); - - yield return ProcessPrefabAsync(gameObject, info, prefabFactory); + yield return InitPrefabAsync(gameObject, info, prefabFactory); } - private static IEnumerator ProcessPrefabAsync(TaskResult gameObject, PrefabInfo info, PrefabFactoryAsync prefabFactory) + private static IEnumerator InitPrefabAsync(TaskResult gameObject, PrefabInfo info, PrefabFactoryAsync prefabFactory) { + yield return prefabFactory(gameObject); + var obj = gameObject.Get(); if(obj == null) { - InternalLogger.Error($"PrefabHandler: Tried to process a null prefab."); + InternalLogger.Error($"PrefabHandler: PrefabFactory returned null for {info.ClassID}"); yield break; } + obj.SetActive(false); var techType = info.TechType; var classId = info.ClassID; @@ -76,7 +77,6 @@ private static IEnumerator ProcessPrefabAsync(TaskResult gameObject, if (Prefabs.TryGetPostProcessorForInfo(info, out var postProcessor)) yield return postProcessor?.Invoke(obj); - gameObject.Set(obj); ModPrefabCache.AddPrefab(obj); } diff --git a/Nautilus/Handlers/SurvivalHandler.cs b/Nautilus/Handlers/SurvivalHandler.cs index f71dd280..0b477069 100644 --- a/Nautilus/Handlers/SurvivalHandler.cs +++ b/Nautilus/Handlers/SurvivalHandler.cs @@ -7,7 +7,7 @@ namespace Nautilus.Handlers; /// /// Handler class that relates to the component. Allows the defining of oxygen or health gains when consuming specific items. /// -public static class SurvivalHandler +public static class SurvivalHandler { /// /// makes the item gives oxygen on use. @@ -15,56 +15,81 @@ public static class SurvivalHandler /// the TechType that you want to make it give oxygen on use /// the oxygen amount the item gives /// set it to if the item is edible and has the component attached to it. - /// defaults to /// public static void GiveOxygenOnConsume(TechType techType, float oxygenGiven, bool isEdible) { - if (SurvivalPatcher.CustomSurvivalInventoryAction.TryGetValue(techType, out List action)) + if (!isEdible) { - action.Add(() => { Player.main.GetComponent().AddOxygen(oxygenGiven); }); // add an action to the list - return; + SurvivalPatcher.InventoryUseables.Add(techType); // add it to the HashSet of useables if its not edible } - // if we reach to this point then the techtype doesn't exist in the dictionary so we add it - SurvivalPatcher.CustomSurvivalInventoryAction[techType] = new List() - { - () => - { - Player.main.GetComponent().AddOxygen(oxygenGiven); - } - }; - if (!isEdible) + if (!SurvivalPatcher.CustomSurvivalInventoryAction.TryGetValue(techType, out List actions)) + actions = new List(); + + // add an action to the list + actions.Add(() => { - SurvivalPatcher.InventoryUseables.Add(techType); - } + var oxygenManager = Player.main.GetComponent(); + if (oxygenGiven > 0f) + oxygenManager.AddOxygen(oxygenGiven); + else + oxygenManager.RemoveOxygen(-oxygenGiven); + }); + + SurvivalPatcher.CustomSurvivalInventoryAction[techType] = actions; } + /// /// makes the item Heal the player on consume. /// /// the TechType that you want it to heal back /// amount to heal the player /// set it to if the item is edible and has the component attached to it. - /// defaults to /// public static void GiveHealthOnConsume(TechType techType, float healthBack, bool isEdible) { - if (SurvivalPatcher.CustomSurvivalInventoryAction.TryGetValue(techType, out List action)) + if (!isEdible) { - action.Add(() => { Player.main.GetComponent().AddHealth(healthBack); }); // add an action to the list - return; + SurvivalPatcher.InventoryUseables.Add(techType); // add it to the HashSet of useables if its not edible } - // if we reach to this point then the techtype doesn't exist in the dictionary so we add it - SurvivalPatcher.CustomSurvivalInventoryAction[techType] = new List() - { - () => - { - Player.main.GetComponent().AddHealth(healthBack); - } - }; + if (!SurvivalPatcher.CustomSurvivalInventoryAction.TryGetValue(techType, out List actions)) + actions = new List(); + + actions.Add(() => { + var liveMixin = Player.main.GetComponent(); + if (healthBack > 0) + liveMixin.AddHealth(healthBack); + else + liveMixin.TakeDamage(-healthBack, default, DamageType.Poison); + }); + + SurvivalPatcher.CustomSurvivalInventoryAction[techType] = actions; + } + + /// + /// runs a custom action on consume. + /// + /// the TechType that you want it to heal back + /// the Action to perform. + /// set it to if the item is edible and has the component attached to it. + /// + public static void RunActionOnConsume(TechType techType, Action customAction, bool isEdible) + { + if (techType == TechType.None) + throw new ArgumentNullException(nameof(techType), "TechType cannot be None."); + if (customAction == null) + throw new ArgumentNullException(nameof(customAction), "Action cannot be null."); + if (!isEdible) { - SurvivalPatcher.InventoryUseables.Add(techType); + SurvivalPatcher.InventoryUseables.Add(techType); // add it to the HashSet of useables if its not edible } + + if (!SurvivalPatcher.CustomSurvivalInventoryAction.TryGetValue(techType, out List actions)) + actions = new List(); + + actions.Add(customAction); + SurvivalPatcher.CustomSurvivalInventoryAction[techType] = actions; } } \ No newline at end of file diff --git a/Nautilus/Patchers/KnownTechPatcher.cs b/Nautilus/Patchers/KnownTechPatcher.cs index f9fec628..6c40ee11 100644 --- a/Nautilus/Patchers/KnownTechPatcher.cs +++ b/Nautilus/Patchers/KnownTechPatcher.cs @@ -14,11 +14,11 @@ internal class KnownTechPatcher internal static HashSet LockedWithNoUnlocks = new(); internal static HashSet HardLocked = new(); internal static HashSet RemovalTechs = new(); - internal static Dictionary> DefaultRemovalTechs = new(); + internal static Dictionary> DefaultRemovalTechs = new(); internal static IDictionary AnalysisTech = new SelfCheckingDictionary("AnalysisTech", AsStringFunction); - internal static IDictionary> BlueprintRequirements = new SelfCheckingDictionary>("BlueprintRequirements", AsStringFunction); + internal static IDictionary> BlueprintRequirements = new SelfCheckingDictionary>("BlueprintRequirements", AsStringFunction); internal static IDictionary CompoundTech = new SelfCheckingDictionary("CompoundTech", AsStringFunction); - internal static IDictionary> RemoveFromSpecificTechs = new SelfCheckingDictionary>("RemoveFromSpecificTechs", AsStringFunction); + internal static IDictionary> RemoveFromSpecificTechs = new SelfCheckingDictionary>("RemoveFromSpecificTechs", AsStringFunction); public static void Patch(Harmony harmony) { @@ -50,15 +50,46 @@ internal static void Reinitialize() private static void InitializePrefix(PDAData data) { - foreach (var unlockedTech in UnlockedAtStart) + // This needs to be done first as this list was supposed to be only for game unlocks. + // Mod unlocks requesting removal are already processed in the KnownTechHandler at the time of the request. + // Mods trying to remove other mods unlocks must make sure to run after the original mod has already added them. + foreach (var removalTech in RemovalTechs) { - if (!data.defaultTech.Contains(unlockedTech)) + if (data.compoundTech.RemoveAll((x)=> x.techType == removalTech) > 0) + InternalLogger.Debug($"RemovalTechs: Removed compoundTech for '{removalTech}'"); + + if (data.analysisTech.RemoveAll((x) => x.techType == removalTech) > 0) + InternalLogger.Debug($"RemovalTechs: Removed analysisTech for '{removalTech}'"); + + foreach (var analysisTech in data.analysisTech) { - data.defaultTech.Add(unlockedTech); - InternalLogger.Debug($"Setting {unlockedTech.AsString()} to be unlocked at start."); + if (analysisTech.unlockTechTypes.Remove(removalTech)) + { + InternalLogger.Debug($"RemovalTechs: Removed unlockTechType '{removalTech}' from '{analysisTech.techType}' AnalysisTech."); + } + } + } + + // Process removals of specific game unlocks as requested by mods. + // Mod unlocks requesting removal are already processed in the KnownTechHandler at the time of the request. + // Mods trying to remove other mods unlocks must make sure to run after the original mod has already added them. + foreach (var analysisTech in data.analysisTech) + { + if (RemoveFromSpecificTechs.TryGetValue(analysisTech.techType, out var techsToRemove)) + { + foreach (var removalTech in techsToRemove) + { + if (analysisTech.unlockTechTypes.Remove(removalTech)) + { + InternalLogger.Debug($"RemoveFromSpecificTechs: Removed unlockTechType '{removalTech}' from '{analysisTech.techType}' AnalysisTech."); + } + } } } + // remove all default unlocks specified by mods. + // Mod unlocks requesting removal are already processed in the KnownTechHandler at the time of the request. + // Mods trying to remove other mods unlocks must make sure to run after the original mod has already added them. foreach (var removalTechsByMod in DefaultRemovalTechs) { foreach (var removalTech in removalTechsByMod.Value) @@ -70,26 +101,46 @@ private static void InitializePrefix(PDAData data) } } + // Add all default unlocks set by mods. + foreach (var unlockedTech in UnlockedAtStart) + { + if (!data.defaultTech.Contains(unlockedTech)) + { + data.defaultTech.Add(unlockedTech); + InternalLogger.Debug($"Setting {unlockedTech.AsString()} to be unlocked at start."); + } + } + + // Add or modify analysisTechs as requested by mods. foreach (var tech in AnalysisTech.Values) { var index = data.analysisTech.FindIndex(analysisTech => analysisTech.techType == tech.techType); if (index == -1) { InternalLogger.Debug($"Adding analysisTech for {tech.techType}"); + + if (tech.unlockSound == null) + tech.unlockSound = KnownTechHandler.DefaultUnlockData.BlueprintUnlockSound; + data.analysisTech.Add(tech); } else { - InternalLogger.Debug($"Replacing analysisTech for {tech.techType}"); - data.analysisTech[index] = tech; - } + InternalLogger.Debug($"Altering original analysisTech for {tech.techType}"); + var existingEntry = data.analysisTech[index]; - if (tech.unlockSound == null) - { - tech.unlockSound = KnownTechHandler.DefaultUnlockData.BlueprintUnlockSound; + existingEntry.unlockMessage = tech.unlockMessage ?? existingEntry.unlockMessage; + existingEntry.unlockSound = tech.unlockSound ?? existingEntry.unlockSound; + existingEntry.unlockPopup = tech.unlockPopup ?? existingEntry.unlockPopup; + existingEntry.unlockTechTypes.AddRange(tech.unlockTechTypes); +#if SUBNAUTICA + existingEntry.storyGoals.AddRange(tech.storyGoals); +#endif } } + // Set unlocks for already existing analysis techs where a AnalysisTech is not created by the mod. + // tbh I don't know why this was added as we used to just make an analysis tech with the data given by the modder if they did not specify one. foreach (var blueprintRequirements in BlueprintRequirements) { var index = data.analysisTech.FindIndex(tech => tech.techType == blueprintRequirements.Key); @@ -103,6 +154,7 @@ private static void InitializePrefix(PDAData data) data.analysisTech[index].unlockTechTypes.AddRange(blueprintRequirements.Value); } + // Add or Replace CompoundTechs as requested by mods. foreach (var tech in CompoundTech.Values) { var index = data.compoundTech.FindIndex(compoundTech => compoundTech.techType == tech.techType); @@ -117,33 +169,6 @@ private static void InitializePrefix(PDAData data) data.compoundTech[index] = tech; } } - - foreach (var analysisTech in data.analysisTech) - { - foreach (var removalTech in RemovalTechs) - { - if (analysisTech.techType == removalTech) - { - continue; - } - - if (analysisTech.unlockTechTypes.Remove(removalTech)) - { - InternalLogger.Debug($"RemovalTechs: Removed unlockTechType '{removalTech}' from '{analysisTech.techType}' AnalysisTech."); - } - } - - if (RemoveFromSpecificTechs.TryGetValue(analysisTech.techType, out var techsToRemove)) - { - foreach (var removalTech in techsToRemove) - { - if (analysisTech.unlockTechTypes.Remove(removalTech)) - { - InternalLogger.Debug($"RemoveFromSpecificTechs: Removed unlockTechType '{removalTech}' from '{analysisTech.techType}' AnalysisTech."); - } - } - } - } } private static void GetAllUnlockablesPostfix(HashSet __result) diff --git a/Nautilus/Patchers/PrefabDatabasePatcher.cs b/Nautilus/Patchers/PrefabDatabasePatcher.cs index 172f2251..93e05e59 100644 --- a/Nautilus/Patchers/PrefabDatabasePatcher.cs +++ b/Nautilus/Patchers/PrefabDatabasePatcher.cs @@ -80,12 +80,7 @@ private static IPrefabRequest GetModPrefabAsync(string classId) } if(ModPrefabCache.Requests.TryGetValue(prefabInfo.ClassID, out var request)) - { - if (request.Done && !request.TryGetPrefab(out _)) - { - return new ModPrefabRequest(prefabInfo); - } - + { return request; } diff --git a/Nautilus/Patchers/SurvivalPatcher.cs b/Nautilus/Patchers/SurvivalPatcher.cs index 00ae3f77..62ec5dcd 100644 --- a/Nautilus/Patchers/SurvivalPatcher.cs +++ b/Nautilus/Patchers/SurvivalPatcher.cs @@ -10,7 +10,7 @@ namespace Nautilus.Patchers; internal class SurvivalPatcher { internal static IDictionary> CustomSurvivalInventoryAction = new SelfCheckingDictionary>("CustomSurvivalInventoryAction", TechTypeExtensions.sTechTypeComparer); - internal static List InventoryUseables = new(); + internal static HashSet InventoryUseables = new(); internal static void Patch(Harmony harmony) {