DoorwayCleanup is now obselete
Created new scripting system Added toggle for EventCallbackScenario for DevDebug window
This commit is contained in:
		
							parent
							
								
									4af194e0f4
								
							
						
					
					
						commit
						25530ebbb3
					
				
					 36 changed files with 2467 additions and 24 deletions
				
			
		|  | @ -11,6 +11,7 @@ using UnityEngine; | |||
| namespace DunGenPlus.Components { | ||||
|   public class DoorwayCleanup : MonoBehaviour, IDungeonCompleteReceiver { | ||||
| 
 | ||||
|     [Header("OBSOLUTE. Please use DoorwayScriptingParent")] | ||||
|     [Header("Doorway References")] | ||||
|     [Tooltip("The doorway reference.")] | ||||
|     public Doorway doorway; | ||||
|  |  | |||
|  | @ -6,6 +6,8 @@ using System.Threading.Tasks; | |||
| using UnityEngine; | ||||
| 
 | ||||
| namespace DunGenPlus.Components.DoorwayCleanupScripting { | ||||
| 
 | ||||
|   [Obsolete("Please use DoorwayScriptingParent")] | ||||
|   public class DCSConnectorBlockerSpawnedPrefab : DoorwayCleanupScript { | ||||
| 
 | ||||
|     public enum Action { SwitchToConnector, SwitchToBlocker }; | ||||
|  |  | |||
|  | @ -6,17 +6,20 @@ using System.Threading.Tasks; | |||
| using UnityEngine; | ||||
| 
 | ||||
| namespace DunGenPlus.Components.DoorwayCleanupScripting { | ||||
| 
 | ||||
|   [Obsolete("Please use DoorwayScriptingParent")] | ||||
|   public class DCSRemoveDoorwayConnectedDoorway : DoorwayCleanupScriptDoorwayCompare { | ||||
| 
 | ||||
|     [Header("Removes Doorway Gameobject\nif the neighboring doorway's priority matches the operation comparison")] | ||||
|     [Header("Operation Comparison")] | ||||
|     public int doorwayPriority; | ||||
|     public int doorwayPriorityB; | ||||
|     public Operation operation = Operation.Equal; | ||||
| 
 | ||||
|     public override void Cleanup(DoorwayCleanup parent) { | ||||
|       var doorway = parent.doorway; | ||||
|       if (doorway.connectedDoorway == null) return; | ||||
|       var result = GetOperation(operation).Invoke(doorway.connectedDoorway, doorwayPriority); | ||||
|       var result = GetOperation(operation).Invoke(doorway.connectedDoorway, new Arguments(doorwayPriority, doorwayPriorityB)); | ||||
| 
 | ||||
|       if (result) { | ||||
|         parent.SwitchDoorwayGameObject(false); | ||||
|  |  | |||
|  | @ -6,6 +6,8 @@ using System.Threading.Tasks; | |||
| using UnityEngine; | ||||
| 
 | ||||
| namespace DunGenPlus.Components.DoorwayCleanupScripting { | ||||
| 
 | ||||
|   [Obsolete("Please use DoorwayScriptingParent")] | ||||
|   public class DCSRemoveDoorwaySpawnedPrefab : DoorwayCleanupScript { | ||||
| 
 | ||||
|     [Header("Removes Doorway Gameobject\nif Doorway instantiates a Connector/Blocker prefab with the target's name")] | ||||
|  |  | |||
|  | @ -6,12 +6,14 @@ using System.Threading.Tasks; | |||
| using UnityEngine; | ||||
| 
 | ||||
| namespace DunGenPlus.Components.DoorwayCleanupScripting { | ||||
|   [Obsolete("Please use DoorwayScriptingParent")] | ||||
|   public class DCSRemoveGameObjectsConnectedDoorway : DoorwayCleanupScriptDoorwayCompare { | ||||
| 
 | ||||
|     [Header("Removes target GameObjects\nif the neighboring doorway's priority matches the operation comparison")] | ||||
|      | ||||
|     [Header("Operation Comparison")] | ||||
|     public int doorwayPriority; | ||||
|     public int doorwayPriorityB; | ||||
|     public Operation operation = Operation.Equal; | ||||
| 
 | ||||
|     [Header("Targets")] | ||||
|  | @ -20,7 +22,7 @@ namespace DunGenPlus.Components.DoorwayCleanupScripting { | |||
|     public override void Cleanup(DoorwayCleanup parent) { | ||||
|       var doorway = parent.doorway; | ||||
|       if (doorway.connectedDoorway == null) return; | ||||
|       var result = GetOperation(operation).Invoke(doorway.connectedDoorway, doorwayPriority); | ||||
|       var result = GetOperation(operation).Invoke(doorway.connectedDoorway, new Arguments(doorwayPriority, doorwayPriorityB)); | ||||
|       if (result) { | ||||
|         foreach(var t in targets) t.SetActive(false); | ||||
|       } | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ using System.Threading.Tasks; | |||
| using UnityEngine; | ||||
| 
 | ||||
| namespace DunGenPlus.Components.DoorwayCleanupScripting { | ||||
|   [Obsolete("Please use DoorwayScriptingParent")] | ||||
|   public abstract class DoorwayCleanupScript : MonoBehaviour { | ||||
| 
 | ||||
|     public abstract void Cleanup(DoorwayCleanup parent); | ||||
|  |  | |||
|  | @ -4,13 +4,44 @@ using System.Collections.Generic; | |||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| namespace DunGenPlus.Components.DoorwayCleanupScripting { | ||||
|   [Obsolete("Please use DoorwayScriptingParent")] | ||||
|   public abstract class DoorwayCleanupScriptDoorwayCompare : DoorwayCleanupScript { | ||||
| 
 | ||||
|     public enum Operation { Equal, NotEqual, LessThan, GreaterThan } | ||||
|     public enum Operation {  | ||||
|       [InspectorName("Equal (target == value)")] | ||||
|       Equal,  | ||||
|       [InspectorName("NotEqual (target != value)")] | ||||
|       NotEqual,  | ||||
|       [InspectorName("LessThan (target < value)")] | ||||
|       LessThan,  | ||||
|       [InspectorName("GreaterThan (target > value)")] | ||||
|       GreaterThan,  | ||||
|       [InspectorName("LessThanEq (target <= value)")] | ||||
|       LessThanEq,  | ||||
|       [InspectorName("GreaterThanEw (target >= value)")] | ||||
|       GreaterThanEq,  | ||||
|       [InspectorName("Between (value < target < valueB)")] | ||||
|       Between, | ||||
|       [InspectorName("BetweenEq (value <= target <= valueB)")] | ||||
|       BetweenEq | ||||
|     } | ||||
|      | ||||
|     public Func<Doorway, int, bool> GetOperation(Operation operation){ | ||||
|     public struct Arguments{ | ||||
|       public int parameterA; | ||||
|       public int parameterB; | ||||
| 
 | ||||
|       public Arguments(int parameterA, int parameterB){ | ||||
|         this.parameterA = parameterA; | ||||
|         this.parameterB = parameterB; | ||||
|       } | ||||
| 
 | ||||
|       public static explicit operator Arguments((int a, int b) pair) => new Arguments(pair.a, pair.b); | ||||
|     } | ||||
| 
 | ||||
|     public Func<Doorway, Arguments, bool> GetOperation(Operation operation){ | ||||
|       switch(operation){ | ||||
|         case Operation.Equal: | ||||
|           return EqualOperation; | ||||
|  | @ -20,24 +51,48 @@ namespace DunGenPlus.Components.DoorwayCleanupScripting { | |||
|           return LessThanOperation; | ||||
|         case Operation.GreaterThan: | ||||
|           return GreaterThanOperation; | ||||
|         case Operation.LessThanEq: | ||||
|           return LessThanEqualOperation; | ||||
|         case Operation.GreaterThanEq: | ||||
|           return GreaterThanEqualOperation; | ||||
|         case Operation.Between: | ||||
|           return BetweenOperation; | ||||
|         case Operation.BetweenEq: | ||||
|           return BetweenEqualOperation; | ||||
|       } | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     public bool EqualOperation(Doorway other, int doorwayPriority){ | ||||
|       return other.DoorPrefabPriority == doorwayPriority; | ||||
|     public bool EqualOperation(Doorway other, Arguments arguments){ | ||||
|       return other.DoorPrefabPriority == arguments.parameterA; | ||||
|     } | ||||
| 
 | ||||
|     public bool NotEqualOperation(Doorway other, int doorwayPriority){ | ||||
|       return other.DoorPrefabPriority != doorwayPriority; | ||||
|     public bool NotEqualOperation(Doorway other, Arguments arguments){ | ||||
|       return other.DoorPrefabPriority != arguments.parameterA; | ||||
|     } | ||||
| 
 | ||||
|     public bool LessThanOperation(Doorway other, int doorwayPriority){ | ||||
|       return other.DoorPrefabPriority < doorwayPriority; | ||||
|     public bool LessThanOperation(Doorway other, Arguments arguments){ | ||||
|       return other.DoorPrefabPriority < arguments.parameterA; | ||||
|     } | ||||
| 
 | ||||
|     public bool GreaterThanOperation(Doorway other, int doorwayPriority){ | ||||
|       return other.DoorPrefabPriority > doorwayPriority; | ||||
|     public bool GreaterThanOperation(Doorway other, Arguments arguments){ | ||||
|       return other.DoorPrefabPriority > arguments.parameterA; | ||||
|     } | ||||
| 
 | ||||
|     public bool LessThanEqualOperation(Doorway other, Arguments arguments){ | ||||
|       return other.DoorPrefabPriority <= arguments.parameterA; | ||||
|     } | ||||
| 
 | ||||
|     public bool GreaterThanEqualOperation(Doorway other, Arguments arguments){ | ||||
|       return other.DoorPrefabPriority >= arguments.parameterA; | ||||
|     } | ||||
| 
 | ||||
|     public bool BetweenOperation(Doorway other, Arguments arguments){ | ||||
|       return arguments.parameterA < other.DoorPrefabPriority && other.DoorPrefabPriority < arguments.parameterB; | ||||
|     } | ||||
| 
 | ||||
|     public bool BetweenEqualOperation(Doorway other, Arguments arguments){ | ||||
|       return arguments.parameterA <= other.DoorPrefabPriority && other.DoorPrefabPriority <= arguments.parameterB; | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
|  |  | |||
|  | @ -0,0 +1,103 @@ | |||
| using DunGen; | ||||
| using DunGenPlus.Managers; | ||||
| using Soukoku.ExpressionParser; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| namespace DunGenPlus.Components.Scripting { | ||||
| 
 | ||||
|   public class DoorwayScriptingParent : DunGenPlusScriptingParent<Doorway> { | ||||
| 
 | ||||
|     [Header("Scripting Debug")] | ||||
|     public Doorway connectedDoorwayDebug; | ||||
| 
 | ||||
|     public override void Awake(){ | ||||
|       base.Awake(); | ||||
| 
 | ||||
|       if (targetReference == null) return; | ||||
| 
 | ||||
|       // steal the scene objects from the doorway and clear them | ||||
|       // before the doorway messes with them before us | ||||
|       // psycho energy | ||||
|       AddNamedReference("connectors", targetReference.ConnectorSceneObjects); | ||||
|       targetReference.ConnectorSceneObjects = new List<GameObject>(); | ||||
| 
 | ||||
|       AddNamedReference("blockers", targetReference.BlockerSceneObjects); | ||||
|       targetReference.BlockerSceneObjects = new List<GameObject>(); | ||||
|     }  | ||||
| 
 | ||||
|     public override void Call(){ | ||||
|       if (targetReference == null) return; | ||||
| 
 | ||||
|       // start up like in original | ||||
|       var isConnected = targetReference.connectedDoorway != null; | ||||
|       SetNamedGameObjectState("connectors", isConnected); | ||||
|       SetNamedGameObjectState("blockers", !isConnected); | ||||
| 
 | ||||
|       base.Call(); | ||||
|     } | ||||
| 
 | ||||
|     Doorway GetDoorway(string name){ | ||||
|       switch(name) { | ||||
|         case "self": | ||||
|           return targetReference; | ||||
|         case "other": | ||||
|           return InDebugMode ? connectedDoorwayDebug : targetReference.ConnectedDoorway; | ||||
|         default: | ||||
|           Utils.Utility.PrintLog($"{name} is not valid doorway expression. Please use 'self' or 'other'", BepInEx.Logging.LogLevel.Error); | ||||
|           return null; | ||||
| 
 | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     public override EvaluationContext CreateContext() { | ||||
|       var context = new EvaluationContext(GetFields); | ||||
|       context.RegisterFunction("doorwaySpawnedGameObject", new FunctionRoutine(2, doorwaySpawnedGameObjectFunction)); | ||||
| 
 | ||||
|       return context; | ||||
|     } | ||||
| 
 | ||||
|     ExpressionToken doorwaySpawnedGameObjectFunction(EvaluationContext context, ExpressionToken[] parameters) { | ||||
|       var targetName = parameters[0].Value; | ||||
|       var target = GetDoorway(targetName); | ||||
|       if (target != null) { | ||||
|         var name = parameters[1].Value; | ||||
|         foreach(Transform child in target.transform) { | ||||
|           if (child.gameObject.activeSelf && child.name.Contains(name)) return ExpressionToken.True; | ||||
|         } | ||||
|       }  | ||||
|       return ExpressionToken.False; | ||||
|     } | ||||
| 
 | ||||
|     (object, ValueTypeHint) GetFields(string field) { | ||||
|       var split = field.Split('.'); | ||||
| 
 | ||||
|       if (split.Length <= 1) { | ||||
|         Utils.Utility.PrintLog($"{field} is not a valid field", BepInEx.Logging.LogLevel.Error); | ||||
|         return (0, ValueTypeHint.Auto); | ||||
|       } | ||||
| 
 | ||||
|       var targetName = split[0]; | ||||
|       var target = GetDoorway(targetName); | ||||
|       var getter = split[1]; | ||||
| 
 | ||||
|       switch(getter) { | ||||
|         case "priority": | ||||
|           if (target != null){ | ||||
|             return (target.DoorPrefabPriority, ValueTypeHint.Auto); | ||||
|           } | ||||
|           return (0, ValueTypeHint.Auto); | ||||
|         case "exists": | ||||
|           return (target != null, ValueTypeHint.Auto); | ||||
|         default: | ||||
|           Utils.Utility.PrintLog($"{getter} is not a valid getter", BepInEx.Logging.LogLevel.Error); | ||||
|           return (0, ValueTypeHint.Auto); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,84 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using UnityEngine; | ||||
| using Soukoku.ExpressionParser; | ||||
| using DunGen; | ||||
| 
 | ||||
| namespace DunGenPlus.Components.Scripting { | ||||
| 
 | ||||
|   public enum ScriptActionType { | ||||
|     SwitchToConnector, | ||||
|     SwitchToBlocker, | ||||
|     SetNamedReferenceState | ||||
|   } | ||||
| 
 | ||||
|   [System.Serializable] | ||||
|   public struct ScriptAction { | ||||
|     public ScriptActionType type; | ||||
|     public string namedReference; | ||||
|     public bool boolValue; | ||||
| 
 | ||||
|     public void CallAction(IDunGenScriptingParent parent){ | ||||
|       switch(type){ | ||||
|         case ScriptActionType.SwitchToConnector: | ||||
|           parent.SetNamedGameObjectState("connectors", true); | ||||
|           parent.SetNamedGameObjectState("blockers", false); | ||||
|           break; | ||||
|         case ScriptActionType.SwitchToBlocker: | ||||
|           parent.SetNamedGameObjectState("connectors", false); | ||||
|           parent.SetNamedGameObjectState("blockers", true); | ||||
|           break; | ||||
|         case ScriptActionType.SetNamedReferenceState: | ||||
|           parent.SetNamedGameObjectState(namedReference, boolValue); | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   public class DunGenPlusScript : MonoBehaviour { | ||||
| 
 | ||||
|     public static bool InDebugMode = false; | ||||
| 
 | ||||
|     public string expression; | ||||
|     public List<ScriptAction> actions; | ||||
| 
 | ||||
|     public bool EvaluateExpression(IDunGenScriptingParent parent){ | ||||
|       var context = parent.CreateContext(); | ||||
|       var evaluator = new Evaluator(context); | ||||
|       try { | ||||
|         InDebugMode = false; | ||||
|         var results = evaluator.Evaluate(expression, true); | ||||
|         return results.ToDouble(context) > 0; | ||||
|       } catch (Exception e) { | ||||
|         Plugin.logger.LogError($"Expression [{expression}] could not be parsed. Returning false"); | ||||
|         Plugin.logger.LogError(e.ToString()); | ||||
|       } | ||||
|        | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     [ContextMenu("Verify")] | ||||
|     public void VerifyExpression(){ | ||||
|       var context = GetComponent<IDunGenScriptingParent>().CreateContext(); | ||||
|       var evaluator = new Evaluator(context); | ||||
|       try { | ||||
|         InDebugMode = true; | ||||
|         var results = evaluator.Evaluate(expression, false); | ||||
|         Debug.Log($"Expression parsed successfully: {results.ToString()} ({evaluator.ConvertTokenToFalseTrue(results).ToString()})"); | ||||
|       } catch (Exception e) { | ||||
|         Debug.LogError($"Expression [{expression}] could not be parsed"); | ||||
|         Debug.LogError(e.ToString()); | ||||
|       }  | ||||
|     } | ||||
| 
 | ||||
|     public void Call(IDunGenScriptingParent parent){ | ||||
|       if (EvaluateExpression(parent)){ | ||||
|         foreach(var action in actions) action.CallAction(parent); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,152 @@ | |||
| using DunGen; | ||||
| using DunGenPlus.Managers; | ||||
| using Soukoku.ExpressionParser; | ||||
| using System; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| namespace DunGenPlus.Components.Scripting { | ||||
| 
 | ||||
|   public enum OverrideGameObjectState { | ||||
|     None, | ||||
|     Active, | ||||
|     Disabled | ||||
|   } | ||||
| 
 | ||||
|   [System.Serializable] | ||||
|   public class NamedGameObjectReference { | ||||
|     public string name; | ||||
|     public List<GameObject> gameObjects; | ||||
|     public OverrideGameObjectState overrideState; | ||||
| 
 | ||||
|     public NamedGameObjectReference(string name, List<GameObject> gameObjects){ | ||||
|       this.name = name; | ||||
|       this.gameObjects = gameObjects; | ||||
|     } | ||||
| 
 | ||||
|     public void SetState(bool state){ | ||||
|       foreach(var g in gameObjects){ | ||||
|         g?.SetActive(state); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     public void FixOverrideState(){ | ||||
|       if (overrideState == OverrideGameObjectState.None) return; | ||||
|       SetState(overrideState == OverrideGameObjectState.Active); | ||||
|     } | ||||
| 
 | ||||
|     public void DestroyInactiveGameObjects(){ | ||||
|       foreach(var g in gameObjects){ | ||||
|         if (g && !g.activeSelf) UnityEngine.Object.DestroyImmediate(g, false); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   public enum DunGenScriptingHook { | ||||
|     SetLevelObjectVariables, | ||||
|     OnMainEntranceTeleportSpawned | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   public interface IDunGenScriptingParent { | ||||
| 
 | ||||
|     DunGenScriptingHook GetScriptingHook { get; } | ||||
| 
 | ||||
|     void Call(); | ||||
| 
 | ||||
|     List<NamedGameObjectReference> GetNamedReferences { get; } | ||||
|      | ||||
|     void AddNamedReference(string name, List<GameObject> gameObjects); | ||||
| 
 | ||||
|     void SetNamedGameObjectState(string name, bool state); | ||||
|     void SetNamedGameObjectOverrideState(string name, OverrideGameObjectState state); | ||||
| 
 | ||||
|     EvaluationContext CreateContext(); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   public abstract class DunGenPlusScriptingParent<T> : MonoBehaviour, IDunGenScriptingParent, IDungeonCompleteReceiver where T: Component { | ||||
| 
 | ||||
|     public static bool InDebugMode => DunGenPlusScript.InDebugMode; | ||||
| 
 | ||||
|     [Header("REQUIRED")] | ||||
|     [Tooltip("The target reference.")] | ||||
|     public T targetReference; | ||||
|     public DunGenScriptingHook callHook = DunGenScriptingHook.OnMainEntranceTeleportSpawned; | ||||
| 
 | ||||
|     [Header("Named References")] | ||||
|     [Tooltip("Provide a variable name for a list of gameObjects. Used in DunGenScripting.")] | ||||
|     public List<NamedGameObjectReference> namedReferences = new List<NamedGameObjectReference>(); | ||||
|     public Dictionary<string, NamedGameObjectReference> namedDictionary = new Dictionary<string, NamedGameObjectReference>(); | ||||
| 
 | ||||
|     public DunGenScriptingHook GetScriptingHook => callHook; | ||||
|     public List<NamedGameObjectReference> GetNamedReferences => namedReferences; | ||||
| 
 | ||||
|     public void OnDungeonComplete(Dungeon dungeon) { | ||||
|       //SetBlockers(true); | ||||
|       //Debug.Log("ONDUNGEONCOMPLETE"); | ||||
|       DoorwayManager.AddDunGenScriptHook(this); | ||||
|     } | ||||
| 
 | ||||
|     public virtual void Awake(){ | ||||
|       foreach(var r in namedReferences){ | ||||
|         namedDictionary.Add(r.name, r); | ||||
|       } | ||||
|     }  | ||||
| 
 | ||||
|     public virtual void Call() { | ||||
|       // call scripts | ||||
|       var scripts = GetComponentsInChildren<DunGenPlusScript>(); | ||||
|       foreach(var c in scripts) c.Call(this); | ||||
|          | ||||
|       // apply any overrides | ||||
|       foreach(var n in namedReferences) n.FixOverrideState(); | ||||
| 
 | ||||
|       // clean up like in original | ||||
|       foreach(var n in namedReferences) DestroyInactiveGameObjects(n.gameObjects); | ||||
|     } | ||||
| 
 | ||||
|     public void AddNamedReference(string name, List<GameObject> gameObjects) { | ||||
|       var item = new NamedGameObjectReference(name, gameObjects); | ||||
|       namedReferences.Add(item); | ||||
|       namedDictionary.Add(name, item); | ||||
|     } | ||||
| 
 | ||||
|     public void SetNamedGameObjectState(string name, bool state){ | ||||
|       if (namedDictionary.TryGetValue(name, out var obj)){ | ||||
|         obj.SetState(state); | ||||
|       } else { | ||||
|         Plugin.logger.LogError($"Named reference: {name} does not exist"); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     public void SetNamedGameObjectOverrideState(string name, OverrideGameObjectState state){ | ||||
|       if (namedDictionary.TryGetValue(name, out var obj)){ | ||||
|         obj.overrideState = state; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     public void DestroyInactiveGameObjects(IEnumerable<GameObject> gameObjects){ | ||||
|       foreach(var g in gameObjects) { | ||||
|         if (g && !g.activeSelf) { | ||||
|           UnityEngine.Object.DestroyImmediate(g, false); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     protected bool CheckIfNotNull(object target, string name){ | ||||
|       if (target == null) { | ||||
|         Utils.Utility.PrintLog($"{name} was null", BepInEx.Logging.LogLevel.Error); | ||||
|         return false; | ||||
|       } | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|     public abstract EvaluationContext CreateContext(); | ||||
| 
 | ||||
|   } | ||||
| } | ||||
|  | @ -32,6 +32,7 @@ namespace DunGenPlus.DevTools.Panels { | |||
|     private GameObject forcedTilesParentGameobject; | ||||
|     private GameObject branchLoopBoostParentGameobject; | ||||
|     private GameObject maxShadowsParentGameobject; | ||||
|     public bool eventCallbackValue = true; | ||||
| 
 | ||||
|     public override void AwakeCall() { | ||||
|       Instance = this; | ||||
|  | @ -70,6 +71,7 @@ namespace DunGenPlus.DevTools.Panels { | |||
|     } | ||||
| 
 | ||||
|     internal const string ActivateDunGenPlusTooltip = "If disabled, the dungeon generation will ignore this DunGenPlusExtender asset and simply create a vanilla dungeon instead when generating."; | ||||
|     internal const string EventCallbackScenarioTooltip = "Sets the EventCallbackScenario.IsDevDebug value"; | ||||
| 
 | ||||
|     public void SetupPanel() { | ||||
|       selectedExtenderer = API.GetDunGenExtender(selectedDungeonFlow); | ||||
|  | @ -77,6 +79,7 @@ namespace DunGenPlus.DevTools.Panels { | |||
|       var parentTransform = selectedListGameObject.transform; | ||||
|       var properties = selectedExtenderer.Properties; | ||||
|       manager.CreateBoolInputField(parentTransform, ("Activate DunGenPlus", ActivateDunGenPlusTooltip), selectedExtenderer.Active, SetActivateDunGenPlus); | ||||
|       manager.CreateBoolInputField(parentTransform, ("EventCallbackScenario state", EventCallbackScenarioTooltip), eventCallbackValue, SetDebugCallbackState); | ||||
|       manager.CreateSpaceUIField(parentTransform); | ||||
| 
 | ||||
|       var mainPathTransform = manager.CreateVerticalLayoutUIField(parentTransform); | ||||
|  | @ -168,6 +171,10 @@ namespace DunGenPlus.DevTools.Panels { | |||
|       selectedExtenderer.Active = state; | ||||
|     } | ||||
| 
 | ||||
|     public void SetDebugCallbackState(bool state){ | ||||
|       eventCallbackValue = state; | ||||
|     } | ||||
| 
 | ||||
|     public void SetMainPathCount(int value) { | ||||
|       selectedExtenderer.Properties.MainPathProperties.MainPathCount = value; | ||||
|       mainPathParentGameobject.SetActive(value > 1); | ||||
|  |  | |||
|  | @ -56,6 +56,9 @@ | |||
|       <SpecificVersion>False</SpecificVersion> | ||||
|       <HintPath>..\..\..\Libraries\MonoMod.Utils.dll</HintPath> | ||||
|     </Reference> | ||||
|     <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | ||||
|       <HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath> | ||||
|     </Reference> | ||||
|     <Reference Include="System" /> | ||||
|     <Reference Include="System.Core" /> | ||||
|     <Reference Include="System.Xml.Linq" /> | ||||
|  | @ -157,7 +160,24 @@ | |||
|     <Compile Include="Components\MainRoomDoorwayGroups.cs" /> | ||||
|     <Compile Include="Components\Props\SpawnSyncedObjectCycle.cs" /> | ||||
|     <Compile Include="Components\Scrap\RandomGuaranteedScrapSpawn.cs" /> | ||||
|     <Compile Include="Components\Scripting\DoorwayScriptingParent.cs" /> | ||||
|     <Compile Include="Components\Scripting\DunGenPlusScript.cs" /> | ||||
|     <Compile Include="Components\Scripting\DunGenPlusScriptingParent.cs" /> | ||||
|     <Compile Include="DevTools\Panels\AssetsPanel.cs" /> | ||||
|     <Compile Include="ExpressionParser\EvaluationContext.cs" /> | ||||
|     <Compile Include="ExpressionParser\Evaluator.cs" /> | ||||
|     <Compile Include="ExpressionParser\ExpressionToken.cs" /> | ||||
|     <Compile Include="ExpressionParser\ExpressionTokenType.cs" /> | ||||
|     <Compile Include="ExpressionParser\FunctionRoutine.cs" /> | ||||
|     <Compile Include="ExpressionParser\Parsing\IExpressionTokenizer.cs" /> | ||||
|     <Compile Include="ExpressionParser\Parsing\InfixTokenizer.cs" /> | ||||
|     <Compile Include="ExpressionParser\Parsing\InfixToPostfixTokenizer.cs" /> | ||||
|     <Compile Include="ExpressionParser\Parsing\KnownOperators.cs" /> | ||||
|     <Compile Include="ExpressionParser\Parsing\ListReader.cs" /> | ||||
|     <Compile Include="ExpressionParser\Parsing\OperatorType.cs" /> | ||||
|     <Compile Include="ExpressionParser\Parsing\RawToken.cs" /> | ||||
|     <Compile Include="ExpressionParser\Parsing\RawTokenizer.cs" /> | ||||
|     <Compile Include="ExpressionParser\ValueTypeHint.cs" /> | ||||
|     <Compile Include="Generation\DunGenPlusGenerationPaths.cs" /> | ||||
|     <Compile Include="Generation\DunGenPlusGeneratorDebug.cs" /> | ||||
|     <Compile Include="Generation\DunGenPlusGeneratorGlobalProps.cs" /> | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										136
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/EvaluationContext.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/EvaluationContext.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,136 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Globalization; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace Soukoku.ExpressionParser | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Context for storing and returning values during an expression evaluation. | ||||
|     /// </summary> | ||||
|     public class EvaluationContext | ||||
|     { | ||||
|         static Dictionary<string, FunctionRoutine> BuiltInFunctions = new Dictionary<string, FunctionRoutine>(StringComparer.OrdinalIgnoreCase) | ||||
|         { | ||||
|             { "pow", new FunctionRoutine(2, (ctx, args)=> | ||||
|                     new ExpressionToken( Math.Pow(args[0].ToDouble(ctx), args[1].ToDouble(ctx)).ToString(ctx.FormatCulture))) }, | ||||
|             { "sin", new FunctionRoutine(1, (ctx, args)=> | ||||
|                     new ExpressionToken( Math.Sin(args[0].ToDouble(ctx)).ToString(ctx.FormatCulture)))}, | ||||
|             { "cos", new FunctionRoutine(1, (ctx, args)=> | ||||
|                     new ExpressionToken( Math.Cos(args[0].ToDouble(ctx)).ToString(ctx.FormatCulture)))}, | ||||
|             { "tan", new FunctionRoutine(1, (ctx, args)=> | ||||
|                     new ExpressionToken( Math.Tan(args[0].ToDouble(ctx)).ToString(ctx.FormatCulture)))} | ||||
|         }; | ||||
| 
 | ||||
|         static readonly Dictionary<string, FunctionRoutine> __staticFuncs = new Dictionary<string, FunctionRoutine>(StringComparer.OrdinalIgnoreCase); | ||||
|         readonly Dictionary<string, FunctionRoutine> _instanceFuncs = new Dictionary<string, FunctionRoutine>(StringComparer.OrdinalIgnoreCase); | ||||
| 
 | ||||
|         Func<string, (object, ValueTypeHint)> _fieldLookup; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="EvaluationContext"/> class. | ||||
|         /// </summary> | ||||
|         public EvaluationContext() { } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="EvaluationContext"/> class. | ||||
|         /// </summary> | ||||
|         /// <param name="fieldLookupRoutine">The field value lookup routine.</param> | ||||
|         public EvaluationContext(Func<string, (object Value, ValueTypeHint TypeHint)> fieldLookupRoutine) | ||||
|         { | ||||
|             _fieldLookup = fieldLookupRoutine; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Resolves the field value. | ||||
|         /// </summary> | ||||
|         /// <param name="field">The field.</param> | ||||
|         /// <returns></returns> | ||||
|         public (object Value, ValueTypeHint TypeHint) ResolveFieldValue(string field) | ||||
|         { | ||||
|             if (_fieldLookup != null) { return _fieldLookup(field); } | ||||
|             return OnResolveFieldValue(field); | ||||
|         } | ||||
| 
 | ||||
|         readonly CultureInfo _usCulture = new CultureInfo("en-US"); | ||||
|         private CultureInfo _formatCulture = null; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets/sets the culture used to parse/format expressions.  | ||||
|         /// Defaults to en-US for certain reasons. | ||||
|         /// </summary> | ||||
|         public CultureInfo FormatCulture | ||||
|         { | ||||
|             get { return _formatCulture ?? _usCulture; } | ||||
|             set { _formatCulture = value; } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the field value. | ||||
|         /// </summary> | ||||
|         /// <param name="field">The field.</param> | ||||
|         /// <returns></returns> | ||||
|         protected virtual (object Value, ValueTypeHint TypeHint) OnResolveFieldValue(string field) | ||||
|         { | ||||
|             return (string.Empty, ValueTypeHint.Auto); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Registers a custom function globally. | ||||
|         /// </summary> | ||||
|         /// <param name="functionName">Name of the function.</param> | ||||
|         /// <param name="info">The information.</param> | ||||
|         public static void RegisterGlobalFunction(string functionName, FunctionRoutine info) | ||||
|         { | ||||
|             __staticFuncs[functionName] = info; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Registers a custom function with this context instance. | ||||
|         /// </summary> | ||||
|         /// <param name="functionName">Name of the function.</param> | ||||
|         /// <param name="info">The information.</param> | ||||
|         public void RegisterFunction(string functionName, FunctionRoutine info) | ||||
|         { | ||||
|             _instanceFuncs[functionName] = info; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the function registered with this context. | ||||
|         /// </summary> | ||||
|         /// <param name="functionName">Name of the function.</param> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="System.NotSupportedException"></exception> | ||||
|         public FunctionRoutine GetFunction(string functionName) | ||||
|         { | ||||
|             if (_instanceFuncs.ContainsKey(functionName)) | ||||
|             { | ||||
|                 return _instanceFuncs[functionName]; | ||||
|             } | ||||
|             if (__staticFuncs.ContainsKey(functionName)) | ||||
|             { | ||||
|                 return __staticFuncs[functionName]; | ||||
|             } | ||||
|             if (BuiltInFunctions.ContainsKey(functionName)) | ||||
|             { | ||||
|                 return BuiltInFunctions[functionName]; | ||||
|             } | ||||
|             return OnGetFunction(functionName) ?? | ||||
|                 throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Function \"{0}\" is not supported.", functionName)); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the function registered with this context. | ||||
|         /// </summary> | ||||
|         /// <param name="functionName">Name of the function.</param> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="System.NotSupportedException"></exception> | ||||
|         protected virtual FunctionRoutine OnGetFunction(string functionName) | ||||
|         { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										438
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Evaluator.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										438
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Evaluator.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,438 @@ | |||
| using Soukoku.ExpressionParser.Parsing; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Diagnostics; | ||||
| using System.Globalization; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using UnityEngine.Events; | ||||
| 
 | ||||
| namespace Soukoku.ExpressionParser | ||||
| { | ||||
|     /// <summary> | ||||
|     /// An expression evaluator. | ||||
|     /// </summary> | ||||
|     public class Evaluator | ||||
|     { | ||||
|         EvaluationContext _context; | ||||
|         Stack<ExpressionToken> _stack; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="Evaluator"/> class. | ||||
|         /// </summary> | ||||
|         /// <param name="context">The context.</param> | ||||
|         /// <exception cref="System.ArgumentNullException">context</exception> | ||||
|         public Evaluator(EvaluationContext context) | ||||
|         { | ||||
|             _context = context ?? throw new ArgumentNullException("context"); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Evaluates the specified input expression. | ||||
|         /// </summary> | ||||
|         /// <param name="input">The input expression (infix).</param> | ||||
|         /// <param name="coerseToBoolean">if set to <c>true</c> then the result will be coersed to boolean true/false if possible. | ||||
|         /// Anything not "false", "0", or "" is considered true.</param> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="NotSupportedException">Unbalanced expression.</exception> | ||||
|         /// <exception cref="System.NotSupportedException"></exception> | ||||
|         public ExpressionToken Evaluate(string input, bool coerseToBoolean = false) | ||||
|         { | ||||
|             if (string.IsNullOrWhiteSpace(input)) | ||||
|             { | ||||
|                 return coerseToBoolean ? ExpressionToken.False : new ExpressionToken(input); | ||||
|             } | ||||
| 
 | ||||
|             var tokens = new InfixToPostfixTokenizer().Tokenize(input); | ||||
|             // resolve field value and type hints here | ||||
|             foreach (var token in tokens.Where(tk => tk.TokenType == ExpressionTokenType.Field)) | ||||
|             { | ||||
|                 token.FieldValue = _context.ResolveFieldValue(token.Value); | ||||
|             } | ||||
| 
 | ||||
|             var reader = new ListReader<ExpressionToken>(tokens); | ||||
| 
 | ||||
|             // from https://en.wikipedia.org/wiki/Reverse_Polish_notation | ||||
|             _stack = new Stack<ExpressionToken>(); | ||||
|             while (!reader.IsEnd) | ||||
|             { | ||||
|                 var tk = reader.Read(); | ||||
|                 switch (tk.TokenType) | ||||
|                 { | ||||
|                     case ExpressionTokenType.Value: | ||||
|                     case ExpressionTokenType.DoubleQuoted: | ||||
|                     case ExpressionTokenType.SingleQuoted: | ||||
|                     case ExpressionTokenType.Field: | ||||
|                         _stack.Push(tk); | ||||
|                         break; | ||||
|                     case ExpressionTokenType.Operator: | ||||
|                         HandleOperator(tk.OperatorType); | ||||
|                         break; | ||||
|                     case ExpressionTokenType.Function: | ||||
|                         HandleFunction(tk.Value); | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (_stack.Count == 1) | ||||
|             { | ||||
|                 var res = _stack.Pop(); | ||||
|                 if (coerseToBoolean) | ||||
|                 { | ||||
|                   return ConvertTokenToFalseTrue(res); | ||||
|                 } | ||||
|                 return res; | ||||
| 
 | ||||
|                 //if (res.IsNumeric()) | ||||
|                 //{ | ||||
|                 //    return res; | ||||
|                 //} | ||||
|                 //else if (IsTrue(res.Value)) | ||||
|                 //{ | ||||
|                 //    return ExpressionToken.True; | ||||
|                 //} | ||||
|             } | ||||
|             throw new NotSupportedException("Unbalanced expression."); | ||||
|         } | ||||
| 
 | ||||
|         public ExpressionToken ConvertTokenToFalseTrue(ExpressionToken token){ | ||||
|           // changed form Value to ToString() so fields by themselves evalute properly | ||||
|           if (IsFalse(token.ToString())) | ||||
|           { | ||||
|               return ExpressionToken.False; | ||||
|           } | ||||
|           return ExpressionToken.True; | ||||
|         } | ||||
| 
 | ||||
|         private void HandleFunction(string functionName) | ||||
|         { | ||||
|             var fun = _context.GetFunction(functionName); | ||||
|             var args = new Stack<ExpressionToken>(fun.ArgumentCount); | ||||
| 
 | ||||
|             while (args.Count < fun.ArgumentCount) | ||||
|             { | ||||
|                 args.Push(_stack.Pop()); | ||||
|             } | ||||
| 
 | ||||
|             _stack.Push(fun.Evaluate(_context, args.ToArray())); | ||||
|         } | ||||
| 
 | ||||
|         #region operator handling | ||||
| 
 | ||||
|         static bool IsDate(string lhs, string rhs, out DateTime lhsDate, out DateTime rhsDate) | ||||
|         { | ||||
|             lhsDate = default(DateTime); | ||||
|             rhsDate = default(DateTime); | ||||
| 
 | ||||
|             if (DateTime.TryParse(lhs, out lhsDate)) | ||||
|             { | ||||
|                 DateTime.TryParse(rhs, out rhsDate); | ||||
|                 return true; | ||||
|             } | ||||
|             else if (DateTime.TryParse(rhs, out rhsDate)) | ||||
|             { | ||||
|                 DateTime.TryParse(lhs, out lhsDate); | ||||
|                 return true; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         bool IsNumber(string lhs, string rhs, out decimal lhsNumber, out decimal rhsNumber) | ||||
|         { | ||||
|             lhsNumber = 0; | ||||
|             rhsNumber = 0; | ||||
| 
 | ||||
|             var islNum = decimal.TryParse(lhs, ExpressionToken.NumberParseStyle, _context.FormatCulture, out lhsNumber); | ||||
|             var isrNum = decimal.TryParse(rhs, ExpressionToken.NumberParseStyle, _context.FormatCulture, out rhsNumber); | ||||
| 
 | ||||
|             return islNum && isrNum; | ||||
|         } | ||||
|         static bool IsBoolean(string lhs, string rhs, out bool lhsBool, out bool rhsBool) | ||||
|         { | ||||
|             bool lIsBool = false; | ||||
|             bool rIsBool = false; | ||||
|             lhsBool = false; | ||||
|             rhsBool = false; | ||||
| 
 | ||||
|             if (!string.IsNullOrEmpty(lhs)) | ||||
|             { | ||||
|                 if (string.Equals(lhs, "true", StringComparison.OrdinalIgnoreCase) || lhs == "1") | ||||
|                 { | ||||
|                     lhsBool = true; | ||||
|                     lIsBool = true; | ||||
|                 } | ||||
|                 else if (string.Equals(lhs, "false", StringComparison.OrdinalIgnoreCase) || lhs == "0") | ||||
|                 { | ||||
|                     lIsBool = true; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (lIsBool && !string.IsNullOrEmpty(rhs)) | ||||
|             { | ||||
|                 if (string.Equals(rhs, "true", StringComparison.OrdinalIgnoreCase) || rhs == "1") | ||||
|                 { | ||||
|                     rhsBool = true; | ||||
|                     rIsBool = true; | ||||
|                 } | ||||
|                 else if (string.Equals(rhs, "false", StringComparison.OrdinalIgnoreCase) || rhs == "0") | ||||
|                 { | ||||
|                     rIsBool = true; | ||||
|                 } | ||||
|             } | ||||
|             return lIsBool && rIsBool; | ||||
| 
 | ||||
|             //lhsBool = false; | ||||
|             //rhsBool = false; | ||||
| 
 | ||||
|             //if (string.Equals(lhs, "true", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(rhs)) | ||||
|             //{ | ||||
|             //    lhsBool = true; | ||||
|             //    rhsBool = IsTrue(rhs); | ||||
|             //    return true; | ||||
|             //} | ||||
|             //else if (string.Equals(lhs, "false", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(rhs)) | ||||
|             //{ | ||||
|             //    rhsBool = IsTrue(rhs); | ||||
|             //    return true; | ||||
|             //} | ||||
|             //else if (string.Equals(rhs, "true", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(lhs)) | ||||
|             //{ | ||||
|             //    rhsBool = true; | ||||
|             //    lhsBool = IsTrue(lhs); | ||||
|             //    return true; | ||||
|             //} | ||||
|             //else if (string.Equals(rhs, "false", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(lhs)) | ||||
|             //{ | ||||
|             //    lhsBool = IsTrue(lhs); | ||||
|             //    return true; | ||||
|             //} | ||||
|             //return false; | ||||
|         } | ||||
| 
 | ||||
|         private void HandleOperator(OperatorType op) | ||||
|         { | ||||
|             switch (op) | ||||
|             { | ||||
|                 case OperatorType.Addition: | ||||
|                     BinaryNumberOperation((a, b) => a + b); | ||||
|                     break; | ||||
|                 case OperatorType.Subtraction: | ||||
|                     BinaryNumberOperation((a, b) => a - b); | ||||
|                     break; | ||||
|                 case OperatorType.Multiplication: | ||||
|                     BinaryNumberOperation((a, b) => a * b); | ||||
|                     break; | ||||
|                 case OperatorType.Division: | ||||
|                     BinaryNumberOperation((a, b) => a / b); | ||||
|                     break; | ||||
|                 case OperatorType.Modulus: | ||||
|                     BinaryNumberOperation((a, b) => a % b); | ||||
|                     break; | ||||
|                 // these logical comparision can be date/num/string! | ||||
|                 case OperatorType.LessThan: | ||||
|                     var rhsToken = _stack.Pop(); | ||||
|                     var lhsToken = _stack.Pop(); | ||||
|                     var rhs = rhsToken.ToString(); | ||||
|                     var lhs = lhsToken.ToString(); | ||||
| 
 | ||||
|                     if (IsNumber(lhs, rhs, out decimal lhsNum, out decimal rhsNum)) | ||||
|                     { | ||||
|                         _stack.Push(lhsNum < rhsNum ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else if (IsDate(lhs, rhs, out DateTime lhsDate, out DateTime rhsDate)) | ||||
|                     { | ||||
|                         _stack.Push(lhsDate < rhsDate ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         _stack.Push(string.Compare(lhs, rhs, StringComparison.OrdinalIgnoreCase) < 0 ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     break; | ||||
|                 case OperatorType.LessThanOrEqual: | ||||
|                     rhsToken = _stack.Pop(); | ||||
|                     lhsToken = _stack.Pop(); | ||||
|                     rhs = rhsToken.ToString(); | ||||
|                     lhs = lhsToken.ToString(); | ||||
| 
 | ||||
|                     if (IsNumber(lhs, rhs, out lhsNum, out rhsNum)) | ||||
|                     { | ||||
|                         _stack.Push(lhsNum <= rhsNum ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else if (IsDate(lhs, rhs, out DateTime lhsDate, out DateTime rhsDate)) | ||||
|                     { | ||||
|                         _stack.Push(lhsDate <= rhsDate ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         _stack.Push(string.Compare(lhs, rhs, StringComparison.OrdinalIgnoreCase) <= 0 ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     break; | ||||
|                 case OperatorType.GreaterThan: | ||||
|                     rhsToken = _stack.Pop(); | ||||
|                     lhsToken = _stack.Pop(); | ||||
|                     rhs = rhsToken.ToString(); | ||||
|                     lhs = lhsToken.ToString(); | ||||
| 
 | ||||
|                     if (IsNumber(lhs, rhs, out lhsNum, out rhsNum)) | ||||
|                     { | ||||
|                         _stack.Push(lhsNum > rhsNum ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else if (IsDate(lhs, rhs, out DateTime lhsDate, out DateTime rhsDate)) | ||||
|                     { | ||||
|                         _stack.Push(lhsDate > rhsDate ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         _stack.Push(string.Compare(lhs, rhs, StringComparison.OrdinalIgnoreCase) > 0 ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     break; | ||||
|                 case OperatorType.GreaterThanOrEqual: | ||||
|                     rhsToken = _stack.Pop(); | ||||
|                     lhsToken = _stack.Pop(); | ||||
|                     rhs = rhsToken.ToString(); | ||||
|                     lhs = lhsToken.ToString(); | ||||
| 
 | ||||
|                     if (IsNumber(lhs, rhs, out lhsNum, out rhsNum)) | ||||
|                     { | ||||
|                         _stack.Push(lhsNum >= rhsNum ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else if (IsDate(lhs, rhs, out DateTime lhsDate, out DateTime rhsDate)) | ||||
|                     { | ||||
|                         _stack.Push(lhsDate >= rhsDate ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         _stack.Push(string.Compare(lhs, rhs, StringComparison.OrdinalIgnoreCase) >= 0 ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     break; | ||||
|                 case OperatorType.Equal: | ||||
|                     rhsToken = _stack.Pop(); | ||||
|                     lhsToken = _stack.Pop(); | ||||
|                     rhs = rhsToken.ToString(); | ||||
|                     lhs = lhsToken.ToString(); | ||||
| 
 | ||||
|                     if (IsBoolean(lhs, rhs, out bool lhsBool, out bool rhsBool)) | ||||
|                     { | ||||
|                         _stack.Push(lhsBool == rhsBool ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else if ((AllowAutoFormat(lhsToken) || AllowAutoFormat(rhsToken)) && | ||||
|                         IsNumber(lhs, rhs, out lhsNum, out rhsNum)) | ||||
|                     { | ||||
|                         _stack.Push(lhsNum == rhsNum ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else if (IsDate(lhs, rhs, out DateTime lhsDate, out DateTime rhsDate)) | ||||
|                     { | ||||
|                         _stack.Push(lhsDate == rhsDate ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         _stack.Push(string.Compare(lhs, rhs, StringComparison.OrdinalIgnoreCase) == 0 ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     break; | ||||
|                 case OperatorType.NotEqual: | ||||
|                     rhsToken = _stack.Pop(); | ||||
|                     lhsToken = _stack.Pop(); | ||||
|                     rhs = rhsToken.ToString(); | ||||
|                     lhs = lhsToken.ToString(); | ||||
| 
 | ||||
|                     if (IsBoolean(lhs, rhs, out lhsBool, out rhsBool)) | ||||
|                     { | ||||
|                         _stack.Push(lhsBool != rhsBool ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else if ((AllowAutoFormat(lhsToken) || AllowAutoFormat(rhsToken)) && | ||||
|                         IsNumber(lhs, rhs, out lhsNum, out rhsNum)) | ||||
|                     { | ||||
|                         _stack.Push(lhsNum != rhsNum ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else if (IsDate(lhs, rhs, out DateTime lhsDate, out DateTime rhsDate)) | ||||
|                     { | ||||
|                         _stack.Push(lhsDate != rhsDate ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         _stack.Push(string.Compare(lhs, rhs, StringComparison.OrdinalIgnoreCase) != 0 ? ExpressionToken.True : ExpressionToken.False); | ||||
|                     } | ||||
|                     break; | ||||
|                 case OperatorType.BitwiseAnd: | ||||
|                     BinaryNumberOperation((a, b) => (int)a & (int)b); | ||||
|                     break; | ||||
|                 case OperatorType.BitwiseOr: | ||||
|                     BinaryNumberOperation((a, b) => (int)a | (int)b); | ||||
|                     break; | ||||
|                 case OperatorType.LogicalAnd: | ||||
|                     BinaryLogicOperation((a, b) => IsTrue(a) && IsTrue(b)); | ||||
|                     break; | ||||
|                 case OperatorType.LogicalOr: | ||||
|                     BinaryLogicOperation((a, b) => IsTrue(a) || IsTrue(b)); | ||||
|                     break; | ||||
|                 case OperatorType.UnaryMinus: | ||||
|                     UnaryNumberOperation(a => -1 * a); | ||||
|                     break; | ||||
|                 case OperatorType.UnaryPlus: | ||||
|                     // no action | ||||
|                     break; | ||||
|                 case OperatorType.LogicalNegation: | ||||
|                     UnaryLogicOperation(a => !IsTrue(a)); | ||||
|                     break; | ||||
|                 case OperatorType.PreIncrement: | ||||
|                     UnaryNumberOperation(a => a + 1); | ||||
|                     break; | ||||
|                 case OperatorType.PreDecrement: | ||||
|                     UnaryNumberOperation(a => a - 1); | ||||
|                     break; | ||||
|                 // TODO: handle assignments & post increments | ||||
|                 default: | ||||
|                     throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "The {0} operation is not currently supported.", op)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         static bool AllowAutoFormat(ExpressionToken token) | ||||
|         { | ||||
|             return token.TokenType != ExpressionTokenType.Field || token.FieldValue.TypeHint != ValueTypeHint.Text; | ||||
|         } | ||||
| 
 | ||||
|         static bool IsTrue(string value) | ||||
|         { | ||||
|             return string.Equals("true", value, StringComparison.OrdinalIgnoreCase) || value == "1"; | ||||
|         } | ||||
| 
 | ||||
|         static bool IsFalse(string value) | ||||
|         { | ||||
|             return string.Equals("false", value, StringComparison.OrdinalIgnoreCase) || value == "0" || string.IsNullOrWhiteSpace(value); | ||||
|         } | ||||
| 
 | ||||
|         void UnaryNumberOperation(Func<decimal, decimal> operation) | ||||
|         { | ||||
|             var op1 = _stack.Pop().ToDecimal(_context); | ||||
|             var res = operation(op1); | ||||
| 
 | ||||
|             _stack.Push(new ExpressionToken(res.ToString(_context.FormatCulture))); | ||||
|         } | ||||
|         void UnaryLogicOperation(Func<string, bool> operation) | ||||
|         { | ||||
|             var op1 = _stack.Pop(); | ||||
|             var res = operation(op1.ToString()) ? "1" : "0"; | ||||
| 
 | ||||
|             _stack.Push(new ExpressionToken(res)); | ||||
|         } | ||||
|         void BinaryLogicOperation(Func<string, string, bool> operation) | ||||
|         { | ||||
|             var op2 = _stack.Pop(); | ||||
|             var op1 = _stack.Pop(); | ||||
| 
 | ||||
|             var res = operation(op1.ToString(), op2.ToString()) ? "1" : "0"; | ||||
| 
 | ||||
|             _stack.Push(new ExpressionToken(res)); | ||||
|         } | ||||
|         void BinaryNumberOperation(Func<decimal, decimal, decimal> operation) | ||||
|         { | ||||
|             var op2 = _stack.Pop().ToDecimal(_context); | ||||
|             var op1 = _stack.Pop().ToDecimal(_context); | ||||
| 
 | ||||
|             var res = operation(op1, op2); | ||||
| 
 | ||||
|             _stack.Push(new ExpressionToken(res.ToString(_context.FormatCulture))); | ||||
|         } | ||||
| 
 | ||||
|         #endregion | ||||
|     } | ||||
| } | ||||
							
								
								
									
										221
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/ExpressionToken.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/ExpressionToken.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,221 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Globalization; | ||||
| using Soukoku.ExpressionParser.Parsing; | ||||
| 
 | ||||
| namespace Soukoku.ExpressionParser | ||||
| { | ||||
|     /// <summary> | ||||
|     /// A token split from the initial text input. | ||||
|     /// </summary> | ||||
|     public class ExpressionToken | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Canonical true value. Actual value is numerical "1". | ||||
|         /// </summary> | ||||
|         public static readonly ExpressionToken True = new ExpressionToken("1"); | ||||
|         /// <summary> | ||||
|         /// Canonical false value. Actual value is numerical "0". | ||||
|         /// </summary> | ||||
|         public static readonly ExpressionToken False = new ExpressionToken("0"); | ||||
| 
 | ||||
|         internal static readonly NumberStyles NumberParseStyle = NumberStyles.Integer | NumberStyles.AllowDecimalPoint | NumberStyles.AllowCurrencySymbol | NumberStyles.Number; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="ExpressionToken"/> class. | ||||
|         /// </summary> | ||||
|         public ExpressionToken() { } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Initializes a new frozen instance of the <see cref="ExpressionToken"/> class | ||||
|         /// with the specified value. | ||||
|         /// </summary> | ||||
|         /// <param name="value">The value.</param> | ||||
|         public ExpressionToken(string value) | ||||
|         { | ||||
|             _type = ExpressionTokenType.Value; | ||||
|             _value = value; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         RawToken _rawToken; // the raw token that makes this token | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the raw token that made this list. | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         public RawToken RawToken { get { return _rawToken; } } | ||||
| 
 | ||||
|         const string FrozenErrorMsg = "Cannot modify frozen token."; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Appends the specified token to this expression. | ||||
|         /// </summary> | ||||
|         /// <param name="token">The token.</param> | ||||
|         /// <exception cref="System.InvalidOperationException"></exception> | ||||
|         public void Append(RawToken token) | ||||
|         { | ||||
|             if (IsFrozen) { throw new InvalidOperationException(FrozenErrorMsg); } | ||||
| 
 | ||||
|             if (_rawToken == null) { _rawToken = token; } | ||||
|             else { _rawToken.Append(token); } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets a value indicating whether this instance is frozen from append. | ||||
|         /// </summary> | ||||
|         /// <value> | ||||
|         ///   <c>true</c> if this instance is frozen; otherwise, <c>false</c>. | ||||
|         /// </value> | ||||
|         public bool IsFrozen { get { return _value != null; } } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Freezes this instance from being appended. | ||||
|         /// </summary> | ||||
|         /// <exception cref="System.InvalidOperationException"></exception> | ||||
|         public void Freeze() | ||||
|         { | ||||
|             if (IsFrozen) { throw new InvalidOperationException(FrozenErrorMsg); } | ||||
| 
 | ||||
|             _value = _rawToken?.ToString(); | ||||
|         } | ||||
| 
 | ||||
|         private ExpressionTokenType _type; | ||||
|         /// <summary> | ||||
|         /// Gets or sets the type of the token. | ||||
|         /// </summary> | ||||
|         /// <value> | ||||
|         /// The type of the token. | ||||
|         /// </value> | ||||
|         public ExpressionTokenType TokenType | ||||
|         { | ||||
|             get { return _type; } | ||||
|             set { if (_value == null) { _type = value; } } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets or sets the type of the operator. This is only used if the <see cref="TokenType"/> | ||||
|         /// is <see cref="ExpressionTokenType.Operator"/>. | ||||
|         /// </summary> | ||||
|         /// <value> | ||||
|         /// The type of the operator. | ||||
|         /// </value> | ||||
|         public OperatorType OperatorType { get; set; } | ||||
| 
 | ||||
|         string _value; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the raw token value. | ||||
|         /// </summary> | ||||
|         /// <value> | ||||
|         /// The value. | ||||
|         /// </value> | ||||
|         public string Value { get { return _value ?? _rawToken?.ToString(); } } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the resolved field value and type hint if token is a field. | ||||
|         /// </summary> | ||||
|         public (object Value, ValueTypeHint TypeHint) FieldValue { get; internal set; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Returns a <see cref="System.String" /> that represents this instance. | ||||
|         /// </summary> | ||||
|         /// <returns> | ||||
|         /// A <see cref="System.String" /> that represents this instance. | ||||
|         /// </returns> | ||||
|         public override string ToString() | ||||
|         { | ||||
|             switch (TokenType) | ||||
|             { | ||||
|                 case ExpressionTokenType.Field: | ||||
|                     return FieldValue.Value?.ToString() ?? ""; | ||||
|                 default: | ||||
|                     return Value ?? ""; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         #region conversion routines | ||||
| 
 | ||||
|         ///// <summary> | ||||
|         ///// Check if the value is considered numeric. | ||||
|         ///// </summary> | ||||
|         ///// <returns></returns> | ||||
|         //public bool IsNumeric() | ||||
|         //{ | ||||
|         //    if (TokenType == ExpressionTokenType.Field && FieldValue.TypeHint == ValueTypeHint.Text) return false; | ||||
| 
 | ||||
|         //    return decimal.TryParse(Value, NumberParseStyle, CultureInfo.InvariantCulture, out decimal dummy); | ||||
|         //} | ||||
| 
 | ||||
|         ///// <summary> | ||||
|         ///// Check if the value is considered true. | ||||
|         ///// </summary> | ||||
|         ///// <returns></returns> | ||||
|         //public bool IsTrue(string value) | ||||
|         //{ | ||||
|         //    if (TokenType == ExpressionTokenType.Field && FieldValue.TypeHint == ValueTypeHint.Text) return false; | ||||
| 
 | ||||
|         //    return string.Equals("true", Value, StringComparison.OrdinalIgnoreCase) || value == "1"; | ||||
|         //} | ||||
| 
 | ||||
|         ///// <summary> | ||||
|         ///// Check if the value is considered false. | ||||
|         ///// </summary> | ||||
|         ///// <returns></returns> | ||||
|         //public bool IsFalse(string value) | ||||
|         //{ | ||||
|         //    if (TokenType == ExpressionTokenType.Field && FieldValue.TypeHint == ValueTypeHint.Text) return false; | ||||
| 
 | ||||
|         //    return string.Equals("false", Value, StringComparison.OrdinalIgnoreCase) || value == "0"; | ||||
|         //} | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Converts to the double value. | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="System.NotSupportedException"></exception> | ||||
|         /// <exception cref="FormatException"></exception> | ||||
|         public double ToDouble(EvaluationContext context) | ||||
|         { | ||||
|             switch (TokenType) | ||||
|             { | ||||
|                 case ExpressionTokenType.Value: | ||||
|                 case ExpressionTokenType.SingleQuoted: | ||||
|                 case ExpressionTokenType.DoubleQuoted: | ||||
|                     return double.Parse(Value, NumberParseStyle, context.FormatCulture); | ||||
|                 case ExpressionTokenType.Field: | ||||
|                     return double.Parse(FieldValue.Value?.ToString(), NumberParseStyle, context.FormatCulture); | ||||
|                 default: | ||||
|                     throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Cannot convert {0}({1}) to a numeric value.", TokenType, Value)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Converts to the decimal value. | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="System.NotSupportedException"></exception> | ||||
|         /// <exception cref="FormatException"></exception> | ||||
|         public decimal ToDecimal(EvaluationContext context) | ||||
|         { | ||||
|             switch (TokenType) | ||||
|             { | ||||
|                 case ExpressionTokenType.Value: | ||||
|                 case ExpressionTokenType.SingleQuoted: | ||||
|                 case ExpressionTokenType.DoubleQuoted: | ||||
|                     return decimal.Parse(Value, NumberParseStyle, context.FormatCulture); | ||||
|                 case ExpressionTokenType.Field: | ||||
|                     return decimal.Parse(FieldValue.Value?.ToString(), NumberParseStyle, context.FormatCulture); | ||||
|                 default: | ||||
|                     throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Cannot convert {0}({1}) to a numeric value.", TokenType, Value)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #endregion | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,54 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace Soukoku.ExpressionParser | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Indicates the expression token type. | ||||
|     /// </summary> | ||||
|     public enum ExpressionTokenType | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Invalid token type. | ||||
|         /// </summary> | ||||
|         None, | ||||
|         /// <summary> | ||||
|         /// The token is an operator. | ||||
|         /// </summary> | ||||
|         Operator, | ||||
|         /// <summary> | ||||
|         /// The token is an open parenthesis. | ||||
|         /// </summary> | ||||
|         OpenParenthesis, | ||||
|         /// <summary> | ||||
|         /// The token is a close parenthesis. | ||||
|         /// </summary> | ||||
|         CloseParenthesis, | ||||
|         /// <summary> | ||||
|         /// The token is a function. | ||||
|         /// </summary> | ||||
|         Function, | ||||
|         /// <summary> | ||||
|         /// The token is a comma. | ||||
|         /// </summary> | ||||
|         Comma, | ||||
|         /// <summary> | ||||
|         /// The token is a field reference. | ||||
|         /// </summary> | ||||
|         Field, | ||||
|         /// <summary> | ||||
|         /// The token is from single quoted value. | ||||
|         /// </summary> | ||||
|         SingleQuoted, | ||||
|         /// <summary> | ||||
|         /// The token is from double quoted value. | ||||
|         /// </summary> | ||||
|         DoubleQuoted, | ||||
|         /// <summary> | ||||
|         /// The token is a yet-to-be-parsed value. | ||||
|         /// </summary> | ||||
|         Value, | ||||
|     } | ||||
| } | ||||
							
								
								
									
										44
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/FunctionRoutine.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/FunctionRoutine.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace Soukoku.ExpressionParser | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Defines a basic function routine. | ||||
|     /// </summary> | ||||
|     public class FunctionRoutine | ||||
|     { | ||||
|         Func<EvaluationContext, ExpressionToken[], ExpressionToken> _routine; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="FunctionRoutine"/> class. | ||||
|         /// </summary> | ||||
|         /// <param name="argCount">The argument count.</param> | ||||
|         /// <param name="routine">The routine.</param> | ||||
|         /// <exception cref="System.ArgumentNullException">routine</exception> | ||||
|         public FunctionRoutine(int argCount, Func<EvaluationContext, ExpressionToken[], ExpressionToken> routine) | ||||
|         { | ||||
|             if (routine == null) { throw new ArgumentNullException("routine"); } | ||||
|             ArgumentCount = argCount; | ||||
|             _routine = routine; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the expected argument count. | ||||
|         /// </summary> | ||||
|         /// <value> | ||||
|         /// The argument count. | ||||
|         /// </value> | ||||
|         public int ArgumentCount { get; private set; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Evaluates using the function routine. | ||||
|         /// </summary> | ||||
|         /// <param name="context">The context.</param> | ||||
|         /// <param name="args">The arguments.</param> | ||||
|         /// <returns></returns> | ||||
|         public ExpressionToken Evaluate(EvaluationContext context, ExpressionToken[] args) { return _routine(context, args); } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,16 @@ | |||
| namespace Soukoku.ExpressionParser.Parsing | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Interface for something that can tokenize an expression. | ||||
|     /// </summary> | ||||
|     public interface IExpressionTokenizer | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Splits the specified input expression into a list of <see cref="ExpressionToken" /> values. | ||||
|         /// </summary> | ||||
|         /// <param name="input">The input.</param> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="System.NotSupportedException"></exception> | ||||
|         ExpressionToken[] Tokenize(string input); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,148 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Globalization; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace Soukoku.ExpressionParser.Parsing | ||||
| { | ||||
|     /// <summary> | ||||
|     /// A tokenizer that parses an input expression string in infix notation into tokens without white spaces | ||||
|     /// in the orders of postfix expressions. | ||||
|     /// </summary> | ||||
|     public class InfixToPostfixTokenizer : IExpressionTokenizer | ||||
|     { | ||||
|         const string UnbalancedParenMsg = "Unbalanced parenthesis in expression."; | ||||
| 
 | ||||
|         List<ExpressionToken> _output; | ||||
|         Stack<ExpressionToken> _stack; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Splits the specified input into a list of <see cref="ExpressionToken" /> values | ||||
|         /// in postfix order. | ||||
|         /// </summary> | ||||
|         /// <param name="input">The input.</param> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="System.NotSupportedException"></exception> | ||||
|         public ExpressionToken[] Tokenize(string input) | ||||
|         { | ||||
|             var infixTokens = new InfixTokenizer().Tokenize(input); | ||||
|             _output = new List<ExpressionToken>(); | ||||
|             _stack = new Stack<ExpressionToken>(); | ||||
| 
 | ||||
|             // this is the shunting-yard algorithm | ||||
|             // https://en.wikipedia.org/wiki/Shunting-yard_algorithm | ||||
| 
 | ||||
|             foreach (var inToken in infixTokens) | ||||
|             { | ||||
|                 switch (inToken.TokenType) | ||||
|                 { | ||||
|                     case ExpressionTokenType.Value: | ||||
|                     case ExpressionTokenType.DoubleQuoted: | ||||
|                     case ExpressionTokenType.SingleQuoted: | ||||
|                     case ExpressionTokenType.Field: | ||||
|                         _output.Add(inToken); | ||||
|                         break; | ||||
|                     case ExpressionTokenType.Function: | ||||
|                         _stack.Push(inToken); | ||||
|                         break; | ||||
|                     case ExpressionTokenType.Comma: | ||||
|                         HandleComma(); | ||||
|                         break; | ||||
|                     case ExpressionTokenType.Operator: | ||||
|                         HandleOperatorToken(inToken); | ||||
|                         break; | ||||
|                     case ExpressionTokenType.OpenParenthesis: | ||||
|                         _stack.Push(inToken); | ||||
|                         break; | ||||
|                     case ExpressionTokenType.CloseParenthesis: | ||||
|                         HandleCloseParenthesis(); | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             while (_stack.Count > 0) | ||||
|             { | ||||
|                 var op = _stack.Pop(); | ||||
|                 if (op.TokenType == ExpressionTokenType.OpenParenthesis) | ||||
|                 { | ||||
|                     throw new NotSupportedException(UnbalancedParenMsg); | ||||
|                 } | ||||
|                 _output.Add(op); | ||||
|             } | ||||
| 
 | ||||
|             return _output.ToArray(); | ||||
|         } | ||||
| 
 | ||||
|         private void HandleComma() | ||||
|         { | ||||
|             bool closed = false; | ||||
|             while (_stack.Count > 1) | ||||
|             { | ||||
|                 var peek = _stack.Peek(); | ||||
|                 if (peek.TokenType == ExpressionTokenType.OpenParenthesis) | ||||
|                 { | ||||
|                     closed = true; | ||||
|                     break; | ||||
|                 } | ||||
|                 _output.Add(_stack.Pop()); | ||||
|             } | ||||
| 
 | ||||
|             if (!closed) | ||||
|             { | ||||
|                 throw new NotSupportedException(UnbalancedParenMsg); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private void HandleOperatorToken(ExpressionToken inToken) | ||||
|         { | ||||
|             while (_stack.Count > 0) | ||||
|             { | ||||
|                 var op2 = _stack.Peek(); | ||||
|                 if (op2.TokenType == ExpressionTokenType.Operator) | ||||
|                 { | ||||
|                     var op1Prec = KnownOperators.GetPrecedence(inToken.OperatorType); | ||||
|                     var op2Prec = KnownOperators.GetPrecedence(op2.OperatorType); | ||||
|                     var op1IsLeft = KnownOperators.IsLeftAssociative(inToken.OperatorType); | ||||
| 
 | ||||
|                     if ((op1IsLeft && op1Prec <= op2Prec) || | ||||
|                         (!op1IsLeft && op1Prec < op2Prec)) | ||||
|                     { | ||||
|                         _output.Add(_stack.Pop()); | ||||
|                         continue; | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             _stack.Push(inToken); | ||||
|         } | ||||
| 
 | ||||
|         private void HandleCloseParenthesis() | ||||
|         { | ||||
|             bool closed = false; | ||||
|             while (_stack.Count > 0) | ||||
|             { | ||||
|                 var pop = _stack.Pop(); | ||||
|                 if (pop.TokenType == ExpressionTokenType.OpenParenthesis) | ||||
|                 { | ||||
|                     closed = true; | ||||
|                     break; | ||||
|                 } | ||||
|                 _output.Add(pop); | ||||
|             } | ||||
| 
 | ||||
|             if (!closed) | ||||
|             { | ||||
|                 throw new NotSupportedException(UnbalancedParenMsg); | ||||
|             } | ||||
|             else if (_stack.Count > 0) | ||||
|             { | ||||
|                 var next = _stack.Peek(); | ||||
|                 if (next != null && next.TokenType == ExpressionTokenType.Function) | ||||
|                 { | ||||
|                     _output.Add(_stack.Pop()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										206
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Parsing/InfixTokenizer.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Parsing/InfixTokenizer.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,206 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Globalization; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace Soukoku.ExpressionParser.Parsing | ||||
| { | ||||
|     /// <summary> | ||||
|     /// A tokenizer that parses an input expression string in infix notation into tokens without white spaces. | ||||
|     /// </summary> | ||||
|     public class InfixTokenizer : IExpressionTokenizer | ||||
|     { | ||||
|         List<ExpressionToken> _currentTokens; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Splits the specified input into a list of <see cref="ExpressionToken" /> values. | ||||
|         /// </summary> | ||||
|         /// <param name="input">The input.</param> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="System.NotSupportedException"></exception> | ||||
|         public ExpressionToken[] Tokenize(string input) | ||||
|         { | ||||
|             _currentTokens = new List<ExpressionToken>(); | ||||
|             ExpressionToken lastExpToken = null; | ||||
| 
 | ||||
|             var reader = new ListReader<RawToken>(new RawTokenizer().Tokenize(input)); | ||||
| 
 | ||||
|             while (!reader.IsEnd) | ||||
|             { | ||||
|                 var curRawToken = reader.Read(); | ||||
|                 switch (curRawToken.TokenType) | ||||
|                 { | ||||
|                     case RawTokenType.WhiteSpace: | ||||
|                         // generially ends previous token outside other special scopes | ||||
|                         lastExpToken = null; | ||||
|                         break; | ||||
|                     case RawTokenType.Literal: | ||||
|                         if (lastExpToken == null || lastExpToken.TokenType != ExpressionTokenType.Value) | ||||
|                         { | ||||
|                             lastExpToken = new ExpressionToken { TokenType = ExpressionTokenType.Value }; | ||||
|                             _currentTokens.Add(lastExpToken); | ||||
|                         } | ||||
|                         lastExpToken.Append(curRawToken); | ||||
|                         break; | ||||
|                     case RawTokenType.Symbol: | ||||
|                         // first do operator match by checking the prev op | ||||
|                         // and see if combined with current token would still match a known operator | ||||
|                         if (KnownOperators.IsKnown(curRawToken.Value)) | ||||
|                         { | ||||
|                             if (lastExpToken != null && lastExpToken.TokenType == ExpressionTokenType.Operator) | ||||
|                             { | ||||
|                                 var testOpValue = lastExpToken.Value + curRawToken.Value; | ||||
|                                 if (KnownOperators.IsKnown(testOpValue)) | ||||
|                                 { | ||||
|                                     // just append it | ||||
|                                     lastExpToken.Append(curRawToken); | ||||
|                                     continue; | ||||
|                                 } | ||||
|                             } | ||||
|                             // start new one | ||||
|                             lastExpToken = new ExpressionToken { TokenType = ExpressionTokenType.Operator }; | ||||
|                             _currentTokens.Add(lastExpToken); | ||||
|                             lastExpToken.Append(curRawToken); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             lastExpToken = HandleNonOperatorSymbolToken(reader, lastExpToken, curRawToken); | ||||
|                         } | ||||
|                         break; | ||||
|                     default: | ||||
|                         // should never happen | ||||
|                         throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Unsupported token type {0} at position {1}.", curRawToken.TokenType, curRawToken.Position)); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             MassageTokens(_currentTokens); | ||||
| 
 | ||||
|             return _currentTokens.ToArray(); | ||||
|         } | ||||
| 
 | ||||
|         ExpressionToken HandleNonOperatorSymbolToken(ListReader<RawToken> reader, ExpressionToken lastExpToken, RawToken curRawToken) | ||||
|         { | ||||
|             switch (curRawToken.Value) | ||||
|             { | ||||
|                 case ",": | ||||
|                     lastExpToken = new ExpressionToken { TokenType = ExpressionTokenType.Comma }; | ||||
|                     _currentTokens.Add(lastExpToken); | ||||
|                     lastExpToken.Append(curRawToken); | ||||
|                     break; | ||||
|                 case "(": | ||||
|                     // if last one is string make it a function | ||||
|                     if (lastExpToken != null && lastExpToken.TokenType == ExpressionTokenType.Value) | ||||
|                     { | ||||
|                         lastExpToken.TokenType = ExpressionTokenType.Function; | ||||
|                     } | ||||
| 
 | ||||
|                     lastExpToken = new ExpressionToken { TokenType = ExpressionTokenType.OpenParenthesis }; | ||||
|                     _currentTokens.Add(lastExpToken); | ||||
|                     lastExpToken.Append(curRawToken); | ||||
|                     break; | ||||
|                 case ")": | ||||
|                     lastExpToken = new ExpressionToken { TokenType = ExpressionTokenType.CloseParenthesis }; | ||||
|                     _currentTokens.Add(lastExpToken); | ||||
|                     lastExpToken.Append(curRawToken); | ||||
|                     break; | ||||
|                 case "{": | ||||
|                     // read until end of } | ||||
|                     lastExpToken = ReadToLiteralAs(reader, "}", ExpressionTokenType.Field); | ||||
|                     break; | ||||
|                 case "\"": | ||||
|                     // read until end of " | ||||
|                     lastExpToken = ReadToLiteralAs(reader, "\"", ExpressionTokenType.DoubleQuoted); | ||||
|                     break; | ||||
|                 case "'": | ||||
|                     // read until end of ' | ||||
|                     lastExpToken = ReadToLiteralAs(reader, "'", ExpressionTokenType.SingleQuoted); | ||||
|                     break; | ||||
|             } | ||||
| 
 | ||||
|             return lastExpToken; | ||||
|         } | ||||
| 
 | ||||
|         ExpressionToken ReadToLiteralAs(ListReader<RawToken> reader, string literalValue, ExpressionTokenType tokenType) | ||||
|         { | ||||
|             ExpressionToken lastExpToken = new ExpressionToken { TokenType = tokenType }; | ||||
|             _currentTokens.Add(lastExpToken); | ||||
|             while (!reader.IsEnd) | ||||
|             { | ||||
|                 var next = reader.Read(); | ||||
|                 if (next.TokenType == RawTokenType.Symbol && next.Value == literalValue) | ||||
|                 { | ||||
|                     break; | ||||
|                 } | ||||
|                 lastExpToken.Append(next); | ||||
|             } | ||||
| 
 | ||||
|             return lastExpToken; | ||||
|         } | ||||
| 
 | ||||
|         static void MassageTokens(List<ExpressionToken> tokens) | ||||
|         { | ||||
|             // do final token parsing based on contexts and cleanup | ||||
| 
 | ||||
|             var reader = new ListReader<ExpressionToken>(tokens); | ||||
|             while (!reader.IsEnd) | ||||
|             { | ||||
|                 var tk = reader.Read(); | ||||
| 
 | ||||
|                 if (tk.TokenType == ExpressionTokenType.Operator) | ||||
|                 { | ||||
|                     // special detection for operators depending on where it is :( | ||||
|                     DetermineOperatorType(reader, tk); | ||||
|                 } | ||||
| 
 | ||||
|                 tk.Freeze(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private static void DetermineOperatorType(ListReader<ExpressionToken> reader, ExpressionToken tk) | ||||
|         { | ||||
|             tk.OperatorType = KnownOperators.TryMap(tk.Value); | ||||
|             switch (tk.OperatorType) | ||||
|             { | ||||
|                 case OperatorType.PreDecrement: | ||||
|                 case OperatorType.PreIncrement: | ||||
|                     // detect if it's really post ++ -- versions  | ||||
|                     var prev = reader.Position > 1 ? reader.Peek(-2) : null; | ||||
|                     if (prev != null && prev.TokenType == ExpressionTokenType.Value) | ||||
|                     { | ||||
|                         if (tk.OperatorType == OperatorType.PreIncrement) | ||||
|                         { | ||||
|                             tk.OperatorType = OperatorType.PostIncrement; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             tk.OperatorType = OperatorType.PostDecrement; | ||||
|                         } | ||||
|                     } | ||||
|                     break; | ||||
|                 case OperatorType.Addition: | ||||
|                 case OperatorType.Subtraction: | ||||
|                     // detect if unary + - | ||||
|                     prev = reader.Position > 1 ? reader.Peek(-2) : null; | ||||
|                     if (prev == null || | ||||
|                         (prev.TokenType == ExpressionTokenType.Operator && | ||||
|                         prev.OperatorType != OperatorType.PostDecrement && | ||||
|                         prev.OperatorType != OperatorType.PostIncrement)) | ||||
|                     { | ||||
|                         if (tk.OperatorType == OperatorType.Addition) | ||||
|                         { | ||||
|                             tk.OperatorType = OperatorType.UnaryPlus; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             tk.OperatorType = OperatorType.UnaryMinus; | ||||
|                         } | ||||
|                     } | ||||
|                     break; | ||||
|                 case OperatorType.None: | ||||
|                     throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Operator {0} is not supported.", tk.Value)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										146
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Parsing/KnownOperators.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Parsing/KnownOperators.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,146 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace Soukoku.ExpressionParser.Parsing | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Contains recognized operator info. | ||||
|     /// </summary> | ||||
|     public static class KnownOperators | ||||
|     { | ||||
|         static readonly Dictionary<string, OperatorType> DefaultMap = new Dictionary<string, OperatorType> | ||||
|         { | ||||
|             // double char | ||||
|             {"++", OperatorType.PreIncrement }, | ||||
|             {"--", OperatorType.PreDecrement }, | ||||
|             {"+=", OperatorType.AdditionAssignment }, | ||||
|             {"-=", OperatorType.SubtractionAssignment }, | ||||
|             {"*=", OperatorType.MultiplicationAssignment }, | ||||
|             {"/=", OperatorType.DivisionAssignment }, | ||||
|             {"%=", OperatorType.ModulusAssignment }, | ||||
|             {"==", OperatorType.Equal }, | ||||
|             {"!=", OperatorType.NotEqual}, | ||||
|             {"<=", OperatorType.LessThanOrEqual }, | ||||
|             {">=", OperatorType.GreaterThanOrEqual }, | ||||
|             {"&&", OperatorType.LogicalAnd }, | ||||
|             {"||", OperatorType.LogicalOr }, | ||||
| 
 | ||||
|             // single char | ||||
|             {"+", OperatorType.Addition }, | ||||
|             {"-", OperatorType.Subtraction }, | ||||
|             {"*", OperatorType.Multiplication }, | ||||
|             {"/", OperatorType.Division }, | ||||
|             {"=", OperatorType.Assignment }, | ||||
|             {"%", OperatorType.Modulus }, | ||||
|             //"^", | ||||
|             {"<", OperatorType.LessThan }, | ||||
|             {">", OperatorType.GreaterThan }, | ||||
|             //"~", | ||||
|             {"&", OperatorType.BitwiseAnd }, | ||||
|             {"|", OperatorType.BitwiseOr }, | ||||
|             {"!", OperatorType.LogicalNegation }, | ||||
|         }; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Determines whether the specified operator value is recognized. | ||||
|         /// </summary> | ||||
|         /// <param name="operatorValue">The operator value.</param> | ||||
|         /// <returns></returns> | ||||
|         public static bool IsKnown(string operatorValue) | ||||
|         { | ||||
|             return DefaultMap.ContainsKey(operatorValue); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Try to get the enum version of the operator string value. | ||||
|         /// </summary> | ||||
|         /// <param name="operatorValue">The operator value.</param> | ||||
|         /// <returns></returns> | ||||
|         public static OperatorType TryMap(string operatorValue) | ||||
|         { | ||||
|             if (DefaultMap.ContainsKey(operatorValue)) | ||||
|             { | ||||
|                 return DefaultMap[operatorValue]; | ||||
|             } | ||||
|             return OperatorType.None; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the precedence of an operator. | ||||
|         /// </summary> | ||||
|         /// <param name="type">The type.</param> | ||||
|         /// <returns></returns> | ||||
|         public static int GetPrecedence(OperatorType type) | ||||
|         { | ||||
|             switch (type) | ||||
|             { | ||||
|                 case OperatorType.PostDecrement: | ||||
|                 case OperatorType.PostIncrement: | ||||
|                     return 100; | ||||
|                 case OperatorType.PreDecrement: | ||||
|                 case OperatorType.PreIncrement: | ||||
|                 case OperatorType.UnaryMinus: | ||||
|                 case OperatorType.UnaryPlus: | ||||
|                 case OperatorType.LogicalNegation: | ||||
|                     return 90; | ||||
|                 case OperatorType.Multiplication: | ||||
|                 case OperatorType.Division: | ||||
|                 case OperatorType.Modulus: | ||||
|                     return 85; | ||||
|                 case OperatorType.Addition: | ||||
|                 case OperatorType.Subtraction: | ||||
|                     return 80; | ||||
|                 case OperatorType.LessThan: | ||||
|                 case OperatorType.LessThanOrEqual: | ||||
|                 case OperatorType.GreaterThan: | ||||
|                 case OperatorType.GreaterThanOrEqual: | ||||
|                     return 75; | ||||
|                 case OperatorType.Equal: | ||||
|                 case OperatorType.NotEqual: | ||||
|                     return 70; | ||||
|                 case OperatorType.BitwiseAnd: | ||||
|                 case OperatorType.BitwiseOr: | ||||
|                     return 65; | ||||
|                 case OperatorType.LogicalAnd: | ||||
|                 case OperatorType.LogicalOr: | ||||
|                     return 60; | ||||
|                 case OperatorType.Assignment: | ||||
|                 case OperatorType.AdditionAssignment: | ||||
|                 case OperatorType.DivisionAssignment: | ||||
|                 case OperatorType.ModulusAssignment: | ||||
|                 case OperatorType.MultiplicationAssignment: | ||||
|                 case OperatorType.SubtractionAssignment: | ||||
|                     return 20; | ||||
|             } | ||||
|             return 0; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Determines whether the operator is left-to-right associative (true) or right-to-left (false). | ||||
|         /// </summary> | ||||
|         /// <param name="type">The type.</param> | ||||
|         /// <returns></returns> | ||||
|         public static bool IsLeftAssociative(OperatorType type) | ||||
|         { | ||||
|             switch (type) | ||||
|             { | ||||
|                 case OperatorType.PreDecrement: | ||||
|                 case OperatorType.PreIncrement: | ||||
|                 case OperatorType.UnaryMinus: | ||||
|                 case OperatorType.UnaryPlus: | ||||
|                 case OperatorType.LogicalNegation: | ||||
|                 case OperatorType.Assignment: | ||||
|                 case OperatorType.AdditionAssignment: | ||||
|                 case OperatorType.DivisionAssignment: | ||||
|                 case OperatorType.ModulusAssignment: | ||||
|                 case OperatorType.MultiplicationAssignment: | ||||
|                 case OperatorType.SubtractionAssignment: | ||||
|                     return false; | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										89
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Parsing/ListReader.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Parsing/ListReader.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,89 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace Soukoku.ExpressionParser.Parsing | ||||
| { | ||||
|     /// <summary> | ||||
|     /// A simple reader for an IList.  | ||||
|     /// </summary> | ||||
|     /// <typeparam name="TItem">The type of the item in the list.</typeparam> | ||||
|     public class ListReader<TItem> | ||||
|     { | ||||
|         IList<TItem> _list; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="ListReader{TItem}"/> class. | ||||
|         /// </summary> | ||||
|         /// <param name="list">The list to read.</param> | ||||
|         /// <exception cref="System.ArgumentNullException">list</exception> | ||||
|         public ListReader(IList<TItem> list) | ||||
|         { | ||||
|             if (list == null) { throw new ArgumentNullException("list"); } | ||||
| 
 | ||||
|             _list = list; | ||||
|         } | ||||
| 
 | ||||
|         private int _position; | ||||
|         /// <summary> | ||||
|         /// Gets or sets the position of the reader. This is the 0-based index. | ||||
|         /// </summary> | ||||
|         /// <value> | ||||
|         /// The position. | ||||
|         /// </value> | ||||
|         /// <exception cref="System.ArgumentOutOfRangeException"></exception> | ||||
|         public int Position | ||||
|         { | ||||
|             get { return _position; } | ||||
|             set | ||||
|             { | ||||
|                 if (value < 0 || value > _list.Count) | ||||
|                 { | ||||
|                     throw new ArgumentOutOfRangeException("value"); | ||||
|                 } | ||||
|                 _position = value; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets a value indicating whether the reader has reached the end of list. | ||||
|         /// </summary> | ||||
|         /// <value> | ||||
|         ///   <c>true</c> if this instance is eol; otherwise, <c>false</c>. | ||||
|         /// </value> | ||||
|         public bool IsEnd { get { return _position >= _list.Count; } } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Reads the current item in the list and moves the <see cref="Position" /> forward. | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="System.ArgumentOutOfRangeException"></exception> | ||||
|         public TItem Read() | ||||
|         { | ||||
|             return _list[Position++]; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Peeks the current item in the list without moving the <see cref="Position"/>. | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="System.ArgumentOutOfRangeException"></exception> | ||||
|         public TItem Peek() | ||||
|         { | ||||
|             return Peek(0); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Peeks the item in the list without moving the <see cref="Position" />. | ||||
|         /// </summary> | ||||
|         /// <param name="offset">The offset from current position.</param> | ||||
|         /// <returns></returns> | ||||
|         /// <exception cref="System.ArgumentOutOfRangeException"></exception> | ||||
|         public TItem Peek(int offset) | ||||
|         { | ||||
|             // let list throw the exception. | ||||
|             return _list[Position + offset]; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										131
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Parsing/OperatorType.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Parsing/OperatorType.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,131 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace Soukoku.ExpressionParser.Parsing | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Indicates the recognized operator types. | ||||
|     /// </summary> | ||||
|     public enum OperatorType | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Unspecified default value. | ||||
|         /// </summary> | ||||
|         None, | ||||
|         /// <summary> | ||||
|         /// ++ after a value. | ||||
|         /// </summary> | ||||
|         PostIncrement, | ||||
|         /// <summary> | ||||
|         /// -- after a value. | ||||
|         /// </summary> | ||||
|         PostDecrement, | ||||
|         /// <summary> | ||||
|         /// ++ before a value. | ||||
|         /// </summary> | ||||
|         PreIncrement, | ||||
|         /// <summary> | ||||
|         /// -- before a value. | ||||
|         /// </summary> | ||||
|         PreDecrement, | ||||
|         /// <summary> | ||||
|         /// + before a value. | ||||
|         /// </summary> | ||||
|         UnaryPlus, | ||||
|         /// <summary> | ||||
|         /// - before a value. | ||||
|         /// </summary> | ||||
|         UnaryMinus, | ||||
|         /// <summary> | ||||
|         /// ! before a value. | ||||
|         /// </summary> | ||||
|         LogicalNegation, | ||||
|         /// <summary> | ||||
|         /// * between values. | ||||
|         /// </summary> | ||||
|         Multiplication, | ||||
|         /// <summary> | ||||
|         /// / between values. | ||||
|         /// </summary> | ||||
|         Division, | ||||
|         /// <summary> | ||||
|         /// % between values. | ||||
|         /// </summary> | ||||
|         Modulus, | ||||
|         /// <summary> | ||||
|         /// + between values. | ||||
|         /// </summary> | ||||
|         Addition, | ||||
|         /// <summary> | ||||
|         /// - between values. | ||||
|         /// </summary> | ||||
|         Subtraction, | ||||
|         /// <summary> | ||||
|         /// < between values. | ||||
|         /// </summary> | ||||
|         LessThan, | ||||
|         /// <summary> | ||||
|         /// <= between values. | ||||
|         /// </summary> | ||||
|         LessThanOrEqual, | ||||
|         /// <summary> | ||||
|         /// > between values. | ||||
|         /// </summary> | ||||
|         GreaterThan, | ||||
|         /// <summary> | ||||
|         /// >= between values. | ||||
|         /// </summary> | ||||
|         GreaterThanOrEqual, | ||||
|         /// <summary> | ||||
|         /// == between values. | ||||
|         /// </summary> | ||||
|         Equal, | ||||
|         /// <summary> | ||||
|         /// != between values. | ||||
|         /// </summary> | ||||
|         NotEqual, | ||||
|         /// <summary> | ||||
|         /// & between values. | ||||
|         /// </summary> | ||||
|         BitwiseAnd, | ||||
|         /// <summary> | ||||
|         /// | between values. | ||||
|         /// </summary> | ||||
|         BitwiseOr, | ||||
|         /// <summary> | ||||
|         /// && between values. | ||||
|         /// </summary> | ||||
|         LogicalAnd, | ||||
|         /// <summary> | ||||
|         /// || between values. | ||||
|         /// </summary> | ||||
|         LogicalOr, | ||||
|         /// <summary> | ||||
|         /// = between values. | ||||
|         /// </summary> | ||||
|         Assignment, | ||||
|         /// <summary> | ||||
|         /// += between values. | ||||
|         /// </summary> | ||||
|         AdditionAssignment, | ||||
|         /// <summary> | ||||
|         /// -= between values. | ||||
|         /// </summary> | ||||
|         SubtractionAssignment, | ||||
|         /// <summary> | ||||
|         /// *= between values. | ||||
|         /// </summary> | ||||
|         MultiplicationAssignment, | ||||
|         /// <summary> | ||||
|         /// /= between values. | ||||
|         /// </summary> | ||||
|         DivisionAssignment, | ||||
|         /// <summary> | ||||
|         /// %= between values. | ||||
|         /// </summary> | ||||
|         ModulusAssignment, | ||||
| 
 | ||||
|     } | ||||
| } | ||||
							
								
								
									
										95
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Parsing/RawToken.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Parsing/RawToken.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,95 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace Soukoku.ExpressionParser.Parsing | ||||
| { | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// A low-level token split from the initial text input. | ||||
|     /// </summary> | ||||
|     public class RawToken | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="RawToken"/> class. | ||||
|         /// </summary> | ||||
|         /// <param name="type">The type.</param> | ||||
|         /// <param name="position">The position.</param> | ||||
|         internal RawToken(RawTokenType type, int position) | ||||
|         { | ||||
|             TokenType = type; | ||||
|             Position = position; | ||||
|             ValueBuilder = new StringBuilder(); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the token type. | ||||
|         /// </summary> | ||||
|         /// <value> | ||||
|         /// The type. | ||||
|         /// </value> | ||||
|         public RawTokenType TokenType { get; private set; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the starting position of this token in the original input. | ||||
|         /// </summary> | ||||
|         /// <value> | ||||
|         /// The position. | ||||
|         /// </value> | ||||
|         public int Position { get; private set; } | ||||
| 
 | ||||
|         // TODO: test pef on using builder or using string directly | ||||
|         internal StringBuilder ValueBuilder { get; private set; } | ||||
| 
 | ||||
|         internal void Append(RawToken token) | ||||
|         { | ||||
|             if (token != null) | ||||
|             { | ||||
|                 ValueBuilder.Append(token.ValueBuilder); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the token value. | ||||
|         /// </summary> | ||||
|         /// <value> | ||||
|         /// The value. | ||||
|         /// </value> | ||||
|         public string Value { get { return ValueBuilder.ToString(); } } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Returns a <see cref="System.String" /> that represents this instance. | ||||
|         /// </summary> | ||||
|         /// <returns> | ||||
|         /// A <see cref="System.String" /> that represents this instance. | ||||
|         /// </returns> | ||||
|         public override string ToString() | ||||
|         { | ||||
|             return Value; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Indicates the low-level token type. | ||||
|     /// </summary> | ||||
|     public enum RawTokenType | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Invalid token type. | ||||
|         /// </summary> | ||||
|         None, | ||||
|         /// <summary> | ||||
|         /// Token is white space. | ||||
|         /// </summary> | ||||
|         WhiteSpace, | ||||
|         /// <summary> | ||||
|         /// Token is a symbol. | ||||
|         /// </summary> | ||||
|         Symbol, | ||||
|         /// <summary> | ||||
|         /// Token is not symbol or white space. | ||||
|         /// </summary> | ||||
|         Literal, | ||||
|     } | ||||
| } | ||||
							
								
								
									
										102
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Parsing/RawTokenizer.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/Parsing/RawTokenizer.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,102 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Text.RegularExpressions; | ||||
| 
 | ||||
| namespace Soukoku.ExpressionParser.Parsing | ||||
| { | ||||
|     /// <summary> | ||||
|     /// A low-level tokenizer that parses an input expression string into tokens. | ||||
|     /// </summary> | ||||
|     public class RawTokenizer | ||||
|     { | ||||
|         static readonly char[] DefaultSymbols = new[] | ||||
|             { | ||||
|                 '+', '-', '*', '/', '=', '%', '^', | ||||
|                 ',', '<', '>', '&', '|', '!', | ||||
|                 '(', ')', '{', '}', '[', ']', | ||||
|                 '"', '\'', '~' | ||||
|             }; | ||||
| 
 | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="RawTokenizer"/> class. | ||||
|         /// </summary> | ||||
|         public RawTokenizer() : this(null) { } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="RawTokenizer" /> class. | ||||
|         /// </summary> | ||||
|         /// <param name="symbols">The char values to count as symbols. If null the <see cref="DefaultSymbols"/> will be used.</param> | ||||
|         public RawTokenizer(params char[] symbols) | ||||
|         { | ||||
|             _symbols = symbols ?? DefaultSymbols; | ||||
|         } | ||||
| 
 | ||||
|         char[] _symbols; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the char values that count as symbols for this tokenizer. | ||||
|         /// </summary> | ||||
|         /// <value> | ||||
|         /// The symbols. | ||||
|         /// </value> | ||||
|         public char[] GetSymbols() { return (char[])_symbols.Clone(); } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Splits the specified input into a list of <see cref="RawToken"/> values using white space and symbols. | ||||
|         /// The tokens can be recombined to rebuild the original input exactly. | ||||
|         /// </summary> | ||||
|         /// <param name="input">The input.</param> | ||||
|         /// <returns></returns> | ||||
|         public RawToken[] Tokenize(string input) | ||||
|         { | ||||
|             var tokens = new List<RawToken>(); | ||||
| 
 | ||||
|             if (input != null) | ||||
|             { | ||||
|                 RawToken lastToken = null; | ||||
|                 for (int i = 0; i < input.Length; i++) | ||||
|                 { | ||||
|                     var ch = input[i]; | ||||
|                     if (char.IsWhiteSpace(ch)) | ||||
|                     { | ||||
|                         lastToken = NewTokenIfNecessary(tokens, lastToken, RawTokenType.WhiteSpace, i); | ||||
|                     } | ||||
|                     else if (_symbols.Contains(ch)) | ||||
|                     { | ||||
|                         lastToken = NewTokenIfNecessary(tokens, lastToken, RawTokenType.Symbol, i); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         lastToken = NewTokenIfNecessary(tokens, lastToken, RawTokenType.Literal, i); | ||||
|                     } | ||||
| 
 | ||||
|                     if (ch == '\\' && ++i < input.Length) | ||||
|                     { | ||||
|                         // assume escape and just append next char as-is | ||||
|                         var next = input[i]; | ||||
|                         lastToken.ValueBuilder.Append(next); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         lastToken.ValueBuilder.Append(ch); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return tokens.ToArray(); | ||||
|         } | ||||
| 
 | ||||
|         static RawToken NewTokenIfNecessary(List<RawToken> tokens, RawToken lastToken, RawTokenType curTokenType, int position) | ||||
|         { | ||||
|             if (lastToken == null || lastToken.TokenType != curTokenType || | ||||
|                 curTokenType == RawTokenType.Symbol) // for symbol always let it be by itself | ||||
|             { | ||||
|                 lastToken = new RawToken(curTokenType, position); | ||||
|                 tokens.Add(lastToken); | ||||
|             } | ||||
|             return lastToken; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										17
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/ValueTypeHint.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								DunGenPlus/DunGenPlus/ExpressionParser/ValueTypeHint.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| namespace Soukoku.ExpressionParser | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Used to indicate how to handle resolve field value. | ||||
|     /// </summary> | ||||
|     public enum ValueTypeHint | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Value is converted to suitable type for comparison purposes. | ||||
|         /// </summary> | ||||
|         Auto, | ||||
|         /// <summary> | ||||
|         /// Value is forced to be text for comparison purposes. | ||||
|         /// </summary> | ||||
|         Text, | ||||
|     } | ||||
| } | ||||
|  | @ -17,6 +17,7 @@ using UnityEngine.Rendering.HighDefinition; | |||
| using BepInEx.Logging; | ||||
| using DunGenPlus.DevTools; | ||||
| using DunGenPlus.Patches; | ||||
| using DunGenPlus.DevTools.Panels; | ||||
| 
 | ||||
| [assembly: SecurityPermission( SecurityAction.RequestMinimum, SkipVerification = true )] | ||||
| namespace DunGenPlus.Generation { | ||||
|  | @ -36,7 +37,7 @@ namespace DunGenPlus.Generation { | |||
|       ActiveAlternative = true; | ||||
| 
 | ||||
|       var props = extender.Properties.Copy(extender.Version); | ||||
|       var callback = new EventCallbackScenario(DevDebugManager.Instance); | ||||
|       var callback = new EventCallbackScenario(DunGenPlusPanel.Instance && DunGenPlusPanel.Instance.eventCallbackValue); | ||||
|       Instance.Events.OnModifyDunGenExtenderProperties.Invoke(props, callback); | ||||
|       props.NormalNodeArchetypesProperties.SetupProperties(generator); | ||||
|       Properties = props; | ||||
|  |  | |||
|  | @ -1,36 +1,92 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Diagnostics; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using DunGen; | ||||
| using DunGen.Adapters; | ||||
| using DunGenPlus.Components; | ||||
| using DunGenPlus.Components.Scripting; | ||||
| using DunGenPlus.Generation; | ||||
| using DunGenPlus.Utils; | ||||
| using static DunGenPlus.Managers.DoorwayManager; | ||||
| 
 | ||||
| namespace DunGenPlus.Managers { | ||||
|   public static class DoorwayManager { | ||||
| 
 | ||||
|     public static ActionList onMainEntranceTeleportSpawnedEvent = new ActionList("onMainEntranceTeleportSpawned"); | ||||
|     public static List<DoorwayCleanup> doorwayCleanupList; | ||||
|     //public static List<DoorwayCleanup> doorwayCleanupList; | ||||
| 
 | ||||
|     public class Scripts { | ||||
|       public List<IDunGenScriptingParent> scriptList; | ||||
|       public List<Action> actionList; | ||||
| 
 | ||||
|       public Scripts(){ | ||||
|         scriptList = new List<IDunGenScriptingParent>(); | ||||
|         actionList = new List<Action>(); | ||||
|       } | ||||
| 
 | ||||
|       public void Add(IDunGenScriptingParent script) { | ||||
|         scriptList.Add(script); | ||||
|       } | ||||
| 
 | ||||
|       public void Add(Action action) { | ||||
|         actionList.Add(action); | ||||
|       } | ||||
| 
 | ||||
|       public bool Call(){ | ||||
|         foreach(var s  in scriptList){ | ||||
|           s.Call(); | ||||
|         } | ||||
| 
 | ||||
|         foreach(var a in actionList){ | ||||
|           a.Invoke(); | ||||
|         } | ||||
| 
 | ||||
|         return scriptList.Count + actionList.Count > 0; | ||||
|       } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static Dictionary<DunGenScriptingHook, Scripts> scriptingLists; | ||||
| 
 | ||||
|     public static void ResetList(){ | ||||
|       doorwayCleanupList = new List<DoorwayCleanup>(); | ||||
|       //doorwayCleanupList = new List<DoorwayCleanup>(); | ||||
|       scriptingLists = new Dictionary<DunGenScriptingHook, Scripts>(); | ||||
|       foreach(DunGenScriptingHook e in Enum.GetValues(typeof(DunGenScriptingHook))){ | ||||
|         scriptingLists.Add(e, new Scripts()); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     public static void AddDoorwayCleanup(DoorwayCleanup cleanup){ | ||||
|       doorwayCleanupList.Add(cleanup); | ||||
|       //doorwayCleanupList.Add(cleanup); | ||||
|     } | ||||
| 
 | ||||
|     public static void onMainEntranceTeleportSpawnedFunction(){ | ||||
|     public static void AddDunGenScriptHook(IDunGenScriptingParent script){ | ||||
|       scriptingLists[script.GetScriptingHook].Add(script); | ||||
|     } | ||||
| 
 | ||||
|     public static void AddActionHook(DunGenScriptingHook hook, Action action){ | ||||
|       scriptingLists[hook].Add(action); | ||||
|     } | ||||
| 
 | ||||
|     public static void OnMainEntranceTeleportSpawnedFunction(){ | ||||
|       if (DunGenPlusGenerator.Active) { | ||||
|         foreach(var d in doorwayCleanupList){ | ||||
|           d.SetBlockers(false); | ||||
|           d.Cleanup(); | ||||
|            | ||||
|         //foreach(var d in doorwayCleanupList){ | ||||
|         //  d.SetBlockers(false); | ||||
|         //  d.Cleanup(); | ||||
|         //  Plugin.logger.LogWarning(d.GetComponentInParent<Tile>().gameObject.name); | ||||
|         //} | ||||
| 
 | ||||
|         var anyFunctionCalled = false; | ||||
|         foreach(var d  in scriptingLists.Values){ | ||||
|           anyFunctionCalled = anyFunctionCalled | d.Call(); | ||||
|         } | ||||
| 
 | ||||
|         // we can leave early if doorway cleanup is not used (most likely for most dungeons anyway) | ||||
|         if (doorwayCleanupList.Count == 0) return; | ||||
|         if (!anyFunctionCalled) return; | ||||
| 
 | ||||
|         try{ | ||||
|           var dungeonGen = RoundManager.Instance.dungeonGenerator; | ||||
|  | @ -45,5 +101,11 @@ namespace DunGenPlus.Managers { | |||
|       } | ||||
|     } | ||||
| 
 | ||||
|     public static void SetLevelObjectVariablesFunction(){ | ||||
|       if (DunGenPlusGenerator.Active) { | ||||
|         scriptingLists[DunGenScriptingHook.SetLevelObjectVariables ].Call(); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -69,5 +69,12 @@ namespace DunGenPlus.Patches { | |||
|       } | ||||
|     } | ||||
| 
 | ||||
|     [HarmonyPrefix] | ||||
|     [HarmonyPriority(Priority.First)] | ||||
|     [HarmonyPatch(typeof(RoundManager), "SetLevelObjectVariables")] | ||||
|     public static void SetLevelObjectVariablesPatch (ref RoundManager __instance) { | ||||
|       DoorwayManager.SetLevelObjectVariablesFunction(); | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ using BepInEx.Logging; | |||
| using DunGen; | ||||
| using DunGen.Graph; | ||||
| using DunGenPlus.Collections; | ||||
| using DunGenPlus.Components.Scripting; | ||||
| using DunGenPlus.Generation; | ||||
| using DunGenPlus.Managers; | ||||
| using DunGenPlus.Patches; | ||||
|  | @ -26,7 +27,7 @@ namespace DunGenPlus { | |||
| 
 | ||||
|     internal const string modGUID = "dev.ladyalice.dungenplus"; | ||||
|     private const string modName = "Dungeon Generation Plus"; | ||||
|     private const string modVersion = "1.3.4"; | ||||
|     private const string modVersion = "1.4.0"; | ||||
| 
 | ||||
|     internal readonly Harmony Harmony = new Harmony(modGUID); | ||||
| 
 | ||||
|  | @ -62,7 +63,7 @@ namespace DunGenPlus { | |||
| 
 | ||||
|       Assets.LoadAssets(); | ||||
|       Assets.LoadAssetBundle(); | ||||
|       DoorwayManager.onMainEntranceTeleportSpawnedEvent.AddEvent("DoorwayCleanup", DoorwayManager.onMainEntranceTeleportSpawnedFunction); | ||||
|       DoorwayManager.onMainEntranceTeleportSpawnedEvent.AddEvent("DoorwayCleanup", DoorwayManager.OnMainEntranceTeleportSpawnedFunction); | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
|  |  | |||
|  | @ -1,8 +1,11 @@ | |||
| using System; | ||||
| using BepInEx.Logging; | ||||
| using DunGenPlus.Components.Scripting; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using UnityEngine; | ||||
| using UnityEngine.Events; | ||||
| 
 | ||||
| namespace DunGenPlus.Utils { | ||||
|  | @ -52,4 +55,26 @@ namespace DunGenPlus.Utils { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   public static class Utility { | ||||
| 
 | ||||
|     public static void PrintLog(string message, LogLevel logLevel){ | ||||
|       if (DunGenPlusScript.InDebugMode){ | ||||
|         switch(logLevel){ | ||||
|           case LogLevel.Error: | ||||
|           case LogLevel.Fatal: | ||||
|             Debug.LogError(message);  | ||||
|             break; | ||||
|           case LogLevel.Warning: | ||||
|             Debug.LogWarning(message); | ||||
|             break; | ||||
|           default: | ||||
|             Debug.Log(message); | ||||
|             break; | ||||
|         } | ||||
|       } else { | ||||
|         Plugin.logger.Log(logLevel, message); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -73,10 +73,12 @@ | |||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Compile Include="DunGenExtenderPropertyDrawer.cs" /> | ||||
|     <Compile Include="NamedGameObjectReferencePropertyDrawer.cs" /> | ||||
|     <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
|     <Compile Include="PropertyDrawerUtility.cs" /> | ||||
|     <Compile Include="PropertyOverridePropertyDrawer.cs" /> | ||||
|     <Compile Include="ReadOnlyPropertyDrawer.cs" /> | ||||
|     <Compile Include="ScriptActionPropertyDrawer.cs" /> | ||||
|   </ItemGroup> | ||||
|   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||||
|   <PropertyGroup> | ||||
|  |  | |||
|  | @ -0,0 +1,27 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using UnityEditor; | ||||
| using UnityEngine.UIElements; | ||||
| using DunGenPlus; | ||||
| using DunGenPlus.Collections; | ||||
| using DunGenPlus.Components.Scripting; | ||||
| using UnityEditor.UIElements; | ||||
| 
 | ||||
| namespace DunGenPlusEditor { | ||||
| 
 | ||||
|   [CustomPropertyDrawer(typeof(NamedGameObjectReference))] | ||||
|   public class NamedGameObjectReferencePropertyDrawer : PropertyDrawer { | ||||
|     public override VisualElement CreatePropertyGUI(SerializedProperty property) { | ||||
| 
 | ||||
|       var container = new VisualElement(); | ||||
|       container.Add(new PropertyField(property.FindPropertyRelative("name"))); | ||||
|       container.Add(new PropertyField(property.FindPropertyRelative("gameObjects"))); | ||||
|       container.Add(new PropertyField(property.FindPropertyRelative("overrideState"))); | ||||
| 
 | ||||
|       return container; | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										43
									
								
								DunGenPlus/DunGenPlusEditor/ScriptActionPropertyDrawer.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								DunGenPlus/DunGenPlusEditor/ScriptActionPropertyDrawer.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using UnityEditor; | ||||
| using UnityEngine.UIElements; | ||||
| using DunGenPlus; | ||||
| using DunGenPlus.Collections; | ||||
| using DunGenPlus.Components.Scripting; | ||||
| using UnityEditor.UIElements; | ||||
| 
 | ||||
| namespace DunGenPlusEditor { | ||||
| 
 | ||||
|   [CustomPropertyDrawer(typeof(ScriptAction))] | ||||
|   public class ScriptActionPropertyDrawer : PropertyDrawer { | ||||
|     public override VisualElement CreatePropertyGUI(SerializedProperty property) { | ||||
| 
 | ||||
|       var container = new VisualElement(); | ||||
|       var typeProperty = property.FindPropertyRelative("type"); | ||||
|       container.Add(new PropertyField(typeProperty)); | ||||
| 
 | ||||
|       switch((ScriptActionType)typeProperty.intValue){ | ||||
|         case ScriptActionType.SetNamedReferenceState: | ||||
|           AddPropertyFields(container, property, ("namedReference", "Named Reference"), ("boolValue", "State")); | ||||
|           break; | ||||
|         default: | ||||
|           break; | ||||
|       } | ||||
| 
 | ||||
|        | ||||
|       container.Add(new PropertyField(property.FindPropertyRelative("overrideState"))); | ||||
| 
 | ||||
|       return container; | ||||
|     } | ||||
| 
 | ||||
|     private void AddPropertyFields(VisualElement container, SerializedProperty property, params (string field, string label)[] pairs){ | ||||
|       foreach(var pair in pairs){ | ||||
|         container.Add(new PropertyField(property.FindPropertyRelative(pair.field), pair.label)); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue