2023-02-20 03:34:32 +00:00
|
|
|
using System.Diagnostics;
|
2022-09-03 23:45:16 +00:00
|
|
|
using System.Numerics;
|
2022-09-09 09:42:46 +00:00
|
|
|
using Dalamud.Game.ClientState.Conditions;
|
2023-09-29 00:53:50 +00:00
|
|
|
using Dalamud.Plugin.Services;
|
2022-09-05 08:02:34 +00:00
|
|
|
using Lumina.Excel.GeneratedSheets;
|
2022-09-03 23:45:16 +00:00
|
|
|
using Newtonsoft.Json;
|
2022-09-04 05:03:59 +00:00
|
|
|
using OrangeGuidanceTomestone.Helpers;
|
2022-09-03 23:45:16 +00:00
|
|
|
|
|
|
|
namespace OrangeGuidanceTomestone;
|
|
|
|
|
|
|
|
internal class Messages : IDisposable {
|
2024-06-05 17:45:23 +00:00
|
|
|
internal const uint MaxAmount = 20;
|
|
|
|
|
2024-06-12 18:16:53 +00:00
|
|
|
internal static readonly string[] VfxPaths = [
|
2022-09-06 04:24:32 +00:00
|
|
|
"bg/ffxiv/fst_f1/common/vfx/eff/b0941trp1a_o.avfx",
|
|
|
|
"bg/ffxiv/fst_f1/common/vfx/eff/b0941trp1b_o.avfx",
|
|
|
|
"bg/ffxiv/fst_f1/common/vfx/eff/b0941trp1c_o.avfx",
|
|
|
|
"bg/ffxiv/fst_f1/common/vfx/eff/b0941trp1d_o.avfx",
|
|
|
|
"bg/ffxiv/fst_f1/common/vfx/eff/b0941trp1e_o.avfx",
|
2022-09-09 09:39:33 +00:00
|
|
|
"bg/ex2/02_est_e3/common/vfx/eff/b0941trp1f_o.avfx",
|
2024-06-12 18:16:53 +00:00
|
|
|
"bg/ex4/07_lak_l5/common/vfx/eff/b2640trp1g_o.avfx",
|
|
|
|
];
|
2022-09-03 23:45:16 +00:00
|
|
|
|
2023-09-29 00:53:50 +00:00
|
|
|
private static string GetPath(IDataManager data, Message message) {
|
2022-09-11 23:57:33 +00:00
|
|
|
var glyph = message.Glyph;
|
|
|
|
if (glyph < 0 || glyph >= VfxPaths.Length) {
|
|
|
|
// not checking if this exists, but the check is really only for the
|
|
|
|
// last file in the array anyway. we're guaranteed to have these
|
|
|
|
// files with an up-to-date install
|
|
|
|
return VfxPaths[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.FileExists(VfxPaths[glyph])
|
|
|
|
? VfxPaths[glyph]
|
|
|
|
: VfxPaths[message.Id.ToByteArray()[^1] % 5];
|
|
|
|
}
|
2022-09-09 09:42:46 +00:00
|
|
|
|
2022-09-03 23:45:16 +00:00
|
|
|
private Plugin Plugin { get; }
|
|
|
|
|
2022-09-04 02:23:35 +00:00
|
|
|
private SemaphoreSlim CurrentMutex { get; } = new(1, 1);
|
2022-09-04 02:52:18 +00:00
|
|
|
private Dictionary<Guid, Message> Current { get; } = new();
|
2022-09-03 23:45:16 +00:00
|
|
|
private Queue<Message> SpawnQueue { get; } = new();
|
|
|
|
|
2024-06-12 18:16:53 +00:00
|
|
|
private HashSet<uint> Trials { get; } = [];
|
|
|
|
private HashSet<uint> DeepDungeons { get; } = [];
|
2022-09-05 08:02:34 +00:00
|
|
|
|
2022-09-09 09:42:46 +00:00
|
|
|
private bool CutsceneActive {
|
|
|
|
get {
|
|
|
|
var condition = this.Plugin.Condition;
|
|
|
|
return condition[ConditionFlag.OccupiedInCutSceneEvent]
|
|
|
|
|| condition[ConditionFlag.WatchingCutscene78];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private bool GposeActive {
|
|
|
|
get {
|
|
|
|
var condition = this.Plugin.Condition;
|
|
|
|
return condition[ConditionFlag.WatchingCutscene];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-09 09:49:52 +00:00
|
|
|
private bool _inCutscene;
|
|
|
|
private bool _inGpose;
|
|
|
|
|
2022-09-03 23:45:16 +00:00
|
|
|
internal Messages(Plugin plugin) {
|
|
|
|
this.Plugin = plugin;
|
|
|
|
|
2022-09-05 08:02:34 +00:00
|
|
|
foreach (var cfc in this.Plugin.DataManager.GetExcelSheet<ContentFinderCondition>()!) {
|
2022-09-06 02:45:45 +00:00
|
|
|
// Trials, Raids, and Ultimate Raids
|
|
|
|
if (cfc.ContentType.Row is 4 or 5 or 28) {
|
|
|
|
// "Raids" - but we only want non-alliance raids
|
|
|
|
if (cfc.ContentType.Row == 5 && cfc.ContentMemberType.Row == 4) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-09-05 08:02:34 +00:00
|
|
|
this.Trials.Add(cfc.TerritoryType.Row);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfc.ContentType.Row == 21) {
|
|
|
|
this.DeepDungeons.Add(cfc.TerritoryType.Row);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-04 20:39:09 +00:00
|
|
|
if (this.Plugin.Config.ApiKey != string.Empty) {
|
|
|
|
this.SpawnVfx();
|
|
|
|
}
|
2022-09-03 23:45:16 +00:00
|
|
|
|
2023-02-20 03:34:32 +00:00
|
|
|
this.Plugin.Framework.Update += this.DetermineIfSpawn;
|
2022-09-09 09:49:52 +00:00
|
|
|
this.Plugin.Framework.Update += this.RemoveConditionally;
|
2022-09-03 23:45:16 +00:00
|
|
|
this.Plugin.Framework.Update += this.HandleSpawnQueue;
|
2023-02-20 03:44:59 +00:00
|
|
|
this.Plugin.ClientState.TerritoryChanged += this.TerritoryChanged;
|
2022-09-03 23:45:16 +00:00
|
|
|
this.Plugin.ClientState.Login += this.SpawnVfx;
|
|
|
|
this.Plugin.ClientState.Logout += this.RemoveVfx;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose() {
|
|
|
|
this.Plugin.ClientState.Logout -= this.RemoveVfx;
|
|
|
|
this.Plugin.ClientState.Login -= this.SpawnVfx;
|
2023-02-20 03:44:59 +00:00
|
|
|
this.Plugin.ClientState.TerritoryChanged -= this.TerritoryChanged;
|
2022-09-03 23:45:16 +00:00
|
|
|
this.Plugin.Framework.Update -= this.HandleSpawnQueue;
|
2022-09-09 09:49:52 +00:00
|
|
|
this.Plugin.Framework.Update -= this.RemoveConditionally;
|
2023-02-20 03:34:32 +00:00
|
|
|
this.Plugin.Framework.Update -= this.DetermineIfSpawn;
|
2022-09-04 02:11:18 +00:00
|
|
|
|
2022-09-05 08:02:34 +00:00
|
|
|
this.RemoveVfx();
|
2022-09-03 23:45:16 +00:00
|
|
|
}
|
|
|
|
|
2023-02-20 03:34:32 +00:00
|
|
|
private readonly Stopwatch _timer = new();
|
2023-02-20 03:44:59 +00:00
|
|
|
|
2023-09-29 00:53:50 +00:00
|
|
|
private void TerritoryChanged(ushort territory) {
|
2023-02-20 03:44:59 +00:00
|
|
|
this._territoryChanged = true;
|
|
|
|
this.RemoveVfx();
|
|
|
|
}
|
|
|
|
|
2023-02-20 03:34:32 +00:00
|
|
|
private ushort _lastTerritory;
|
2023-02-20 03:44:59 +00:00
|
|
|
private bool _territoryChanged;
|
2023-02-20 03:34:32 +00:00
|
|
|
|
2023-09-29 00:53:50 +00:00
|
|
|
private void DetermineIfSpawn(IFramework framework) {
|
2023-02-20 03:34:32 +00:00
|
|
|
var current = this.Plugin.ClientState.TerritoryType;
|
2023-02-20 03:44:59 +00:00
|
|
|
|
|
|
|
var diffTerritory = current != this._lastTerritory;
|
|
|
|
var playerPresent = this.Plugin.ClientState.LocalPlayer != null;
|
|
|
|
|
|
|
|
if ((this._territoryChanged || diffTerritory) && playerPresent) {
|
|
|
|
this._territoryChanged = false;
|
2023-02-20 03:34:32 +00:00
|
|
|
this._timer.Start();
|
|
|
|
}
|
|
|
|
|
2023-02-20 03:44:59 +00:00
|
|
|
if (this._timer.Elapsed >= TimeSpan.FromSeconds(1.5)) {
|
2023-02-20 03:34:32 +00:00
|
|
|
this._timer.Reset();
|
|
|
|
this.SpawnVfx();
|
|
|
|
}
|
|
|
|
|
|
|
|
this._lastTerritory = current;
|
|
|
|
}
|
|
|
|
|
2023-09-29 00:53:50 +00:00
|
|
|
private void RemoveConditionally(IFramework framework) {
|
2022-09-09 09:49:52 +00:00
|
|
|
var nowCutscene = this.CutsceneActive;
|
|
|
|
var cutsceneChanged = this._inCutscene != nowCutscene;
|
|
|
|
if (this.Plugin.Config.DisableInCutscene && cutsceneChanged) {
|
|
|
|
if (nowCutscene) {
|
|
|
|
this.RemoveVfx();
|
|
|
|
} else {
|
|
|
|
this.SpawnVfx();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var nowGpose = this.GposeActive;
|
|
|
|
var gposeChanged = this._inGpose != nowGpose;
|
2022-09-09 09:52:10 +00:00
|
|
|
if (this.Plugin.Config.DisableInGpose && gposeChanged) {
|
2022-09-09 09:49:52 +00:00
|
|
|
if (nowGpose) {
|
|
|
|
this.RemoveVfx();
|
|
|
|
} else {
|
|
|
|
this.SpawnVfx();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this._inCutscene = nowCutscene;
|
|
|
|
this._inGpose = nowGpose;
|
|
|
|
}
|
|
|
|
|
2023-09-29 00:53:50 +00:00
|
|
|
private unsafe void HandleSpawnQueue(IFramework framework) {
|
2022-09-03 23:45:16 +00:00
|
|
|
if (!this.SpawnQueue.TryDequeue(out var message)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-09-29 00:53:50 +00:00
|
|
|
Plugin.Log.Debug($"spawning vfx for {message.Id}");
|
2022-09-04 21:02:13 +00:00
|
|
|
var rotation = Quaternion.CreateFromYawPitchRoll(message.Yaw, 0, 0);
|
2022-09-11 23:57:33 +00:00
|
|
|
var path = GetPath(this.Plugin.DataManager, message);
|
2022-09-06 04:24:32 +00:00
|
|
|
if (this.Plugin.Vfx.SpawnStatic(message.Id, path, message.Position, rotation) == null) {
|
2023-09-29 00:53:50 +00:00
|
|
|
Plugin.Log.Debug("trying again");
|
2022-09-04 02:52:18 +00:00
|
|
|
this.SpawnQueue.Enqueue(message);
|
|
|
|
}
|
2022-09-03 23:45:16 +00:00
|
|
|
}
|
|
|
|
|
2022-09-04 05:10:48 +00:00
|
|
|
internal void SpawnVfx() {
|
2022-09-03 23:45:16 +00:00
|
|
|
var territory = this.Plugin.ClientState.TerritoryType;
|
2022-09-04 23:43:58 +00:00
|
|
|
if (territory == 0 || this.Plugin.Config.BannedTerritories.Contains(territory)) {
|
2022-09-03 23:45:16 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-20 04:52:04 +00:00
|
|
|
var world = this.Plugin.ClientState.LocalPlayer?.CurrentWorld.Id ?? 0;
|
|
|
|
if (world == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-20 02:55:17 +00:00
|
|
|
var housing = this.Plugin.Common.Functions.Housing.Location;
|
|
|
|
var ward = housing?.Ward;
|
2023-02-20 03:13:29 +00:00
|
|
|
var plot = housing?.CombinedPlot();
|
2023-01-29 09:26:58 +00:00
|
|
|
|
2022-09-05 08:02:34 +00:00
|
|
|
if (this.Plugin.Config.DisableTrials && this.Trials.Contains(territory)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.Plugin.Config.DisableDeepDungeon && this.DeepDungeons.Contains(territory)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-09-09 09:42:46 +00:00
|
|
|
if (this.Plugin.Config.DisableInCutscene && this.CutsceneActive) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.Plugin.Config.DisableInGpose && this.GposeActive) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-09-03 23:45:16 +00:00
|
|
|
this.RemoveVfx(null, null);
|
|
|
|
|
|
|
|
Task.Run(async () => {
|
2023-01-22 00:03:43 +00:00
|
|
|
try {
|
2023-02-20 04:52:04 +00:00
|
|
|
await this.DownloadMessages(world, territory, ward, plot);
|
2023-01-22 00:03:43 +00:00
|
|
|
} catch (Exception ex) {
|
2023-09-29 00:53:50 +00:00
|
|
|
Plugin.Log.Error(ex, $"Failed to get messages for territory {territory}");
|
2022-09-03 23:45:16 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-02-20 04:52:04 +00:00
|
|
|
private async Task DownloadMessages(uint world, ushort territory, ushort? ward, ushort? plot) {
|
2023-01-29 09:26:58 +00:00
|
|
|
var route = $"/messages/{territory}";
|
|
|
|
if (ward != null) {
|
|
|
|
route += $"?ward={ward}";
|
2023-02-20 02:55:17 +00:00
|
|
|
|
|
|
|
if (plot != null) {
|
|
|
|
route += $"&plot={plot}";
|
|
|
|
}
|
2023-02-20 04:52:04 +00:00
|
|
|
|
|
|
|
route += $"&world={world}";
|
2023-01-29 09:26:58 +00:00
|
|
|
}
|
|
|
|
|
2023-01-22 00:03:43 +00:00
|
|
|
var resp = await ServerHelper.SendRequest(
|
|
|
|
this.Plugin.Config.ApiKey,
|
|
|
|
HttpMethod.Get,
|
2023-01-29 09:26:58 +00:00
|
|
|
route
|
2023-01-22 00:03:43 +00:00
|
|
|
);
|
|
|
|
var json = await resp.Content.ReadAsStringAsync();
|
|
|
|
var messages = JsonConvert.DeserializeObject<Message[]>(json)!;
|
|
|
|
|
|
|
|
await this.CurrentMutex.WaitAsync();
|
2023-01-22 03:42:03 +00:00
|
|
|
try {
|
|
|
|
this.Current.Clear();
|
2023-01-22 00:03:43 +00:00
|
|
|
|
2023-01-22 03:42:03 +00:00
|
|
|
foreach (var message in messages) {
|
|
|
|
this.Current[message.Id] = message;
|
|
|
|
this.SpawnQueue.Enqueue(message);
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
this.CurrentMutex.Release();
|
2023-01-22 00:03:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-03 23:45:16 +00:00
|
|
|
private void RemoveVfx(object? sender, EventArgs? e) {
|
2022-09-04 23:46:23 +00:00
|
|
|
this.RemoveVfx();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void RemoveVfx() {
|
2022-09-03 23:45:16 +00:00
|
|
|
this.Plugin.Vfx.RemoveAll();
|
|
|
|
}
|
2022-09-04 02:23:35 +00:00
|
|
|
|
2022-09-05 08:02:34 +00:00
|
|
|
internal void Clear() {
|
|
|
|
this.CurrentMutex.Wait();
|
2023-01-22 03:42:03 +00:00
|
|
|
try {
|
|
|
|
this.Current.Clear();
|
|
|
|
} finally {
|
|
|
|
this.CurrentMutex.Release();
|
|
|
|
}
|
2022-09-05 08:02:34 +00:00
|
|
|
}
|
|
|
|
|
2022-09-04 02:52:18 +00:00
|
|
|
internal IEnumerable<Message> Nearby() {
|
2022-09-04 02:23:35 +00:00
|
|
|
if (this.Plugin.ClientState.LocalPlayer is not { } player) {
|
|
|
|
return Array.Empty<Message>();
|
|
|
|
}
|
|
|
|
|
|
|
|
var position = player.Position;
|
|
|
|
|
2023-01-22 03:42:03 +00:00
|
|
|
List<Message> nearby;
|
2022-09-04 02:52:18 +00:00
|
|
|
this.CurrentMutex.Wait();
|
2023-01-22 03:42:03 +00:00
|
|
|
try {
|
|
|
|
nearby = this.Current
|
|
|
|
.Values
|
|
|
|
.Where(msg => Math.Abs(msg.Position.Y - position.Y) <= 1f)
|
|
|
|
.Where(msg => Vector3.Distance(msg.Position, position) <= 2f)
|
|
|
|
.ToList();
|
|
|
|
} finally {
|
|
|
|
this.CurrentMutex.Release();
|
|
|
|
}
|
|
|
|
|
2022-09-04 02:52:18 +00:00
|
|
|
|
|
|
|
return nearby;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void Add(Message message) {
|
2022-09-04 05:19:30 +00:00
|
|
|
this.CurrentMutex.Wait();
|
2023-01-22 03:42:03 +00:00
|
|
|
try {
|
|
|
|
this.Current[message.Id] = message;
|
|
|
|
} finally {
|
|
|
|
this.CurrentMutex.Release();
|
|
|
|
}
|
|
|
|
|
2022-09-04 02:52:18 +00:00
|
|
|
this.SpawnQueue.Enqueue(message);
|
2022-09-04 02:23:35 +00:00
|
|
|
}
|
2022-09-04 05:19:30 +00:00
|
|
|
|
|
|
|
internal void Remove(Guid id) {
|
|
|
|
this.CurrentMutex.Wait();
|
2023-01-22 03:42:03 +00:00
|
|
|
try {
|
|
|
|
this.Current.Remove(id);
|
|
|
|
} finally {
|
|
|
|
this.CurrentMutex.Release();
|
|
|
|
}
|
2022-09-04 05:19:30 +00:00
|
|
|
}
|
2022-09-03 23:45:16 +00:00
|
|
|
}
|