forked from liza/Questionable
126 lines
4.3 KiB
C#
126 lines
4.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Dalamud.Game.ClientState.Conditions;
|
|
using Dalamud.Game.ClientState.Objects.Enums;
|
|
using Dalamud.Game.ClientState.Objects.Types;
|
|
using Dalamud.Plugin.Services;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
using Questionable.Controller.Steps.BaseFactory;
|
|
using Questionable.Model;
|
|
using Questionable.Model.V1;
|
|
|
|
namespace Questionable.Controller.Steps.InteractionFactory;
|
|
|
|
internal static class Interact
|
|
{
|
|
internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
|
|
{
|
|
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
|
|
{
|
|
if (step.InteractionType != EInteractionType.Interact)
|
|
yield break;
|
|
|
|
ArgumentNullException.ThrowIfNull(step.DataId);
|
|
|
|
// if we're fast enough, it is possible to get the smalltalk prompt
|
|
if (sequence.Sequence == 0 && sequence.Steps.IndexOf(step) == 0)
|
|
yield return serviceProvider.GetRequiredService<WaitAtEnd.WaitDelay>();
|
|
|
|
yield return serviceProvider.GetRequiredService<DoInteract>()
|
|
.With(step.DataId.Value, step.TargetTerritoryId != null);
|
|
}
|
|
|
|
public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
|
=> throw new InvalidOperationException();
|
|
}
|
|
|
|
internal sealed class DoInteract(GameFunctions gameFunctions, ICondition condition, ILogger<DoInteract> logger)
|
|
: ITask
|
|
{
|
|
private bool _needsUnmount;
|
|
private bool _interacted;
|
|
private DateTime _continueAt = DateTime.MinValue;
|
|
|
|
private uint DataId { get; set; }
|
|
private bool SkipMarkerCheck { get; set; }
|
|
|
|
public ITask With(uint dataId, bool skipMarkerCheck)
|
|
{
|
|
DataId = dataId;
|
|
SkipMarkerCheck = skipMarkerCheck;
|
|
return this;
|
|
}
|
|
|
|
public bool Start()
|
|
{
|
|
GameObject? gameObject = gameFunctions.FindObjectByDataId(DataId);
|
|
if (gameObject == null)
|
|
{
|
|
logger.LogWarning("No game object with dataId {DataId}", DataId);
|
|
return false;
|
|
}
|
|
|
|
// this is only relevant for followers on quests
|
|
if (!gameObject.IsTargetable && condition[ConditionFlag.Mounted])
|
|
{
|
|
_needsUnmount = true;
|
|
gameFunctions.Unmount();
|
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
|
return true;
|
|
}
|
|
|
|
if (gameObject.IsTargetable && HasAnyMarker(gameObject))
|
|
{
|
|
_interacted = gameFunctions.InteractWith(DataId);
|
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public ETaskResult Update()
|
|
{
|
|
if (DateTime.Now <= _continueAt)
|
|
return ETaskResult.StillRunning;
|
|
|
|
if (_needsUnmount)
|
|
{
|
|
if (condition[ConditionFlag.Mounted])
|
|
{
|
|
gameFunctions.Unmount();
|
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
else
|
|
_needsUnmount = false;
|
|
}
|
|
|
|
if (!_interacted)
|
|
{
|
|
GameObject? gameObject = gameFunctions.FindObjectByDataId(DataId);
|
|
if (gameObject == null || !gameObject.IsTargetable || !HasAnyMarker(gameObject))
|
|
return ETaskResult.StillRunning;
|
|
|
|
_interacted = gameFunctions.InteractWith(DataId);
|
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
private unsafe bool HasAnyMarker(GameObject gameObject)
|
|
{
|
|
if (SkipMarkerCheck || gameObject.ObjectKind != ObjectKind.EventNpc)
|
|
return true;
|
|
|
|
var gameObjectStruct = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)gameObject.Address;
|
|
return gameObjectStruct->NamePlateIconId != 0;
|
|
}
|
|
|
|
public override string ToString() => $"Interact({DataId})";
|
|
}
|
|
}
|