HUDManager/HUD Manager/Statuses.cs

176 lines
5.9 KiB
C#
Raw Permalink Normal View History

2021-03-08 15:03:11 +00:00
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
2021-08-24 01:21:29 +00:00
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Lumina.Excel.GeneratedSheets;
using Newtonsoft.Json;
2020-09-03 02:24:34 +00:00
using Newtonsoft.Json.Converters;
2021-08-24 01:21:29 +00:00
using Condition = Dalamud.Game.ClientState.Conditions.Condition;
// TODO: Zone swaps?
2021-03-08 15:03:11 +00:00
namespace HUD_Manager {
public class Statuses {
2021-03-08 15:03:11 +00:00
private Plugin Plugin { get; }
2021-03-08 15:03:11 +00:00
private readonly Dictionary<Status, bool> _condition = new();
private ClassJob? _job;
2021-08-24 01:21:29 +00:00
internal static byte GetStatus(GameObject obj) {
2021-04-15 20:38:18 +00:00
// Updated: 5.5
// 40 57 48 83 EC 70 48 8B F9 E8 ?? ?? ?? ?? 81 BF ?? ?? ?? ?? ?? ?? ?? ??
const int offset = 0x19A0;
2021-08-24 01:21:29 +00:00
return Marshal.ReadByte(obj.Address + offset);
}
2021-08-24 01:21:29 +00:00
internal static byte GetOnlineStatus(GameObject obj) {
2021-04-15 20:38:18 +00:00
// Updated: 5.5
// E8 ?? ?? ?? ?? 48 85 C0 75 54
const int offset = 0x197F;
2021-08-24 01:21:29 +00:00
return Marshal.ReadByte(obj.Address + offset);
}
2021-08-24 01:21:29 +00:00
internal static byte GetBardThing(GameObject obj) {
2021-04-15 20:38:18 +00:00
// Updated: 5.5
// E8 ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 0F B6 43 50
const int offset = 0x197C;
2021-08-24 01:21:29 +00:00
return Marshal.ReadByte(obj.Address + offset);
2021-03-14 22:52:37 +00:00
}
2021-03-08 15:03:11 +00:00
public Statuses(Plugin plugin) {
this.Plugin = plugin;
}
2021-03-08 15:03:11 +00:00
public bool Update(PlayerCharacter? player) {
if (player == null) {
return false;
}
2021-03-08 15:03:11 +00:00
var anyChanged = false;
2021-08-24 01:21:29 +00:00
var currentJob = this.Plugin.DataManager.GetExcelSheet<ClassJob>()!.GetRow(player.ClassJob.Id);
2021-03-08 15:03:11 +00:00
if (this._job != null && this._job != currentJob) {
anyChanged = true;
}
2021-03-08 15:03:11 +00:00
this._job = currentJob;
foreach (Status status in Enum.GetValues(typeof(Status))) {
2021-03-08 15:03:11 +00:00
var old = this._condition.ContainsKey(status) && this._condition[status];
2021-08-24 01:21:29 +00:00
this._condition[status] = status.Active(player, this.Plugin.Condition);
2021-03-08 15:03:11 +00:00
anyChanged |= old != this._condition[status];
}
return anyChanged;
}
2021-03-08 15:03:11 +00:00
private Guid CalculateCurrentHud() {
2021-08-24 01:21:29 +00:00
var player = this.Plugin.ClientState.LocalPlayer;
if (player == null) {
return Guid.Empty;
}
2021-03-08 15:03:11 +00:00
foreach (var match in this.Plugin.Config.HudConditionMatches) {
if ((!match.Status.HasValue || this._condition[match.Status.Value]) &&
2021-08-24 01:21:29 +00:00
(match.ClassJob == null || this._job?.Abbreviation?.ToString() == match.ClassJob)) {
return match.LayoutId;
2020-09-03 02:24:34 +00:00
}
}
2020-09-03 02:24:34 +00:00
return Guid.Empty;
}
2021-03-08 15:03:11 +00:00
public void SetHudLayout(PlayerCharacter? player, bool update = false) {
if (update && player != null) {
this.Update(player);
}
2021-03-08 15:03:11 +00:00
var layoutId = this.CalculateCurrentHud();
if (layoutId == Guid.Empty) {
return; // FIXME: do something better
}
2021-03-19 18:40:07 +00:00
if (!this.Plugin.Config.Layouts.ContainsKey(layoutId)) {
return; // FIXME: do something better
}
this.Plugin.Hud.WriteEffectiveLayout(this.Plugin.Config.StagingSlot, layoutId);
2021-03-08 15:03:11 +00:00
this.Plugin.Hud.SelectSlot(this.Plugin.Config.StagingSlot, true);
}
}
public class HudConditionMatch {
/// <summary>
/// Values stored here should be the abbreviation of the class/job name (all caps).
/// We do this because using <see cref="ClassJob"/> results in circular dependency errors when serializing.
/// </summary>
2021-03-08 15:03:11 +00:00
public string? ClassJob { get; set; }
2020-09-03 02:24:34 +00:00
[JsonConverter(typeof(StringEnumConverter))]
public Status? Status { get; set; }
public Guid LayoutId { get; set; }
}
2020-09-03 02:24:34 +00:00
// Note: Changing the names of these is a breaking change
public enum Status {
InCombat = ConditionFlag.InCombat,
WeaponDrawn = -1,
InInstance = ConditionFlag.BoundByDuty,
Crafting = ConditionFlag.Crafting,
Gathering = ConditionFlag.Gathering,
Fishing = ConditionFlag.Fishing,
Roleplaying = -2,
2021-03-14 22:52:37 +00:00
PlayingMusic = -3,
}
public static class StatusExtensions {
public static string Name(this Status status) {
switch (status) {
case Status.InCombat:
return "In combat";
case Status.WeaponDrawn:
return "Weapon drawn";
case Status.InInstance:
return "In instance";
case Status.Crafting:
return "Crafting";
case Status.Gathering:
return "Gathering";
case Status.Fishing:
return "Fishing";
case Status.Roleplaying:
return "Roleplaying";
2021-03-14 22:52:37 +00:00
case Status.PlayingMusic:
return "Playing music";
}
throw new ApplicationException($"No name was set up for {status}");
}
2021-08-24 01:21:29 +00:00
public static bool Active(this Status status, PlayerCharacter player, Condition condition) {
2020-07-31 17:58:56 +00:00
if (player == null) {
throw new ArgumentNullException(nameof(player), "PlayerCharacter cannot be null");
}
if (status > 0) {
var flag = (ConditionFlag) status;
2021-08-24 01:21:29 +00:00
return condition[flag];
}
switch (status) {
case Status.WeaponDrawn:
return (Statuses.GetStatus(player) & 4) > 0;
case Status.Roleplaying:
return Statuses.GetOnlineStatus(player) == 22;
2021-03-14 22:52:37 +00:00
case Status.PlayingMusic:
return Statuses.GetBardThing(player) == 16;
}
return false;
}
}
}