qstbak/Questionable/Questionable.Controller.CombatModules/ItemUseModule.cs
2025-10-09 08:41:52 +10:00

158 lines
4.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Questionable.Model.Questing;
namespace Questionable.Controller.CombatModules;
internal sealed class ItemUseModule : ICombatModule
{
private readonly IServiceProvider _serviceProvider;
private readonly ICondition _condition;
private readonly ILogger<ItemUseModule> _logger;
private ICombatModule? _delegate;
private CombatController.CombatData? _combatData;
private bool _isDoingRotation;
private DateTime _continueAt;
public ItemUseModule(IServiceProvider serviceProvider, ICondition condition, ILogger<ItemUseModule> logger)
{
_serviceProvider = serviceProvider;
_condition = condition;
_logger = logger;
}
public bool CanHandleFight(CombatController.CombatData combatData)
{
if (combatData.CombatItemUse == null)
{
return false;
}
_delegate = (from x in _serviceProvider.GetRequiredService<IEnumerable<ICombatModule>>()
where !(x is ItemUseModule)
select x).FirstOrDefault((ICombatModule x) => x.CanHandleFight(combatData));
_logger.LogInformation("ItemUse delegate: {Delegate}", _delegate?.GetType().Name);
return _delegate != null;
}
public bool Start(CombatController.CombatData combatData)
{
if (_delegate.Start(combatData))
{
_combatData = combatData;
_isDoingRotation = true;
_continueAt = DateTime.Now;
return true;
}
return false;
}
public bool Stop()
{
if (_isDoingRotation)
{
_delegate.Stop();
_isDoingRotation = false;
_combatData = null;
_delegate = null;
_continueAt = DateTime.Now;
}
return true;
}
public unsafe void Update(IGameObject nextTarget)
{
if (_delegate == null || _continueAt > DateTime.Now)
{
return;
}
if (_combatData?.CombatItemUse == null)
{
_delegate.Update(nextTarget);
}
else if (_combatData.KillEnemyDataIds.Contains(nextTarget.BaseId) || _combatData.ComplexCombatDatas.Any((ComplexCombatData x) => x.DataId == nextTarget.BaseId && (!x.NameId.HasValue || (nextTarget is ICharacter character && x.NameId == character.NameId))))
{
if (_isDoingRotation)
{
if (InventoryManager.Instance()->GetInventoryItemCount(_combatData.CombatItemUse.ItemId, isHq: false, checkEquipped: true, checkArmory: true, 0) == 0)
{
_isDoingRotation = false;
_delegate.Stop();
}
else if (ShouldUseItem(nextTarget))
{
_isDoingRotation = false;
_delegate.Stop();
_logger.LogInformation("Using item {ItemId}", _combatData.CombatItemUse.ItemId);
AgentInventoryContext.Instance()->UseItem(_combatData.CombatItemUse.ItemId, InventoryType.Invalid, 0u, 0);
_continueAt = DateTime.Now.AddSeconds(2.0);
}
else
{
_delegate.Update(nextTarget);
}
}
else if (_condition[ConditionFlag.Casting])
{
DateTime dateTime = DateTime.Now.AddSeconds(0.5);
if (dateTime > _continueAt)
{
_continueAt = dateTime;
}
}
else
{
_isDoingRotation = true;
_delegate.Start(_combatData);
}
}
else if (_isDoingRotation)
{
_delegate.Update(nextTarget);
}
}
private unsafe bool ShouldUseItem(IGameObject gameObject)
{
if (_combatData?.CombatItemUse == null)
{
return false;
}
if (gameObject is IBattleChara)
{
BattleChara* address = (BattleChara*)gameObject.Address;
if (_combatData.CombatItemUse.Condition == ECombatItemUseCondition.Incapacitated)
{
return (address->ActorControlFlags & 0x40) != 0;
}
if (_combatData.CombatItemUse.Condition == ECombatItemUseCondition.HealthPercent)
{
return 100f * (float)address->Health / (float)address->MaxHealth < (float)_combatData.CombatItemUse.Value;
}
if (_combatData.CombatItemUse.Condition == ECombatItemUseCondition.MissingStatus)
{
return !address->StatusManager.HasStatus((uint)_combatData.CombatItemUse.Value);
}
}
return false;
}
public bool CanAttack(IBattleNpc target)
{
return _delegate.CanAttack(target);
}
}