using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Types; using HSUI.Config; using HSUI.Helpers; using HSUI.Interface.GeneralElements; using FFXIVClientStructs.FFXIV.Client.Game.Control; using FFXIVClientStructs.FFXIV.Client.Graphics; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.System.Framework; using FFXIVClientStructs.FFXIV.Common.Component.BGCollision; using System.Numerics; namespace HSUI.Interface.Nameplates { internal class NameplatesHud : HudElement { private NameplatesGeneralConfig Config => (NameplatesGeneralConfig)_config; private NameplateWithPlayerBar _playerHud; private NameplateWithEnemyBar _enemyHud; private NameplateWithPlayerBar _partyMemberHud; private NameplateWithPlayerBar _allianceMemberHud; private NameplateWithPlayerBar _friendsHud; private NameplateWithPlayerBar _otherPlayersHud; private NameplateWithBar _petHud; private NameplateWithBar _npcHud; private Nameplate _minionNPCHud; private Nameplate _objectHud; private bool _wasHovering; public NameplatesHud(NameplatesGeneralConfig config) : base(config) { ConfigurationManager manager = ConfigurationManager.Instance; _playerHud = new NameplateWithPlayerBar(manager.GetConfigObject()); _enemyHud = new NameplateWithEnemyBar(manager.GetConfigObject()); _partyMemberHud = new NameplateWithPlayerBar(manager.GetConfigObject()); _allianceMemberHud = new NameplateWithPlayerBar(manager.GetConfigObject()); _friendsHud = new NameplateWithPlayerBar(manager.GetConfigObject()); _otherPlayersHud = new NameplateWithPlayerBar(manager.GetConfigObject()); _petHud = new NameplateWithBar(manager.GetConfigObject()); _npcHud = new NameplateWithBar(manager.GetConfigObject()); _minionNPCHud = new Nameplate(manager.GetConfigObject()); _objectHud = new Nameplate(manager.GetConfigObject()); } public void StopPreview() { _enemyHud.StopPreview(); } public void StopMouseover() { if (_wasHovering) { InputsHelper.Instance.ClearTarget(); _wasHovering = false; } } protected override void CreateDrawActions(Vector2 origin) { if (!_config.Enabled || NameplatesManager.Instance == null) { StopMouseover(); return; } IGameObject? mouseoveredActor = null; bool ignoreMouseover = false; foreach (NameplateData data in NameplatesManager.Instance.Data) { Nameplate? nameplate = GetNameplate(data); if (nameplate == null || !nameplate.Enabled) { continue; } // raycasting if (IsPointObstructed(data)) { continue; } if (nameplate is NameplateWithBar nameplateWithBar) { // draw bar AddDrawActions(nameplateWithBar.GetBarDrawActions(data)); // find mouseovered nameplate var (isHovering, ignore) = nameplateWithBar.GetMouseoverState(data); if (isHovering) { mouseoveredActor = data.GameObject; ignoreMouseover = ignore; } } // draw elements AddDrawActions(nameplate.GetElementsDrawActions(data)); } // mouseover if (mouseoveredActor != null) { _wasHovering = true; InputsHelper.Instance.SetTarget(mouseoveredActor, ignoreMouseover); if (InputsHelper.Instance.LeftButtonClicked) { Plugin.TargetManager.Target = mouseoveredActor; InputsHelper.Instance.ClearClicks(); } else if (InputsHelper.Instance.RightButtonClicked) { InputsHelper.Instance.ClearClicks(); } } else if (_wasHovering) { InputsHelper.Instance.ClearTarget(); _wasHovering = false; } } private unsafe Nameplate? GetNameplate(NameplateData data) { switch (data.Kind) { case ObjectKind.Player: if (data.GameObject?.EntityId == Plugin.ObjectTable.LocalPlayer?.EntityId) { return _playerHud; } if (data.GameObject is ICharacter character2) { if ((character2.StatusFlags & StatusFlags.PartyMember) != 0) // PartyMember { return _partyMemberHud; } else if ((character2.StatusFlags & StatusFlags.AllianceMember) != 0) // AllianceMember { return _allianceMemberHud; } else if ((character2.StatusFlags & StatusFlags.Friend) != 0) // Friend { return _friendsHud; } } return _otherPlayersHud; case ObjectKind.BattleNpc: if (data.GameObject is IBattleNpc battleNpc) { if ((BattleNpcSubKind)battleNpc.SubKind == BattleNpcSubKind.Pet || (BattleNpcSubKind)battleNpc.SubKind == BattleNpcSubKind.Chocobo) { return _petHud; } else if ((BattleNpcSubKind)battleNpc.SubKind == BattleNpcSubKind.Enemy || (BattleNpcSubKind)battleNpc.SubKind == BattleNpcSubKind.BattleNpcPart) { return Utils.IsHostile(data.GameObject) ? _enemyHud : _npcHud; } else if (battleNpc.SubKind == 10) // island released minions { return _npcHud; } } break; case ObjectKind.EventNpc: return _npcHud; case ObjectKind.Companion: return _minionNPCHud; default: return _objectHud; } return null; } private unsafe bool IsPointObstructed(NameplateData data) { if (data.GameObject == null) { return true; } if (Config.OcclusionMode == NameplatesOcclusionMode.None || data.IgnoreOcclusion) { return false; } Camera camera = Control.Instance()->CameraManager.Camera->CameraBase.SceneCamera; Vector3 cameraPos = camera.Object.Position; BGCollisionModule* collisionModule = Framework.Instance()->BGCollisionModule; if (collisionModule == null) { return false; } int flag = Config.RaycastFlag(); int* flags = stackalloc int[] { flag, 0, flag, 0 }; bool obstructed = false; // simple mode if (Config.OcclusionMode == NameplatesOcclusionMode.Simple) { Vector3 direction = Vector3.Normalize(data.WorldPosition - cameraPos); RaycastHit hit; obstructed = collisionModule->RaycastMaterialFilter(&hit, &cameraPos, &direction, data.Distance, 1, flags); } // full mode else { int obstructionCount = 0; Vector2[] points = new Vector2[] { data.ScreenPosition + new Vector2(-30, 0), // left data.ScreenPosition + new Vector2(30, 0), // right }; foreach (Vector2 point in points) { Ray ray = camera.ScreenPointToRay(point); RaycastHit hit; Vector3 origin = new Vector3(ray.Origin.X, ray.Origin.Y, ray.Origin.Z); Vector3 direction = new Vector3(ray.Direction.X, ray.Direction.Y, ray.Direction.Z); if (collisionModule->RaycastMaterialFilter(&hit, &origin, &direction, data.Distance, 1, flags)) { obstructionCount++; } } obstructed = obstructionCount == points.Length; } return obstructed; } } }