diff --git a/DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs b/DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs index 261d34a..c7218dc 100644 --- a/DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs +++ b/DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs @@ -41,7 +41,7 @@ namespace DunGenPlus.Collections { [Tooltip("The factor that's multiplied with the base size AND the dungeon's size. The resulting value is added to the base size of the bounds.\n\n0 means that the bound size is not influenced by the dungeon's size and is therefore a constant.")] public Vector3 DungeonSizeFactor = new Vector3(1f, 0f, 1f); [Tooltip("The base positional offset of the bounds.")] - public Vector3 DungeonPositionOffset; + public Vector3 DungeonPositionOffset = Vector3.zero; [Tooltip("The pivot of the bounds.")] public Vector3 DungeonPositionPivot = new Vector3(0.5f, 0f, 0.5f); @@ -54,10 +54,18 @@ namespace DunGenPlus.Collections { [Header("Forced Tiles")] [Tooltip("If enabled, attempts to forcefully spawn tiles from ForcedTileSets after branching paths are generated.\n\nCan only be used if MainPathCount > 1.")] - public bool UseForcedTiles; + public bool UseForcedTiles = false; [Tooltip("The list of tiles that will be attempted to forcefully spawn. Each entry will spawn only one tile from it's list.\n\nIf the tile cannot be forcefully spawned, the dungeon generation will not restart.")] public List ForcedTileSets = new List(); + [Header("Branch Loop Boost")] + [Tooltip("If enabled, dungeon generation will prioritize branch tiles that connect to already generated tiles.\n\nThis increases the chance of circular/looping paths. Slows dungeon generation times a bit at the end.")] + public bool UseBranchLoopBoost = false; + [Tooltip("The maximum amount of tiles the dungeon generation will consider before choosing the best tile.\nIncreasing this value gives the dungeon generation a higher chance of finding a good tile but impacts dungeon generation times. Decreasing this value gives the dungeon generation a lower chance of finding a good tile but also lessens the impact to dungeon generation times.")] + public int BranchLoopBoostTileSearch = 5; + [Tooltip("The tile weight scale added for each additional doorway connection if that tile was selected.")] + public float BranchLoopBoostTileScale = 0.25f; + [Header("Line Randomizer")] [Tooltip("If enabled, every archetype in LineRandomizerArchetypes will have the last LineRandomizerTakeCount tilesets replaced by a randomly selected set of tilesets from LineRandomizerTileSets. This applies for both archetype's TileSets and BranchCapTileSets.\n\nThis is designed for the scenario where dungeon generation takes a long time due to the combination of too many tiles and/or doorways in those tiles. This can reduce dungeon generation time while keeping some of the randomness of dungeon generation.\n\nAs stated previously, this WILL replace the last LineRandomizerTakeCount tilesets in the archetype's TileSets and BranchCapTileSets. As such you must guarantee that those elements can be replaced.")] public bool UseLineRandomizer = false; @@ -141,6 +149,10 @@ namespace DunGenPlus.Collections { copy.UseForcedTiles = UseForcedTiles; copy.ForcedTileSets = ForcedTileSets; + copy.UseBranchLoopBoost = UseBranchLoopBoost; + copy.BranchLoopBoostTileSearch = BranchLoopBoostTileSearch; + copy.BranchLoopBoostTileScale = BranchLoopBoostTileScale; + copy.UseLineRandomizer = UseLineRandomizer; copy.LineRandomizerTileSets = LineRandomizerTileSets; copy.LineRandomizerArchetypes = LineRandomizerArchetypes; diff --git a/DunGenPlus/DunGenPlus/Components/Props/SpawnSyncedObjectCycle.cs b/DunGenPlus/DunGenPlus/Components/Props/SpawnSyncedObjectCycle.cs index ef4e8c7..9045ef9 100644 --- a/DunGenPlus/DunGenPlus/Components/Props/SpawnSyncedObjectCycle.cs +++ b/DunGenPlus/DunGenPlus/Components/Props/SpawnSyncedObjectCycle.cs @@ -25,7 +25,7 @@ namespace DunGenPlus.Components.Props } internal static void UpdateCycle(int value){ - Plugin.logger.LogInfo($"Updating SpawnSyncedObject start cycle to {value}"); + Plugin.logger.LogDebug($"Updating SpawnSyncedObject start cycle to {value}"); cycle = value; cycleDictionary = new Dictionary(); } @@ -37,7 +37,7 @@ namespace DunGenPlus.Components.Props } cycleDictionary[id] = value + 1; - Plugin.logger.LogInfo($"Cycle{id}: {value}"); + Plugin.logger.LogDebug($"Cycle{id}: {value}"); return value; } diff --git a/DunGenPlus/DunGenPlus/DunGenPlus/DunGenPlus.dll b/DunGenPlus/DunGenPlus/DunGenPlus/DunGenPlus.dll new file mode 100644 index 0000000..41dafe6 Binary files /dev/null and b/DunGenPlus/DunGenPlus/DunGenPlus/DunGenPlus.dll differ diff --git a/DunGenPlus/DunGenPlus/Generation/DoorwaySistersRule.cs b/DunGenPlus/DunGenPlus/Generation/DoorwaySistersRule.cs index e59815f..481f7d6 100644 --- a/DunGenPlus/DunGenPlus/Generation/DoorwaySistersRule.cs +++ b/DunGenPlus/DunGenPlus/Generation/DoorwaySistersRule.cs @@ -22,7 +22,7 @@ namespace DunGenPlus.Generation { public static void UpdateCache(IEnumerable list){ if (!DunGenPlusGenerator.Active || !DunGenPlusGenerator.Properties.UseDoorwaySisters) return; - Plugin.logger.LogInfo("Updating DoorwayProxy cache for DoorwaySistersRule"); + Plugin.logger.LogDebug("Updating DoorwayProxy cache for DoorwaySistersRule"); doorwayDictionary = new Dictionary(); doorwayProxyDictionary = new Dictionary(); @@ -51,6 +51,8 @@ namespace DunGenPlus.Generation { } public static bool CanDoorwaysConnect(bool result, TileProxy tileA, TileProxy tileB, DoorwayProxy doorwayA, DoorwayProxy doorwayB){ + //if (tileA.Prefab.name.ToLowerInvariant().Contains("mayor") || tileB.Prefab.name.ToLowerInvariant().Contains("mayor")) + //Plugin.logger.LogInfo($"{tileA.Prefab.name} <-> {tileB.Prefab.name}: {(doorwayA.Position - doorwayB.Position).sqrMagnitude}"); if (!result) return false; if (!DunGenPlusGenerator.Active || !DunGenPlusGenerator.Properties.UseDoorwaySisters) return true; diff --git a/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs b/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs index bfd1fa3..2c56c06 100644 --- a/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs +++ b/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs @@ -43,11 +43,11 @@ namespace DunGenPlus.Generation { generator.RestrictDungeonToBounds = Properties.UseDungeonBounds; var bounds = Properties.GetDungeonBounds(generator.LengthMultiplier); generator.TilePlacementBounds = bounds; - Plugin.logger.LogInfo($"Dungeon Bounds: {bounds}"); + Plugin.logger.LogDebug($"Dungeon Bounds: {bounds}"); } if (Properties.UseMaxShadowsRequestUpdate) { - Plugin.logger.LogInfo($"Updating HDRP asset: setting max shadows request to {Properties.MaxShadowsRequestAmount}"); + Plugin.logger.LogDebug($"Updating HDRP asset: setting max shadows request to {Properties.MaxShadowsRequestAmount}"); try { previousHDRPAsset = QualitySettings.renderPipeline as HDRenderPipelineAsset; newHDRPAsset = ScriptableObject.Instantiate(previousHDRPAsset); @@ -74,7 +74,7 @@ namespace DunGenPlus.Generation { ActiveAlternative = false; if (previousHDRPAsset && QualitySettings.renderPipeline == newHDRPAsset) { - Plugin.logger.LogInfo("Restoring original HDRP asset"); + Plugin.logger.LogDebug("Restoring original HDRP asset"); QualitySettings.renderPipeline = previousHDRPAsset; previousHDRPAsset = null; @@ -229,13 +229,13 @@ namespace DunGenPlus.Generation { var tileProxy = gen.AddTile(previousTile, useableTileSets, lineDepthRatio, archetype, TilePlacementResult.None); if (tileProxy == null) { - Plugin.logger.LogInfo($"Alt. main branch gen failed at {b}:{lineDepthRatio}"); + Plugin.logger.LogDebug($"Alt. main branch gen failed at {b}:{lineDepthRatio}"); yield return gen.Wait(gen.InnerGenerate(true)); yield break; } if (lineDepthRatio >= 1f){ - Plugin.logger.LogInfo($"Alt. main branch at {b} ended with {tileProxy.PrefabTile.name}"); + Plugin.logger.LogDebug($"Alt. main branch at {b} ended with {tileProxy.PrefabTile.name}"); } tileProxy.Placement.BranchDepth = t; @@ -267,12 +267,12 @@ namespace DunGenPlus.Generation { } ActiveAlternative = false; - Plugin.logger.LogInfo($"Created {altCount} alt. paths, creating branches now"); + Plugin.logger.LogDebug($"Created {altCount} alt. paths, creating branches now"); gen.ChangeStatus(GenerationStatus.Branching); // this is major trickery and it works still for(var b = 0; b < altCount + 1; ++b){ - Plugin.logger.LogInfo($"Branch {b}"); + Plugin.logger.LogDebug($"Branch {b}"); RandomizeLineArchetypes(gen, false); gen.proxyDungeon.MainPathTiles = allMainPathTiles[b]; yield return gen.Wait(gen.GenerateBranchPaths()); @@ -286,7 +286,9 @@ namespace DunGenPlus.Generation { } public static void AddForcedTiles(DungeonGenerator gen){ - var forcedTileSetLists = DunGenPlusGenerator.Properties.ForcedTileSets.ToList(); + if (!Properties.UseForcedTiles) return; + + var forcedTileSetLists = Properties.ForcedTileSets.ToList(); while(forcedTileSetLists.Count > 0){ var item = forcedTileSetLists[forcedTileSetLists.Count - 1]; @@ -311,15 +313,133 @@ namespace DunGenPlus.Generation { tileProxy.Placement.GraphNode = t.Placement.GraphNode; tileProxy.Placement.GraphLine = t.Placement.GraphLine; - Plugin.logger.LogInfo($"Forcefully added tile {tileProxy.Prefab.name}"); + Plugin.logger.LogDebug($"Forcefully added tile {tileProxy.Prefab.name}"); break; } forcedTileSetLists.RemoveAt(forcedTileSetLists.Count - 1); } - Plugin.logger.LogInfo($"Forcefully added all tiles"); + Plugin.logger.LogDebug($"Forcefully added all tiles"); + } + public static (TilePlacementResult result, TileProxy tile) ProcessDoorwayPairs(DungeonGenerator gen, DungeonArchetype archetype, Queue doorwayPairs) { + if (Properties.UseBranchLoopBoost && gen.Status == GenerationStatus.Branching) { + return EncourageBranchPathLoopEncouragement(gen, doorwayPairs); + } + + while(doorwayPairs.Count > 0) { + var pair = doorwayPairs.Dequeue(); + var result = gen.TryPlaceTile(pair, archetype, out var tileProxy); + if (result == TilePlacementResult.None) return (result, tileProxy); + gen.AddTilePlacementResult(result); + } + + return (TilePlacementResult.NoValidTile, null); + } + + public static (TilePlacementResult result, TileProxy tile) EncourageBranchPathLoopEncouragement(DungeonGenerator gen, Queue doorwayPairs) { + // get list of 5 potential targets + var validTiles = new List(); + while(doorwayPairs.Count > 0) { + var pair = doorwayPairs.Dequeue(); + var value = GetTileProxyOfDoorwayPair(gen, pair); + if (value.result == TilePlacementResult.None) { + validTiles.Add(value); + if (validTiles.Count >= Properties.BranchLoopBoostTileSearch) break; + } + } + + if (validTiles.Count == 0) { + return (TilePlacementResult.NoValidTile, null); + } + + // update their weight based on their potential doorway partners + var allDungeonDoorways = gen.proxyDungeon.AllTiles.SelectMany(t => t.Doorways); + //Plugin.logger.LogInfo("NEW TILES"); + foreach(var t in validTiles) { + var doorwayCount = 0; + foreach(var d in allDungeonDoorways) { + foreach(var l in t.tile.doorways) { + if (d.TileProxy == t.previousDoorway.TileProxy) continue; + if (gen.DungeonFlow.CanDoorwaysConnect(d.TileProxy.PrefabTile, l.TileProxy.PrefabTile, d.DoorwayComponent, l.DoorwayComponent) && Vector3.SqrMagnitude(d.Position - l.Position) < 1E-05) + doorwayCount++; + } + } + + if (doorwayCount > 0) { + //Plugin.logger.LogInfo($"{t.weight} -> {t.weight * (1f + doorwayCount * Properties.BranchLoopBoostTileScale)} ({doorwayCount})"); + t.weight *= (1f + doorwayCount * Properties.BranchLoopBoostTileScale); + } else { + //Plugin.logger.LogInfo($"{t.weight}"); + } + } + + var bestChoice = validTiles.OrderByDescending(t => t.weight).FirstOrDefault(); + //Plugin.logger.LogInfo($"Best: {bestChoice.weight}"); + + MakeTileProxyConnection(gen, bestChoice); + gen.AddTilePlacementResult(bestChoice.result); + + return (bestChoice.result, bestChoice.tile); + } + + private class TilePlacementResultProxy { + public TilePlacementResult result; + public TileProxy tile; + public DoorwayProxy previousDoorway; + public DoorwayProxy nextDoorway; + public float weight; + + public TilePlacementResultProxy(TilePlacementResult result) { + this.result = result; + tile = null; + previousDoorway = null; + nextDoorway = null; + weight = 0f; + } + + public TilePlacementResultProxy(TilePlacementResult result, TileProxy tile, DoorwayProxy previousDoorway, DoorwayProxy nextDoorway, float weight) { + this.result = result; + this.tile = tile; + this.previousDoorway = previousDoorway; + this.nextDoorway = nextDoorway; + this.weight = weight; + } + + } + + private static TilePlacementResultProxy GetTileProxyOfDoorwayPair(DungeonGenerator gen, DoorwayPair pair){ + var nextTemplate = pair.NextTemplate; + var previousDoorway = pair.PreviousDoorway; + if (nextTemplate == null) return new TilePlacementResultProxy(TilePlacementResult.TemplateIsNull); + + var index = pair.NextTemplate.Doorways.IndexOf(pair.NextDoorway); + var tile = new TileProxy(nextTemplate); + tile.Placement.isOnMainPath = false; + tile.Placement.TileSet = pair.NextTileSet; + + if (previousDoorway != null) { + var myDoorway = tile.Doorways[index]; + tile.PositionBySocket(myDoorway, previousDoorway); + var bounds = tile.Placement.Bounds; + if (gen.RestrictDungeonToBounds && !gen.TilePlacementBounds.Contains(bounds)) return new TilePlacementResultProxy(TilePlacementResult.OutOfBounds); + if (gen.IsCollidingWithAnyTile(tile, previousDoorway.TileProxy)) return new TilePlacementResultProxy(TilePlacementResult.TileIsColliding); + } + + if (tile == null) return new TilePlacementResultProxy(TilePlacementResult.NewTileIsNull); + + tile.Placement.PathDepth = pair.PreviousTile.Placement.PathDepth; + tile.Placement.BranchDepth = pair.PreviousTile.Placement.IsOnMainPath ? 0 : (pair.PreviousTile.Placement.BranchDepth + 1); + + return new TilePlacementResultProxy(TilePlacementResult.None, tile, previousDoorway, tile.Doorways[index], pair.TileWeight); + } + + private static void MakeTileProxyConnection(DungeonGenerator gen, TilePlacementResultProxy proxy) { + if (proxy.previousDoorway != null) { + gen.proxyDungeon.MakeConnection(proxy.previousDoorway, proxy.nextDoorway); + } + gen.proxyDungeon.AddTile(proxy.tile); } public static void RandomizeLineArchetypes(DungeonGenerator gen, bool randomizeMainPath){ diff --git a/DunGenPlus/DunGenPlus/Managers/DoorwayManager.cs b/DunGenPlus/DunGenPlus/Managers/DoorwayManager.cs index 5ee59f9..6670389 100644 --- a/DunGenPlus/DunGenPlus/Managers/DoorwayManager.cs +++ b/DunGenPlus/DunGenPlus/Managers/DoorwayManager.cs @@ -33,7 +33,7 @@ namespace DunGenPlus.Managers { var dungeonGen = RoundManager.Instance.dungeonGenerator; var navmesh = dungeonGen.transform.parent.GetComponentInChildren(); navmesh.Run(dungeonGen.Generator); - Plugin.logger.LogInfo("Rebuild nav mesh"); + Plugin.logger.LogDebug("Rebuild nav mesh"); } catch (Exception e){ Plugin.logger.LogError("Failed to rebuild nav mesh"); Plugin.logger.LogError(e.ToString()); diff --git a/DunGenPlus/DunGenPlus/Patches/DoorwayConnectionPatch.cs b/DunGenPlus/DunGenPlus/Patches/DoorwayConnectionPatch.cs index 602c7fc..90fa4b5 100644 --- a/DunGenPlus/DunGenPlus/Patches/DoorwayConnectionPatch.cs +++ b/DunGenPlus/DunGenPlus/Patches/DoorwayConnectionPatch.cs @@ -26,7 +26,7 @@ namespace DunGenPlus.Patches { public static IEnumerable ConnectOverlappingDoorwaysPatch(IEnumerable instructions){ var callFunction = typeof(DunGen.Graph.DungeonFlow).GetMethod("CanDoorwaysConnect", BindingFlags.Instance | BindingFlags.Public); - var sequence = new InstructionSequence("doorway connect", false); + var sequence = new InstructionSequenceStandard("doorway connect", false); sequence.AddBasic(OpCodes.Callvirt, callFunction); sequence.AddBasic(OpCodes.Brfalse); diff --git a/DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs b/DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs index 20b261f..4cf00b8 100644 --- a/DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs +++ b/DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs @@ -11,6 +11,7 @@ using System.Collections; using DunGenPlus.Utils; using DunGenPlus.Generation; using DunGenPlus.Managers; +using DunGenPlus.Collections; namespace DunGenPlus.Patches { internal class DungeonGeneratorPatch { @@ -39,7 +40,7 @@ namespace DunGenPlus.Patches { var addArchFunction = typeof(List).GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); - var archSequence = new InstructionSequence("archetype node"); + var archSequence = new InstructionSequenceStandard("archetype node"); archSequence.AddOperandTypeCheck(OpCodes.Ldfld, typeof(List)); archSequence.AddBasic(OpCodes.Ldnull); archSequence.AddBasic(OpCodes.Callvirt, addArchFunction); @@ -66,11 +67,78 @@ namespace DunGenPlus.Patches { archSequence.ReportComplete(); } + [HarmonyTranspiler] + [HarmonyPatch(typeof(DungeonGenerator), "AddTile")] + public static IEnumerable AddTilePatch(IEnumerable instructions, ILGenerator generator) { + + var getCountFunction = typeof(Queue).GetMethod("get_Count", BindingFlags.Instance | BindingFlags.Public); + + var whileLoopSequence = new InstructionSequenceHold("while loop"); + whileLoopSequence.AddBasic(OpCodes.Br); + whileLoopSequence.AddBasicLocal(OpCodes.Ldloc_S, 8); + whileLoopSequence.AddAny(); + whileLoopSequence.AddBasic(OpCodes.Ldc_I4_0); + whileLoopSequence.AddBasic(OpCodes.Bgt); + + foreach(var instruction in instructions){ + var yieldInstruction = true; + var result = whileLoopSequence.VerifyStage(instruction); + + switch(result) { + case InstructionSequenceHold.HoldResult.None: + break; + case InstructionSequenceHold.HoldResult.Hold: + yieldInstruction = false; + break; + case InstructionSequenceHold.HoldResult.Release: + foreach(var i in whileLoopSequence.Instructions){ + yield return i; + } + whileLoopSequence.ClearInstructions(); + yieldInstruction = false; + break; + case InstructionSequenceHold.HoldResult.Finished: + + // my special function + var specialFunction = typeof(DunGenPlusGenerator).GetMethod("ProcessDoorwayPairs"); + var resultLocal = generator.DeclareLocal(typeof((TilePlacementResult result, TileProxy tile))); + + yield return new CodeInstruction(OpCodes.Ldarg_0); + yield return new CodeInstruction(OpCodes.Ldarg_S, 4); + yield return new CodeInstruction(OpCodes.Ldloc_S, 8); + yield return new CodeInstruction(OpCodes.Call, specialFunction); + + var item1Field = typeof((TilePlacementResult result, TileProxy tile)).GetField("Item1"); + var item2Field = typeof((TilePlacementResult result, TileProxy tile)).GetField("Item2"); + + yield return new CodeInstruction(OpCodes.Stloc_S, resultLocal); + + yield return new CodeInstruction(OpCodes.Ldloc_S, resultLocal); + yield return new CodeInstruction(OpCodes.Ldfld, item1Field); + yield return new CodeInstruction(OpCodes.Stloc_S, 9); + + yield return new CodeInstruction(OpCodes.Ldloc_S, resultLocal); + yield return new CodeInstruction(OpCodes.Ldfld, item2Field); + yield return new CodeInstruction(OpCodes.Stloc_S, 10); + + whileLoopSequence.ClearInstructions(); + yieldInstruction = false; + + break; + } + + if (yieldInstruction) yield return instruction; + } + + whileLoopSequence.ReportComplete(); + + } + [HarmonyPostfix] [HarmonyPatch(typeof(RoundManager), "FinishGeneratingLevel")] public static void GenerateBranchPathsPatch(){ if (DunGenPlusGenerator.Active) { - Plugin.logger.LogInfo("Alt. InnerGenerate() function complete"); + Plugin.logger.LogDebug("Alt. InnerGenerate() function complete"); } } diff --git a/DunGenPlus/DunGenPlus/Patches/RoundManagerPatch.cs b/DunGenPlus/DunGenPlus/Patches/RoundManagerPatch.cs index 922a433..1412850 100644 --- a/DunGenPlus/DunGenPlus/Patches/RoundManagerPatch.cs +++ b/DunGenPlus/DunGenPlus/Patches/RoundManagerPatch.cs @@ -25,7 +25,7 @@ namespace DunGenPlus.Patches { foreach(var s in sources) { var result = s.CreateItem(__instance, __instance.currentLevel.spawnableScrap); if (result.itemReference != null) { - Plugin.logger.LogInfo($"Created guaranteed item {result.itemReference.gameObject.name} w/ value {result.scrapValue}"); + Plugin.logger.LogDebug($"Created guaranteed item {result.itemReference.gameObject.name} w/ value {result.scrapValue}"); spawnedScrapList.Add(result.itemReference); scrapValuesList.Add(result.scrapValue); } diff --git a/DunGenPlus/DunGenPlus/Plugin.cs b/DunGenPlus/DunGenPlus/Plugin.cs index b61b465..5c3b55c 100644 --- a/DunGenPlus/DunGenPlus/Plugin.cs +++ b/DunGenPlus/DunGenPlus/Plugin.cs @@ -24,7 +24,7 @@ namespace DunGenPlus { internal const string modGUID = "dev.ladyalice.dungenplus"; private const string modName = "Dungeon Generation Plus"; - private const string modVersion = "1.0.4"; + private const string modVersion = "1.0.5"; internal readonly Harmony Harmony = new Harmony(modGUID); diff --git a/DunGenPlus/DunGenPlus/Utils/TranspilerUtilities.cs b/DunGenPlus/DunGenPlus/Utils/TranspilerUtilities.cs index 0f29595..a0cc7ca 100644 --- a/DunGenPlus/DunGenPlus/Utils/TranspilerUtilities.cs +++ b/DunGenPlus/DunGenPlus/Utils/TranspilerUtilities.cs @@ -46,7 +46,7 @@ namespace DunGenPlus.Utils { if (counter == 0) { Plugin.logger.LogError($"{debugFunction} could not inject {name}. Probably scary"); } else if (!expectedCounter.HasValue) { - Plugin.logger.LogInfo($"{debugFunction} inject {name} {counter} time(s)"); + Plugin.logger.LogDebug($"{debugFunction} inject {name} {counter} time(s)"); } else if (expectedCounter.Value != counter){ Plugin.logger.LogWarning($"{debugFunction} inject {name} {counter} time(s) (Expected {expectedCounter.Value}). Probably not an error but be warned"); } @@ -54,22 +54,22 @@ namespace DunGenPlus.Utils { } - internal class InstructionSequence { + internal abstract class InstructionSequence { + protected List> seq; + protected string name; + protected string extraErrorMessage; + protected int stage; + protected bool completed; + protected bool single; - public static ManualLogSource logger => Plugin.logger; + public InstructionSequence(string name, bool single = true, string extraErrorMessage = default(string)) { + seq = new List>(); + stage = 0; + completed = false; - List> seq; - string name; - string extraErrorMessage; - int stage = 0; - bool completed = false; - bool single; - - public InstructionSequence(string name, bool single = true, string extraErrorMessage = default(string)){ this.name = name; this.single = single; this.extraErrorMessage = extraErrorMessage; - seq = new List>(); } public void Add(Func next){ @@ -88,6 +88,10 @@ namespace DunGenPlus.Utils { seq.Add((i) => i.opcode == opcode && (i.operand as LocalBuilder).LocalIndex == operand); } + public void AddAny(){ + seq.Add(null); + } + public void AddOperandTypeCheck(OpCode opcode, Type operandType){ seq.Add((i) => { var fieldInfo = i.operand as FieldInfo; @@ -98,7 +102,6 @@ namespace DunGenPlus.Utils { }); } - public void AddBasicWithAlternateMethodName(OpCode opcode, object operand, string methodName){ seq.Add((i) => { if (i.opcode == opcode && i.operand == operand) return true; @@ -115,17 +118,40 @@ namespace DunGenPlus.Utils { seq.Add((i) => i.opcode == opcode && extra.Invoke(i)); } - public void AddQuickInjection(MethodInfo methodInfo){ - + public void ReportComplete(){ + if (completed == false){ + var errorM = string.IsNullOrWhiteSpace(extraErrorMessage) ? "BIG PROBLEM!" : extraErrorMessage; + Plugin.logger.LogError($"HarmonyTranspiler for {name} has failed. {errorM}"); + } } - public bool VerifyStage(CodeInstruction current){ + protected enum AdvanceResult { + Failed, + Advanced, + Finished + } + + protected AdvanceResult AdvanceStage(CodeInstruction current) { var s = seq[stage]; - if (s.Invoke(current)) { - //Plugin.logger.LogInfo($"{name}({stage}): current.ToString()"); - stage++; + var result = AdvanceResult.Failed; + + // null is magic number to accept anything, + // increase the counter if the NEXT sequence succeeds + // but not reset the counter if it fails + if (s == null) { + s = seq[stage + 1]; + if (s.Invoke(current)) { + stage += 2; + } + result = AdvanceResult.Advanced; } else { - stage = 0; + if (s.Invoke(current)) { + stage++; + result = AdvanceResult.Advanced; + } else { + stage = 0; + result = AdvanceResult.Failed; + } } if (stage >= seq.Count){ @@ -136,19 +162,67 @@ namespace DunGenPlus.Utils { stage = 0; completed = true; - return true; + result = AdvanceResult.Finished; } - return false; + return result; } - public void ReportComplete(){ - if (completed == false){ - var errorM = string.IsNullOrWhiteSpace(extraErrorMessage) ? "BIG PROBLEM!" : extraErrorMessage; - logger.LogError($"HarmonyTranspiler for {name} has failed. {errorM}"); + } + + internal class InstructionSequenceStandard : InstructionSequence { + + public InstructionSequenceStandard(string name, bool single = true, string extraErrorMessage = default(string)) : base(name, single, extraErrorMessage) { } + + public bool VerifyStage(CodeInstruction current){ + return AdvanceStage(current) == AdvanceResult.Finished; + } + } + + internal class InstructionSequenceHold : InstructionSequence { + + public enum HoldResult { + None, + Hold, + Release, + Finished + } + + public List Instructions; + List> seq; + string name; + string extraErrorMessage; + int stage = 0; + bool completed = false; + + public InstructionSequenceHold(string name, bool single = true, string extraErrorMessage = default(string)) : base(name, single, extraErrorMessage) { + Instructions = new List(); + } + + public HoldResult VerifyStage(CodeInstruction current) { + var result = AdvanceStage(current); + if (result == AdvanceResult.Failed) { + if (Instructions.Count > 0) { + Instructions.Add(current); + return HoldResult.Release; + } + return HoldResult.None; + } + else if (result == AdvanceResult.Advanced) { + Instructions.Add(current); + return HoldResult.Hold; + } + else { + Instructions.Add(current); + return HoldResult.Finished; } } + public void ClearInstructions(){ + Instructions.Clear(); + } + + } internal class TranspilerUtilities { diff --git a/DunGenPlus/DunGenPlus/Utils/Utility.cs b/DunGenPlus/DunGenPlus/Utils/Utility.cs index 8657432..7cca9db 100644 --- a/DunGenPlus/DunGenPlus/Utils/Utility.cs +++ b/DunGenPlus/DunGenPlus/Utils/Utility.cs @@ -3,21 +3,28 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using UnityEngine.Events; namespace DunGenPlus.Utils { public class ActionList { public string name; public List<(string name, Action action)> actionList; + public List<(string name, Action action)> temporaryActionList; public ActionList(string name){ this.name = name; actionList = new List<(string, Action)>(); + temporaryActionList = new List<(string name, Action action)>(); } public void AddEvent(string name, Action act){ actionList.Add((name, act)); } + public void AddTemporaryEvent(string name, Action act){ + temporaryActionList.Add((name, act)); + } + public void Call(){ foreach(var pair in actionList){ try { @@ -27,6 +34,21 @@ namespace DunGenPlus.Utils { Plugin.logger.LogError(e.ToString()); } } + + foreach(var pair in temporaryActionList){ + try { + pair.action.Invoke(); + } catch (Exception e) { + Plugin.logger.LogError($"Error with event {name}/{pair.name}"); + Plugin.logger.LogError(e.ToString()); + } + } + + ClearTemporaryActionList(); + } + + public void ClearTemporaryActionList(){ + temporaryActionList.Clear(); } }