Added BranchLoopBoost feature

Changed some LogInfo into LogDebug
This commit is contained in:
LadyAliceMargatroid 2024-08-19 02:15:57 -07:00
parent 383c879b96
commit ee39083689
12 changed files with 345 additions and 47 deletions

View File

@ -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<ForcedTileSetList> ForcedTileSets = new List<ForcedTileSetList>();
[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;

View File

@ -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<int, int>();
}
@ -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;
}

Binary file not shown.

View File

@ -22,7 +22,7 @@ namespace DunGenPlus.Generation {
public static void UpdateCache(IEnumerable<DoorwayProxy> 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<Doorway, Data>();
doorwayProxyDictionary = new Dictionary<DoorwayProxy, Data>();
@ -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;

View File

@ -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<DoorwayPair> 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<DoorwayPair> doorwayPairs) {
// get list of 5 potential targets
var validTiles = new List<TilePlacementResultProxy>();
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){

View File

@ -33,7 +33,7 @@ namespace DunGenPlus.Managers {
var dungeonGen = RoundManager.Instance.dungeonGenerator;
var navmesh = dungeonGen.transform.parent.GetComponentInChildren<UnityNavMeshAdapter>();
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());

View File

@ -26,7 +26,7 @@ namespace DunGenPlus.Patches {
public static IEnumerable<CodeInstruction> ConnectOverlappingDoorwaysPatch(IEnumerable<CodeInstruction> 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);

View File

@ -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<DungeonArchetype>).GetMethod("Add", BindingFlags.Instance | BindingFlags.Public);
var archSequence = new InstructionSequence("archetype node");
var archSequence = new InstructionSequenceStandard("archetype node");
archSequence.AddOperandTypeCheck(OpCodes.Ldfld, typeof(List<DungeonArchetype>));
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<CodeInstruction> AddTilePatch(IEnumerable<CodeInstruction> instructions, ILGenerator generator) {
var getCountFunction = typeof(Queue<DoorwayPair>).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");
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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<Func<CodeInstruction, bool>> 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<Func<CodeInstruction, bool>>();
stage = 0;
completed = false;
List<Func<CodeInstruction, bool>> 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<Func<CodeInstruction, bool>>();
}
public void Add(Func<CodeInstruction, bool> 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<CodeInstruction> Instructions;
List<Func<CodeInstruction, bool>> 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<CodeInstruction>();
}
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 {

View File

@ -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();
}
}