From ee390836894ddb048283e45f053241b077c8781d Mon Sep 17 00:00:00 2001 From: LadyAliceMargatroid Date: Mon, 19 Aug 2024 02:15:57 -0700 Subject: [PATCH] Added BranchLoopBoost feature Changed some LogInfo into LogDebug --- .../Collections/DunGenExtenderProperties.cs | 16 +- .../Props/SpawnSyncedObjectCycle.cs | 4 +- .../DunGenPlus/DunGenPlus/DunGenPlus.dll | Bin 0 -> 88064 bytes .../Generation/DoorwaySistersRule.cs | 4 +- .../Generation/DunGenPlusGenerator.cs | 140 ++++++++++++++++-- .../DunGenPlus/Managers/DoorwayManager.cs | 2 +- .../Patches/DoorwayConnectionPatch.cs | 2 +- .../Patches/DungeonGeneratorPatch.cs | 72 ++++++++- .../DunGenPlus/Patches/RoundManagerPatch.cs | 2 +- DunGenPlus/DunGenPlus/Plugin.cs | 2 +- .../DunGenPlus/Utils/TranspilerUtilities.cs | 126 ++++++++++++---- DunGenPlus/DunGenPlus/Utils/Utility.cs | 22 +++ 12 files changed, 345 insertions(+), 47 deletions(-) create mode 100644 DunGenPlus/DunGenPlus/DunGenPlus/DunGenPlus.dll 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 0000000000000000000000000000000000000000..41dafe695e64504490feccd44b1be5c19ca0ee19 GIT binary patch literal 88064 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 MWAML@{|6)RACtEfRR910 literal 0 HcmV?d00001 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(); } }