qstbak/Questionable/Questionable.Controller.Steps.Interactions/Combat.cs
2025-10-09 07:53:51 +10:00

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;
}
}
}