punish v6.8.18.0
This commit is contained in:
commit
060278c1b7
317 changed files with 554155 additions and 0 deletions
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
|
||||
namespace Questionable.Controller.Steps.Common;
|
||||
|
||||
internal abstract class AbstractDelayedTaskExecutor<T> : TaskExecutor<T> where T : class, ITask
|
||||
{
|
||||
private DateTime _continueAt;
|
||||
|
||||
protected TimeSpan Delay { get; set; }
|
||||
|
||||
protected AbstractDelayedTaskExecutor()
|
||||
: this(TimeSpan.FromSeconds(5L))
|
||||
{
|
||||
}
|
||||
|
||||
protected AbstractDelayedTaskExecutor(TimeSpan delay)
|
||||
{
|
||||
Delay = delay;
|
||||
}
|
||||
|
||||
protected sealed override bool Start()
|
||||
{
|
||||
bool result = StartInternal();
|
||||
_continueAt = DateTime.Now.Add(Delay);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract bool StartInternal();
|
||||
|
||||
public override ETaskResult Update()
|
||||
{
|
||||
if (_continueAt >= DateTime.Now)
|
||||
{
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
return UpdateInternal();
|
||||
}
|
||||
|
||||
protected virtual ETaskResult UpdateInternal()
|
||||
{
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
}
|
244
Questionable/Questionable.Controller.Steps.Common/Mount.cs
Normal file
244
Questionable/Questionable.Controller.Steps.Common/Mount.cs
Normal file
|
@ -0,0 +1,244 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Common.Math;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Data;
|
||||
using Questionable.Functions;
|
||||
|
||||
namespace Questionable.Controller.Steps.Common;
|
||||
|
||||
internal static class Mount
|
||||
{
|
||||
internal sealed record MountTask : ITask
|
||||
{
|
||||
public ushort TerritoryId { get; init; }
|
||||
|
||||
public EMountIf MountIf { get; init; }
|
||||
|
||||
public FFXIVClientStructs.FFXIV.Common.Math.Vector3? Position { get; }
|
||||
|
||||
public MountTask(ushort TerritoryId, EMountIf MountIf, FFXIVClientStructs.FFXIV.Common.Math.Vector3? Position = null)
|
||||
{
|
||||
this.TerritoryId = TerritoryId;
|
||||
this.MountIf = MountIf;
|
||||
this.Position = ((MountIf == EMountIf.AwayFromPosition) ? new FFXIVClientStructs.FFXIV.Common.Math.Vector3?(Position ?? throw new ArgumentNullException("Position")) : ((FFXIVClientStructs.FFXIV.Common.Math.Vector3?)null));
|
||||
base._002Ector();
|
||||
}
|
||||
|
||||
public bool ShouldRedoOnInterrupt()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Mount";
|
||||
}
|
||||
|
||||
[CompilerGenerated]
|
||||
public void Deconstruct(out ushort TerritoryId, out EMountIf MountIf, out FFXIVClientStructs.FFXIV.Common.Math.Vector3? Position)
|
||||
{
|
||||
TerritoryId = this.TerritoryId;
|
||||
MountIf = this.MountIf;
|
||||
Position = this.Position;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class MountEvaluator(GameFunctions gameFunctions, ICondition condition, TerritoryData territoryData, IClientState clientState, ILogger<MountEvaluator> logger)
|
||||
{
|
||||
public unsafe MountResult EvaluateMountState(MountTask task, bool dryRun, ref DateTime retryAt)
|
||||
{
|
||||
if (condition[ConditionFlag.Mounted])
|
||||
{
|
||||
return MountResult.DontMount;
|
||||
}
|
||||
LogLevel logLevel = (dryRun ? LogLevel.None : LogLevel.Information);
|
||||
if (!territoryData.CanUseMount(task.TerritoryId))
|
||||
{
|
||||
logger.Log(logLevel, "Can't use mount in current territory {Id}", task.TerritoryId);
|
||||
return MountResult.DontMount;
|
||||
}
|
||||
if (gameFunctions.HasStatusPreventingMount())
|
||||
{
|
||||
logger.Log(logLevel, "Can't mount due to status preventing sprint or mount");
|
||||
return MountResult.DontMount;
|
||||
}
|
||||
if (task.MountIf == EMountIf.AwayFromPosition)
|
||||
{
|
||||
float num = System.Numerics.Vector3.Distance((FFXIVClientStructs.FFXIV.Common.Math.Vector3)(clientState.LocalPlayer?.Position ?? ((System.Numerics.Vector3)FFXIVClientStructs.FFXIV.Common.Math.Vector3.Zero)), task.Position.GetValueOrDefault());
|
||||
if (task.TerritoryId == clientState.TerritoryType && num < 30f && !Conditions.Instance()->Diving)
|
||||
{
|
||||
logger.Log(logLevel, "Not using mount, as we're close to the target");
|
||||
return MountResult.DontMount;
|
||||
}
|
||||
logger.Log(logLevel, "Want to use mount if away from destination ({Distance} yalms), trying (in territory {Id})...", num, task.TerritoryId);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Log(logLevel, "Want to use mount, trying (in territory {Id})...", task.TerritoryId);
|
||||
}
|
||||
if (!condition[ConditionFlag.InCombat])
|
||||
{
|
||||
if (dryRun)
|
||||
{
|
||||
retryAt = DateTime.Now.AddSeconds(0.5);
|
||||
}
|
||||
return MountResult.Mount;
|
||||
}
|
||||
return MountResult.WhenOutOfCombat;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class MountExecutor(GameFunctions gameFunctions, ICondition condition, MountEvaluator mountEvaluator, ILogger<MountExecutor> logger) : TaskExecutor<MountTask>()
|
||||
{
|
||||
private bool _mountTriggered;
|
||||
|
||||
private DateTime _retryAt = DateTime.MinValue;
|
||||
|
||||
protected override bool Start()
|
||||
{
|
||||
_mountTriggered = false;
|
||||
return mountEvaluator.EvaluateMountState(base.Task, dryRun: false, ref _retryAt) == MountResult.Mount;
|
||||
}
|
||||
|
||||
public override ETaskResult Update()
|
||||
{
|
||||
if (_mountTriggered && !condition[ConditionFlag.Mounted] && DateTime.Now > _retryAt)
|
||||
{
|
||||
logger.LogInformation("Not mounted, retrying...");
|
||||
_mountTriggered = false;
|
||||
_retryAt = DateTime.MaxValue;
|
||||
}
|
||||
if (!_mountTriggered)
|
||||
{
|
||||
if (gameFunctions.HasStatusPreventingMount())
|
||||
{
|
||||
logger.LogInformation("Can't mount due to status preventing sprint or mount");
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
base.ProgressContext = InteractionProgressContext.FromActionUse(() => _mountTriggered = gameFunctions.Mount());
|
||||
_retryAt = DateTime.Now.AddSeconds(5.0);
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
if (!condition[ConditionFlag.Mounted])
|
||||
{
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
|
||||
public override bool ShouldInterruptOnDamage()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal enum MountResult
|
||||
{
|
||||
DontMount,
|
||||
Mount,
|
||||
WhenOutOfCombat
|
||||
}
|
||||
|
||||
internal sealed record UnmountTask : ITask
|
||||
{
|
||||
public bool ShouldRedoOnInterrupt()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Unmount";
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class UnmountExecutor(ICondition condition, ILogger<UnmountTask> logger, GameFunctions gameFunctions, IClientState clientState) : TaskExecutor<UnmountTask>()
|
||||
{
|
||||
private bool _unmountTriggered;
|
||||
|
||||
private DateTime _continueAt = DateTime.MinValue;
|
||||
|
||||
protected override bool Start()
|
||||
{
|
||||
if (!condition[ConditionFlag.Mounted])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
logger.LogInformation("Step explicitly wants no mount, trying to unmount...");
|
||||
if (condition[ConditionFlag.InFlight])
|
||||
{
|
||||
gameFunctions.Unmount();
|
||||
_continueAt = DateTime.Now.AddSeconds(1.0);
|
||||
return true;
|
||||
}
|
||||
_unmountTriggered = gameFunctions.Unmount();
|
||||
_continueAt = DateTime.Now.AddSeconds(1.0);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override ETaskResult Update()
|
||||
{
|
||||
if (_continueAt >= DateTime.Now)
|
||||
{
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
if (IsUnmounting())
|
||||
{
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
if (!_unmountTriggered)
|
||||
{
|
||||
if (condition[ConditionFlag.InFlight])
|
||||
{
|
||||
gameFunctions.Unmount();
|
||||
}
|
||||
else
|
||||
{
|
||||
_unmountTriggered = gameFunctions.Unmount();
|
||||
}
|
||||
_continueAt = DateTime.Now.AddSeconds(1.0);
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
if (condition[ConditionFlag.Mounted] && condition[ConditionFlag.InCombat])
|
||||
{
|
||||
_unmountTriggered = gameFunctions.Unmount();
|
||||
_continueAt = DateTime.Now.AddSeconds(1.0);
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
if (!condition[ConditionFlag.Mounted])
|
||||
{
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
|
||||
private unsafe bool IsUnmounting()
|
||||
{
|
||||
IPlayerCharacter localPlayer = clientState.LocalPlayer;
|
||||
if (localPlayer != null)
|
||||
{
|
||||
BattleChara* address = (BattleChara*)localPlayer.Address;
|
||||
return (address->Mount.Flags & 1) == 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldInterruptOnDamage()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public enum EMountIf
|
||||
{
|
||||
Always,
|
||||
AwayFromPosition
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Functions;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller.Steps.Common;
|
||||
|
||||
internal static class NextQuest
|
||||
{
|
||||
internal sealed class Factory(QuestFunctions questFunctions) : SimpleTaskFactory()
|
||||
{
|
||||
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
||||
{
|
||||
if (step.InteractionType != EInteractionType.CompleteQuest)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (step.NextQuestId == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (step.NextQuestId == quest.Id)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (questFunctions.GetPriorityQuests(onlyClassAndRoleQuests: true).Contains(step.NextQuestId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new SetQuestTask(step.NextQuestId, quest.Id);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record SetQuestTask(ElementId NextQuestId, ElementId CurrentQuestId) : ITask
|
||||
{
|
||||
public bool ShouldRedoOnInterrupt()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"SetNextQuest({NextQuestId})";
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class NextQuestExecutor(QuestRegistry questRegistry, QuestController questController, QuestFunctions questFunctions, ILogger<NextQuestExecutor> logger) : TaskExecutor<SetQuestTask>()
|
||||
{
|
||||
protected override bool Start()
|
||||
{
|
||||
QuestController.EAutomationType automationType = questController.AutomationType;
|
||||
Quest quest;
|
||||
if ((uint)(automationType - 3) <= 1u)
|
||||
{
|
||||
logger.LogInformation("Won't set next quest to {QuestId}, automation type is CurrentQuestOnly", base.Task.NextQuestId);
|
||||
questController.SetNextQuest(null);
|
||||
}
|
||||
else if (questFunctions.IsQuestLocked(base.Task.NextQuestId, base.Task.CurrentQuestId))
|
||||
{
|
||||
logger.LogInformation("Can't set next quest to {QuestId}, quest is locked", base.Task.NextQuestId);
|
||||
questController.SetNextQuest(null);
|
||||
}
|
||||
else if (questRegistry.TryGetQuest(base.Task.NextQuestId, out quest))
|
||||
{
|
||||
logger.LogInformation("Setting next quest to {QuestId}: '{QuestName}'", base.Task.NextQuestId, quest.Info.Name);
|
||||
questController.SetNextQuest(quest);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("Next quest with id {QuestId} not found", base.Task.NextQuestId);
|
||||
questController.SetNextQuest(null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override ETaskResult Update()
|
||||
{
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
|
||||
public override bool ShouldInterruptOnDamage()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Questionable.Data;
|
||||
using Questionable.External;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller.Steps.Common;
|
||||
|
||||
internal static class SendNotification
|
||||
{
|
||||
internal sealed class Factory(AutomatonIpc automatonIpc, AutoDutyIpc autoDutyIpc, BossModIpc bossModIpc, TerritoryData territoryData) : SimpleTaskFactory()
|
||||
{
|
||||
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
||||
{
|
||||
switch (step.InteractionType)
|
||||
{
|
||||
case EInteractionType.Snipe:
|
||||
if (!automatonIpc.IsAutoSnipeEnabled)
|
||||
{
|
||||
return new Task(step.InteractionType, step.Comment);
|
||||
}
|
||||
break;
|
||||
case EInteractionType.Duty:
|
||||
if (!autoDutyIpc.IsConfiguredToRunContent(step.DutyOptions))
|
||||
{
|
||||
EInteractionType interactionType = step.InteractionType;
|
||||
uint? num = step.DutyOptions?.ContentFinderConditionId;
|
||||
object comment;
|
||||
if (num.HasValue)
|
||||
{
|
||||
uint valueOrDefault = num.GetValueOrDefault();
|
||||
comment = territoryData.GetContentFinderCondition(valueOrDefault)?.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
comment = step.Comment;
|
||||
}
|
||||
return new Task(interactionType, (string?)comment);
|
||||
}
|
||||
break;
|
||||
case EInteractionType.SinglePlayerDuty:
|
||||
if (!bossModIpc.IsConfiguredToRunSoloInstance(quest.Id, step.SinglePlayerDutyOptions))
|
||||
{
|
||||
return new Task(step.InteractionType, quest.Info.Name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record Task(EInteractionType InteractionType, string? Comment) : ITask
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return "SendNotification";
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class Executor(NotificationMasterIpc notificationMasterIpc, IChatGui chatGui, Configuration configuration) : TaskExecutor<Task>()
|
||||
{
|
||||
protected override bool Start()
|
||||
{
|
||||
if (!configuration.Notifications.Enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string text;
|
||||
switch (base.Task.InteractionType)
|
||||
{
|
||||
case EInteractionType.Duty:
|
||||
text = "Duty";
|
||||
break;
|
||||
case EInteractionType.SinglePlayerDuty:
|
||||
text = "Single player duty";
|
||||
break;
|
||||
case EInteractionType.WaitForManualProgress:
|
||||
case EInteractionType.Snipe:
|
||||
case EInteractionType.Instruction:
|
||||
text = "Manual interaction required";
|
||||
break;
|
||||
default:
|
||||
text = $"{base.Task.InteractionType}";
|
||||
break;
|
||||
}
|
||||
string text2 = text;
|
||||
if (!string.IsNullOrEmpty(base.Task.Comment))
|
||||
{
|
||||
text2 = text2 + " - " + base.Task.Comment;
|
||||
}
|
||||
if (configuration.Notifications.ChatType != XivChatType.None)
|
||||
{
|
||||
XivChatEntry xivChatEntry;
|
||||
switch (configuration.Notifications.ChatType)
|
||||
{
|
||||
case XivChatType.Say:
|
||||
case XivChatType.Shout:
|
||||
case XivChatType.TellOutgoing:
|
||||
case XivChatType.TellIncoming:
|
||||
case XivChatType.Party:
|
||||
case XivChatType.Alliance:
|
||||
case XivChatType.Ls1:
|
||||
case XivChatType.Ls2:
|
||||
case XivChatType.Ls3:
|
||||
case XivChatType.Ls4:
|
||||
case XivChatType.Ls5:
|
||||
case XivChatType.Ls6:
|
||||
case XivChatType.Ls7:
|
||||
case XivChatType.Ls8:
|
||||
case XivChatType.FreeCompany:
|
||||
case XivChatType.NoviceNetwork:
|
||||
case XivChatType.Yell:
|
||||
case XivChatType.CrossParty:
|
||||
case XivChatType.PvPTeam:
|
||||
case XivChatType.CrossLinkShell1:
|
||||
case XivChatType.NPCDialogue:
|
||||
case XivChatType.NPCDialogueAnnouncements:
|
||||
case XivChatType.CrossLinkShell2:
|
||||
case XivChatType.CrossLinkShell3:
|
||||
case XivChatType.CrossLinkShell4:
|
||||
case XivChatType.CrossLinkShell5:
|
||||
case XivChatType.CrossLinkShell6:
|
||||
case XivChatType.CrossLinkShell7:
|
||||
case XivChatType.CrossLinkShell8:
|
||||
xivChatEntry = new XivChatEntry
|
||||
{
|
||||
Message = text2,
|
||||
Type = configuration.Notifications.ChatType,
|
||||
Name = new SeStringBuilder().AddUiForeground("Questionable", 576).Build()
|
||||
};
|
||||
break;
|
||||
default:
|
||||
xivChatEntry = new XivChatEntry
|
||||
{
|
||||
Message = new SeStringBuilder().AddUiForeground("[Questionable] ", 576).Append(text2).Build(),
|
||||
Type = configuration.Notifications.ChatType
|
||||
};
|
||||
break;
|
||||
}
|
||||
XivChatEntry chat = xivChatEntry;
|
||||
chatGui.Print(chat);
|
||||
}
|
||||
notificationMasterIpc.Notify(text2);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override ETaskResult Update()
|
||||
{
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
|
||||
public override bool ShouldInterruptOnDamage()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
|
||||
namespace Questionable.Controller.Steps.Common;
|
||||
|
||||
internal static class WaitCondition
|
||||
{
|
||||
internal sealed record Task(Func<bool> Predicate, string Description) : ITask
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return Description;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class WaitConditionExecutor : TaskExecutor<Task>
|
||||
{
|
||||
private DateTime _continueAt = DateTime.MaxValue;
|
||||
|
||||
protected override bool Start()
|
||||
{
|
||||
return !base.Task.Predicate();
|
||||
}
|
||||
|
||||
public override ETaskResult Update()
|
||||
{
|
||||
if (_continueAt == DateTime.MaxValue && base.Task.Predicate())
|
||||
{
|
||||
_continueAt = DateTime.Now.AddSeconds(0.5);
|
||||
}
|
||||
if (!(DateTime.Now >= _continueAt))
|
||||
{
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
|
||||
public override bool ShouldInterruptOnDamage()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
namespace Questionable.Controller.Steps.Common;
|
||||
|
||||
internal sealed class WaitNavmesh
|
||||
{
|
||||
internal sealed record Task : ITask
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return "Wait(navmesh)";
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class Executor(MovementController movementController) : TaskExecutor<Task>(), IDebugStateProvider, ITaskExecutor
|
||||
{
|
||||
protected override bool Start()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override ETaskResult Update()
|
||||
{
|
||||
if (!movementController.IsNavmeshReady)
|
||||
{
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
|
||||
public override bool ShouldInterruptOnDamage()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public string? GetDebugState()
|
||||
{
|
||||
if (!movementController.IsNavmeshReady)
|
||||
{
|
||||
return $"Navmesh: {movementController.BuiltNavmeshPercent}%";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue