197 lines
5.1 KiB
C#
197 lines
5.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using Dalamud.Game.ClientState.Conditions;
|
|
using Dalamud.Plugin.Services;
|
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
|
using FFXIVClientStructs.FFXIV.Client.System.Input;
|
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
|
using Microsoft.Extensions.Logging;
|
|
using Questionable.Controller.Steps.Common;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Questing;
|
|
|
|
namespace Questionable.Controller.Steps.Interactions;
|
|
|
|
internal static class Dive
|
|
{
|
|
internal sealed class Factory : SimpleTaskFactory
|
|
{
|
|
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
|
{
|
|
if (step.InteractionType != EInteractionType.Dive)
|
|
{
|
|
return null;
|
|
}
|
|
return new Task();
|
|
}
|
|
}
|
|
|
|
internal sealed class Task : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return "Dive";
|
|
}
|
|
}
|
|
|
|
internal sealed class DoDive(ICondition condition, ILogger<DoDive> logger) : AbstractDelayedTaskExecutor<Task>(TimeSpan.FromSeconds(5L)), IStoppableTaskExecutor, ITaskExecutor
|
|
{
|
|
private readonly Queue<(uint Type, nint Key)> _keysToPress = new Queue<(uint, nint)>();
|
|
|
|
private readonly HashSet<nint> _pressedKeys = new HashSet<nint>();
|
|
|
|
private int _attempts;
|
|
|
|
protected override bool StartInternal()
|
|
{
|
|
if (condition[ConditionFlag.Diving])
|
|
{
|
|
return false;
|
|
}
|
|
if (condition[ConditionFlag.Mounted] || condition[ConditionFlag.Swimming])
|
|
{
|
|
Descend();
|
|
return true;
|
|
}
|
|
throw new TaskException("You aren't swimming, so we can't dive.");
|
|
}
|
|
|
|
public unsafe override ETaskResult Update()
|
|
{
|
|
if (_keysToPress.TryDequeue(out (uint, nint) result))
|
|
{
|
|
if (result.Item1 == 0)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
logger.LogDebug("{Action} key {KeyCode:X2}", (result.Item1 == 256) ? "Pressing" : "Releasing", result.Item2);
|
|
NativeMethods.SendMessage((nint)Device.Instance()->hWnd, result.Item1, result.Item2, IntPtr.Zero);
|
|
if (result.Item1 == 256)
|
|
{
|
|
_pressedKeys.Add(result.Item2);
|
|
}
|
|
else if (result.Item1 == 257)
|
|
{
|
|
_pressedKeys.Remove(result.Item2);
|
|
}
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return base.Update();
|
|
}
|
|
|
|
public unsafe void StopNow()
|
|
{
|
|
foreach (nint pressedKey in _pressedKeys)
|
|
{
|
|
logger.LogDebug("Releasing stuck key {KeyCode:X2} on stop", pressedKey);
|
|
NativeMethods.SendMessage((nint)Device.Instance()->hWnd, 257u, pressedKey, IntPtr.Zero);
|
|
}
|
|
_pressedKeys.Clear();
|
|
_keysToPress.Clear();
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
protected override ETaskResult UpdateInternal()
|
|
{
|
|
if (condition[ConditionFlag.Diving])
|
|
{
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
if (_attempts >= 5)
|
|
{
|
|
throw new TaskException("Please dive manually.");
|
|
}
|
|
Descend();
|
|
_attempts++;
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
|
|
private unsafe void Descend()
|
|
{
|
|
Keybind* keybind = UIInputData.Instance()->GetKeybind(InputId.MOVE_DESCENT);
|
|
if (keybind == null)
|
|
{
|
|
throw new TaskException("Could not find descent keybind");
|
|
}
|
|
Span<KeySetting> keySettings = keybind->KeySettings;
|
|
SeVirtualKey key = keySettings[0].Key;
|
|
KeyModifierFlag keyModifier = keySettings[0].KeyModifier;
|
|
SeVirtualKey key2 = keySettings[1].Key;
|
|
KeyModifierFlag keyModifier2 = keySettings[1].KeyModifier;
|
|
logger.LogInformation("Dive keybind: {Key1} + {Modifier1}, {Key2} + {Modifier2}", key, keyModifier, key2, keyModifier2);
|
|
int num = 2;
|
|
List<List<nint>> list = new List<List<nint>>(num);
|
|
CollectionsMarshal.SetCount(list, num);
|
|
Span<List<nint>> span = CollectionsMarshal.AsSpan(list);
|
|
int num2 = 0;
|
|
span[num2] = GetKeysToPress(key, keyModifier);
|
|
num2++;
|
|
span[num2] = GetKeysToPress(key2, keyModifier2);
|
|
List<nint> list2 = (from x in list
|
|
where x != null
|
|
select (x)).MinBy((List<nint> x) => x.Count);
|
|
if (list2 == null || list2.Count == 0)
|
|
{
|
|
throw new TaskException("No useable keybind found for diving");
|
|
}
|
|
foreach (nint item in list2)
|
|
{
|
|
_keysToPress.Enqueue((256u, item));
|
|
for (int num3 = 0; num3 < 15; num3++)
|
|
{
|
|
_keysToPress.Enqueue((0u, 0));
|
|
}
|
|
}
|
|
for (int num4 = 0; num4 < 5; num4++)
|
|
{
|
|
_keysToPress.Enqueue((0u, 0));
|
|
}
|
|
list2.Reverse();
|
|
foreach (nint item2 in list2)
|
|
{
|
|
_keysToPress.Enqueue((257u, item2));
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class NativeMethods
|
|
{
|
|
public const uint WM_KEYUP = 257u;
|
|
|
|
public const uint WM_KEYDOWN = 256u;
|
|
|
|
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
|
|
public static extern nint SendMessage(nint hWnd, uint Msg, nint wParam, nint lParam);
|
|
}
|
|
|
|
private static List<nint>? GetKeysToPress(SeVirtualKey key, KeyModifierFlag modifier)
|
|
{
|
|
List<nint> list = new List<nint>();
|
|
if ((modifier & KeyModifierFlag.Ctrl) != KeyModifierFlag.None)
|
|
{
|
|
list.Add(17);
|
|
}
|
|
if ((modifier & KeyModifierFlag.Shift) != KeyModifierFlag.None)
|
|
{
|
|
list.Add(16);
|
|
}
|
|
if ((modifier & KeyModifierFlag.Alt) != KeyModifierFlag.None)
|
|
{
|
|
list.Add(18);
|
|
}
|
|
nint num = (nint)key;
|
|
if (num == 0)
|
|
{
|
|
return null;
|
|
}
|
|
list.Add(num);
|
|
return list;
|
|
}
|
|
}
|