Initial release: HSUI v1.0.0.0 - HUD replacement with configurable hotbars
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
Copyright(c) 2021 xorus (https://github.com/xorus/EngageTimer)
|
||||
Modifications Copyright(c) 2021 HSUI
|
||||
09/21/2021 - Extracted code to hook the game's pulltimer functions.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Logging;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
|
||||
namespace HSUI.Helpers
|
||||
{
|
||||
public unsafe class PullTimerHelper
|
||||
{
|
||||
#region Singleton
|
||||
private PullTimerHelper()
|
||||
{
|
||||
PullTimerState = new PullTimerState();
|
||||
|
||||
try
|
||||
{
|
||||
_countdownTimerHook = Plugin.GameInteropProvider.HookFromAddress<AgentInterface.Delegates.Update>(
|
||||
AgentModule.Instance()->GetAgentByInternalId(AgentId.CountDownSettingDialog)->VirtualTable->Update,
|
||||
CountdownTimerFunc);
|
||||
_countdownTimerHook?.Enable();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Plugin.Logger.Error("PullTimeHelper CountdownTimer Hook failed!!!");
|
||||
}
|
||||
}
|
||||
public static void Initialize() { Instance = new PullTimerHelper(); }
|
||||
public static PullTimerHelper Instance { get; private set; } = null!;
|
||||
|
||||
~PullTimerHelper()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_countdownTimerHook?.Disable();
|
||||
_countdownTimerHook?.Dispose();
|
||||
|
||||
Instance = null!;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private DateTime _combatTimeEnd;
|
||||
private DateTime _combatTimeStart;
|
||||
|
||||
private ulong _agentData;
|
||||
public bool CountDownRunning;
|
||||
|
||||
private int _countDownStallTicks;
|
||||
|
||||
private readonly Hook<AgentInterface.Delegates.Update>? _countdownTimerHook;
|
||||
public float LastCountDownValue;
|
||||
private bool _shouldRestartCombatTimer = true;
|
||||
private bool _lastMaxValueSet = false;
|
||||
|
||||
public readonly PullTimerState PullTimerState;
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (PullTimerState.Mocked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateCountDown();
|
||||
UpdateEncounterTimer();
|
||||
PullTimerState.InInstance = Plugin.Condition[ConditionFlag.BoundByDuty];
|
||||
}
|
||||
|
||||
private void CountdownTimerFunc(AgentInterface* agentInterface, uint frameCount)
|
||||
{
|
||||
_agentData = (ulong)agentInterface;
|
||||
_countdownTimerHook?.Original(agentInterface, frameCount);
|
||||
}
|
||||
|
||||
private void UpdateEncounterTimer()
|
||||
{
|
||||
if (Plugin.Condition[ConditionFlag.InCombat])
|
||||
{
|
||||
PullTimerState.InCombat = true;
|
||||
if (_shouldRestartCombatTimer)
|
||||
{
|
||||
_shouldRestartCombatTimer = false;
|
||||
_combatTimeStart = DateTime.Now;
|
||||
}
|
||||
|
||||
_combatTimeEnd = DateTime.Now;
|
||||
}
|
||||
else
|
||||
{
|
||||
PullTimerState.InCombat = false;
|
||||
_shouldRestartCombatTimer = true;
|
||||
}
|
||||
|
||||
PullTimerState.CombatStart = _combatTimeStart;
|
||||
PullTimerState.CombatDuration = _combatTimeEnd - _combatTimeStart;
|
||||
PullTimerState.CombatEnd = _combatTimeEnd;
|
||||
}
|
||||
|
||||
private void UpdateCountDown()
|
||||
{
|
||||
PullTimerState.CountingDown = false;
|
||||
|
||||
if (_agentData == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
byte countdownActive = Marshal.PtrToStructure<byte>((IntPtr)_agentData + 0x38);
|
||||
if (countdownActive == 0)
|
||||
{
|
||||
_lastMaxValueSet = false;
|
||||
return;
|
||||
}
|
||||
|
||||
float countDownPointerValue = Marshal.PtrToStructure<float>((IntPtr)_agentData + 0x2c);
|
||||
|
||||
// is last value close enough (workaround for floating point approx)
|
||||
if (Math.Abs(countDownPointerValue - LastCountDownValue) < 0.001f)
|
||||
{
|
||||
_countDownStallTicks++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_countDownStallTicks = 0;
|
||||
CountDownRunning = true;
|
||||
}
|
||||
|
||||
if (_countDownStallTicks > 50)
|
||||
{
|
||||
CountDownRunning = false;
|
||||
}
|
||||
|
||||
if (countDownPointerValue > 0 && CountDownRunning)
|
||||
{
|
||||
PullTimerState.CountDownValue = countDownPointerValue;
|
||||
PullTimerState.CountingDown = true;
|
||||
}
|
||||
|
||||
if (!_lastMaxValueSet && CountDownRunning)
|
||||
{
|
||||
PullTimerState.CountDownMax = countDownPointerValue;
|
||||
_lastMaxValueSet = true;
|
||||
}
|
||||
else if (_lastMaxValueSet && countDownPointerValue <= 0)
|
||||
{
|
||||
_lastMaxValueSet = false;
|
||||
}
|
||||
|
||||
LastCountDownValue = countDownPointerValue;
|
||||
}
|
||||
}
|
||||
|
||||
public class PullTimerState
|
||||
{
|
||||
private bool _inCombat;
|
||||
private bool _countingDown;
|
||||
public TimeSpan CombatDuration { get; set; }
|
||||
public DateTime CombatEnd { get; set; }
|
||||
public DateTime CombatStart { get; set; }
|
||||
|
||||
public bool Mocked { get; set; }
|
||||
|
||||
public bool InCombat
|
||||
{
|
||||
get => _inCombat;
|
||||
set
|
||||
{
|
||||
if (_inCombat == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_inCombat = value;
|
||||
InCombatChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public bool CountingDown
|
||||
{
|
||||
get => _countingDown;
|
||||
set
|
||||
{
|
||||
if (_countingDown == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_countingDown = value;
|
||||
CountingDownChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public bool InInstance { get; set; }
|
||||
|
||||
public float CountDownValue { get; set; } = 0f;
|
||||
public float CountDownMax { get; set; } = 0f;
|
||||
public event EventHandler? InCombatChanged;
|
||||
public event EventHandler? CountingDownChanged;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user