Files
HSMappy/Mappy/MapRenderer/MapRenderer.Player.cs
T
2026-02-26 03:54:51 -05:00

170 lines
7.7 KiB
C#

using System;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Client.UI.Arrays;
using Mappy.Classes;
using Mappy.Extensions;
namespace Mappy.MapRenderer;
public partial class MapRenderer
{
private unsafe void DrawPlayer()
{
if (AgentMap.Instance()->SelectedMapId != AgentMap.Instance()->CurrentMapId) return;
if (Service.ObjectTable.LocalPlayer is { } localPlayer) {
var position = ImGui.GetWindowPos() +
DrawPosition +
(localPlayer.GetMapPosition() -
DrawHelpers.GetMapOffsetVector() +
DrawHelpers.GetMapCenterOffsetVector()) * Scale;
DrawLookLine(position);
DrawPlayerIcon(position);
}
}
private void DrawLookLine(Vector2 position)
{
var angle = GetCameraRotation();
var lineLength = System.SystemConfig.ConeSize * (System.SystemConfig.ScalePlayerCone ? 1.0f : Scale);
var halfConeAngle = DegreesToRadians(90.0f) / 2.0f;
DrawAngledLineFromCenter(position, lineLength, angle - halfConeAngle);
DrawAngledLineFromCenter(position, lineLength, angle + halfConeAngle);
DrawLineArcFromCenter(position, lineLength, angle);
DrawFilledSemiCircle(position, lineLength, angle);
}
private static void DrawAngledLineFromCenter(Vector2 center, float lineLength, float angle, Vector4? outlineColor = null)
{
var lineSegment = new Vector2(lineLength * MathF.Cos(angle), lineLength * MathF.Sin(angle));
var color = outlineColor ?? System.SystemConfig.PlayerConeOutlineColor;
ImGui.GetWindowDrawList().AddLine(center, center + lineSegment, ImGui.GetColorU32(color), 3.0f);
}
private static void DrawLineArcFromCenter(Vector2 center, float distance, float rotation, Vector4? outlineColor = null)
{
var halfConeAngle = DegreesToRadians(90.0f) / 2.0f;
var color = outlineColor ?? System.SystemConfig.PlayerConeOutlineColor;
var start = rotation - halfConeAngle;
var stop = rotation + halfConeAngle;
ImGui.GetWindowDrawList().PathArcTo(center, distance, start, stop);
ImGui.GetWindowDrawList().PathStroke(ImGui.GetColorU32(color), ImDrawFlags.None, 3.0f);
}
private static void DrawFilledSemiCircle(Vector2 center, float distance, float rotation)
{
var halfConeAngle = DegreesToRadians(90.0f) / 2.0f;
var coneColor = ImGui.GetColorU32(System.SystemConfig.PlayerConeColor);
var startAngle = rotation - halfConeAngle;
var stopAngle = rotation + halfConeAngle;
var startPosition = new Vector2(distance * MathF.Cos(rotation - halfConeAngle), distance * MathF.Sin(rotation - halfConeAngle));
ImGui.GetWindowDrawList().PathArcTo(center, distance, startAngle, stopAngle);
ImGui.GetWindowDrawList().PathLineTo(center);
ImGui.GetWindowDrawList().PathLineTo(center + startPosition);
ImGui.GetWindowDrawList().PathFillConvex(coneColor);
}
private static unsafe float GetCameraRotation() => -DegreesToRadians(AreaMapNumberArray.Instance()->ConeRotation) - 0.5f * MathF.PI;
private static float DegreesToRadians(float degrees) => MathF.PI / 180.0f * degrees;
private void DrawPlayerIcon(Vector2 position)
{
if (!System.SystemConfig.ShowPlayerIcon) return;
if (Service.ObjectTable is not { LocalPlayer: { } player }) return;
var texture = Service.TextureProvider.GetFromGameIcon(60443).GetWrapOrEmpty();
var angle = -player.Rotation + MathF.PI / 2.0f;
var scale = System.SystemConfig.ScaleWithZoom ? Scale : 1.0f;
scale *= System.SystemConfig.PlayerIconScale;
var vectors = GetRotationVectors(angle, position, texture.Size / 2.0f * scale);
ImGui.GetWindowDrawList().AddImageQuad(texture.Handle, vectors[0], vectors[1], vectors[2], vectors[3]);
}
/// <summary>
/// Draw only the minimap player cone (direction indicator). Call before DrawMinimapMarkers so markers draw on top of the cone.
/// </summary>
private void DrawMinimapConeAtCenter(Vector2 centerPos, float mapScale)
{
if (!System.SystemConfig.MinimapShowPlayerCone) return;
var angle = GetCameraRotation();
var lineLength = System.SystemConfig.ConeSize * 0.5f;
var halfConeAngle = DegreesToRadians(90.0f) / 2.0f;
DrawMinimapConeGradient(centerPos, lineLength, angle - halfConeAngle, angle + halfConeAngle);
var softWhite = new Vector4(1f, 1f, 1f, 0.2f);
DrawAngledLineFromCenter(centerPos, lineLength, angle - halfConeAngle, softWhite);
DrawAngledLineFromCenter(centerPos, lineLength, angle + halfConeAngle, softWhite);
DrawLineArcFromCenter(centerPos, lineLength, angle, softWhite);
}
/// <summary>
/// Draw player icon at center (for minimap). Cone is drawn earlier so markers can be drawn on top of it.
/// </summary>
private void DrawPlayerAtCenter(Vector2 centerPos, float mapScale)
{
if (Service.ObjectTable.LocalPlayer is not { } localPlayer) return;
if (!System.SystemConfig.ShowPlayerIcon) return;
var texture = Service.TextureProvider.GetFromGameIcon(60443).GetWrapOrEmpty();
var angle = -localPlayer.Rotation + MathF.PI / 2.0f;
var iconScale = System.SystemConfig.PlayerIconScale * 1.5f; // 1.5x for minimap visibility
var vectors = GetRotationVectors(angle, centerPos, texture.Size / 2.0f * iconScale);
ImGui.GetWindowDrawList().AddImageQuad(texture.Handle, vectors[0], vectors[1], vectors[2], vectors[3]);
}
/// <summary>Draw minimap cone as white light with radial gradient (bright at center, fading at edge). Kept quite transparent so markers underneath remain visible.</summary>
private static void DrawMinimapConeGradient(Vector2 center, float radius, float startAngle, float endAngle)
{
const int segments = 24;
const float maxAlpha = 0.18f;
var drawList = ImGui.GetWindowDrawList();
for (var j = segments - 1; j >= 0; j--) {
var rInner = radius * j / segments;
var rOuter = radius * (j + 1) / segments;
var t = (j + 0.5f) / segments;
var alpha = maxAlpha * (1f - t);
if (alpha <= 0f) continue;
var color = ImGui.GetColorU32(new Vector4(1f, 1f, 1f, alpha));
var outerStart = center + new Vector2(rOuter * MathF.Cos(startAngle), rOuter * MathF.Sin(startAngle));
var innerEnd = center + new Vector2(rInner * MathF.Cos(endAngle), rInner * MathF.Sin(endAngle));
drawList.PathClear();
drawList.PathLineTo(center);
drawList.PathLineTo(outerStart);
drawList.PathArcTo(center, rOuter, startAngle, endAngle);
drawList.PathLineTo(innerEnd);
drawList.PathArcTo(center, rInner, endAngle, startAngle);
drawList.PathFillConvex(color);
}
}
private static Vector2[] GetRotationVectors(float angle, Vector2 center, Vector2 size)
{
var cosA = MathF.Cos(angle + 0.5f * MathF.PI);
var sinA = MathF.Sin(angle + 0.5f * MathF.PI);
Vector2[] vectors =
[
center + ImRotate(new Vector2(-size.X * 0.5f, -size.Y * 0.5f), cosA, sinA),
center + ImRotate(new Vector2(+size.X * 0.5f, -size.Y * 0.5f), cosA, sinA),
center + ImRotate(new Vector2(+size.X * 0.5f, +size.Y * 0.5f), cosA, sinA),
center + ImRotate(new Vector2(-size.X * 0.5f, +size.Y * 0.5f), cosA, sinA),
];
return vectors;
}
private static Vector2 ImRotate(Vector2 v, float cosA, float sinA) => new(v.X * cosA - v.Y * sinA, v.X * sinA + v.Y * cosA);
}