193 lines
6.8 KiB
C#
193 lines
6.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Questionable.Controller.Steps.Common;
|
|
using Questionable.Controller.Steps.Shared;
|
|
using Questionable.Controller.Utils;
|
|
using Questionable.Functions;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Questing;
|
|
|
|
namespace Questionable.Controller.Steps.Interactions;
|
|
|
|
internal static class Combat
|
|
{
|
|
internal sealed class Factory(GameFunctions gameFunctions) : ITaskFactory
|
|
{
|
|
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
|
|
{
|
|
if (step.InteractionType != EInteractionType.Combat)
|
|
{
|
|
yield break;
|
|
}
|
|
ArgumentNullException.ThrowIfNull(step.EnemySpawnType, "step.EnemySpawnType");
|
|
if (gameFunctions.GetMountId() != 128 && gameFunctions.GetMountId() != 147)
|
|
{
|
|
yield return new Mount.UnmountTask();
|
|
}
|
|
if (step.CombatDelaySecondsAtStart.HasValue)
|
|
{
|
|
yield return new WaitAtStart.WaitDelay(TimeSpan.FromSeconds(step.CombatDelaySecondsAtStart.Value));
|
|
}
|
|
switch (step.EnemySpawnType)
|
|
{
|
|
case EEnemySpawnType.AfterInteraction:
|
|
ArgumentNullException.ThrowIfNull(step.DataId, "step.DataId");
|
|
yield return new Interact.Task(step.DataId.Value, quest, EInteractionType.None, SkipMarkerCheck: true);
|
|
yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(1L));
|
|
yield return CreateTask(quest, sequence, step);
|
|
break;
|
|
case EEnemySpawnType.AfterItemUse:
|
|
ArgumentNullException.ThrowIfNull(step.ItemId, "step.ItemId");
|
|
if (step.GroundTarget == true)
|
|
{
|
|
if (step.DataId.HasValue)
|
|
{
|
|
yield return new UseItem.UseOnGround(quest.Id, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags, StartingCombat: true);
|
|
}
|
|
else
|
|
{
|
|
ArgumentNullException.ThrowIfNull(step.Position, "step.Position");
|
|
yield return new UseItem.UseOnPosition(quest.Id, step.Position.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags, StartingCombat: true);
|
|
}
|
|
}
|
|
else if (step.DataId.HasValue)
|
|
{
|
|
yield return new UseItem.UseOnObject(quest.Id, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags, StartingCombat: true);
|
|
}
|
|
else
|
|
{
|
|
yield return new UseItem.UseOnSelf(quest.Id, step.ItemId.Value, step.CompletionQuestVariablesFlags, StartingCombat: true);
|
|
}
|
|
yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(1L));
|
|
yield return CreateTask(quest, sequence, step);
|
|
break;
|
|
case EEnemySpawnType.AfterAction:
|
|
ArgumentNullException.ThrowIfNull(step.DataId, "step.DataId");
|
|
ArgumentNullException.ThrowIfNull(step.Action, "step.Action");
|
|
if (!step.Action.Value.RequiresMount())
|
|
{
|
|
yield return new Mount.UnmountTask();
|
|
}
|
|
yield return new Action.UseOnObject(step.DataId.Value, null, step.Action.Value, null);
|
|
yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(1L));
|
|
yield return CreateTask(quest, sequence, step);
|
|
break;
|
|
case EEnemySpawnType.AfterEmote:
|
|
ArgumentNullException.ThrowIfNull(step.Emote, "step.Emote");
|
|
yield return new Mount.UnmountTask();
|
|
if (step.DataId.HasValue)
|
|
{
|
|
yield return new Emote.UseOnObject(step.Emote.Value, step.DataId.Value);
|
|
}
|
|
else
|
|
{
|
|
yield return new Emote.UseOnSelf(step.Emote.Value);
|
|
}
|
|
yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(1L));
|
|
yield return CreateTask(quest, sequence, step);
|
|
break;
|
|
case EEnemySpawnType.AutoOnEnterArea:
|
|
if (!step.CombatDelaySecondsAtStart.HasValue)
|
|
{
|
|
yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(1L));
|
|
}
|
|
yield return CreateTask(quest, sequence, step);
|
|
break;
|
|
case EEnemySpawnType.OverworldEnemies:
|
|
case EEnemySpawnType.FateEnemies:
|
|
yield return CreateTask(quest, sequence, step);
|
|
break;
|
|
case EEnemySpawnType.FinishCombatIfAny:
|
|
yield return CreateTask(quest, sequence, step);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException("step", $"Unknown spawn type {step.EnemySpawnType}");
|
|
}
|
|
}
|
|
|
|
private static Task CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(step.EnemySpawnType, "step.EnemySpawnType");
|
|
bool isLastStep = sequence.Steps.Last() == step;
|
|
return CreateTask(quest.Id, sequence.Sequence, isLastStep, step.EnemySpawnType.Value, step.KillEnemyDataIds, step.CompletionQuestVariablesFlags, step.ComplexCombatData, step.CombatItemUse);
|
|
}
|
|
|
|
internal static Task CreateTask(ElementId? elementId, int sequence, bool isLastStep, EEnemySpawnType enemySpawnType, IList<uint> killEnemyDataIds, IList<QuestWorkValue?> completionQuestVariablesFlags, IList<ComplexCombatData> complexCombatData, CombatItemUse? combatItemUse)
|
|
{
|
|
return new Task(new CombatController.CombatData
|
|
{
|
|
ElementId = elementId,
|
|
Sequence = sequence,
|
|
CompletionQuestVariablesFlags = completionQuestVariablesFlags,
|
|
SpawnType = enemySpawnType,
|
|
KillEnemyDataIds = killEnemyDataIds.ToList(),
|
|
ComplexCombatDatas = complexCombatData.ToList(),
|
|
CombatItemUse = combatItemUse
|
|
}, completionQuestVariablesFlags, isLastStep);
|
|
}
|
|
}
|
|
|
|
internal sealed record Task(CombatController.CombatData CombatData, IList<QuestWorkValue?> CompletionQuestVariableFlags, bool IsLastStep) : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
if (CombatData.SpawnType == EEnemySpawnType.FinishCombatIfAny)
|
|
{
|
|
return "HandleCombat(wait: not in combat, optional)";
|
|
}
|
|
if (QuestWorkUtils.HasCompletionFlags(CompletionQuestVariableFlags))
|
|
{
|
|
return "HandleCombat(wait: QW flags)";
|
|
}
|
|
if (IsLastStep)
|
|
{
|
|
return "HandleCombat(wait: next sequence)";
|
|
}
|
|
return "HandleCombat(wait: not in combat)";
|
|
}
|
|
}
|
|
|
|
internal sealed class HandleCombat(CombatController combatController, QuestFunctions questFunctions) : TaskExecutor<Task>()
|
|
{
|
|
private CombatController.EStatus _status;
|
|
|
|
protected override bool Start()
|
|
{
|
|
return combatController.Start(base.Task.CombatData);
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
_status = combatController.Update();
|
|
if (_status != CombatController.EStatus.Complete)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (QuestWorkUtils.HasCompletionFlags(base.Task.CompletionQuestVariableFlags) && base.Task.CombatData.ElementId is QuestId elementId)
|
|
{
|
|
QuestProgressInfo questProgressInfo = questFunctions.GetQuestProgressInfo(elementId);
|
|
if (questProgressInfo == null)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (QuestWorkUtils.MatchesQuestWork(base.Task.CompletionQuestVariableFlags, questProgressInfo))
|
|
{
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (base.Task.IsLastStep)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
combatController.Stop("Combat task complete");
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|