Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of FsmMaster v0.3.6
plugins/FsmMaster.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using DebugMod; using DebugMod.SaveStates; using HarmonyLib; using HutongGames.PlayMaker; using InControl; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("FsmMaster")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.3.6.0")] [assembly: AssemblyInformationalVersion("0.3.6")] [assembly: AssemblyProduct("FsmMaster")] [assembly: AssemblyTitle("FsmMaster")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/ConstructiveCynicism/FsmMaster")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.3.6.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace FsmMaster { internal sealed class ConfigurationManagerAttributes { public bool? Browsable; public bool? IsAdvanced; } internal sealed class DebugModCompat : IDebugModCompat { private const string ActiveEditsCustomDataKey = "FsmMaster.ActiveEdits"; private const float PendingReloadTimeoutSeconds = 5f; private readonly FsmEditManager _editManager; private readonly Action _rescanLiveFsms; private readonly ManualLogSource _logger; private object? _pendingReloadState; private float _pendingReloadDeadline; internal DebugModCompat(FsmEditManager editManager, Action rescanLiveFsms, ManualLogSource logger) { _editManager = editManager; _rescanLiveFsms = rescanLiveFsms; _logger = logger; } internal void Hook() { SaveState.OnSave += HandleSave; SaveState.BeforeLoad += HandlePrimeBeforeLoad; SaveState.AfterLoad += HandleAfterLoad; DebugMod.Log("FsmMaster detected DebugMod - hooking savestate save/load to persist active FSM edits."); } public void Unhook() { SaveState.OnSave -= HandleSave; SaveState.BeforeLoad -= HandlePrimeBeforeLoad; SaveState.AfterLoad -= HandleAfterLoad; } private void HandleSave(SaveState state) { try { List<FsmEditSet> list = new List<FsmEditSet>(); foreach (string editedFsmKey in _editManager.GetEditedFsmKeys()) { if (_editManager.GetLiveInstances(editedFsmKey).Count != 0) { FsmEditSet activeEditSet = _editManager.GetActiveEditSet(editedFsmKey); if (activeEditSet != null) { list.Add(activeEditSet); } } } if (list.Count != 0) { state.data.customData["FsmMaster.ActiveEdits"] = FsmSaveDataStore.SerializeEditSets(list); DebugMod.Log($"FsmMaster saved {list.Count} active FSM edit set(s) into this savestate."); } } catch (Exception ex) { _logger.LogWarning((object)("[FsmMaster] Failed to save FSM edits into DebugMod savestate: " + ex.Message)); } } private void HandlePrimeBeforeLoad(SaveState state) { try { if (!state.data.customData.TryGetValue("FsmMaster.ActiveEdits", out var value) || string.IsNullOrEmpty(value)) { return; } foreach (FsmEditSet item in FsmSaveDataStore.DeserializeEditSets(value)) { _editManager.PrimeActiveEditSet(item); } } catch (Exception ex) { _logger.LogWarning((object)("[FsmMaster] Failed to prime FSM edits ahead of DebugMod savestate load: " + ex.Message)); } } private void HandleAfterLoad(SaveState state) { ReapplyPersistedEdits(state); if ((Object)(object)GameManager.UnsafeInstance != (Object)null && GameManager.UnsafeInstance.isLoading) { _pendingReloadState = state; _pendingReloadDeadline = Time.realtimeSinceStartup + 5f; } } public void PollPendingReload() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown if (_pendingReloadState == null) { return; } bool flag = (Object)(object)GameManager.UnsafeInstance != (Object)null && GameManager.UnsafeInstance.isLoading; if (!flag || !(Time.realtimeSinceStartup < _pendingReloadDeadline)) { if (flag) { _logger.LogWarning((object)"[FsmMaster] GameManager.isLoading never cleared after a savestate load; reapplying anyway."); } SaveState state = (SaveState)_pendingReloadState; _pendingReloadState = null; ReapplyPersistedEdits(state); } } private void ReapplyPersistedEdits(SaveState state) { try { _rescanLiveFsms(); HashSet<string> hashSet = new HashSet<string>(); if (state.data.customData.TryGetValue("FsmMaster.ActiveEdits", out var value) && !string.IsNullOrEmpty(value)) { List<FsmEditSet> list = FsmSaveDataStore.DeserializeEditSets(value); foreach (FsmEditSet item in list) { _editManager.ApplyEditSet(item); hashSet.Add(item.FsmKey); } DebugMod.Log($"FsmMaster restored {list.Count} FSM edit set(s) from this savestate."); } foreach (string item2 in new List<string>(_editManager.GetEditedFsmKeys())) { if (!hashSet.Contains(item2)) { FsmEditSet activeEditSet = _editManager.GetActiveEditSet(item2); if (activeEditSet != null) { _editManager.ApplyEditSet(activeEditSet); } } } } catch (Exception ex) { _logger.LogWarning((object)("[FsmMaster] Failed to restore FSM edits from DebugMod savestate: " + ex.Message)); } } } internal interface IDebugModCompat { void Unhook(); void PollPendingReload(); } internal static class DebugModCompatFactory { public static IDebugModCompat? TryCreate(FsmEditManager editManager, Action rescanLiveFsms, ManualLogSource logger) { if (!Chainloader.PluginInfos.ContainsKey("io.github.hk-speedrunning.debugmod")) { return null; } return CreateAndHook(editManager, rescanLiveFsms, logger); } [MethodImpl(MethodImplOptions.NoInlining)] private static IDebugModCompat CreateAndHook(FsmEditManager editManager, Action rescanLiveFsms, ManualLogSource logger) { DebugModCompat debugModCompat = new DebugModCompat(editManager, rescanLiveFsms, logger); debugModCompat.Hook(); return debugModCompat; } } [HarmonyPatch(typeof(HollowKnightInputModule), "ProcessMove")] internal static class FocusOnHoverSuppressionPatch { [HarmonyPrefix] private static void Prefix(HollowKnightInputModule __instance, out bool __state) { __state = __instance.focusOnMouseHover; if (CanvasTextField.AnyFieldFocused) { __instance.focusOnMouseHover = false; } } [HarmonyPostfix] private static void Postfix(HollowKnightInputModule __instance, bool __state) { __instance.focusOnMouseHover = __state; } } internal sealed class BepInExConfigValue<T> : IFsmConfigValue<T> { private readonly ConfigEntry<T> _entry; public T Value { get { return _entry.Value; } set { _entry.Value = value; } } public BepInExConfigValue(ConfigEntry<T> entry) { _entry = entry; } } internal sealed class BepInExHotkey : IFsmHotkey { private readonly ConfigEntry<KeyboardShortcut> _entry; public BepInExHotkey(ConfigEntry<KeyboardShortcut> entry) { _entry = entry; } public bool IsDown() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = _entry.Value; return ((KeyboardShortcut)(ref value)).IsDown(); } } internal sealed class BepInExLog : IFsmLog { private readonly ManualLogSource _logger; public BepInExLog(ManualLogSource logger) { _logger = logger; } public void LogInfo(string message) { _logger.LogInfo((object)message); } public void LogWarning(string message) { _logger.LogWarning((object)message); } public void LogError(string message) { _logger.LogError((object)message); } } internal sealed class FsmGraphColorConfig : IFsmGraphColorConfig { private const string StateSection = "Graph Colors - State Palette"; private const string TransitionSection = "Graph Colors - Transition Palette"; private const string OverlaySection = "Graph Colors - Overlay"; public IFsmConfigValue<Color>[] StateColors { get; } public IFsmConfigValue<Color>[] TransitionColors { get; } public IFsmConfigValue<Color> GlobalTransitionColor { get; } public IFsmConfigValue<Color> VignetteColor { get; } public IFsmConfigValue<Color> GlobalPseudoNodeColor { get; } public IFsmConfigValue<Color> GlobalPseudoNodeOutlineColor { get; } public IFsmConfigValue<Color> GlobalPseudoNodeTextColor { get; } public IFsmConfigValue<Color> NodeOutlineColor { get; } public IFsmConfigValue<Color> TransitionRowBackgroundColor { get; } public IFsmConfigValue<Color> ActiveStateColor { get; } public IFsmConfigValue<Color> ActiveTitleBackgroundColor { get; } public IFsmConfigValue<Color> ActiveTitleTextColor { get; } public IFsmConfigValue<Color> SelectedStateColor { get; } public IFsmConfigValue<Color> DisabledOutlineColor { get; } public IFsmConfigValue<Color> DisabledTitleTextColor { get; } public IFsmConfigValue<Color> DisabledEventTextColor { get; } public IFsmConfigValue<Color> DisabledTransitionLineColor { get; } public IFsmConfigValue<Color> DragTransitionColor { get; } private FsmGraphColorConfig(IFsmConfigValue<Color>[] stateColors, IFsmConfigValue<Color>[] transitionColors, IFsmConfigValue<Color> globalTransitionColor, IFsmConfigValue<Color> vignetteColor, IFsmConfigValue<Color> globalPseudoNodeColor, IFsmConfigValue<Color> globalPseudoNodeOutlineColor, IFsmConfigValue<Color> globalPseudoNodeTextColor, IFsmConfigValue<Color> nodeOutlineColor, IFsmConfigValue<Color> transitionRowBackgroundColor, IFsmConfigValue<Color> activeStateColor, IFsmConfigValue<Color> activeTitleBackgroundColor, IFsmConfigValue<Color> activeTitleTextColor, IFsmConfigValue<Color> selectedStateColor, IFsmConfigValue<Color> disabledOutlineColor, IFsmConfigValue<Color> disabledTitleTextColor, IFsmConfigValue<Color> disabledEventTextColor, IFsmConfigValue<Color> disabledTransitionLineColor, IFsmConfigValue<Color> dragTransitionColor) { StateColors = stateColors; TransitionColors = transitionColors; GlobalTransitionColor = globalTransitionColor; VignetteColor = vignetteColor; GlobalPseudoNodeColor = globalPseudoNodeColor; GlobalPseudoNodeOutlineColor = globalPseudoNodeOutlineColor; GlobalPseudoNodeTextColor = globalPseudoNodeTextColor; NodeOutlineColor = nodeOutlineColor; TransitionRowBackgroundColor = transitionRowBackgroundColor; ActiveStateColor = activeStateColor; ActiveTitleBackgroundColor = activeTitleBackgroundColor; ActiveTitleTextColor = activeTitleTextColor; SelectedStateColor = selectedStateColor; DisabledOutlineColor = disabledOutlineColor; DisabledTitleTextColor = disabledTitleTextColor; DisabledEventTextColor = disabledEventTextColor; DisabledTransitionLineColor = disabledTransitionLineColor; DragTransitionColor = dragTransitionColor; } public static FsmGraphColorConfig Bind(ConfigFile config) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03c8: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Unknown result type (might be due to invalid IL or missing references) //IL_0434: Unknown result type (might be due to invalid IL or missing references) //IL_045d: Unknown result type (might be due to invalid IL or missing references) //IL_0486: Unknown result type (might be due to invalid IL or missing references) Color[] array = (Color[])(object)new Color[8] { new Color(0.5019608f, 0.5019608f, 0.5019608f), new Color(67f / 85f, 0.45490196f, 0.6784314f), new Color(0.22745098f, 0.7137255f, 0.6509804f), new Color(31f / 85f, 0.6431373f, 0.20784314f), new Color(0.88235295f, 0.99607843f, 10f / 51f), new Color(47f / 51f, 0.5137255f, 0.18039216f), new Color(11f / 15f, 0.29411766f, 0.29411766f), new Color(39f / 85f, 0.20784314f, 0.6431373f) }; Color[] array2 = (Color[])(object)new Color[8] { Color.white, new Color(0.972549f, 0.77254903f, 77f / 85f), new Color(53f / 85f, 0.88235295f, 72f / 85f), new Color(61f / 85f, 0.88235295f, 53f / 85f), new Color(0.88235295f, 0.99607843f, 0.4f), new Color(1f, 66f / 85f, 0.59607846f), new Color(0.88235295f, 53f / 85f, 32f / 51f), new Color(0.77254903f, 53f / 85f, 0.88235295f) }; IFsmConfigValue<Color>[] array3 = new IFsmConfigValue<Color>[array.Length]; for (int i = 0; i < array.Length; i++) { array3[i] = BindColor(config, "Graph Colors - State Palette", $"State Color {i}", array[i], $"Fill color for a state whose FsmState.ColorIndex is {i} (colorIndex 0 is PlayMaker's \"no color set\" default)."); } IFsmConfigValue<Color>[] array4 = new IFsmConfigValue<Color>[array2.Length]; for (int j = 0; j < array2.Length; j++) { array4[j] = BindColor(config, "Graph Colors - Transition Palette", $"Transition Color {j}", array2[j], $"Transition name/line color for a state whose FsmState.ColorIndex is {j}."); } Color val = default(Color); ((Color)(ref val))..ctor(0f, 1f, 1f); return new FsmGraphColorConfig(array3, array4, BindColor(config, "Graph Colors - Overlay", "Global Transition Color", new Color(0.6f, 0.6f, 0.6f), "Line color for a global transition's connecting arrow."), BindColor(config, "Graph Colors - Overlay", "Vignette Color", new Color(0f, 0f, 0f, 0.6f), "Dimming fill drawn over the graph wherever the selection panel isn't."), BindColor(config, "Graph Colors - Overlay", "Global Pseudo Node Color", new Color(0.82f, 0.82f, 0.82f), "Fill color of a global transition's pseudo-node box."), BindColor(config, "Graph Colors - Overlay", "Global Pseudo Node Outline Color", Color.black, "Outline color of a global transition's pseudo-node box."), BindColor(config, "Graph Colors - Overlay", "Global Pseudo Node Text Color", Color.black, "Event label color on a global transition's pseudo-node box."), BindColor(config, "Graph Colors - Overlay", "Node Outline Color", Color.white, "Default (non-active, non-disabled) inner ring and title/row divider color on a state node."), BindColor(config, "Graph Colors - Overlay", "Transition Row Background Color", new Color(0.2f, 0.2f, 0.2f), "Background color of a state's transition rows."), BindColor(config, "Graph Colors - Overlay", "Active State Color", val, "Outer halo, inner-ring fallback, and outgoing-line color for the FSM's currently active state."), BindColor(config, "Graph Colors - Overlay", "Active Title Background Color", Color.Lerp(val, Color.white, 0.5f), "Title band background for the currently active state."), BindColor(config, "Graph Colors - Overlay", "Active Title Text Color", Color.black, "Title text color for the currently active state."), BindColor(config, "Graph Colors - Overlay", "Selected State Color", Color.yellow, "Outline and outgoing-line color for whichever state is currently selected."), BindColor(config, "Graph Colors - Overlay", "Disabled Outline Color", new Color(0.5f, 0.5f, 0.5f), "Inner ring and title/row divider color for a disabled state."), BindColor(config, "Graph Colors - Overlay", "Disabled Title Text Color", new Color(0.75f, 0.75f, 0.75f), "Title text color for a disabled state."), BindColor(config, "Graph Colors - Overlay", "Disabled Event Text Color", Color.black, "Transition row text color for a disabled state."), BindColor(config, "Graph Colors - Overlay", "Disabled Transition Line Color", new Color(0.55f, 0.55f, 0.55f), "Line color for a disabled transition."), BindColor(config, "Graph Colors - Overlay", "Drag Transition Color", new Color(0f, 1f, 0f), "Rubber-band preview line color while dragging a transition endpoint.")); } private static IFsmConfigValue<Color> BindColor(ConfigFile config, string section, string key, Color defaultValue, string description) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown return new BepInExConfigValue<Color>(config.Bind<Color>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { IsAdvanced = true } }))); } } internal sealed class FsmGraphPerformanceConfig : IFsmGraphPerformanceConfig { public IFsmConfigValue<GraphLineStyle> LineStyle { get; } public IFsmConfigValue<GraphBoxStyle> BoxStyle { get; } public IFsmConfigValue<bool> DiagnosticsEnabled { get; } public int Generation { get; private set; } private FsmGraphPerformanceConfig(ConfigEntry<GraphLineStyle> lineStyle, ConfigEntry<GraphBoxStyle> boxStyle, ConfigEntry<bool> diagnostics) { LineStyle = new BepInExConfigValue<GraphLineStyle>(lineStyle); BoxStyle = new BepInExConfigValue<GraphBoxStyle>(boxStyle); DiagnosticsEnabled = new BepInExConfigValue<bool>(diagnostics); } public static FsmGraphPerformanceConfig Bind(ConfigFile config) { ConfigEntry<GraphLineStyle> lineStyle = config.Bind<GraphLineStyle>("Performance", "Line Style", GraphLineStyle.Thin, "How transition lines are drawn. Thick: antialiased curved lines with arrowheads (most detailed, most expensive). Thin: the same curves drawn as hard-edged 1px lines with arrowheads. Straight: a plain straight segment between each transition's endpoints, no arrowhead (cheapest)."); ConfigEntry<GraphBoxStyle> boxStyle = config.Bind<GraphBoxStyle>("Performance", "Box Style", GraphBoxStyle.Detailed, "How state/event boxes are drawn. Detailed: rounded corners, a border ring on every box, and divider lines between the title and its transition rows. Standard: square corners, no border ring unless the state is active or selected, and no divider lines (cheapest)."); ConfigEntry<bool> diagnostics = config.Bind<bool>("Performance", "Diagnostics", false, "Logs a periodic per-phase timing breakdown of the graph overlay's rendering (layout, line/box gathering, GL emission, labels) to the console while the overlay is open. For diagnosing why a large FSM's graph is expensive to draw; leave off for normal play."); FsmGraphPerformanceConfig instance = new FsmGraphPerformanceConfig(lineStyle, boxStyle, diagnostics); config.SettingChanged += delegate { instance.Generation++; }; return instance; } } internal sealed class FsmPanelLayoutConfig : IFsmPanelLayoutConfig { private const string Section = "UI Layout"; private static readonly Vector2 Unset = new Vector2(-1f, -1f); private readonly ConfigEntry<Vector2> _position; private readonly ConfigEntry<Vector2> _size; public IFsmConfigValue<Vector2> Position { get; } public IFsmConfigValue<Vector2> Size { get; } public bool HasSavedPosition { get { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (_position.Value.x >= 0f) { return _position.Value.y >= 0f; } return false; } } public bool HasSavedSize { get { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (_size.Value.x >= 0f) { return _size.Value.y >= 0f; } return false; } } private FsmPanelLayoutConfig(ConfigEntry<Vector2> position, ConfigEntry<Vector2> size) { _position = position; _size = size; Position = new BepInExConfigValue<Vector2>(position); Size = new BepInExConfigValue<Vector2>(size); } public static FsmPanelLayoutConfig Bind(ConfigFile config, string panelName) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { Browsable = false }; ConfigEntry<Vector2> position = config.Bind<Vector2>("UI Layout", panelName + " Position", Unset, new ConfigDescription("Saved screen position of the " + panelName + ", in pixels from the top-left. (-1, -1) means no saved position yet.", (AcceptableValueBase)null, new object[1] { configurationManagerAttributes })); ConfigEntry<Vector2> size = config.Bind<Vector2>("UI Layout", panelName + " Size", Unset, new ConfigDescription("Saved size of the " + panelName + ", in pixels. (-1, -1) means no saved size yet.", (AcceptableValueBase)null, new object[1] { configurationManagerAttributes })); return new FsmPanelLayoutConfig(position, size); } } [BepInDependency("org.silksong-modding.modlist", "0.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("io.github.constructivecynicism.fsmmaster", "FsmMaster", "0.3.6")] public class FsmMasterPlugin : BaseUnityPlugin { private IFsmLog? _log; private FsmEditManager? _editManager; private IDebugModCompat? _debugModCompat; private FsmVariableTracker? _variableTracker; private FsmTabManager? _tabManager; private FsmGraphOverlay? _graphOverlay; private UICommon? _uiCommon; private GameObject? _canvasGameObject; private bool _ownsEventSystem; private FsmRightPanel? _rightPanel; private FsmMonitorPanel? _monitorPanel; private FsmPanelLayoutConfig? _rightPanelLayout; private FsmPanelLayoutConfig? _monitorPanelLayout; private ConfigEntry<bool>? _autoLoadConfig; private ConfigEntry<KeyboardShortcut>? _toggleOverlayHotkey; private ConfigEntry<bool>? _firstRunComplete; private ConfigFile? _uiStateConfig; private readonly List<CanvasNode> _rightPanelSubtreeBuffer = new List<CanvasNode>(); private readonly List<CanvasNode> _monitorPanelSubtreeBuffer = new List<CanvasNode>(); private int _rightPanelSubtreeVersion = -1; private int _monitorPanelSubtreeVersion = -1; private static Harmony? _harmony; private bool _cursorPatchInstalled; private static readonly MethodInfo SetCursorVisibleMethod = AccessTools.Method(typeof(InputHandler), "SetCursorVisible", (Type[])null, (Type[])null); private static readonly HarmonyMethod ForceCursorVisiblePrefix = new HarmonyMethod(typeof(ForceCursorVisiblePatch), "Prefix", (Type[])null); private bool _uiInputUnlocked; public const string Id = "io.github.constructivecynicism.fsmmaster"; internal static FsmEditManager? ActiveEditManagerForPatches { get; private set; } internal static FsmMasterPlugin? ActiveInstanceForPatches { get; private set; } internal static bool ForceCursorVisible { get; private set; } public static bool ShowEditIndicator { get { return FsmGraphOverlay.ShowEditIndicator; } set { FsmGraphOverlay.ShowEditIndicator = value; } } public static bool FirstRunComplete { get { FsmMasterPlugin activeInstanceForPatches = ActiveInstanceForPatches; if (activeInstanceForPatches != null) { ConfigEntry<bool> firstRunComplete = activeInstanceForPatches._firstRunComplete; if (firstRunComplete != null) { return firstRunComplete.Value; } } return false; } set { FsmMasterPlugin activeInstanceForPatches = ActiveInstanceForPatches; if (activeInstanceForPatches != null) { ConfigEntry<bool> firstRunComplete = activeInstanceForPatches._firstRunComplete; if (firstRunComplete != null) { firstRunComplete.Value = value; } } } } internal FsmVariableTracker? VariableTracker => _variableTracker; public static string Name => "FsmMaster"; public static string Version => "0.3.6"; public static string GetActiveEdits() { return FsmSaveDataStore.SerializeEditSets(ActiveInstanceForPatches?._editManager?.GetAllActiveEditSets() ?? new List<FsmEditSet>()); } private void PatchCursorOverride() { Harmony? harmony = _harmony; if (harmony != null) { harmony.Patch((MethodBase)SetCursorVisibleMethod, ForceCursorVisiblePrefix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private void UnpatchCursorOverride() { Harmony? harmony = _harmony; if (harmony != null) { harmony.Unpatch((MethodBase)SetCursorVisibleMethod, (HarmonyPatchType)1, _harmony.Id); } } private void Awake() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Expected O, but got Unknown //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Expected O, but got Unknown ((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin " + Name + " silk_0.3.6 (io.github.constructivecynicism.fsmmaster) has loaded!")); _log = new BepInExLog(((BaseUnityPlugin)this).Logger); _harmony = Harmony.CreateAndPatchAll(typeof(FsmActivatedPatch), (string)null); _harmony.PatchAll(typeof(GameFileLoadedPatch)); _harmony.PatchAll(typeof(FocusOnHoverSuppressionPatch)); _toggleOverlayHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Toggle Overlay", new KeyboardShortcut((KeyCode)284, Array.Empty<KeyCode>()), "Shows or hides the FSM graph overlay and its right-side panel."); ConfigEntry<KeyboardShortcut> entry = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Toggle Minimal View", KeyboardShortcut.Empty, "While the overlay is visible, switches between the full selection UI and a minimal graph-only view. Unbound by default."); FsmGraphColorConfig colors = FsmGraphColorConfig.Bind(((BaseUnityPlugin)this).Config); FsmGraphPerformanceConfig performance = FsmGraphPerformanceConfig.Bind(((BaseUnityPlugin)this).Config); _autoLoadConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Auto Load Last Configuration", false, "When enabled, each FSM's most recently saved/loaded named configuration is automatically reapplied whenever a scene containing that FSM loads. Toggled in-game by the panel's Auto button."); Directory.CreateDirectory(FsmSaveDataStore.DataDirectory); _uiStateConfig = new ConfigFile(Path.Combine(FsmSaveDataStore.DataDirectory, "io.github.constructivecynicism.fsmmaster.UIState.cfg"), false); _rightPanelLayout = FsmPanelLayoutConfig.Bind(_uiStateConfig, "FsmMaster Panel"); _monitorPanelLayout = FsmPanelLayoutConfig.Bind(_uiStateConfig, "Monitor Panel"); ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { Browsable = false }; _firstRunComplete = _uiStateConfig.Bind<bool>("General", "First Run Complete", false, new ConfigDescription("Set automatically once the mod has shown its first-run hotkey hint after a save file loads. Not meant to be hand-edited.", (AcceptableValueBase)null, new object[1] { configurationManagerAttributes })); _editManager = new FsmEditManager(_log); ActiveEditManagerForPatches = _editManager; ActiveInstanceForPatches = this; _debugModCompat = DebugModCompatFactory.TryCreate(_editManager, RescanLiveFsmsForDebugModLoad, ((BaseUnityPlugin)this).Logger); _variableTracker = new FsmVariableTracker((string fsmKey) => _editManager.GetLiveInstances(fsmKey)); _tabManager = new FsmTabManager(); _graphOverlay = new FsmGraphOverlay(_log, _editManager, _tabManager, new BepInExHotkey(_toggleOverlayHotkey), new BepInExHotkey(entry), colors, performance); string safeSceneName = FsmSceneNaming.GetSafeSceneName(delegate { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); return ((Scene)(ref activeScene)).name; }, _log); PlayMakerFSM[] components = Object.FindObjectsByType<PlayMakerFSM>((FindObjectsInactive)1, (FindObjectsSortMode)0); Dictionary<string, List<PlayMakerFSM>> groupsByFsmKey = ApplyPersistedEditsForScene(safeSceneName, components); _graphOverlay.RefreshSnapshot(safeSceneName, components); _tabManager.RebindAfterRefresh(groupsByFsmKey); BuildRightPanel(); TryUnlockUiInput(); SceneManager.sceneLoaded += OnSceneLoaded; } private void TryUnlockUiInput() { if (!_uiInputUnlocked) { GameManager instance = GameManager.instance; InputHandler val = ((instance != null) ? instance.inputHandler : null); if (!((Object)(object)val == (Object)null)) { val.StartUIInput(); _uiInputUnlocked = true; } } } internal void ShowFirstRunUiIfNeeded() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<bool> firstRunComplete = _firstRunComplete; if (firstRunComplete != null && !firstRunComplete.Value && _graphOverlay != null && _rightPanel != null && _uiCommon != null && _toggleOverlayHotkey != null) { _graphOverlay.Show(); _rightPanel.ShowStatus($"{_toggleOverlayHotkey.Value} to toggle UI", _uiCommon.AccentColor, 12f); _firstRunComplete.Value = true; } } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; _debugModCompat?.Unhook(); _debugModCompat = null; _editManager?.RevertAllForUnload(); _editManager = null; ActiveEditManagerForPatches = null; ActiveInstanceForPatches = null; _variableTracker = null; _tabManager = null; _graphOverlay?.Shutdown(); _graphOverlay = null; ForceCursorVisible = false; DestroyRightPanel(); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; _cursorPatchInstalled = false; _uiStateConfig = null; } private void Update() { _editManager?.PollPendingActivations(); _debugModCompat?.PollPendingReload(); if (!_uiInputUnlocked) { TryUnlockUiInput(); } _graphOverlay?.Update(); bool flag = _graphOverlay?.IsVisible ?? false; if (flag != _cursorPatchInstalled) { if (flag) { PatchCursorOverride(); } else { UnpatchCursorOverride(); } _cursorPatchInstalled = flag; } ForceCursorVisible = flag; if (flag) { EventSystem current = EventSystem.current; BaseInputModule obj = ((current != null) ? current.currentInputModule : null); InControlInputModule val = (InControlInputModule)(object)((obj is InControlInputModule) ? obj : null); if (val != null) { val.allowMouseInput = true; } } if (_rightPanel != null) { bool flag2 = flag && _graphOverlay.SelectionUiVisible; _rightPanel.ActiveSelf = flag2; if (flag2) { FsmTabState fsmTabState = _tabManager?.GetActive(); FsmInfo fsm = ((fsmTabState == null || !fsmTabState.IsLive) ? null : _graphOverlay?.ResolveFsmInfo(fsmTabState.FsmKey)); _rightPanel.ActiveStatePanel.Refresh(fsm, fsmTabState?.SelectedStateName, fsmTabState?.FsmKey); int? num = fsmTabState?.PendingScrollActionIndex; if (num.HasValue) { int valueOrDefault = num.GetValueOrDefault(); _rightPanel.ActiveStatePanel.ScrollToAction(valueOrDefault); fsmTabState.PendingScrollActionIndex = null; } _rightPanel.ActiveStatePanel.RefreshLiveValues(); if (_rightPanelSubtreeVersion != CanvasPanel.StructureVersion) { _rightPanelSubtreeBuffer.Clear(); _rightPanel.CollectSubtree(_rightPanelSubtreeBuffer); _rightPanelSubtreeVersion = CanvasPanel.StructureVersion; } foreach (CanvasNode item in _rightPanelSubtreeBuffer) { if (item.ActiveInHierarchy) { item.Update(); } } } } if (_monitorPanel == null) { return; } _monitorPanel.ActiveSelf = flag; if (!flag) { return; } _monitorPanel.Locked = !_graphOverlay.SelectionUiVisible; _monitorPanel.RefreshRows(_variableTracker); if (_monitorPanelSubtreeVersion != CanvasPanel.StructureVersion) { _monitorPanelSubtreeBuffer.Clear(); _monitorPanel.CollectSubtree(_monitorPanelSubtreeBuffer); _monitorPanelSubtreeVersion = CanvasPanel.StructureVersion; } foreach (CanvasNode item2 in _monitorPanelSubtreeBuffer) { if (item2.ActiveInHierarchy) { item2.Update(); } } } private void OnGUI() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) Rect? rightPanelScreenRect = null; FsmRightPanel rightPanel = _rightPanel; if (rightPanel != null && rightPanel.ActiveInHierarchy) { rightPanelScreenRect = new Rect(_rightPanel.Position.x, _rightPanel.Position.y, _rightPanel.Size.x, _rightPanel.Size.y); } Rect? monitorPanelScreenRect = null; FsmMonitorPanel monitorPanel = _monitorPanel; if (monitorPanel != null && monitorPanel.ActiveInHierarchy) { monitorPanelScreenRect = _monitorPanel.ScreenRect; } Rect? openDropdownScreenRect = _rightPanel?.OpenDropdownScreenRect; _graphOverlay?.OnGUI(_tabManager?.GetActive(), rightPanelScreenRect, monitorPanelScreenRect, openDropdownScreenRect); } private void BuildRightPanel() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Object.FindFirstObjectByType<EventSystem>() == (Object)null) { GameObject val = new GameObject("FsmMasterEventSystem"); val.AddComponent<EventSystem>(); val.AddComponent<StandaloneInputModule>(); _ownsEventSystem = true; } _canvasGameObject = new GameObject("FsmMasterCanvas"); _canvasGameObject.transform.SetParent(((Component)this).transform, false); _canvasGameObject.AddComponent<Canvas>().renderMode = (RenderMode)0; _canvasGameObject.AddComponent<GraphicRaycaster>(); _uiCommon = new UICommon(); _rightPanel = new FsmRightPanel(_uiCommon, _tabManager, _editManager, _variableTracker, () => _graphOverlay?.CurrentSnapshot, _log, _rightPanelLayout, new BepInExConfigValue<bool>(_autoLoadConfig), delegate { _graphOverlay?.Hide(); }); _rightPanel.ActiveSelf = false; _rightPanel.Build(_canvasGameObject.transform); _monitorPanel = new FsmMonitorPanel(_uiCommon, _variableTracker, _monitorPanelLayout); _monitorPanel.ActiveSelf = false; _monitorPanel.Build(_canvasGameObject.transform); } private void DestroyRightPanel() { _rightPanel?.Destroy(); _rightPanel = null; _monitorPanel?.Destroy(); _monitorPanel = null; if ((Object)(object)_canvasGameObject != (Object)null) { Object.Destroy((Object)(object)_canvasGameObject); _canvasGameObject = null; } if (_ownsEventSystem) { EventSystem val = Object.FindFirstObjectByType<EventSystem>(); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } _ownsEventSystem = false; } _uiCommon?.Destroy(); _uiCommon = null; } internal void RescanLiveFsmsForDebugModLoad() { string safeSceneName = FsmSceneNaming.GetSafeSceneName(delegate { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); return ((Scene)(ref activeScene)).name; }, _log); PlayMakerFSM[] components = Object.FindObjectsByType<PlayMakerFSM>((FindObjectsInactive)1, (FindObjectsSortMode)0); Dictionary<string, List<PlayMakerFSM>> groupsByFsmKey = ApplyPersistedEditsForScene(safeSceneName, components); _graphOverlay?.RefreshSnapshot(safeSceneName, components); _tabManager?.RebindAfterRefresh(groupsByFsmKey); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) string safeSceneName = FsmSceneNaming.GetSafeSceneName(() => ((Scene)(ref scene)).name, _log); PlayMakerFSM[] components = Object.FindObjectsByType<PlayMakerFSM>((FindObjectsInactive)1, (FindObjectsSortMode)0); Dictionary<string, List<PlayMakerFSM>> groupsByFsmKey = ApplyPersistedEditsForScene(safeSceneName, components); _graphOverlay?.RefreshSnapshot(FsmSceneNaming.GetSafeSceneName(delegate { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); return ((Scene)(ref activeScene)).name; }, _log), components); _tabManager?.RebindAfterRefresh(groupsByFsmKey); } private Dictionary<string, List<PlayMakerFSM>> ApplyPersistedEditsForScene(string sceneName, PlayMakerFSM[] components) { if (_editManager == null || _tabManager == null) { return new Dictionary<string, List<PlayMakerFSM>>(); } Dictionary<string, List<PlayMakerFSM>> dictionary = FsmIdentity.DiscoverFsmGroups(components); _editManager.ReplaceLiveInstances(dictionary.ToDictionary((KeyValuePair<string, List<PlayMakerFSM>> g) => g.Key, (KeyValuePair<string, List<PlayMakerFSM>> g) => g.Value.Select((PlayMakerFSM c) => c.Fsm).ToList())); ConfigEntry<bool> autoLoadConfig = _autoLoadConfig; if (autoLoadConfig == null || !autoLoadConfig.Value) { return dictionary; } IEnumerable<string> fsmKeysPresent = _tabManager.Tabs.Select((FsmTabState tab) => tab.FsmKey).Where(dictionary.ContainsKey); foreach (FsmEditSet item in FsmSaveDataStore.LoadLastChosenForScene(sceneName, fsmKeysPresent)) { _editManager.ApplyEditSet(item); } return dictionary; } public void ResetFsm(string sceneName, string fsmKey) { _editManager?.ResetFsm(fsmKey); FsmSaveDataStore.ClearAllSavesForFsm(sceneName, fsmKey); } } [HarmonyPatch(typeof(Fsm), "Preprocess", new Type[] { })] internal static class FsmActivatedPatch { [HarmonyPostfix] private static void Postfix(Fsm __instance) { FsmEditManager activeEditManagerForPatches = FsmMasterPlugin.ActiveEditManagerForPatches; if (activeEditManagerForPatches == null) { return; } PlayMakerFSM fsmComponent = __instance.FsmComponent; if (!((Object)(object)fsmComponent == (Object)null)) { string fsmKey = FsmIdentity.GetFsmKey(fsmComponent); activeEditManagerForPatches.ReconcileLiveInstance(fsmKey, __instance); FsmEditSet activeEditSet = activeEditManagerForPatches.GetActiveEditSet(fsmKey); if (activeEditSet != null) { activeEditManagerForPatches.ApplyEditSet(activeEditSet); } } } } [HarmonyPatch(typeof(GameManager), "SetLoadedGameData", new Type[] { typeof(SaveGameData), typeof(int) })] internal static class GameFileLoadedPatch { [HarmonyPostfix] private static void Postfix() { FsmMasterPlugin.ActiveInstanceForPatches?.ShowFirstRunUiIfNeeded(); } } internal static class ForceCursorVisiblePatch { internal static void Prefix(ref bool value) { if (FsmMasterPlugin.ForceCursorVisible) { value = true; } } } internal static class BuildInfo { public const string Prefix = "silk"; public const string Version = "0.3.6"; public const string ReleaseName = "silk_0.3.6"; } internal interface IFsmConfigValue<T> { T Value { get; set; } } internal interface IFsmGraphColorConfig { IFsmConfigValue<Color>[] StateColors { get; } IFsmConfigValue<Color>[] TransitionColors { get; } IFsmConfigValue<Color> GlobalTransitionColor { get; } IFsmConfigValue<Color> VignetteColor { get; } IFsmConfigValue<Color> GlobalPseudoNodeColor { get; } IFsmConfigValue<Color> GlobalPseudoNodeOutlineColor { get; } IFsmConfigValue<Color> GlobalPseudoNodeTextColor { get; } IFsmConfigValue<Color> NodeOutlineColor { get; } IFsmConfigValue<Color> TransitionRowBackgroundColor { get; } IFsmConfigValue<Color> ActiveStateColor { get; } IFsmConfigValue<Color> ActiveTitleBackgroundColor { get; } IFsmConfigValue<Color> ActiveTitleTextColor { get; } IFsmConfigValue<Color> SelectedStateColor { get; } IFsmConfigValue<Color> DisabledOutlineColor { get; } IFsmConfigValue<Color> DisabledTitleTextColor { get; } IFsmConfigValue<Color> DisabledEventTextColor { get; } IFsmConfigValue<Color> DisabledTransitionLineColor { get; } IFsmConfigValue<Color> DragTransitionColor { get; } } internal enum GraphLineStyle { Thick, Thin, Straight } internal enum GraphBoxStyle { Detailed, Standard } internal interface IFsmGraphPerformanceConfig { IFsmConfigValue<GraphLineStyle> LineStyle { get; } IFsmConfigValue<GraphBoxStyle> BoxStyle { get; } IFsmConfigValue<bool> DiagnosticsEnabled { get; } int Generation { get; } } internal interface IFsmHotkey { bool IsDown(); } internal interface IFsmLog { void LogInfo(string message); void LogWarning(string message); void LogError(string message); } internal interface IFsmPanelLayoutConfig { IFsmConfigValue<Vector2> Position { get; } IFsmConfigValue<Vector2> Size { get; } bool HasSavedPosition { get; } bool HasSavedSize { get; } } internal interface IModHost { IFsmLog Log { get; } string StoragePath { get; } string Version { get; } } internal sealed class FsmActiveStateTracker { private sealed class TrackedFsm { public Fsm Instance; public Action<FsmState> Handler; public readonly HashSet<string> EnteredSinceCommit = new HashSet<string>(); public readonly Dictionary<string, float> FadingStates = new Dictionary<string, float>(); } private const float FadeDurationSeconds = 1f; private readonly Dictionary<string, TrackedFsm> _tracked = new Dictionary<string, TrackedFsm>(); private readonly HashSet<string> _visibleThisFrame = new HashSet<string>(); public void EnsureTracked(string fsmKey, Fsm instance) { _visibleThisFrame.Add(fsmKey); if (_tracked.TryGetValue(fsmKey, out TrackedFsm value)) { if (value.Instance == instance) { return; } Fsm instance2 = value.Instance; instance2.StateChanged = (Action<FsmState>)Delegate.Remove(instance2.StateChanged, value.Handler); _tracked.Remove(fsmKey); } TrackedFsm entry = new TrackedFsm { Instance = instance }; entry.Handler = delegate(FsmState state) { entry.EnteredSinceCommit.Add(state.Name); }; instance.StateChanged = (Action<FsmState>)Delegate.Combine(instance.StateChanged, entry.Handler); _tracked[fsmKey] = entry; } public void CommitFrame() { List<string> list = null; foreach (KeyValuePair<string, TrackedFsm> item in _tracked) { string key = item.Key; TrackedFsm value = item.Value; if ((Object)(object)value.Instance.FsmComponent == (Object)null || !_visibleThisFrame.Contains(key)) { Fsm instance = value.Instance; instance.StateChanged = (Action<FsmState>)Delegate.Remove(instance.StateChanged, value.Handler); (list ?? (list = new List<string>())).Add(key); continue; } string activeStateName = value.Instance.ActiveStateName; foreach (string item2 in value.EnteredSinceCommit) { if (item2 == activeStateName) { value.FadingStates.Remove(item2); } else { value.FadingStates[item2] = Time.unscaledTime; } } value.EnteredSinceCommit.Clear(); if (value.FadingStates.Count <= 0) { continue; } List<string> list2 = null; foreach (KeyValuePair<string, float> fadingState in value.FadingStates) { if (Time.unscaledTime - fadingState.Value >= 1f) { (list2 ?? (list2 = new List<string>())).Add(fadingState.Key); } } if (list2 == null) { continue; } foreach (string item3 in list2) { value.FadingStates.Remove(item3); } } if (list != null) { foreach (string item4 in list) { _tracked.Remove(item4); } } _visibleThisFrame.Clear(); } public float? GetFadeProgress(string fsmKey, string stateName) { if (!_tracked.TryGetValue(fsmKey, out TrackedFsm value) || !value.FadingStates.TryGetValue(stateName, out var value2)) { return null; } return Mathf.Clamp01((Time.unscaledTime - value2) / 1f); } public bool HasAnyFading(string fsmKey) { if (_tracked.TryGetValue(fsmKey, out TrackedFsm value)) { return value.FadingStates.Count > 0; } return false; } public void UnsubscribeAll() { foreach (TrackedFsm value in _tracked.Values) { Fsm instance = value.Instance; instance.StateChanged = (Action<FsmState>)Delegate.Remove(instance.StateChanged, value.Handler); } _tracked.Clear(); _visibleThisFrame.Clear(); } } internal sealed class FsmConsoleLogger { private readonly IFsmLog _logger; public FsmConsoleLogger(IFsmLog logger) { _logger = logger; } public void LogSnapshot(FsmSnapshot snapshot) { _logger.LogInfo($"[FsmMaster] Scene \"{snapshot.SceneName}\": {snapshot.Fsms.Count} live PlayMakerFSM instance(s)"); foreach (FsmIdentityInfo fsm in snapshot.Fsms) { if (!((Object)(object)fsm.Component == (Object)null)) { LogFsm(FsmDataCollector.CollectFsmInfo(fsm.Component)); } } } public void LogFsm(FsmInfo fsm) { _logger.LogInfo("[FsmMaster] FSM \"" + fsm.FsmName + "\" on \"" + fsm.GameObjectName + "\" - state \"" + fsm.ActiveStateName + "\""); LogFsmDetails(fsm); } private void LogFsmDetails(FsmInfo fsm) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) _logger.LogInfo($"[FsmMaster] {fsm.States.Count} state(s)"); foreach (FsmStateInfo state in fsm.States) { _logger.LogInfo("[FsmMaster] State \"" + state.Name + "\""); Rect position = state.State.Position; _logger.LogInfo(string.Format(CultureInfo.InvariantCulture, "[FsmMaster] Position=({0:F1}, {1:F1}, {2:F1}, {3:F1}) ColorIndex={4}", ((Rect)(ref position)).x, ((Rect)(ref position)).y, ((Rect)(ref position)).width, ((Rect)(ref position)).height, state.State.ColorIndex)); LogActions(state.Actions); foreach (FsmTransitionInfo transition in state.Transitions) { _logger.LogInfo("[FsmMaster] \"" + transition.EventName + "\" -> \"" + transition.ToState + "\""); } } if (fsm.GlobalTransitions.Count > 0) { _logger.LogInfo($"[FsmMaster] {fsm.GlobalTransitions.Count} global transition(s)"); foreach (FsmTransitionInfo globalTransition in fsm.GlobalTransitions) { _logger.LogInfo("[FsmMaster] \"" + globalTransition.EventName + "\" -> \"" + globalTransition.ToState + "\""); } } LogFsmVariables(fsm.Fsm.Variables); } private void LogActions(List<FsmActionInfo> actions) { _logger.LogInfo($"[FsmMaster] {actions.Count} action(s)"); for (int i = 0; i < actions.Count; i++) { FsmActionInfo fsmActionInfo = actions[i]; _logger.LogInfo($"[FsmMaster] [{i}] {fsmActionInfo.ActionType.Name}"); foreach (FsmActionFieldInfo field in fsmActionInfo.Fields) { LogActionField(fsmActionInfo.Action, field.FieldName, field.FieldValue); } } } private void LogActionField(FsmStateAction action, string fieldName, object? fieldValue) { FsmArray val = (FsmArray)((fieldValue is FsmArray) ? fieldValue : null); if (val == null) { Array array = fieldValue as Array; if (array != null) { LogActionFieldArray(action, fieldName, array.Length, (int i) => array.GetValue(i)); } else { _logger.LogInfo("[FsmMaster] " + fieldName + ": " + FormatActionFieldValue(action, fieldValue)); } } else { LogActionFieldArray(action, WithVariableName(fieldName, ((NamedVariable)val).Name), val.Length, (int i) => val.Values[i]); } } private static string WithVariableName(string fieldName, string variableName) { if (string.IsNullOrEmpty(variableName)) { return fieldName; } return fieldName + " (\"" + variableName + "\")"; } private void LogActionFieldArray(FsmStateAction action, string fieldName, int length, Func<int, object?> getElement) { _logger.LogInfo($"[FsmMaster] {fieldName}: {length} element(s)"); for (int i = 0; i < length; i++) { object fieldValue = getElement(i); _logger.LogInfo($"[FsmMaster] [{i}]: {FormatActionFieldValue(action, fieldValue)}"); } } internal static string FormatActionFieldValue(FsmStateAction action, object? fieldValue) { if (fieldValue != null) { NamedVariable val = (NamedVariable)((fieldValue is NamedVariable) ? fieldValue : null); if (val == null) { FsmEvent val2 = (FsmEvent)((fieldValue is FsmEvent) ? fieldValue : null); if (val2 == null) { FsmOwnerDefault val3 = (FsmOwnerDefault)((fieldValue is FsmOwnerDefault) ? fieldValue : null); if (val3 == null) { FsmEventTarget val4 = (FsmEventTarget)((fieldValue is FsmEventTarget) ? fieldValue : null); if (val4 != null) { return FormatEventTarget(action.Fsm, val4); } return fieldValue.ToString(); } return FormatOwnerDefault(action.Fsm, val3); } return val2.Name; } string text = val.RawValue?.ToString() ?? "null"; if (string.IsNullOrEmpty(val.Name)) { return text; } return "\"" + val.Name + "\": " + text; } return "null"; } internal static string FormatOwnerDefault(Fsm? fsm, FsmOwnerDefault ownerDefault) { if (fsm == null) { return "[uninitialized]"; } GameObject ownerDefaultTarget = fsm.GetOwnerDefaultTarget(ownerDefault); if (!((Object)(object)ownerDefaultTarget != (Object)null)) { return "[none]"; } return "[" + ((Object)ownerDefaultTarget).name + "]"; } internal static string FormatEventTarget(Fsm? fsm, FsmEventTarget eventTarget) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected I4, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) EventTarget target = eventTarget.target; switch ((int)target) { case 0: return "EventTarget(Self)"; case 1: return "EventTarget(GameObject): " + FormatOwnerDefault(fsm, eventTarget.gameObject); case 2: { string text2 = eventTarget.fsmName.Value ?? ""; return "EventTarget(GameObjectFSM): " + FormatOwnerDefault(fsm, eventTarget.gameObject) + "." + text2; } case 3: { PlayMakerFSM fsmComponent = eventTarget.fsmComponent; string text = (((Object)(object)fsmComponent != (Object)null) ? (((Object)((Component)fsmComponent).gameObject).name + "." + fsmComponent.FsmName) : "none"); return "EventTarget(FSMComponent): [" + text + "]"; } default: return $"EventTarget({eventTarget.target})"; } } private void LogFsmVariables(FsmVariables variables) { _logger.LogInfo("[FsmMaster] Variables"); LogVariableArray<FsmFloat>("Float", variables.FloatVariables, (Func<FsmFloat, object>)((FsmFloat v) => v.Value)); LogVariableArray<FsmInt>("Int", variables.IntVariables, (Func<FsmInt, object>)((FsmInt v) => v.Value)); LogVariableArray<FsmBool>("Bool", variables.BoolVariables, (Func<FsmBool, object>)((FsmBool v) => v.Value)); LogVariableArray<FsmString>("String", variables.StringVariables, (Func<FsmString, object>)((FsmString v) => v.Value)); LogVariableArray<FsmVector2>("Vector2", variables.Vector2Variables, (Func<FsmVector2, object>)((FsmVector2 v) => v.Value)); LogVariableArray<FsmVector3>("Vector3", variables.Vector3Variables, (Func<FsmVector3, object>)((FsmVector3 v) => v.Value)); LogVariableArray<FsmRect>("Rect", variables.RectVariables, (Func<FsmRect, object>)((FsmRect v) => v.Value)); LogVariableArray<FsmQuaternion>("Quaternion", variables.QuaternionVariables, (Func<FsmQuaternion, object>)((FsmQuaternion v) => v.Value)); LogVariableArray<FsmColor>("Color", variables.ColorVariables, (Func<FsmColor, object>)((FsmColor v) => v.Value)); LogVariableArray<FsmGameObject>("GameObject", variables.GameObjectVariables, (Func<FsmGameObject, object>)((FsmGameObject v) => v.Value)); LogVariableArray<FsmObject>("Object", variables.ObjectVariables, (Func<FsmObject, object>)((FsmObject v) => v.Value)); LogVariableArray<FsmMaterial>("Material", variables.MaterialVariables, (Func<FsmMaterial, object>)((FsmMaterial v) => v.Value)); LogVariableArray<FsmTexture>("Texture", variables.TextureVariables, (Func<FsmTexture, object>)((FsmTexture v) => v.Value)); LogVariableArray<FsmEnum>("Enum", variables.EnumVariables, (Func<FsmEnum, object>)((FsmEnum v) => v.Value)); LogVariableArray<FsmArray>("Array", variables.ArrayVariables, (Func<FsmArray, object>)((FsmArray v) => string.Join(", ", Array.ConvertAll(v.Values, (object x) => x?.ToString() ?? string.Empty)))); } private void LogVariableArray<T>(string typeName, T[] items, Func<T, object> getValue) where T : NamedVariable { if (items.Length != 0) { foreach (T val in items) { _logger.LogInfo($"[FsmMaster] {typeName} \"{((NamedVariable)val).Name}\": {getValue(val)}"); } } } } internal sealed class FsmSnapshot { public string SceneName { get; set; } = ""; public List<FsmIdentityInfo> Fsms { get; set; } = new List<FsmIdentityInfo>(); } internal sealed class FsmIdentityInfo { public PlayMakerFSM Component { get; set; } public string FsmName { get; set; } = ""; public string GameObjectName { get; set; } = ""; } internal sealed class FsmInfo { public PlayMakerFSM Component { get; set; } public Fsm Fsm { get; set; } public string FsmName { get; set; } = ""; public string GameObjectName { get; set; } = ""; public string ActiveStateName { get; set; } = ""; public List<FsmStateInfo> States { get; set; } = new List<FsmStateInfo>(); public List<FsmTransitionInfo> GlobalTransitions { get; set; } = new List<FsmTransitionInfo>(); } internal sealed class FsmStateInfo { public FsmState State { get; set; } public string Name { get; set; } = ""; public List<FsmActionInfo> Actions { get; set; } = new List<FsmActionInfo>(); public List<FsmTransitionInfo> Transitions { get; set; } = new List<FsmTransitionInfo>(); } internal sealed class FsmActionInfo { public FsmStateAction Action { get; set; } public Type ActionType { get; set; } public List<FsmActionFieldInfo> Fields { get; set; } = new List<FsmActionFieldInfo>(); } internal sealed class FsmActionFieldInfo { public string FieldName { get; set; } = ""; public object? FieldValue { get; set; } public FieldInfo Field { get; set; } public bool IsHidden { get; set; } } internal sealed class FsmTransitionInfo { public string EventName { get; set; } = ""; public string ToState { get; set; } = ""; } internal static class FsmDataCollector { private static readonly Dictionary<Type, FieldInfo[]> RelevantFieldsByActionType = new Dictionary<Type, FieldInfo[]>(); public static FsmSnapshot CollectSnapshot(string sceneName, PlayMakerFSM[] components) { List<FsmIdentityInfo> list = new List<FsmIdentityInfo>(components.Length); foreach (PlayMakerFSM val in components) { list.Add(new FsmIdentityInfo { Component = val, FsmName = val.FsmName, GameObjectName = ((Object)((Component)val).gameObject).name }); } return new FsmSnapshot { SceneName = sceneName, Fsms = list }; } public static FsmInfo CollectFsmInfo(PlayMakerFSM component) { Fsm fsm = component.Fsm; List<FsmStateInfo> list = new List<FsmStateInfo>(fsm.States.Length); FsmState[] states = fsm.States; foreach (FsmState state in states) { list.Add(CollectStateInfo(state)); } return new FsmInfo { Component = component, Fsm = fsm, FsmName = component.FsmName, GameObjectName = ((Object)((Component)component).gameObject).name, ActiveStateName = component.ActiveStateName, States = list, GlobalTransitions = CollectTransitions(fsm.GlobalTransitions) }; } private static FsmStateInfo CollectStateInfo(FsmState state) { return new FsmStateInfo { State = state, Name = state.Name, Actions = CollectActions(state.Actions), Transitions = CollectTransitions(state.Transitions) }; } private static FieldInfo[] GetRelevantFields(Type actionType) { if (RelevantFieldsByActionType.TryGetValue(actionType, out FieldInfo[] value)) { return value; } List<FieldInfo> list = new List<FieldInfo>(); FieldInfo[] fields = actionType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!(fieldInfo.DeclaringType == typeof(FsmStateAction)) && !fieldInfo.IsDefined(typeof(CompilerGeneratedAttribute), inherit: false)) { list.Add(fieldInfo); } } FieldInfo[] array = list.ToArray(); RelevantFieldsByActionType[actionType] = array; return array; } private static List<FsmActionInfo> CollectActions(FsmStateAction[] actions) { List<FsmActionInfo> list = new List<FsmActionInfo>(actions.Length); foreach (FsmStateAction val in actions) { Type type = ((object)val).GetType(); FieldInfo[] relevantFields = GetRelevantFields(type); List<FsmActionFieldInfo> list2 = new List<FsmActionFieldInfo>(relevantFields.Length); FieldInfo[] array = relevantFields; foreach (FieldInfo fieldInfo in array) { list2.Add(new FsmActionFieldInfo { FieldName = fieldInfo.Name, FieldValue = fieldInfo.GetValue(val), Field = fieldInfo, IsHidden = !fieldInfo.IsPublic }); } list.Add(new FsmActionInfo { Action = val, ActionType = type, Fields = list2 }); } return list; } private static List<FsmTransitionInfo> CollectTransitions(FsmTransition[] transitions) { List<FsmTransitionInfo> list = new List<FsmTransitionInfo>(transitions.Length); foreach (FsmTransition val in transitions) { list.Add(new FsmTransitionInfo { EventName = val.EventName, ToState = val.ToState }); } return list; } } internal static class FsmIdentity { private static readonly Regex DuplicateSuffixPattern = new Regex("^(?<base>.+) \\(\\d+\\)$", RegexOptions.Compiled); public static string GetObjectBaseName(string gameObjectName) { Match match = DuplicateSuffixPattern.Match(gameObjectName); if (!match.Success) { return gameObjectName; } return match.Groups["base"].Value; } public static string GetFsmKey(string baseObjectName, string fsmName) { return baseObjectName + "::" + fsmName; } public static string GetFsmKey(PlayMakerFSM component) { return GetFsmKey(GetObjectBaseName(((Object)((Component)component).gameObject).name), component.FsmName); } public static Dictionary<string, List<PlayMakerFSM>> DiscoverFsmGroups(IEnumerable<PlayMakerFSM> components) { Dictionary<string, List<PlayMakerFSM>> dictionary = new Dictionary<string, List<PlayMakerFSM>>(); foreach (PlayMakerFSM component in components) { string fsmKey = GetFsmKey(component); if (!dictionary.TryGetValue(fsmKey, out var value)) { value = (dictionary[fsmKey] = new List<PlayMakerFSM>()); } value.Add(component); } return dictionary; } } internal sealed class FsmPristineSnapshot { public FsmEditSet OriginalValues { get; } = new FsmEditSet(); public Dictionary<string, List<int>> NeuteredActionIndices { get; } = new Dictionary<string, List<int>>(); public List<FsmStateAction> InjectedExitActions { get; } = new List<FsmStateAction>(); public List<(FsmStateAction Original, FsmStateAction Sequencer)> InstalledSequencers { get; } = new List<(FsmStateAction, FsmStateAction)>(); } internal static class FsmSceneNaming { internal const string UnknownSceneName = "unknown"; public static string GetSafeSceneName(Func<string?> getName, IFsmLog? logger = null) { try { return getName() ?? string.Empty; } catch (Exception ex) { logger?.LogWarning("[FsmMaster] Failed to read scene name for a save/load path; using 'unknown': " + ex.Message); return "unknown"; } } } internal static class FsmSaveDataStore { [Serializable] private sealed class FsmEditSetListWire { public List<string> EditSets = new List<string>(); } [Serializable] private sealed class FsmEditSetWire { public string FsmKey = ""; public List<string> VariableOverrides = new List<string>(); public List<string> ActionFieldOverrides = new List<string>(); public List<string> DisabledStates = new List<string>(); public List<string> TransitionRetargets = new List<string>(); public List<string> SequencerOverrides = new List<string>(); } internal static readonly string DataDirectory = Path.Combine(Application.persistentDataPath, "FsmMasterData"); private static readonly Dictionary<string, Dictionary<string, string>> LastChosenByScene = new Dictionary<string, Dictionary<string, string>>(); private const string PairSeparator = "; "; private const string KeyValueSeparator = "="; private const string PatternSeparator = ", "; private static string SanitizeForFileName(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { value = value.Replace(oldChar, '_'); } return value; } private static string GetSceneDirectory(string sceneName) { return Path.Combine(DataDirectory, SanitizeForFileName(sceneName)); } private static string GetFsmDirectory(string sceneName, string fsmKey) { return Path.Combine(GetSceneDirectory(sceneName), SanitizeForFileName(fsmKey)); } public static string GetFilePath(string sceneName, string fsmKey, string saveName) { return Path.Combine(GetFsmDirectory(sceneName, fsmKey), SanitizeForFileName(saveName) + ".json"); } public static FsmEditSet? Load(string sceneName, string fsmKey, string saveName) { string filePath = GetFilePath(sceneName, fsmKey, saveName); if (!File.Exists(filePath)) { return null; } return FromWire(JsonUtility.FromJson<FsmEditSetWire>(File.ReadAllText(filePath))); } public static List<string> ListSaveNames(string sceneName, string fsmKey) { List<string> list = new List<string>(); string fsmDirectory = GetFsmDirectory(sceneName, fsmKey); if (!Directory.Exists(fsmDirectory)) { return list; } string[] files = Directory.GetFiles(fsmDirectory, "*.json"); foreach (string path in files) { list.Add(Path.GetFileNameWithoutExtension(path)); } list.Sort(StringComparer.OrdinalIgnoreCase); return list; } public static string Save(string sceneName, string saveName, FsmEditSet edits) { string fsmDirectory = GetFsmDirectory(sceneName, edits.FsmKey); if (!Directory.Exists(fsmDirectory)) { Directory.CreateDirectory(fsmDirectory); } string text = JsonUtility.ToJson((object)ToWire(edits), true); File.WriteAllText(GetFilePath(sceneName, edits.FsmKey, saveName), text); return text; } public static void ClearAllSavesForFsm(string sceneName, string fsmKey) { string fsmDirectory = GetFsmDirectory(sceneName, fsmKey); if (Directory.Exists(fsmDirectory)) { Directory.Delete(fsmDirectory, recursive: true); } if (LastChosenByScene.TryGetValue(sceneName, out Dictionary<string, string> value)) { value.Remove(fsmKey); } } public static List<FsmEditSet> LoadLastChosenForScene(string sceneName, IEnumerable<string> fsmKeysPresent) { List<FsmEditSet> list = new List<FsmEditSet>(); List<string> list2 = ((fsmKeysPresent is List<string> list3) ? list3 : fsmKeysPresent.ToList()); if (list2.Count == 0 || !LastChosenByScene.TryGetValue(sceneName, out Dictionary<string, string> value)) { return list; } foreach (string item in list2) { if (value.TryGetValue(item, out var value2)) { FsmEditSet fsmEditSet = Load(sceneName, item, value2); if (fsmEditSet != null) { list.Add(fsmEditSet); } } } return list; } public static string? GetLastChosenSaveName(string sceneName, string fsmKey) { if (!LastChosenByScene.TryGetValue(sceneName, out Dictionary<string, string> value) || !value.TryGetValue(fsmKey, out var value2)) { return null; } return value2; } public static void SetLastChosenSaveName(string sceneName, string fsmKey, string saveName) { if (!LastChosenByScene.TryGetValue(sceneName, out Dictionary<string, string> value)) { value = new Dictionary<string, string>(); LastChosenByScene[sceneName] = value; } value[fsmKey] = saveName; } public static string SerializeEditSets(IEnumerable<FsmEditSet> editSets) { FsmEditSetListWire fsmEditSetListWire = new FsmEditSetListWire(); foreach (FsmEditSet editSet in editSets) { fsmEditSetListWire.EditSets.Add(JsonUtility.ToJson((object)ToWire(editSet))); } return JsonUtility.ToJson((object)fsmEditSetListWire); } public static List<FsmEditSet> DeserializeEditSets(string json) { List<FsmEditSet> list = new List<FsmEditSet>(); FsmEditSetListWire fsmEditSetListWire = JsonUtility.FromJson<FsmEditSetListWire>(json); if (fsmEditSetListWire == null) { return list; } foreach (string editSet in fsmEditSetListWire.EditSets) { list.Add(FromWire(JsonUtility.FromJson<FsmEditSetWire>(editSet))); } return list; } private static string JoinPairs(params string[] keysAndValues) { List<string> list = new List<string>(keysAndValues.Length / 2); for (int i = 0; i + 1 < keysAndValues.Length; i += 2) { list.Add(keysAndValues[i] + "=" + keysAndValues[i + 1]); } return string.Join("; ", list.ToArray()); } private static Dictionary<string, string> SplitPairs(string joined) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); string[] array = joined.Split(new string[1] { "; " }, StringSplitOptions.None); foreach (string text in array) { int num = text.IndexOf("=", StringComparison.Ordinal); if (num >= 0) { dictionary[text.Substring(0, num)] = text.Substring(num + 1); } } return dictionary; } private static FsmEditSetWire ToWire(FsmEditSet edits) { FsmEditSetWire fsmEditSetWire = new FsmEditSetWire { FsmKey = edits.FsmKey }; foreach (VariableOverride variableOverride in edits.VariableOverrides) { fsmEditSetWire.VariableOverrides.Add(JoinPairs("type", variableOverride.VariableType, "name", variableOverride.Name, "array", variableOverride.ArrayIndex.ToString(CultureInfo.InvariantCulture), "value", variableOverride.StringValue)); } foreach (ActionFieldOverride actionFieldOverride in edits.ActionFieldOverrides) { fsmEditSetWire.ActionFieldOverrides.Add(JoinPairs("state", actionFieldOverride.StateName, "action", actionFieldOverride.ActionIndex.ToString(CultureInfo.InvariantCulture), "type", actionFieldOverride.ExpectedActionTypeName, "field", actionFieldOverride.FieldName, "array", actionFieldOverride.ArrayIndex.ToString(CultureInfo.InvariantCulture), "value", actionFieldOverride.StringValue)); } fsmEditSetWire.DisabledStates.AddRange(edits.DisabledStates); foreach (TransitionRetarget transitionRetarget in edits.TransitionRetargets) { fsmEditSetWire.TransitionRetargets.Add(JoinPairs("state", transitionRetarget.StateName, "event", transitionRetarget.EventName, "newState", transitionRetarget.NewStateName, "newTo", transitionRetarget.NewToState, "newEvent", transitionRetarget.NewEventName)); } foreach (SequencerOverride sequencerOverride in edits.SequencerOverrides) { fsmEditSetWire.SequencerOverrides.Add(JoinPairs("state", sequencerOverride.StateName, "action", sequencerOverride.ActionIndex.ToString(CultureInfo.InvariantCulture), "repeat", sequencerOverride.RepeatCount.ToString(CultureInfo.InvariantCulture), "pattern", string.Join(", ", sequencerOverride.Pattern.ToArray()))); } return fsmEditSetWire; } private static FsmEditSet FromWire(FsmEditSetWire? wire) { FsmEditSet fsmEditSet = new FsmEditSet(); if (wire == null) { return fsmEditSet; } fsmEditSet.FsmKey = wire.FsmKey; foreach (string variableOverride in wire.VariableOverrides) { Dictionary<string, string> dictionary = SplitPairs(variableOverride); fsmEditSet.VariableOverrides.Add(new VariableOverride { VariableType = dictionary["type"], Name = dictionary["name"], ArrayIndex = (dictionary.TryGetValue("array", out var value) ? int.Parse(value, CultureInfo.InvariantCulture) : (-1)), StringValue = dictionary["value"] }); } foreach (string actionFieldOverride in wire.ActionFieldOverrides) { Dictionary<string, string> dictionary2 = SplitPairs(actionFieldOverride); fsmEditSet.ActionFieldOverrides.Add(new ActionFieldOverride { StateName = dictionary2["state"], ActionIndex = int.Parse(dictionary2["action"], CultureInfo.InvariantCulture), ExpectedActionTypeName = dictionary2["type"], FieldName = dictionary2["field"], ArrayIndex = (dictionary2.TryGetValue("array", out var value2) ? int.Parse(value2, CultureInfo.InvariantCulture) : (-1)), StringValue = dictionary2["value"] }); } fsmEditSet.DisabledStates.AddRange(wire.DisabledStates); foreach (string transitionRetarget in wire.TransitionRetargets) { Dictionary<string, string> dictionary3 = SplitPairs(transitionRetarget); fsmEditSet.TransitionRetargets.Add(new TransitionRetarget { StateName = dictionary3["state"], EventName = dictionary3["event"], NewStateName = dictionary3["newState"], NewToState = dictionary3["newTo"], NewEventName = (dictionary3.TryGetValue("newEvent", out var value3) ? value3 : "") }); } foreach (string sequencerOverride2 in wire.SequencerOverrides) { Dictionary<string, string> dictionary4 = SplitPairs(sequencerOverride2); SequencerOverride sequencerOverride = new SequencerOverride { StateName = dictionary4["state"], ActionIndex = int.Parse(dictionary4["action"], CultureInfo.InvariantCulture), RepeatCount = int.Parse(dictionary4["repeat"], CultureInfo.InvariantCulture) }; if (dictionary4.TryGetValue("pattern", out var value4) && value4.Length > 0) { sequencerOverride.Pattern.AddRange(value4.Split(new string[1] { ", " }, StringSplitOptions.None)); } fsmEditSet.SequencerOverrides.Add(sequencerOverride); } return fsmEditSet; } } internal sealed class FsmVariableTracker { private readonly Func<string, List<Fsm>> _getLiveInstances; private readonly List<TrackedVariablePath> _tracked = new List<TrackedVariablePath>(); private readonly List<TrackedVariableValue> _resultBuffer = new List<TrackedVariableValue>(); public int Version { get; private set; } public FsmVariableTracker(Func<string, List<Fsm>> getLiveInstances) { _getLiveInstances = getLiveInstances; } public void TrackVariable(string fsmKey, string variableName) { AddIfAbsent(new TrackedVariablePath(fsmKey, variableName, null, -1, null)); } public void TrackActionField(string fsmKey, string stateName, int actionIndex, string fieldName) { AddIfAbsent(new TrackedVariablePath(fsmKey, null, stateName, actionIndex, fieldName)); } public void TrackState(string fsmKey, string stateName) { AddIfAbsent(new TrackedVariablePath(fsmKey, null, stateName, -1, null)); } public void TrackVariableArrayElement(string fsmKey, string variableName, int arrayIndex) { AddIfAbsent(new TrackedVariablePath(fsmKey, variableName, null, -1, null, arrayIndex)); } public void TrackActionFieldArrayElement(string fsmKey, string stateName, int actionIndex, string fieldName, int arrayIndex) { AddIfAbsent(new TrackedVariablePath(fsmKey, null, stateName, actionIndex, fieldName, arrayIndex)); } private void AddIfAbsent(TrackedVariablePath path) { if (!_tracked.Contains(path)) { _tracked.Add(path); Version++; } } public void UntrackVariable(string fsmKey, string variableName) { if (_tracked.Remove(new TrackedVariablePath(fsmKey, variableName, null, -1, null))) { Version++; } } public void UntrackActionField(string fsmKey, string stateName, int actionIndex, string fieldName) { if (_tracked.Remove(new TrackedVariablePath(fsmKey, null, stateName, actionIndex, fieldName))) { Version++; } } public void UntrackState(string fsmKey, string stateName) { if (_tracked.Remove(new TrackedVariablePath(fsmKey, null, stateName, -1, null))) { Version++; } } public void UntrackVariableArrayElement(string fsmKey, string variableName, int arrayIndex) { if (_tracked.Remove(new TrackedVariablePath(fsmKey, variableName, null, -1, null, arrayIndex))) { Version++; } } public void UntrackActionFieldArrayElement(string fsmKey, string stateName, int actionIndex, string fieldName, int arrayIndex) { if (_tracked.Remove(new TrackedVariablePath(fsmKey, null, stateName, actionIndex, fieldName, arrayIndex))) { Version++; } } public bool IsVariableTracked(string fsmKey, string variableName) { return _tracked.Contains(new TrackedVariablePath(fsmKey, variableName, null, -1, null)); } public bool IsActionFieldTracked(string fsmKey, string stateName, int actionIndex, string fieldName) { return _tracked.Contains(new TrackedVariablePath(fsmKey, null, stateName, actionIndex, fieldName)); } public bool IsStateTracked(string fsmKey, string stateName) { return _tracked.Contains(new TrackedVariablePath(fsmKey, null, stateName, -1, null)); } public bool IsVariableArrayElementTracked(string fsmKey, string variableName, int arrayIndex) { return _tracked.Contains(new TrackedVariablePath(fsmKey, variableName, null, -1, null, arrayIndex)); } public bool IsActionFieldArrayElementTracked(string fsmKey, string stateName, int actionIndex, string fieldName, int arrayIndex) { return _tracked.Contains(new TrackedVariablePath(fsmKey, null, stateName, actionIndex, fieldName, arrayIndex)); } public List<TrackedVariableValue> GetTracked() { while (_resultBuffer.Count < _tracked.Count) { _resultBuffer.Add(new TrackedVariableValue()); } if (_resultBuffer.Count > _tracked.Count) { _resultBuffer.RemoveRange(_tracked.Count, _resultBuffer.Count - _tracked.Count); } for (int i = 0; i < _tracked.Count; i++) { TrackedVariablePath trackedVariablePath = _tracked[i]; List<Fsm> list = _getLiveInstances(trackedVariablePath.FsmKey); Fsm val = ((list.Count > 0) ? list[0] : null); TrackedVariableValue trackedVariableValue = _resultBuffer[i]; trackedVariableValue.FsmKey = trackedVariablePath.FsmKey; trackedVariableValue.DisplayLabel = trackedVariablePath.DisplayLabel; trackedVariableValue.CurrentValue = ((val == null) ? "<no live instance>" : ResolveCurrentValue(val, trackedVariablePath)); } return _resultBuffer; } private static string ResolveCurrentValue(Fsm fsm, TrackedVariablePath path) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) int? arrayIndex; if (path.VariableName != null) { NamedVariable val = fsm.Variables.FindVariable(path.VariableName); if (val == null) { return "<not found>"; } arrayIndex = path.ArrayIndex; if (arrayIndex.HasValue) { int valueOrDefault = arrayIndex.GetValueOrDefault(); FsmArray val2 = (FsmArray)(object)((val is FsmArray) ? val : null); if (val2 == null || valueOrDefault >= val2.Length) { return "<not found>"; } return FsmEditManager.FormatArrayElement(val2.ElementType, val2.Get(valueOrDefault)); } return FormatValue(val.RawValue); } if (path.ActionIndex < 0 && path.FieldName == null) { if (fsm.GetState(path.StateName) != null) { if (!(fsm.ActiveStateName == path.StateName)) { return "Inactive"; } return "Active"; } return "<not found>"; } FsmState state = fsm.GetState(path.StateName); if (state == null || path.ActionIndex < 0 || path.ActionIndex >= state.Actions.Length) { return "<not found>"; } FsmStateAction val3 = state.Actions[path.ActionIndex]; FieldInfo field = ((object)val3).GetType().GetField(path.FieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { return "<not found>"; } object value = field.GetValue(val3); arrayIndex = path.ArrayIndex; if (arrayIndex.HasValue) { int valueOrDefault2 = arrayIndex.GetValueOrDefault(); if (!(value is Array array) || valueOrDefault2 >= array.Length) { return "<not found>"; } object value2 = array.GetValue(valueOrDefault2); NamedVariable val4 = (NamedVariable)((value2 is NamedVariable) ? value2 : null); return FormatValue((val4 != null) ? val4.RawValue : value2); } NamedVariable val5 = (NamedVariable)((value is NamedVariable) ? value : null); return FormatValue((val5 != null) ? val5.RawValue : value); } private static string FormatValue(object? value) { return value?.ToString() ?? "null"; } } internal sealed class TrackedVariablePath : IEquatable<TrackedVariablePath> { public string FsmKey { get; } public string? VariableName { get; } public string? StateName { get; } public int ActionIndex { get; } public string? FieldName { get; } public int? ArrayIndex { get; } public string DisplayLabel { get; } public TrackedVariablePath(string fsmKey, string? variableName, string? stateName, int actionIndex, string? fieldName, int? arrayIndex = null) { FsmKey = fsmKey; VariableName = variableName; StateName = stateName; ActionIndex = actionIndex; FieldName = fieldName; ArrayIndex = arrayIndex; string text; if (VariableName == null) { if (FieldName == null) { text = fsmKey + " / " + stateName + " (state)"; } else if (arrayIndex.HasValue) { int valueOrDefault = arrayIndex.GetValueOrDefault(); text = $"{fsmKey} / {stateName}[{actionIndex}].{fieldName}[{valueOrDefault}]"; } else { text = $"{fsmKey} / {stateName}[{actionIndex}].{fieldName}"; } } else if (arrayIndex.HasValue) { int valueOrDefault2 = arrayIndex.GetValueOrDefault(); text = $"{fsmKey} / {variableName}[{valueOrDefault2}]"; } else { text = fsmKey + " / " + variableName; } DisplayLabel = text; } public bool Equals(TrackedVariablePath? other) { if (other != null && FsmKey == other.FsmKey && VariableName == other.VariableName && StateName == other.StateName && ActionIndex == other.ActionIndex && FieldName == other.FieldName) { return ArrayIndex == other.ArrayIndex; } return false; } public override bool Equals(object? obj) { return Equals(obj as TrackedVariablePath); } public override int GetHashCode() { return (((((17 * 31 + FsmKey.GetHashCode()) * 31 + (VariableName?.GetHashCode() ?? 0)) * 31 + (StateName?.GetHashCode() ?? 0)) * 31 + ActionIndex) * 31 + (FieldName?.GetHashCode() ?? 0)) * 31 + (ArrayIndex?.GetHashCode() ?? 0); } } internal sealed class TrackedVariableValue { public string FsmKey = ""; public string DisplayLabel = ""; public string CurrentValue = ""; } internal sealed class SequenceSendEventAction : FsmStateAction { private readonly FsmEvent[] _sequence; private readonly int _repeatCount; private readonly FsmStateAction _originalAction; private readonly FsmState _state; private int _index; private int _completedCycles; public SequenceSendEventAction(FsmEvent[] sequence, int repeatCount, FsmStateAction originalAction, FsmState state) { if (sequence.Length == 0) { throw new ArgumentException("Sequence must contain at least one event.", "sequence"); } _sequence = sequence; _repeatCount = repeatCount; _originalAction = originalAction; _state = state; } public override void OnEnter() { if (_repeatCount > 0 && _completedCycles >= _repeatCount) { RestoreOriginalAction(); _originalAction.OnEnter(); return; } ((FsmStateAction)this).Fsm.Event(_sequence[_index]); _index++; if (_index >= _sequence.Length) { _index = 0; _completedCycles++; } ((FsmStateAction)this).Finish(); } private void RestoreOriginalAction() { _originalAction.Enabled = true; int num = Array.IndexOf(_state.Actions, (FsmStateAction)(object)this); if (num >= 0) { _state.RemoveAction(num); } } } internal static class FsmActionSequencer { public static int IndexRandomEventAction(FsmState state, int rank) { int num = 0; for (int i = 0; i < state.Actions.Length; i++) { if (((object)state.Actions[i]).GetType().Name.Contains("Random")) { if (num == rank) { return i; } num++; } } return -1; } public static FsmEvent[] ExtractEventCandidates(FsmStateAction sourceAction, FsmState state) { FieldInfo fieldInfo = ((object)sourceAction).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault((FieldInfo f) => f.FieldType == typeof(FsmEvent[])); if (fieldInfo != null && fieldInfo.GetValue(sourceAction) is FsmEvent[] result) { return result; } return state.Transitions.Select((FsmTransition t) => t.FsmEvent).ToArray(); } public static FsmEvent[] ExpandPattern(FsmEvent[] events, int[] repeatCounts) { List<FsmEvent> list = new List<FsmEvent>(); for (int i = 0; i < repeatCounts.Length; i++) { for (int j = 0; j < repeatCounts[i]; j++) { list.Add(events[i]); } } return list.ToArray(); } } internal sealed class FsmEditManager { private sealed class SendExitEventAction : FsmStateAction { private readonly FsmEvent _exitEvent; public SendExitEventAction(FsmEvent exitEvent) { _exitEvent = exitEvent; } public override void OnEnter() { ((FsmStateAction)this).Fsm.Event(_exitEvent); ((FsmStateAction)this).Finish(); } } private static readonly string[] ExitEventPriority = new string[3] { "CANCEL", "FINISHED", "NEXT" }; private static readonly HashSet<VariableType> SupportedVariableTypes = new HashSet<VariableType> { (VariableType)0, (VariableType)1, (VariableType)2, (VariableType)4, (VariableType)5, (VariableType)6, (VariableType)8, (VariableType)11, (VariableType)7, (VariableType)14 }; private static readonly HashSet<VariableType> SupportedArrayElementTypes = new HashSet<VariableType> { (VariableType)0, (VariableType)1, (VariableType)2, (VariableType)4 }; private readonly IFsmLog _logger; private readonly Dictionary<string, List<Fsm>> _liveInstances = new Dictionary<string, List<Fsm>>(); private readonly Dictionary<string, FsmPristineSnapshot> _pristine = new Dictionary<string, FsmPristineSnapshot>(); private readonly Dictionary<string, FsmEditSet> _activeEdits = new Dictionary<string, FsmEditSet>(); private readonly Dictionary<string, Stack<Action>> _undoStacks = new Dictionary<string, Stack<Action>>(); private bool _isUndoing; private readonly HashSet<string> _pendingActivationFsmKeys = new HashSet<string>(); public int EditGeneration { get; private set; } private void BumpEditGeneration() { EditGeneration++; } public FsmEditManager(IFsmLog logger) { _logger = logger; } public void ReplaceLiveInstances(Dictionary<string, List<Fsm>> instancesByKey) { _liveInstances.Clear(); foreach (KeyValuePair<string, List<Fsm>> item in instancesByKey) { _liveInstances[item.Key] = item.Value; } PruneStaleSnapshotEntries(); } private void PruneStaleSnapshotEntries() { foreach (KeyValuePair<string, FsmPristineSnapshot> item in _pristine) { HashSet<Fsm> alive = new HashSet<Fsm>(GetLiveInstances(item.Key)); item.Value.InjectedExitActions.RemoveAll((FsmStateAction action) => !alive.Contains(action.State.Fsm)); item.Value.InstalledSequencers.RemoveAll(((FsmStateAction Original, FsmStateAction Sequencer) pair) => !alive.Contains(pair.Sequencer.State.Fsm)); } } public List<Fsm> GetLiveInstances(string fsmKey) { if (!_liveInstances.TryGetValue(fsmKey, out List<Fsm> value)) { return new List<Fsm>(); } return value; } public void ReconcileLiveInstance(string fsmKey, Fsm fsm) { if (!_liveInstances.TryGetValue(fsmKey, out List<Fsm> value)) { value = new List<Fsm>(); _liveInstances[fsmKey] = value; } value.RemoveAll((Fsm existing) => existing != fsm && (Object)(object)existing.FsmComponent == (Object)(object)fsm.FsmComponent); if (!value.Contains(fsm)) { value.Add(fsm); } } public void PollPendingActivations() { if (_pendingActivationFsmKeys.Count == 0) { return; } string[] array = _pendingActivationFsmKeys.ToArray(); foreach (string text in array) { _pendingActivationFsmKeys.Remove(text); FsmEditSet activeEditSet = GetActiveEditSet(text); if (activeEditSet == null) { continue; } Fsm[] array2 = GetLiveInstances(text).ToArray(); for (int j = 0; j < array2.Length; j++) { PlayMakerFSM fsmComponent = array2[j].FsmComponent; if (fsmComponent != null) { Fsm fsm = fsmComponent.Fsm; if (fsm != null) { ReconcileLiveInstance(text, fsm); } } } ApplyEditSet(activeEditSet); } } public ICollection<string> GetEditedFsmKeys() { return _activeEdits.Keys; } public List<FsmEditSet> GetAllActiveEditSets() { return new List<FsmEditSet>(_activeEdits.Values); } public FsmEditSet? GetActiveEditSet(string fsmKey) { if (!_activeEdits.TryGetValue(fsmKey, out FsmEditSet value)) { return null; } return value; } private void PruneActiveEditSetIfEmpty(string fsmKey) { if (_activeEdits.TryGetValue(fsmKey, out FsmEditSet value) && value.IsEmpty) { _activeEdits.Remove(fsmKey); } } public void PrimeActiveEditSet(FsmEditSet editSet) { _activeEdits[editSet.FsmKey] = editSet; } public void ApplyEditSet(FsmEditSet editSet) { if (!_liveInstances.TryGetValue(editSet.FsmKey, out List<Fsm> value) || value.Count == 0) { _logger.LogWarning("[FsmMaster] Fsm key '" + editSet.FsmKey + "' is disconnected (no live instances in the current scene); skipping edit set."); return; } VariableOverride[] array = editSet.VariableOverrides.ToArray(); ActionFieldOverride[] array2 = editSet.ActionFieldOverrides.ToArray(); string[] array3 = editSet.DisabledStates.ToArray(); TransitionRetarget[] array4 = editSet.TransitionRetargets.ToArray(); SequencerOverride[] array5 = editSet.SequencerOverrides.ToArray(); foreach (Fsm item in value) { VariableOverride[] array6 = array; foreach (VariableOverride ov in array6) { ApplyVariableOverride(editSet.FsmKey, item, ov); } ActionFieldOverride[] array7 = array2; foreach (ActionFieldOverride ov2 in array7) { ApplyActionFieldOverride(editSet.FsmKey, item, ov2); } string[] array8 = array3; foreach (string stateName in array8) { DisableState(editSet.FsmKey, item, stateName); } TransitionRetarget[] array9 = array4; foreach (TransitionRetarget retarget in array9) { ApplyTransitionRetarget(editSet.FsmKey, item, retarget); } SequencerOverride[] array10 = array5; foreach (SequencerOverride seq in array10) { InstallSequencer(editSet.FsmKey, item, seq); } } _logger.LogInfo($"[FsmMaster] Applied edit set for fsm key '{editSet.FsmKey}' to {value.Count} live instance(s): " + $"{array.Length} variable, {array2.Length} action-field, {array3.Length} disabled-state, " + $"{array4.Length} transition, {array5.Length} sequencer override(s)."); } public void ResetFsm(string fsmKey) { if (_pristine.TryGetValue(fsmKey, out FsmPristineSnapshot value)) { _liveInstances.TryGetValue(fsmKey, out List<Fsm> value2); RestoreSnapshot(value, value2 ?? new List<Fsm>()); _pristine.Remove(fsmKey); _activeEdits.Remove(fsmKey); _undoStacks.Remove(fsmKey); BumpEditGeneration(); } } public void RevertAllForUnload() { foreach (KeyValuePair<string, FsmPristineSnapshot> item in _pristine) { _liveInstances.TryGetValue(item.Key, out List<Fsm> value); RestoreSnapshot(item.Value, value ?? new List<Fsm>()); } _pristine.Clear(); _liveInstances.Clear(); _activeEdits.Clear(); _undoStacks.Clear(); } public bool HasUndo(string fsmKey) { if (_undoStacks.TryGetValue(fsmKey, out Stack<Action> value)) { return value.Count > 0; } return false; } public void Undo(string fsmKey) { if (!_undoStacks.TryGetValue(fsmKey, out Stack<Action> value) || value.Count == 0) { return; } Action action = value.Pop(); _isUndoing = true; try { action(); } finally { _isUndoing = false; } } private void PushUndo(string fsmKey, Action undo) { if (!_isUndoing) { if (!_undoStacks.TryGetValue(fsmKey, out Stack<Action> value)) { value = new Stack<Action>(); _undoStacks[fsmKey] = value; } value.Push(undo); } } public void SetVariable(string fsmKey, string variableName, string variableType, string stringValue) { string previousValue = GetCurrentVariableValue(fsmKey, variableName); VariableOverride ov = new VariableOverride { VariableType = variableType, Name = variableName, StringValue = stringValue }; foreach (Fsm liveInstance in GetLiveInstances(fsmKey)) { ApplyVariableOverride(fsmKey, liveInstance, ov); } if (previousValue != null) { PushUndo(fsmKey, delegate { SetVariable(fsmKey, variableName, variableType, previousValue); }); } } public void SetActionField(string fsmKey, string stateName, int actionIndex, string expectedActionTypeName, string fieldName, string stringValue) { string previousValue = GetCurrentActionFieldValue(fsmKey, stateName, actionIndex, expectedActionTypeName, fieldName); ActionFieldOverride ov = new ActionFieldOverride { StateName = stateName, ActionIndex = actionIndex, ExpectedActionTypeName = expectedActionTypeName, FieldName = fieldName, StringValue = stringValue }; foreach (Fsm liveInstance in GetLiveInstances(fsmKey)) { ApplyActionFieldOverride(fsmKey, liveInstance, ov); } if (previousValue != null) { PushUndo(fsmKey, delegate { SetActionField(fsmKey, stateName, actionIndex, expectedActionTypeName, fieldName, previousValue); }); } } public void SetVariableArrayElement(string fsmKey, string variableName, int arrayIndex, string elementStringValue) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) string previousValue = GetCurrentVariableArrayElementValue(fsmKey, variableName, arrayIndex); VariableOverride ov = new VariableOverride { VariableType = ((object)(VariableType)13/*cast due to .constrained prefix*/).ToString(), Name = variableName, ArrayIndex = arrayIndex, StringValue = elementStringValue }; foreach (Fsm liveInstance in GetLiveInstances(fsmKey)) { ApplyVariableOverride(fsmKey, liveInstance, ov); } if (previousValue != null) { PushUndo(fsmKey, delegate { SetVariableArrayElement(fsmKey, variableName, arrayIndex, previousValue); }); } } public void SetActionFieldArrayElement(string fsmKey, string stateName, int actionIndex, string expectedActionTypeName, string fieldName, int arrayIndex, string elementStringValue) { string previousValue = GetCurrentActionFieldArrayElementValue(fsmKey, stateName, actionIndex, expectedActionTypeName, fieldName, arrayIndex); ActionFieldOverride ov = new ActionFieldOverride { StateName = stateName, ActionIndex = actionIndex, ExpectedActionTypeName = expectedActionTypeName, FieldName = fieldName, ArrayIndex = arrayIndex, StringValue = elementStringValue }; foreach (Fsm liveInstance in GetLiveInstances(fsmKey)) { ApplyActionFieldOverride(fsmKey, liveInstance, ov); } if (previousValue != null) { PushUndo(fsmKey, delegate { SetActionFieldArrayElement(fsmKey, stateName, actionIndex, expectedActionTypeName, fieldName, arrayIndex, previousValue); }); } } private string? GetCurrentVariableValue(string fsmKey, string variableName) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) foreach (Fsm liveInstance in GetLiveInstances(fsmKey)) { NamedVariable val = liveInstance.Variables.FindVariable(variableName); if (val != null && SupportedVariableTypes.Contains(val.VariableType)) { return FormatNamedVariable(val); } } return null; } private string? GetCurrentVariableArrayElementValue(string fsmKey, string variableName, int arrayIndex) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) foreach (Fsm liveInstance in GetLiveInstances(fsmKey)) { NamedVariable obj = liveInstance.Variables.FindVariable(variableName); FsmArray val = (FsmArray)(object)((obj is FsmArray) ? obj : null); if (val != null && arrayIndex < val.Length) { return FormatArrayElement(val.ElementType, val.Get(arrayIndex)); } } return null; } private string? GetCurrentActionFieldValue(string fsmKey, string stateName, int actionIndex, string expectedActionTypeName, string fieldName) { foreach (Fsm liveInstance in