diff --git a/.gitignore b/.gitignore index aa981d0..fa3181d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ obj/ riderModule.iml /_ReSharper.Caches/ .idea/ -LogicWorld/ \ No newline at end of file +LogicWorld +HarmonyForLogicWorld +EccsLogicWorldAPI diff --git a/CriticalPathAnalyzer.sln b/CriticalPathAnalyzer.sln index 5769d30..772f06f 100644 --- a/CriticalPathAnalyzer.sln +++ b/CriticalPathAnalyzer.sln @@ -2,6 +2,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CriticalPathAnalyzer", "CriticalPathAnalyzer\CriticalPathAnalyzer.csproj", "{0328A941-5B63-44F5-B7BF-BF036CC9A5FE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EccsLogicWorldAPI", "EccsLogicWorldAPI\EccsLogicWorldAPI.csproj", "{EDBF6818-E1D2-4A57-AC4B-647B69858817}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +14,9 @@ Global {0328A941-5B63-44F5-B7BF-BF036CC9A5FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {0328A941-5B63-44F5-B7BF-BF036CC9A5FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {0328A941-5B63-44F5-B7BF-BF036CC9A5FE}.Release|Any CPU.Build.0 = Release|Any CPU + {EDBF6818-E1D2-4A57-AC4B-647B69858817}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EDBF6818-E1D2-4A57-AC4B-647B69858817}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDBF6818-E1D2-4A57-AC4B-647B69858817}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EDBF6818-E1D2-4A57-AC4B-647B69858817}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer.csproj b/CriticalPathAnalyzer/CriticalPathAnalyzer.csproj index 97144c8..0dc01cc 100644 --- a/CriticalPathAnalyzer/CriticalPathAnalyzer.csproj +++ b/CriticalPathAnalyzer/CriticalPathAnalyzer.csproj @@ -8,6 +8,9 @@ + + ..\HarmonyForLogicWorld\HarmonyForLogicWorld\assemblies\client\0Harmony.dll + ..\LogicWorld\Logic_World_Data\Managed\FancyInput.dll @@ -20,12 +23,45 @@ ..\LogicWorld\Logic_World_Data\Managed\LogicLog.dll + + ..\LogicWorld\Logic_World_Data\Managed\LogicUI.dll + + + ..\LogicWorld\Logic_World_Data\Managed\LogicWorld.Building.dll + + + ..\LogicWorld\Logic_World_Data\Managed\LogicWorld.BuildingManagement.dll + + + ..\LogicWorld\Logic_World_Data\Managed\LogicWorld.Players.dll + + + ..\LogicWorld\Logic_World_Data\Managed\LogicWorld.Physics.dll + + + ..\LogicWorld\Logic_World_Data\Managed\LogicWorld.GameStates.dll + + + ..\LogicWorld\Logic_World_Data\Managed\LogicWorld.Input.dll + + + ..\LogicWorld\Logic_World_Data\Managed\LogicWorld.Interfaces.dll + ..\LogicWorld\Logic_World_Data\Managed\LogicWorld.Modding.dll + + ..\LogicWorld\Logic_World_Data\Managed\LogicWorld.Outlines.dll + ..\LogicWorld\Logic_World_Data\Managed\LogicWorld.SharedCode.dll + + ..\LogicWorld\Logic_World_Data\Managed\UnityEngine.CoreModule.dll + + + + diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/FancyInput/ContextMetadata.jecs b/CriticalPathAnalyzer/CriticalPathAnalyzer/FancyInput/ContextMetadata.jecs index 0a7f323..f4ccc8e 100644 --- a/CriticalPathAnalyzer/CriticalPathAnalyzer/FancyInput/ContextMetadata.jecs +++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/FancyInput/ContextMetadata.jecs @@ -1,3 +1,6 @@ CriticalPathAnalyzer.CriticalPathAnalyzer: + InjectTriggersInto: + - MHG.BuildActions Triggers: - - CriticalPathAnalyzer.AnalyzeCriticalPath + - CriticalPathAnalyzer.AnalyzePathStart + - CriticalPathAnalyzer.AnalyzePathEnd diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/FancyInput/TriggerMetadata.jecs b/CriticalPathAnalyzer/CriticalPathAnalyzer/FancyInput/TriggerMetadata.jecs index 475f9d8..414fe7a 100644 --- a/CriticalPathAnalyzer/CriticalPathAnalyzer/FancyInput/TriggerMetadata.jecs +++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/FancyInput/TriggerMetadata.jecs @@ -1,4 +1,10 @@ -CriticalPathAnalyzer.AnalyzeCriticalPath: +CriticalPathAnalyzer.AnalyzePathStart: + Heading: "CriticalPathAnalyzer" + DefaultBinding: + Options: + - I + +CriticalPathAnalyzer.AnalyzePathEnd: Heading: "CriticalPathAnalyzer" DefaultBinding: Options: diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/languages/English/English_input.jecs b/CriticalPathAnalyzer/CriticalPathAnalyzer/languages/English/English_input.jecs index 91470e4..5c08852 100644 --- a/CriticalPathAnalyzer/CriticalPathAnalyzer/languages/English/English_input.jecs +++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/languages/English/English_input.jecs @@ -1,6 +1,11 @@ MHG.SettingsMenu.Pages.Controls.Headings.CriticalPathAnalyzer: "Mod: Critical Path Analyzer" -FancyInput.Trigger.CriticalPathAnalyzer.AnalyzeCriticalPath: "Analyze Critical Path" -FancyInput.Trigger.CriticalPathAnalyzer.AnalyzeCriticalPath.Description : """ - Press once to select the start of the path and a second time to select the end. +FancyInput.Trigger.CriticalPathAnalyzer.AnalyzePathStart: "Select start of path" +FancyInput.Trigger.CriticalPathAnalyzer.AnalyzePathStart.Description : """ + Press to select the start of the path to be analyzed. + """ + +FancyInput.Trigger.CriticalPathAnalyzer.AnalyzePathEnd: "Select end of path" +FancyInput.Trigger.CriticalPathAnalyzer.AnalyzePathEnd.Description : """ + Press to select the end of the path to be analyzed. """ diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerClient.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerClient.cs index 7432e56..5064e8f 100644 --- a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerClient.cs +++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerClient.cs @@ -1,16 +1,27 @@  using CriticalPathAnalyzer.Client.Keybindings; +using EccsLogicWorldAPI.Client.Injectors; using FancyInput; using LogicAPI.Client; +using LogicLog; namespace CriticalPathAnalyzer.Client { public class CriticalPathAnalyzerClient : ClientMod { + public static ILogicLogger LoggerInstance; protected override void Initialize() { + LoggerInstance = Logger; Logger.Info("CriticalPathAnalyzer mod loading"); + // Register keybindings in the settings menu CustomInput.Register("CriticalPathAnalyzer"); + // Inject our own game state + GameStateInjector.inject(CriticalPathAnalyzerGameState.Id, typeof(CriticalPathAnalyzerGameState)); + + // Inject a hook into the game code to check for our keybindings + KeybindingsInjector.Inject(); + Logger.Info("CriticalPathAnalyzer mod loaded"); } } diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerGameState.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerGameState.cs new file mode 100644 index 0000000..85613c7 --- /dev/null +++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/CriticalPathAnalyzerGameState.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using CriticalPathAnalyzer.Client.Keybindings; +using CriticalPathAnalyzer.Client.tool; +using FancyInput; +using LogicUI; +using LogicWorld.GameStates; + +namespace CriticalPathAnalyzer.Client { + public class CriticalPathAnalyzerGameState : GameState { + public const string Id = "CriticalPathAnalyzer.Analyzing"; + + public override bool MouseLocked => true; + public override string TextID => Id; + + public override IEnumerable HelpScreenTriggers => new InputTrigger[] { + UITrigger.Back, + CriticalPathAnalyzerTrigger.AnalyzePathStart, + CriticalPathAnalyzerTrigger.AnalyzePathEnd, + }; + + public override void OnEnter() { + CriticalPathAnalyzerClient.LoggerInstance.Info("CPA enter"); + } + + public override void OnRun() { + if (CustomInput.DownThisFrame(UITrigger.Back)) { + GameStateManager.TransitionBackToBuildingState(); + } else if (CustomInput.DownThisFrame(CriticalPathAnalyzerTrigger.AnalyzePathStart)) { + CriticalPathAnalyzerTool.SelectPathStart(); + } else if (CustomInput.DownThisFrame(CriticalPathAnalyzerTrigger.AnalyzePathEnd)) { + CriticalPathAnalyzerTool.SelectPathEnd(); + } + } + + public override void OnExit() { + CriticalPathAnalyzerClient.LoggerInstance.Info("CPA exit"); + } + } +} \ No newline at end of file diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/KeybindingsInjector.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/KeybindingsInjector.cs new file mode 100644 index 0000000..41d52ea --- /dev/null +++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/KeybindingsInjector.cs @@ -0,0 +1,31 @@ +using CriticalPathAnalyzer.Client.Keybindings; +using EccsLogicWorldAPI.Shared.AccessHelper; +using FancyInput; +using HarmonyLib; +using LogicWorld.Building; +using LogicWorld.GameStates; + +namespace CriticalPathAnalyzer.Client { + public class KeybindingsInjector { + // Fairly generic hook into adding custom keybindings to the main building game state of LW. + // Allows to add keybindings to open new GUI elements or add new core building actions (which are not build-operations). + public static void Inject() { + var methodTarget = Methods.getPrivateStatic(typeof(StuffDeleter), "RunFirstPersonWireDeleting"); + var methodHook = Methods.getPrivateStatic(typeof(KeybindingsInjector), nameof(Hook)); + var harmony = new Harmony(nameof(CriticalPathAnalyzer)); + harmony.Patch(methodTarget, prefix: new HarmonyMethod(methodHook)); + } + + private static bool Hook(out bool deletedWire) { + if (CustomInput.DownThisFrame(CriticalPathAnalyzerTrigger.AnalyzePathStart)) { + GameStateManager.TransitionTo(CriticalPathAnalyzerGameState.Id); + + deletedWire = true; // True = Cancel remaining keybinding handling. + return false; // Prevent execution of the original/further functionality. + } + + deletedWire = false; // Default, might be overwritten. False = Do not cancel remaining keybinding handling. + return true; // Allow original/further functionality. + } + } +} \ No newline at end of file diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/keybindings/CriticalPathAnalyzerTrigger.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/keybindings/CriticalPathAnalyzerTrigger.cs index 23fe0d9..a40cece 100644 --- a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/keybindings/CriticalPathAnalyzerTrigger.cs +++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/keybindings/CriticalPathAnalyzerTrigger.cs @@ -1,6 +1,7 @@ namespace CriticalPathAnalyzer.Client.Keybindings { public enum CriticalPathAnalyzerTrigger { None, - AnalyzeCriticalPath + AnalyzePathStart, + AnalyzePathEnd, } } \ No newline at end of file diff --git a/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/CriticalPathAnalyzerTool.cs b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/CriticalPathAnalyzerTool.cs new file mode 100644 index 0000000..32a32a4 --- /dev/null +++ b/CriticalPathAnalyzer/CriticalPathAnalyzer/src/client/tool/CriticalPathAnalyzerTool.cs @@ -0,0 +1,51 @@ +using LogicAPI.Data; +using LogicWorld.Interfaces; +using LogicWorld.Physics; +using LogicWorld.Players; + +namespace CriticalPathAnalyzer.Client.tool { + public class CriticalPathAnalyzerTool { + private static PegAddress _startPegAddress; + private static PegAddress _endPegAddress; + + private static PegAddress? RayCastPeg() { + // Ray-cast into the world to find what the player is looking at + HitInfo hitInfo = PlayerCaster.CameraCast(Masks.Environment | Masks.Structure | Masks.Peg | Masks.Wire); + if (!hitInfo.HitSomething) { + return null; + } + + // Resolve hit target: + if (hitInfo.HitPeg) { + CriticalPathAnalyzerClient.LoggerInstance.Info("Hit peg"); + return _startPegAddress = hitInfo.pAddress; + } + + if (hitInfo.HitWire) { + CriticalPathAnalyzerClient.LoggerInstance.Info("Hit wire"); + WireAddress wireAddress = hitInfo.wAddress; + Wire wire = Instances.MainWorld.Data.Lookup(wireAddress); + // Assume that wire is never null, as we did just ray-casted it. + return wire.Point1.IsInputAddress() ? wire.Point1 : wire.Point2; + } + + return null; + } + + public static void SelectPathStart() { + CriticalPathAnalyzerClient.LoggerInstance.Info("Analyze Path Start"); + PegAddress? pegAddress = RayCastPeg(); + if (pegAddress != null) { + _startPegAddress = pegAddress.Value; + } + } + + public static void SelectPathEnd() { + CriticalPathAnalyzerClient.LoggerInstance.Info("Analyze Path End"); + PegAddress? pegAddress = RayCastPeg(); + if (pegAddress != null) { + _endPegAddress = pegAddress.Value; + } + } + } +} \ No newline at end of file