9659f7a7d1
Made-with: Cursor
170 lines
7.7 KiB
C#
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);
|
|
} |