using System; using System.Collections.Generic; using System.Numerics; using FFXIVClientStructs.FFXIV.Client.Game.Fate; using FFXIVClientStructs.FFXIV.Client.Game.InstanceContent; using FFXIVClientStructs.FFXIV.Client.Game.WKS; namespace LLib.GameData; public sealed class PublicEventHelper { public unsafe IReadOnlyList GetActiveFates() { FateManager* ptr = FateManager.Instance(); if (ptr == null) { return Array.Empty(); } List list = new List(); for (int i = 0; i < ptr->Fates.Count; i++) { FateContext* value = ptr->Fates[i].Value; if (value != null) { FateState state = value->State; if (state - 3 <= FateState.Unk1) { list.Add(ToPublicEvent(value)); } } } return list; } public unsafe IReadOnlyList GetActiveDynamicEvents() { DynamicEventContainer* instance = DynamicEventContainer.GetInstance(); if (instance == null) { return Array.Empty(); } List list = new List(); Span events = instance->Events; for (int i = 0; i < events.Length; i++) { ref DynamicEvent reference = ref events[i]; if (reference.State != DynamicEventState.Inactive) { list.Add(ToPublicEvent(ref reference)); } } return list; } public unsafe IReadOnlyList GetActiveMechaEvents() { WKSManager* ptr = WKSManager.Instance(); if (ptr == null) { return Array.Empty(); } WKSMechaEventModule* mechaEventModule = ptr->MechaEventModule; if (mechaEventModule == null) { return Array.Empty(); } List list = new List(); Span events = mechaEventModule->Events; for (int i = 0; i < events.Length; i++) { ref WKSMechaEvent reference = ref events[i]; if ((reference.Flags & WKSMechaEventFlag.IsEventActive) != 0) { list.Add(ToPublicEvent(ref reference)); } } return list; } public IReadOnlyList GetAllActiveEvents() { List list = new List(); list.AddRange(GetActiveFates()); list.AddRange(GetActiveDynamicEvents()); list.AddRange(GetActiveMechaEvents()); return list; } public PublicEvent? GetEventById(EPublicEventType type, uint id) { return type switch { EPublicEventType.Fate => GetFateById((ushort)id), EPublicEventType.DynamicEvent => GetDynamicEventById((ushort)id), EPublicEventType.MechaEvent => GetMechaEventById(id), _ => null, }; } public unsafe PublicEvent? GetCurrentPlayerEvent() { FateManager* ptr = FateManager.Instance(); if (ptr == null || ptr->CurrentFate == null) { return null; } return ToPublicEvent(ptr->CurrentFate); } public unsafe PublicEvent? GetCurrentDynamicEvent() { DynamicEventContainer* instance = DynamicEventContainer.GetInstance(); if (instance == null) { return null; } DynamicEvent* currentEvent = instance->GetCurrentEvent(); if (currentEvent == null || currentEvent->State == DynamicEventState.Inactive) { return null; } return ToPublicEvent(ref *currentEvent); } public unsafe PublicEvent? GetCurrentMechaEvent() { WKSManager* ptr = WKSManager.Instance(); if (ptr == null) { return null; } WKSMechaEventModule* mechaEventModule = ptr->MechaEventModule; if (mechaEventModule == null) { return null; } WKSMechaEvent* currentEvent = mechaEventModule->CurrentEvent; if (currentEvent == null || (currentEvent->Flags & WKSMechaEventFlag.IsEventActive) == 0) { return null; } return ToPublicEvent(ref *currentEvent); } public unsafe bool IsFateActive(ushort fateId) { if (fateId == 0) { return false; } FateManager* ptr = FateManager.Instance(); if (ptr == null) { return false; } FateContext* fateById = ptr->GetFateById(fateId); if (fateById == null) { return false; } return fateById->State == FateState.Running; } private unsafe PublicEvent? GetFateById(ushort fateId) { if (fateId == 0) { return null; } FateManager* ptr = FateManager.Instance(); if (ptr == null) { return null; } FateContext* fateById = ptr->GetFateById(fateId); if (fateById == null) { return null; } return ToPublicEvent(fateById); } private unsafe PublicEvent? GetDynamicEventById(ushort dynamicEventId) { if (dynamicEventId == 0) { return null; } DynamicEventContainer* instance = DynamicEventContainer.GetInstance(); if (instance == null) { return null; } Span events = instance->Events; for (int i = 0; i < events.Length; i++) { ref DynamicEvent reference = ref events[i]; if (reference.DynamicEventId == dynamicEventId && reference.State != DynamicEventState.Inactive) { return ToPublicEvent(ref reference); } } return null; } private unsafe PublicEvent? GetMechaEventById(uint mechaEventDataRowId) { if (mechaEventDataRowId == 0) { return null; } WKSManager* ptr = WKSManager.Instance(); if (ptr == null) { return null; } WKSMechaEventModule* mechaEventModule = ptr->MechaEventModule; if (mechaEventModule == null) { return null; } Span events = mechaEventModule->Events; for (int i = 0; i < events.Length; i++) { ref WKSMechaEvent reference = ref events[i]; if (reference.WKSMechaEventDataRowId == mechaEventDataRowId && (reference.Flags & WKSMechaEventFlag.IsEventActive) != 0) { return ToPublicEvent(ref reference); } } return null; } private unsafe static PublicEvent ToPublicEvent(FateContext* fate) { float num = fate->Duration; float timeRemaining = 0f; if (num > 0f && fate->StartTimeEpoch > 0) { float num2 = DateTimeOffset.UtcNow.ToUnixTimeSeconds() - fate->StartTimeEpoch; timeRemaining = Math.Max(0f, num - num2); } return new PublicEvent(EPublicEventType.Fate, fate->FateId, fate->Name.ToString(), fate->Location, fate->Radius, fate->Level, fate->MaxLevel, (int)fate->Progress, num, timeRemaining, fate->State == FateState.Running, fate->State == FateState.Preparing, fate->IsBonus, fate->Rule, fate->MotivationNpc, fate->FATEChain); } private static PublicEvent ToPublicEvent(ref DynamicEvent dynamicEvent) { float num = dynamicEvent.SecondsDuration; float num2 = dynamicEvent.SecondsLeft; uint dynamicEventId = dynamicEvent.DynamicEventId; string name = dynamicEvent.Name.ToString(); Vector3 position = dynamicEvent.MapMarker.Position; float radius = dynamicEvent.MapMarker.Radius; float progress = (int)dynamicEvent.Progress; float duration = num; float timeRemaining = num2; bool isActive = dynamicEvent.State == DynamicEventState.Battle; DynamicEventState state = dynamicEvent.State; bool isPending = state - 1 <= DynamicEventState.Register; return new PublicEvent(EPublicEventType.DynamicEvent, dynamicEventId, name, position, radius, 0, 0, progress, duration, timeRemaining, isActive, isPending, HasBonus: false, 0, 0u, 0); } private unsafe static PublicEvent ToPublicEvent(ref WKSMechaEvent mechaEvent) { long num = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); float duration = ((mechaEvent.EventEndTimestamp > mechaEvent.EventStartTimestamp) ? ((float)(mechaEvent.EventEndTimestamp - mechaEvent.EventStartTimestamp)) : 0f); float timeRemaining = ((mechaEvent.EventEndTimestamp > num) ? ((float)(mechaEvent.EventEndTimestamp - num)) : 0f); Vector3 position = Vector3.Zero; float radius = 0f; string name = string.Empty; if (mechaEvent.MapMarkerPtrs.Count > 0) { WKSMechaEventMapMarker* value = mechaEvent.MapMarkerPtrs[0].Value; if (value != null) { name = value->Name.ToString(); position = value->MapMarkerData.Position; radius = value->MapMarkerData.Radius; } } float progress = ((mechaEvent.EventProgressMax > 0) ? ((float)mechaEvent.EventProgress / (float)mechaEvent.EventProgressMax * 100f) : 0f); bool isPending = (mechaEvent.Flags & WKSMechaEventFlag.PilotRegistrationOpen) != 0 && (mechaEvent.Flags & WKSMechaEventFlag.IsEventActive) != 0 && mechaEvent.EventProgress == 0; return new PublicEvent(EPublicEventType.MechaEvent, mechaEvent.WKSMechaEventDataRowId, name, position, radius, 0, 0, progress, duration, timeRemaining, (mechaEvent.Flags & WKSMechaEventFlag.IsEventActive) != 0, isPending, HasBonus: false, 0, 0u, 0); } }