Initial HSMappy release (fork of Mappy)

Made-with: Cursor
This commit is contained in:
2026-02-26 03:54:51 -05:00
commit 9659f7a7d1
72 changed files with 6625 additions and 0 deletions
+427
View File
@@ -0,0 +1,427 @@
using System;
using System.Drawing;
using System.Linq;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Components;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using KamiLib.Classes;
using KamiLib.CommandManager;
using KamiLib.Extensions;
using KamiLib.Window;
using Mappy.Classes;
using Mappy.Data;
namespace Mappy.Windows;
public class ConfigurationWindow : Window
{
private readonly TabBar tabBar = new("mappy_tab_bar", [
new IconConfigurationTab(),
new MapFunctionsTab(),
new StyleOptionsTab(),
new MinimapOptionsTab(),
new PlayerOptionsTab(),
]);
public ConfigurationWindow() : base("HSMappy Configuration Window", new Vector2(500.0f, 580.0f))
{
System.CommandManager.RegisterCommand(new CommandHandler
{
Delegate = _ => System.ConfigWindow.Toggle(), ActivationPath = "/",
});
}
protected override void DrawContents() => tabBar.Draw();
}
public class MapFunctionsTab : ITabItem
{
public string Name => "Map Functions";
public bool Disabled => false;
public void Draw()
{
var configChanged = false;
ImGuiTweaks.Header("Zoom Options");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.Checkbox("Use Linear Zoom", ref System.SystemConfig.UseLinearZoom);
configChanged |= ImGui.Checkbox("Scale icons with zoom", ref System.SystemConfig.ScaleWithZoom);
configChanged |= ImGui.Checkbox("Scale text labels with zoom", ref System.SystemConfig.ScaleTextWithZoom);
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGuiTweaks.Checkbox("Auto Zoom", ref System.SystemConfig.AutoZoom, "Automatically sets zoom to a reasonable value relative to the map size.");
configChanged |= ImGui.SliderFloat("Auto Zoom Scale Factor", ref System.SystemConfig.AutoZoomScaleFactor, 0.20f, 1.00f);
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGui.SliderFloat("Zoom Speed", ref System.SystemConfig.ZoomSpeed, 0.001f, 0.500f);
configChanged |= ImGui.SliderFloat("Icon Scale", ref System.SystemConfig.IconScale, 0.10f, 3.0f);
}
ImGuiTweaks.Header("When Opening Map");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.Checkbox("Follow On Open", ref System.SystemConfig.FollowOnOpen);
ImGuiHelpers.ScaledDummy(5.0f);
DrawCenterModeRadio();
}
ImGuiTweaks.Header("Link Behaviors");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.Checkbox("Center on Flags", ref System.SystemConfig.CenterOnFlag);
configChanged |= ImGui.Checkbox("Center on Gathering Areas", ref System.SystemConfig.CenterOnGathering);
configChanged |= ImGui.Checkbox("Center on Quest", ref System.SystemConfig.CenterOnQuest);
}
ImGuiTweaks.Header("Misc Options");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.Checkbox("Show Misc Tooltips", ref System.SystemConfig.ShowMiscTooltips);
configChanged |= ImGui.Checkbox("Lock Map on Center", ref System.SystemConfig.LockCenterOnMap);
configChanged |= ImGui.Checkbox("Show Other Players", ref System.SystemConfig.ShowPlayers);
configChanged |= ImGui.Checkbox("Disable Map Focus on Appear", ref System.SystemConfig.NoFocusOnAppear);
configChanged |= ImGui.Checkbox("Suppress the native map's open/close sound effect.",
ref System.SystemConfig.SuppressNativeMapOpenSound);
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGui.Checkbox("Show Text Labels", ref System.SystemConfig.ShowTextLabels);
configChanged |= ImGui.DragFloat("Large Label Scale", ref System.SystemConfig.LargeAreaTextScale, 0.01f, 1.0f, 4.0f);
configChanged |= ImGui.DragFloat("Small Label Scale", ref System.SystemConfig.SmallAreaTextScale, 0.01f, 0.5f, 3.0f);
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGui.Checkbox("Show Fog of War", ref System.SystemConfig.ShowFogOfWar);
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGui.Checkbox("Debug Mode", ref System.SystemConfig.DebugMode);
}
ImGuiTweaks.Header("Toolbar");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.Checkbox("Always Show", ref System.SystemConfig.AlwaysShowToolbar);
configChanged |= ImGui.Checkbox("Show On Hover", ref System.SystemConfig.ShowToolbarOnHover);
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGui.DragFloat("Opacity##toolbar", ref System.SystemConfig.ToolbarFade, 0.01f, 0.0f, 1.0f);
}
ImGuiTweaks.Header("Coordinates");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.Checkbox("Show Coordinate Bar", ref System.SystemConfig.ShowCoordinateBar);
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGuiTweaks.ColorEditWithDefault("Text Color", ref System.SystemConfig.CoordinateTextColor, KnownColor.White.Vector());
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGui.DragFloat("Opacity##coordinatebar", ref System.SystemConfig.CoordinateBarFade, 0.01f, 0.0f, 1.0f);
}
if (configChanged) {
SystemConfig.Save();
}
}
private void DrawCenterModeRadio()
{
var enumObject = System.SystemConfig.CenterOnOpen;
var firstLine = true;
foreach (Enum enumValue in Enum.GetValues(enumObject.GetType())) {
if (!firstLine) ImGui.SameLine();
if (ImGui.RadioButton(enumValue.GetDescription(), enumValue.Equals(enumObject))) {
System.SystemConfig.CenterOnOpen = (CenterTarget)enumValue;
SystemConfig.Save();
}
firstLine = false;
}
ImGui.SameLine();
ImGui.Text("\t\tCenter on Open");
}
}
public class StyleOptionsTab : ITabItem
{
public string Name => "Style";
public bool Disabled => false;
public void Draw()
{
var configChanged = false;
ImGuiTweaks.Header("Window Options");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.Checkbox("Keep Open", ref System.SystemConfig.KeepOpen);
configChanged |= ImGui.Checkbox("Lock Window Position", ref System.SystemConfig.LockWindow);
configChanged |= ImGui.Checkbox("Hide Window Frame", ref System.SystemConfig.HideWindowFrame);
configChanged |= ImGui.Checkbox("Hide Window Background", ref System.SystemConfig.HideWindowBackground);
configChanged |= ImGui.Checkbox("Enable Shift + Drag to Move Window Frame", ref System.SystemConfig.EnableShiftDragMove);
}
ImGuiTweaks.Header("Window Hiding");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.Checkbox("Hide With Game GUI", ref System.SystemConfig.HideWithGameGui);
configChanged |= ImGui.Checkbox("Hide Between Areas", ref System.SystemConfig.HideBetweenAreas);
configChanged |= ImGui.Checkbox("Hide in Combat", ref System.SystemConfig.HideInCombat);
}
ImGuiTweaks.Header("Window Title");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.Checkbox("Show Region Text", ref System.SystemConfig.ShowRegionLabel);
configChanged |= ImGui.Checkbox("Show Map Text", ref System.SystemConfig.ShowMapLabel);
configChanged |= ImGui.Checkbox("Show Area Text", ref System.SystemConfig.ShowAreaLabel);
configChanged |= ImGui.Checkbox("Show Sub-Area Text", ref System.SystemConfig.ShowSubAreaLabel);
}
ImGuiTweaks.Header("Window Location");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.DragFloat2("Window Position", ref System.SystemConfig.WindowPosition);
configChanged |= ImGui.DragFloat2("Window Size", ref System.SystemConfig.WindowSize);
}
ImGuiTweaks.Header("Fade Options");
using (ImRaii.PushIndent()) {
using (var columns = ImRaii.Table("fade_options_toggles", 2)) {
if (!columns) return;
var value = System.SystemConfig.FadeMode;
ImGui.TableNextColumn();
foreach (Enum enumValue in Enum.GetValues(value.GetType())) {
var isFlagSet = value.HasFlag(enumValue);
if (ImGuiComponents.ToggleButton(enumValue.ToString(), ref isFlagSet)) {
var sourceValue = Convert.ToInt32(value);
var targetValue = Convert.ToInt32(enumValue);
if (value.HasFlag(enumValue)) {
System.SystemConfig.FadeMode = (FadeMode)Enum.ToObject(value.GetType(), sourceValue & ~targetValue);
}
else {
System.SystemConfig.FadeMode = (FadeMode)Enum.ToObject(value.GetType(), sourceValue | targetValue);
}
configChanged = true;
}
ImGui.SameLine();
ImGui.TextUnformatted(enumValue.GetDescription());
ImGui.TableNextColumn();
}
}
configChanged |= ImGui.DragFloat("Fade Opacity", ref System.SystemConfig.FadePercent, 0.01f, 0.05f, 1.0f);
}
ImGuiTweaks.Header("Area Style");
using (ImRaii.PushIndent()) {
configChanged |= ImGuiTweaks.ColorEditWithDefault("Area Color", ref System.SystemConfig.AreaColor, KnownColor.CornflowerBlue.Vector() with { W = 0.33f });
configChanged |= ImGuiTweaks.ColorEditWithDefault("Area Outline Color", ref System.SystemConfig.AreaOutlineColor,
KnownColor.CornflowerBlue.Vector() with { W = 0.30f });
}
if (configChanged) {
if (System.MapWindow.SizeConstraints is { } constraints) {
System.SystemConfig.WindowSize.X = MathF.Max(System.SystemConfig.WindowSize.X, constraints.MinimumSize.X);
System.SystemConfig.WindowSize.Y = MathF.Max(System.SystemConfig.WindowSize.Y, constraints.MinimumSize.Y);
}
System.MapWindow.RefreshTitle();
SystemConfig.Save();
}
}
}
public class MinimapOptionsTab : ITabItem
{
public string Name => "Minimap";
public bool Disabled => false;
public void Draw()
{
var configChanged = false;
ImGuiTweaks.Header("Minimap");
using (ImRaii.PushIndent()) {
var showMinimap = System.SystemConfig.ShowMinimap;
if (ImGui.Checkbox("Show Minimap", ref showMinimap)) {
System.SystemConfig.ShowMinimap = showMinimap;
configChanged = true;
if (showMinimap)
System.MinimapWindow?.UnCollapseOrShow();
}
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGui.DragFloat("Size", ref System.SystemConfig.MinimapSize, 5.0f, 80.0f, 400.0f, "%.0f");
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
ImGui.SetTooltip("Minimap size. Resize only via this setting (no corner grip).");
configChanged |= ImGui.DragFloat2("Position", ref System.SystemConfig.MinimapPosition);
configChanged |= ImGui.DragFloat("Opacity", ref System.SystemConfig.MinimapOpacity, 0.01f, 0.2f, 1.0f);
configChanged |= ImGui.SliderFloat("Zoom Level", ref System.SystemConfig.MinimapZoom, 0.03f, 0.112f,
"%.2f (0.1 = zoomed out, lower = zoomed in)");
configChanged |= ImGui.Checkbox("Show Player Cone", ref System.SystemConfig.MinimapShowPlayerCone);
configChanged |= ImGui.Checkbox("Show Quest Direction Arrow", ref System.SystemConfig.MinimapShowQuestDirectionArrow);
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
ImGui.SetTooltip("Show an arrow at the edge of the minimap pointing toward your quest objective or waymark (like the default game minimap).");
configChanged |= ImGui.Checkbox("Show FATE Direction Arrows", ref System.SystemConfig.MinimapShowFateDirectionArrows);
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
ImGui.SetTooltip("Show purple arrows at the edge of the minimap pointing toward nearby FATEs.");
configChanged |= ImGui.Checkbox("Show Quest Area Radius", ref System.SystemConfig.MinimapShowQuestAreaRadius);
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
ImGui.SetTooltip("Show quest objective area circles on the minimap (same as on the Area Map).");
configChanged |= ImGui.Checkbox("Hide Minimap With Game GUI", ref System.SystemConfig.MinimapHideWithGameGui);
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
ImGui.SetTooltip("When enabled, the minimap hides during NPC dialogue, object interaction, and when the game hides nameplates (same as the main map). When disabled, the minimap stays visible in those situations.");
configChanged |= ImGui.Checkbox("Lock Position", ref System.SystemConfig.MinimapLockPosition);
}
if (configChanged) {
SystemConfig.Save();
}
}
}
public class PlayerOptionsTab : ITabItem
{
public string Name => "Player";
public bool Disabled => false;
public void Draw()
{
var configChanged = false;
ImGuiTweaks.Header("Cone Options");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.Checkbox("Scale Player Cone", ref System.SystemConfig.ScalePlayerCone);
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGui.DragFloat("Cone Size", ref System.SystemConfig.ConeSize, 0.25f);
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGuiTweaks.ColorEditWithDefault("Cone Color", ref System.SystemConfig.PlayerConeColor, KnownColor.CornflowerBlue.Vector() with { W = 0.33f });
configChanged |= ImGuiTweaks.ColorEditWithDefault("Cone Outline Color", ref System.SystemConfig.PlayerConeOutlineColor,
KnownColor.CornflowerBlue.Vector() with { W = 1.00f });
}
ImGuiTweaks.Header("Radar Options");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.Checkbox("Show Radar Radius", ref System.SystemConfig.ShowRadar);
configChanged |= ImGui.Checkbox("Show in Duties", ref System.SystemConfig.ShowRadarInDuties);
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGuiTweaks.ColorEditWithDefault("Radar Area Color", ref System.SystemConfig.RadarColor, KnownColor.Gray.Vector() with { W = 0.10f });
configChanged |= ImGuiTweaks.ColorEditWithDefault("Radar Outline Color", ref System.SystemConfig.RadarOutlineColor, KnownColor.Gray.Vector() with { W = 0.30f });
}
ImGuiTweaks.Header("Player Icon Options");
using (ImRaii.PushIndent()) {
configChanged |= ImGui.Checkbox("Show Player Icon", ref System.SystemConfig.ShowPlayerIcon);
ImGuiHelpers.ScaledDummy(5.0f);
configChanged |= ImGui.DragFloat("Player Icon Size", ref System.SystemConfig.PlayerIconScale, 0.05f);
}
if (configChanged) {
SystemConfig.Save();
}
}
}
public class IconConfigurationTab : ITabItem
{
public string Name => "Icon Settings";
public bool Disabled => false;
private IconSetting? currentSetting;
public void Draw()
{
using (var leftChild = ImRaii.Child("left_child", new Vector2(48.0f * ImGuiHelpers.GlobalScale + ImGui.GetStyle().ItemSpacing.X, ImGui.GetContentRegionAvail().Y))) {
if (leftChild) {
using var selectionList = ImRaii.ListBox("iconSelection", ImGui.GetContentRegionAvail());
foreach (var (iconId, settings) in System.IconConfig.IconSettingMap.OrderBy(pairData => pairData.Key)) {
if (iconId is 0) continue;
if (DrawHelpers.IsDisallowedIcon(iconId)) continue;
var texture = Service.TextureProvider.GetFromGameIcon(iconId).GetWrapOrEmpty();
var cursorStart = ImGui.GetCursorScreenPos();
if (ImGui.Selectable($"##iconSelect{iconId}", currentSetting == settings, ImGuiSelectableFlags.None, ImGuiHelpers.ScaledVector2(32.0f, 32.0f))) {
currentSetting = currentSetting == settings ? null : settings;
}
ImGui.SetCursorScreenPos(cursorStart);
ImGui.Image(texture.Handle, texture.Size / 2.0f * ImGuiHelpers.GlobalScale);
}
}
}
ImGui.SameLine();
using (var rightChild = ImRaii.Child("right_child", ImGui.GetContentRegionAvail(), false, ImGuiWindowFlags.NoScrollbar)) {
if (rightChild) {
if (currentSetting is null) {
using var textColor = ImRaii.PushColor(ImGuiCol.Text, KnownColor.Orange.Vector());
ImGui.SetCursorPosY(ImGui.GetContentRegionAvail().Y / 2.0f);
ImGuiHelpers.CenteredText("Select an Icon to Edit Settings");
}
else {
// Draw background texture
var settingsChanged = false;
var texture = Service.TextureProvider.GetFromGameIcon(currentSetting.IconId).GetWrapOrEmpty();
var smallestAxis = MathF.Min(ImGui.GetContentRegionAvail().X, ImGui.GetContentRegionAvail().Y);
if (ImGui.GetContentRegionAvail().X > ImGui.GetContentRegionAvail().Y) {
var remainingSpace = ImGui.GetContentRegionAvail().X - smallestAxis;
ImGui.SetCursorPosX(remainingSpace / 2.0f);
}
ImGui.Image(texture.Handle, new Vector2(smallestAxis, smallestAxis), Vector2.Zero, Vector2.One, new Vector4(1.0f, 1.0f, 1.0f, 0.20f));
ImGui.SetCursorPos(Vector2.Zero);
// Draw settings
ImGuiTweaks.Header($"Configure Marker #{currentSetting.IconId}");
using (ImRaii.PushIndent()) {
settingsChanged |= ImGui.Checkbox("Hide Icon", ref currentSetting.Hide);
settingsChanged |= ImGui.Checkbox("Allow Tooltip", ref currentSetting.AllowTooltip);
settingsChanged |= ImGui.Checkbox("Allow Click Interaction", ref currentSetting.AllowClick);
ImGuiHelpers.ScaledDummy(5.0f);
settingsChanged |= ImGuiTweaks.ColorEditWithDefault("Color", ref currentSetting.Color, KnownColor.White.Vector());
ImGuiHelpers.ScaledDummy(5.0f);
settingsChanged |= ImGui.DragFloat("Icon Scale", ref currentSetting.Scale, 0.01f, 0.05f, 20.0f);
}
ImGui.SetCursorPosY(ImGui.GetContentRegionMax().Y - 25.0f * ImGuiHelpers.GlobalScale);
if (ImGui.Button("Reset to Default", new Vector2(ImGui.GetContentRegionAvail().X, 25.0f * ImGuiHelpers.GlobalScale))) {
currentSetting.Reset();
System.IconConfig.Save();
}
if (settingsChanged) {
System.IconConfig.Save();
}
}
}
}
}
}
+136
View File
@@ -0,0 +1,136 @@
using System;
using System.Drawing;
using System.Linq;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.Text;
using Dalamud.Interface;
using Dalamud.Interface.Textures;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Client.Game.Fate;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using KamiLib.Extensions;
using KamiLib.Window;
using Mappy.Data;
using Mappy.Extensions;
namespace Mappy.Windows;
public class FateListWindow : Window
{
private const float ElementHeight = 48.0f;
public FateListWindow() : base("HSMappy Fate List Window", new Vector2(300.0f, 400.0f))
{
AdditionalInfoTooltip = "Shows Fates for the zone you are currently in";
}
protected override unsafe void DrawContents()
{
DrawBackgroundImage();
if (Service.FateTable.Length > 0) {
DrawOptions();
foreach (var index in Enumerable.Range(0, Service.FateTable.Length)) {
var fate = FateManager.Instance()->Fates[index].Value;
var cursorStart = ImGui.GetCursorScreenPos();
if (ImGui.Selectable($"##{fate->FateId}_Selectable", false, ImGuiSelectableFlags.None,
new Vector2(ImGui.GetContentRegionAvail().X, ElementHeight * ImGuiHelpers.GlobalScale))) {
OnFateClick(fate);
}
ImGui.SetCursorScreenPos(cursorStart);
DrawFateInfo(fate);
}
}
else {
const string text = "No FATE's available";
var textSize = ImGui.CalcTextSize(text);
ImGui.SetCursorPosX(ImGui.GetContentRegionAvail().X / 2.0f - textSize.X / 2.0f);
ImGui.SetCursorPosY(ImGui.GetContentRegionAvail().Y / 2.0f - textSize.Y / 2.0f);
ImGui.TextColored(KnownColor.Orange.Vector(), text);
}
}
private static void DrawBackgroundImage()
{
var windowCursorStart = ImGui.GetCursorPos();
var windowWidth = ImGui.GetContentRegionMax().X;
var windowHeight = ImGui.GetContentRegionMax().Y;
var startPos = windowHeight / 2 - windowWidth / 2;
ImGui.SetCursorPosY(startPos);
ImGui.Spacing();
ImGui.Image(
Service.TextureProvider.GetFromGameIcon(new GameIconLookup { IconId = 60502 }).GetWrapOrEmpty().Handle,
new Vector2(ImGui.GetContentRegionMax().X, ImGui.GetContentRegionMax().X),
Vector2.Zero,
Vector2.One,
new Vector4(1.0f, 1.0f, 1.0f, 0.15f));
ImGui.SetCursorPos(windowCursorStart);
}
private static void DrawOptions()
{
using var toolbarChild = ImRaii.Child("fatelist_toolbar", new Vector2(ImGui.GetContentRegionAvail().X, 32.0f));
if (toolbarChild) {
using var color = ImRaii.PushColor(ImGuiCol.Button, ImGui.GetStyle().GetColor(ImGuiCol.ButtonActive), System.SystemConfig.SetFlagOnFateClick);
ImGui.Spacing();
if (ImGui.Checkbox("Place Map Flag on Click", ref System.SystemConfig.SetFlagOnFateClick)) {
SystemConfig.Save();
}
}
ImGui.Separator();
}
private static unsafe void OnFateClick(FateContext* fate)
{
System.IntegrationsController.OpenOccupiedMap();
System.SystemConfig.FollowPlayer = false;
System.MapRenderer.DrawOffset = -new Vector2(fate->Location.X, fate->Location.Z);
if (System.SystemConfig.SetFlagOnFateClick) {
AgentMap.Instance()->FlagMarkerCount = 0;
AgentMap.Instance()->SetFlagMapMarker(AgentMap.Instance()->CurrentTerritoryId, AgentMap.Instance()->CurrentMapId, fate->Location.X, fate->Location.Z);
AgentChatLog.Instance()->InsertTextCommandParam(1048, false);
}
}
private static unsafe void DrawFateInfo(FateContext* fate)
{
using (ImRaii.Child($"image_child_{fate->FateId}", new Vector2(ElementHeight, ElementHeight), false, ImGuiWindowFlags.NoInputs)) {
ImGui.Image(Service.TextureProvider.GetFromGameIcon(fate->IconId).GetWrapOrEmpty().Handle, ImGuiHelpers.ScaledVector2(ElementHeight, ElementHeight));
}
ImGui.SameLine();
using (ImRaii.Child($"text_child_{fate->FateId}", new Vector2(ImGui.GetContentRegionAvail().X, ElementHeight), false, ImGuiWindowFlags.NoInputs)) {
ImGui.TextColored(FateContextExtensions.GetColor(fate, 1.0f), $"Lv. {fate->Level} {fate->Name}");
if (fate->State is FateState.Running) {
ImGui.TextUnformatted($"Progress: {fate->Progress}%");
var timeRemaining = FateContextExtensions.GetTimeRemaining(fate);
if (timeRemaining != TimeSpan.Zero) {
var timeString = $"{(fate->IsBonus ? "Exp Bonus!\t" : string.Empty)}{SeIconChar.Clock.ToIconString()} {FateContextExtensions.GetTimeRemaining(fate):mm\\:ss}";
ImGui.SameLine(ImGui.GetContentRegionMax().X - ImGui.CalcTextSize(timeString).X);
ImGui.Text(timeString);
}
}
else {
ImGui.TextUnformatted(fate->State.ToString());
}
}
}
public override void OnClose()
{
System.WindowManager.RemoveWindow(this);
}
}
+98
View File
@@ -0,0 +1,98 @@
using System.Collections.Immutable;
using System.Drawing;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using KamiLib.Window;
using Mappy.Data;
namespace Mappy.Windows;
public class FlagHistoryWindow : Window
{
private static float FlagElementHeight => 95.0f * ImGuiHelpers.GlobalScale;
public FlagHistoryWindow() : base("HSMappy Flag History Window", new Vector2(400.0f, 400.0f))
{
AdditionalInfoTooltip = "Shows a list of all recently used flags";
}
protected override void DrawContents()
{
ImGuiClip.ClippedDraw(System.FlagConfig.FlagHistory.ToImmutableList(), DrawFlag, FlagElementHeight);
}
private void DrawFlag(Flag flag)
{
using var id = ImRaii.PushId(flag.GetIdString());
using (ImRaii.Child("flag_container", new Vector2(ImGui.GetContentRegionAvail().X, FlagElementHeight - ImGui.GetStyle().FramePadding.Y * 2.0f))) {
using (ImRaii.Child("flag_image_container", new Vector2(155.0f * ImGuiHelpers.GlobalScale, ImGui.GetContentRegionAvail().Y))) {
DrawFlagImage(flag);
}
ImGui.SameLine();
using (ImRaii.Child("flag_contents_container", ImGui.GetContentRegionAvail())) {
DrawFlagData(flag);
DrawButtons(flag);
}
}
ImGui.Spacing();
}
private void DrawFlagImage(Flag flag)
{
var texture = flag.GetMapTexture();
if (texture is not null) {
ImGui.Image(texture.Handle, ImGui.GetContentRegionAvail(), new Vector2(0.15f, 0.15f), new Vector2(0.85f, 0.85f));
}
else {
ImGuiHelpers.ScaledDummy(ImGui.GetContentRegionAvail());
}
}
private void DrawFlagData(Flag flag)
{
ImGui.Text(flag.GetMap().PlaceName.Value.Name.ExtractText());
ImGui.SameLine();
var flagCoordinate = flag.GetMapCoordinate();
var coordinateString = $"{flagCoordinate.X:F1}, {flagCoordinate.Y:F1}";
var coordinateStringSize = ImGui.CalcTextSize(coordinateString);
ImGui.SetCursorPosX(ImGui.GetContentRegionMax().X - coordinateStringSize.X);
ImGui.Text(coordinateString);
ImGui.TextColored(KnownColor.Gray.Vector().Lighten(0.20f), flag.GetTerritoryType().PlaceNameZone.Value.Name.ExtractText());
if (flag.IsFlagSet()) {
ImGui.Spacing();
ImGui.TextColored(KnownColor.ForestGreen.Vector().Lighten(0.40f), "Flag is currently active");
}
}
private void DrawButtons(Flag flag)
{
var buttonSize = ImGuiHelpers.ScaledVector2(100.0f, 24.0f);
ImGui.SetCursorPos(new Vector2(0.0f, ImGui.GetContentRegionMax().Y - buttonSize.Y));
if (ImGui.Button("Focus", buttonSize)) {
flag.Focus();
}
ImGui.SetCursorPos(ImGui.GetContentRegionMax() - buttonSize);
using (ImRaii.Disabled(flag.IsFlagSet())) {
if (ImGui.Button("Place", buttonSize)) {
flag.PlaceFlag();
}
}
}
public override void OnClose()
{
System.WindowManager.RemoveWindow(this);
}
}
+56
View File
@@ -0,0 +1,56 @@
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Interface.Utility;
using KamiLib.Window;
using Lumina.Excel.Sheets;
using Mappy.Classes.SelectionWindowComponents;
using Aetheryte = Lumina.Excel.Sheets.Aetheryte;
namespace Mappy.Windows;
public class MapSelectionWindow : SelectionWindowBase<DrawableOption>
{
protected override bool AllowMultiSelect => false;
protected override float SelectionHeight => 75.0f * ImGuiHelpers.GlobalScale;
public MapSelectionWindow() : base(new Vector2(500.0f, 800.0f), alternativeName: "Map Selection Window")
{
var maps = Service.DataManager.GetExcelSheet<Map>()
.Where(map => map is { PlaceName.RowId: not 0, TerritoryType.ValueNullable.LoadingImage.RowId: not 0, })
.Where(map => map is not { PriorityUI: 0, PriorityCategoryUI: 0 })
.Select(map => new MapDrawableOption { Map = map, })
.OfType<DrawableOption>()
.ToList();
var poi = Service.DataManager.GetSubrowExcelSheet<MapMarker>()
.SelectMany(subRowCollection => subRowCollection)
.Where(marker => marker is { PlaceNameSubtext.RowId: not 0, Icon: 60442, })
.Select(marker => new PoiDrawableOption { MapMarker = marker, })
.OfType<DrawableOption>()
.ToList();
var aetherytes = Service.DataManager.GetExcelSheet<Aetheryte>()
.Where(aetheryte => aetheryte is not { PlaceName.RowId: 0, AethernetName.RowId: 0, AethernetGroup: 0, Map.RowId: 0, })
.Select(aetheryte => new AetheryteDrawableOption { Aetheryte = aetheryte, })
.OfType<DrawableOption>()
.ToList();
SelectionOptions = maps
.Concat(poi)
.Concat(aetherytes)
.ToList();
SelectionOptions.RemoveAll(option => option.Map.RowId is 0);
}
protected override void DrawSelection(DrawableOption option)
{
option.Draw();
}
protected override IEnumerable<string> GetFilterStrings(DrawableOption option) => option.GetFilterStrings();
protected override string GetElementKey(DrawableOption element) => $"{element.Map.RowId}{element.MarkerLocation}{element.ExtraLineShort}{element.ExtraLineLong}";
}
+505
View File
@@ -0,0 +1,505 @@
using System.Drawing;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Keys;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using KamiLib.Classes;
using KamiLib.CommandManager;
using KamiLib.Window;
using Lumina.Excel.Sheets;
using Mappy.Classes;
using Mappy.Classes.MapWindowComponents;
using Mappy.Controllers;
using Mappy.Data;
using Map = Lumina.Excel.Sheets.Map;
namespace Mappy.Windows;
public class MapWindow : Window
{
public Vector2 MapDrawOffset { get; private set; }
public HoverFlags HoveredFlags { get; private set; }
public bool ProcessingCommand { get; set; }
private bool isDragStarted;
private Vector2 lastWindowSize;
private uint lastMapId;
private uint lastAreaPlaceNameId;
private uint lastSubAreaPlaceNameId;
private readonly MapToolbar mapToolbar = new();
private readonly MapCoordinateBar mapCoordinateBar = new();
private readonly MapContextMenu mapContextMenu = new();
public MapWindow() : base("###HSMappyMapWindow", new Vector2(400.0f, 250.0f))
{
UpdateTitle();
DisableWindowSounds = true;
RegisterCommands();
}
public override bool DrawConditions() => IntegrationsController.ShouldShowMap();
public override unsafe void PreOpenCheck()
{
// If you managed to open the window while the agent says it should be closed
if (System.MapWindow.IsOpen && AgentMap.Instance()->AddonId is 0)
{
Service.Log.Debug("[OnShow] MapWindow can not be open now.");
IsOpen = false;
}
if (System.SystemConfig.KeepOpen) {
IsOpen = true;
}
if (Service.ClientState is { IsLoggedIn: false } or { IsPvP: true }) IsOpen = false;
}
public override void OnOpen()
{
if (ProcessingCommand) {
ProcessingCommand = false;
System.SystemConfig.FollowPlayer = false;
return;
}
if (System.SystemConfig.FollowOnOpen) {
System.IntegrationsController.OpenOccupiedMap();
System.SystemConfig.FollowPlayer = true;
}
switch (System.SystemConfig.CenterOnOpen) {
case CenterTarget.Player when Service.ObjectTable.LocalPlayer is { } localPlayer:
System.MapRenderer.CenterOnGameObject(localPlayer);
break;
case CenterTarget.Map:
System.SystemConfig.FollowPlayer = false;
System.MapRenderer.DrawOffset = Vector2.Zero;
break;
case CenterTarget.Disabled:
default:
break;
}
}
protected override void DrawContents()
{
UpdateTitle();
UpdateStyle();
UpdateSizePosition();
HoveredFlags = HoverFlags.Nothing;
if (WindowBounds.IsBoundedBy(ImGui.GetMousePos(), ImGui.GetCursorScreenPos(), ImGui.GetCursorScreenPos() + ImGui.GetContentRegionMax())) {
HoveredFlags |= HoverFlags.Window;
}
MapDrawOffset = ImGui.GetCursorScreenPos();
using var fade = ImRaii.PushStyle(ImGuiStyleVar.Alpha, System.SystemConfig.FadePercent, ShouldFade());
using (var renderChild = ImRaii.Child("render_child", ImGui.GetContentRegionAvail(), false, ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoScrollbar)) {
if (!renderChild) return;
if (!System.SystemConfig.AcceptedSpoilerWarning) {
DrawSpoilerWarning();
return;
}
DrawMapElements();
// Reset Draw Position for Overlay Extras
ImGui.SetCursorPos(Vector2.Zero);
DrawToolbar();
DrawCoordinateBar();
}
if (ImGui.IsItemHovered()) {
HoveredFlags |= HoverFlags.WindowInnerFrame;
}
// Process Inputs
ProcessInputs();
}
private void DrawMapElements()
{
System.MapRenderer.DrawBaseTexture();
if (ImGui.IsItemHovered()) {
HoveredFlags |= HoverFlags.MapTexture;
}
System.MapRenderer.DrawDynamicElements();
}
private void DrawToolbar()
{
if (!ShouldShowToolbar()) return;
using (ImRaii.Group()) {
mapToolbar.Draw();
}
if (ImGui.IsItemHovered()) {
HoveredFlags |= HoverFlags.Toolbar;
}
}
private void DrawCoordinateBar()
{
if (!System.SystemConfig.ShowCoordinateBar) return;
using (ImRaii.Group()) {
mapCoordinateBar.Draw(HoveredFlags.HasFlag(HoverFlags.MapTexture), MapDrawOffset);
}
if (ImGui.IsItemHovered()) {
HoveredFlags |= HoverFlags.CoordinateBar;
}
}
private bool ShouldShowToolbar()
{
if (isDragStarted) return false;
if (System.SystemConfig.ShowToolbarOnHover && HoveredFlags.Any()) return true;
if (System.SystemConfig.AlwaysShowToolbar) return true;
return false;
}
private unsafe void UpdateTitle()
{
var mapChanged = lastMapId != AgentMap.Instance()->SelectedMapId;
var areaChanged = lastAreaPlaceNameId != TerritoryInfo.Instance()->AreaPlaceNameId;
var subAreaChanged = lastSubAreaPlaceNameId != TerritoryInfo.Instance()->SubAreaPlaceNameId;
var locationChanged = mapChanged || areaChanged || subAreaChanged;
if (!locationChanged) return;
var subLocationString = string.Empty;
var mapData = Service.DataManager.GetExcelSheet<Map>().GetRow(AgentMap.Instance()->SelectedMapId);
if (System.SystemConfig.ShowRegionLabel) {
var mapRegionName = mapData.PlaceNameRegion.Value.Name.ExtractText();
subLocationString += $" - {mapRegionName}";
}
if (System.SystemConfig.ShowMapLabel) {
var mapName = mapData.PlaceName.Value.Name.ExtractText();
subLocationString += $" - {mapName}";
}
// Don't show specific locations if we aren't there.
if (AgentMap.Instance()->SelectedMapId == AgentMap.Instance()->CurrentMapId) {
if (TerritoryInfo.Instance()->AreaPlaceNameId is not 0 && System.SystemConfig.ShowAreaLabel) {
var areaLabel = Service.DataManager.GetExcelSheet<PlaceName>().GetRow(TerritoryInfo.Instance()->AreaPlaceNameId);
subLocationString += $" - {areaLabel.Name}";
}
if (TerritoryInfo.Instance()->SubAreaPlaceNameId is not 0 && System.SystemConfig.ShowSubAreaLabel) {
var subAreaLabel = Service.DataManager.GetExcelSheet<PlaceName>().GetRow(TerritoryInfo.Instance()->SubAreaPlaceNameId);
subLocationString += $" - {subAreaLabel.Name}";
}
}
WindowName = $"{subLocationString}###HSMappyMapWindow".TrimStart(['-',' ']);
lastMapId = AgentMap.Instance()->SelectedMapId;
lastAreaPlaceNameId = TerritoryInfo.Instance()->AreaPlaceNameId;
lastSubAreaPlaceNameId = TerritoryInfo.Instance()->SubAreaPlaceNameId;
}
public void RefreshTitle()
{
lastMapId = 0;
lastAreaPlaceNameId = 0;
lastSubAreaPlaceNameId = 0;
}
private void ProcessInputs()
{
if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) {
ImGui.OpenPopup("Mappy_Context_Menu");
}
else {
if (HoveredFlags.Any()) {
if (System.SystemConfig.EnableShiftDragMove && ImGui.GetIO().KeyShift) {
Flags &= ~ImGuiWindowFlags.NoMove;
}
else {
ProcessMouseScroll();
ProcessMapDragStart();
Flags |= ImGuiWindowFlags.NoMove;
}
}
ProcessMapDragDragging();
ProcessMapDragEnd();
}
// Draw Context Menu
mapContextMenu.Draw(MapDrawOffset);
}
private unsafe void UpdateStyle()
{
if (System.SystemConfig.HideWindowFrame) {
Flags |= ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoBackground;
}
else {
Flags &= ~(ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoBackground);
}
if (System.SystemConfig.LockWindow) {
Flags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove;
}
else {
Flags &= ~(ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove);
}
if (System.SystemConfig.NoFocusOnAppear) {
Flags |= ImGuiWindowFlags.NoFocusOnAppearing;
}
else {
Flags &= ~ImGuiWindowFlags.NoFocusOnAppearing;
}
if (System.SystemConfig.HideWindowBackground) {
Flags |= ImGuiWindowFlags.NoBackground;
}
else {
Flags &= ~ImGuiWindowFlags.NoBackground;
}
if (Service.KeyState[VirtualKey.ESCAPE] && IsFocused && !IsMapLocked()) {
AgentMap.Instance()->Hide();
}
if (System.SystemConfig.FollowPlayer && Service.ObjectTable is { LocalPlayer: { } localPlayer }) {
System.MapRenderer.CenterOnGameObject(localPlayer);
}
if (System.SystemConfig.LockCenterOnMap) {
System.SystemConfig.FollowPlayer = false;
System.MapRenderer.DrawOffset = Vector2.Zero;
}
}
private void UpdateSizePosition()
{
var systemConfig = System.SystemConfig;
var windowPosition = ImGui.GetWindowPos();
var windowSize = ImGui.GetWindowSize();
if (!IsFocused) {
if (windowPosition != systemConfig.WindowPosition) {
ImGui.SetWindowPos(systemConfig.WindowPosition);
}
if (windowSize != systemConfig.WindowSize) {
ImGui.SetWindowSize(systemConfig.WindowSize);
}
}
else {
// If focused
if (systemConfig.WindowPosition != windowPosition) {
systemConfig.WindowPosition = windowPosition;
SystemConfig.Save();
}
if (systemConfig.WindowSize != windowSize) {
systemConfig.WindowSize = windowSize;
SystemConfig.Save();
}
}
}
private static void DrawSpoilerWarning()
{
using (ImRaii.PushColor(ImGuiCol.Text, KnownColor.Orange.Vector())) {
const string warningLine1 = "Warning, HSMappy does not protect you from spoilers and will show everything.";
const string warningLine2 = "Do not use HSMappy if you are not comfortable with this.";
ImGui.SetCursorPos(ImGui.GetContentRegionAvail() / 2.0f - (ImGui.CalcTextSize(warningLine1) * 2.0f) with { X = 0.0f });
ImGuiHelpers.CenteredText(warningLine1);
ImGuiHelpers.CenteredText(warningLine2);
}
ImGuiHelpers.ScaledDummy(30.0f);
ImGui.SetCursorPosX(ImGui.GetContentRegionAvail().X / 3.0f);
using (ImRaii.Disabled(!(ImGui.GetIO().KeyShift && ImGui.GetIO().KeyCtrl))) {
if (ImGui.Button("I understand", new Vector2(ImGui.GetContentRegionAvail().X / 2.0f, 23.0f * ImGuiHelpers.GlobalScale))) {
System.SystemConfig.AcceptedSpoilerWarning = true;
SystemConfig.Save();
}
using (ImRaii.PushStyle(ImGuiStyleVar.Alpha, 1.0f)) {
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) {
ImGui.SetTooltip("Hold Shift + Control while clicking activate button");
}
}
}
}
public override unsafe void OnClose()
{
AgentMap.Instance()->Hide();
SystemConfig.Save();
}
private void ProcessMouseScroll()
{
if (System.SystemConfig.ZoomLocked) return;
if (ImGui.GetIO().MouseWheel is 0) return;
if (!HoveredFlags.HasFlag(HoverFlags.WindowInnerFrame)) return;
if (System.SystemConfig.UseLinearZoom) {
MapRenderer.MapRenderer.Scale += System.SystemConfig.ZoomSpeed * ImGui.GetIO().MouseWheel;
}
else {
MapRenderer.MapRenderer.Scale *= 1.0f + System.SystemConfig.ZoomSpeed * ImGui.GetIO().MouseWheel;
}
}
private void ProcessMapDragDragging()
{
if (ImGui.IsMouseDragging(ImGuiMouseButton.Left) && isDragStarted) {
System.MapRenderer.DrawOffset += ImGui.GetMouseDragDelta() / MapRenderer.MapRenderer.Scale;
ImGui.ResetMouseDragDelta();
}
}
private void ProcessMapDragEnd()
{
if (ImGui.IsMouseReleased(ImGuiMouseButton.Left)) {
isDragStarted = false;
}
}
private void ProcessMapDragStart()
{
// Don't allow a drag to start if the window size is changing
if (ImGui.GetWindowSize() == lastWindowSize) {
if (ImGui.IsItemClicked(ImGuiMouseButton.Left) && !isDragStarted) {
isDragStarted = true;
System.SystemConfig.FollowPlayer = false;
}
}
else {
lastWindowSize = ImGui.GetWindowSize();
isDragStarted = false;
}
}
private unsafe bool ShouldFade() =>
System.SystemConfig.FadeMode.HasFlag(FadeMode.Always) ||
System.SystemConfig.FadeMode.HasFlag(FadeMode.WhenFocused) && IsFocused ||
System.SystemConfig.FadeMode.HasFlag(FadeMode.WhenMoving) && AgentMap.Instance()->IsPlayerMoving ||
System.SystemConfig.FadeMode.HasFlag(FadeMode.WhenUnFocused) && !IsFocused;
private void RegisterCommands()
{
System.CommandManager.RegisterCommand(new ToggleCommandHandler
{
UseShowHideText = true,
BaseActivationPath = "/map",
EnableDelegate = _ => System.MapWindow.UnCollapseOrShow(),
DisableDelegate = _ => System.MapWindow.Close(),
ToggleDelegate = _ => System.MapWindow.UnCollapseOrToggle(),
});
System.CommandManager.RegisterCommand(new CommandHandler
{
ActivationPath = "/map/follow",
Delegate = _ =>
{
System.SystemConfig.FollowPlayer = true;
SystemConfig.Save();
},
});
System.CommandManager.RegisterCommand(new CommandHandler
{
ActivationPath = "/map/unfollow",
Delegate = _ =>
{
System.SystemConfig.FollowPlayer = false;
SystemConfig.Save();
},
});
System.CommandManager.RegisterCommand(new ToggleCommandHandler
{
BaseActivationPath = "/autofollow",
EnableDelegate = _ =>
{
System.SystemConfig.FollowOnOpen = true;
SystemConfig.Save();
},
DisableDelegate = _ =>
{
System.SystemConfig.FollowOnOpen = false;
SystemConfig.Save();
},
ToggleDelegate = _ =>
{
System.SystemConfig.FollowOnOpen = !System.SystemConfig.FollowOnOpen;
SystemConfig.Save();
},
});
System.CommandManager.RegisterCommand(new ToggleCommandHandler
{
BaseActivationPath = "/keepopen",
EnableDelegate = _ =>
{
System.SystemConfig.KeepOpen = true;
SystemConfig.Save();
},
DisableDelegate = _ =>
{
System.SystemConfig.KeepOpen = false;
SystemConfig.Save();
},
ToggleDelegate = _ =>
{
System.SystemConfig.KeepOpen = !System.SystemConfig.KeepOpen;
SystemConfig.Save();
},
});
System.CommandManager.RegisterCommand(new CommandHandler
{
ActivationPath = "/center/player",
Delegate = _ =>
{
if (Service.ObjectTable.LocalPlayer is { } localPlayer) {
System.MapRenderer.CenterOnGameObject(localPlayer);
}
},
});
System.CommandManager.RegisterCommand(new CommandHandler
{
ActivationPath = "/center/map",
Delegate = _ =>
{
System.SystemConfig.FollowPlayer = false;
System.MapRenderer.DrawOffset = Vector2.Zero;
},
});
}
private static unsafe bool IsMapLocked()
{
var addon = Service.GameGui.GetAddonByName<AddonAreaMap>("AreaMap");
if (addon is null || addon->RootNode is null) return false;
return (addon->Param & 0x8_0000) > 0;
}
}
+127
View File
@@ -0,0 +1,127 @@
using System;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii;
using KamiLib.Window;
using Mappy.Controllers;
using Mappy.Data;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
namespace Mappy.Windows;
public class MinimapWindow : Window
{
public MinimapWindow() : base("HSMappy Minimap###HSMappyMinimap", new Vector2(200.0f, 200.0f))
{
DisableWindowSounds = true;
Flags |= ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoScrollWithMouse;
}
public override bool DrawConditions() =>
IntegrationsController.ShouldShowMinimap() && System.SystemConfig.ShowMinimap;
public override void PreOpenCheck()
{
if (Service.ClientState is { IsLoggedIn: false } or { IsPvP: true })
IsOpen = false;
}
protected override unsafe void DrawContents()
{
var agent = AgentMap.Instance();
// Try loading from Lumina first so minimap can show without ever opening the area map
if (!System.MapRenderer.HasMinimapCacheFor(agent->CurrentMapId) && agent->SelectedMapId != agent->CurrentMapId)
System.MapRenderer.TryEnsureLuminaCacheFor(agent->CurrentMapId);
var mapLoaded = agent->SelectedMapId == agent->CurrentMapId || System.MapRenderer.HasMinimapCacheFor(agent->CurrentMapId);
if (!mapLoaded)
{
// Map data could not be loaded for this area (no Lumina path matched). Minimap shows automatically when data is available.
const string hint = "Map unavailable for this area.";
var textSize = ImGui.CalcTextSize(hint);
var pos = (ImGui.GetWindowSize() - textSize) * 0.5f;
ImGui.SetCursorPos(new Vector2(Math.Max(0, pos.X), Math.Max(20, pos.Y)));
ImGui.TextColored(new Vector4(0.7f, 0.7f, 0.7f, 0.9f), hint);
UpdateStyle();
UpdateSizePosition();
return;
}
UpdateStyle();
UpdateSizePosition();
// Compensate for window padding: draw the minimap child so it fills the full window (no black bands).
var padding = ImGui.GetStyle().WindowPadding;
var winSize = ImGui.GetWindowSize();
ImGui.SetCursorPos(new Vector2(-padding.X, -padding.Y));
var contentSize = winSize;
if (contentSize.X <= 0 || contentSize.Y <= 0) return;
using (ImRaii.PushStyle(ImGuiStyleVar.Alpha, System.SystemConfig.MinimapOpacity))
using (ImRaii.PushStyle(ImGuiStyleVar.ChildBorderSize, 0f))
using (ImRaii.PushStyle(ImGuiStyleVar.WindowPadding, Vector2.Zero))
using (var child = ImRaii.Child("minimap_render", contentSize, false, ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse))
{
if (child) {
// Use window size so map fills the full window; renderer clamps draw position so map always covers the view.
System.MapRenderer.DrawMinimapContents(contentSize);
// Mouse wheel over minimap: zoom in/out, and consume wheel so the window doesn't scroll
if (ImGui.IsItemHovered()) {
var io = ImGui.GetIO();
var wheel = io.MouseWheel;
if (wheel != 0) {
var zoom = System.SystemConfig.MinimapZoom;
zoom -= wheel * 0.012f; // Small step so zoom is incremental between max out (0.1) and max in (0.03)
System.SystemConfig.MinimapZoom = Math.Clamp(zoom, 0.03f, 0.112f);
SystemConfig.Save();
}
// Consume wheel so the window doesn't scroll when at min/max zoom or when we handled it
io.MouseWheel = 0f;
io.MouseWheelH = 0f;
}
}
}
// Restore default padding for the next window is done in plugin Draw callback (PopStyleVar after all windows).
}
public override void OnOpen()
{
ImGui.SetWindowPos(System.SystemConfig.MinimapPosition);
ImGui.SetWindowSize(new Vector2(System.SystemConfig.MinimapSize, System.SystemConfig.MinimapSize));
}
private void UpdateStyle()
{
if (System.SystemConfig.MinimapLockPosition)
Flags |= ImGuiWindowFlags.NoMove;
else
Flags &= ~ImGuiWindowFlags.NoMove;
}
private void UpdateSizePosition()
{
var config = System.SystemConfig;
var windowPosition = ImGui.GetWindowPos();
var windowSize = ImGui.GetWindowSize();
var configSize = config.MinimapSize;
// Size is config-only (set in Mappy settings); always apply config size to window.
if (Math.Abs(windowSize.X - configSize) > 0.1f || Math.Abs(windowSize.Y - configSize) > 0.1f)
ImGui.SetWindowSize(new Vector2(configSize, configSize));
if (!ImGui.IsWindowFocused(ImGuiFocusedFlags.RootAndChildWindows)) {
// Not focused: apply config position to window
if (windowPosition != config.MinimapPosition)
ImGui.SetWindowPos(config.MinimapPosition);
} else {
// Focused: save window position to config (size is changed only via settings)
if (config.MinimapPosition != windowPosition) {
config.MinimapPosition = windowPosition;
SystemConfig.Save();
}
}
}
}
+150
View File
@@ -0,0 +1,150 @@
using System.Drawing;
using System.Linq;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using KamiLib.Classes;
using KamiLib.Window;
using Lumina.Excel.Sheets;
using Mappy.Classes;
using Mappy.Extensions;
using Map = FFXIVClientStructs.FFXIV.Client.Game.UI.Map;
namespace Mappy.Windows;
public class QuestListWindow : Window
{
private readonly TabBar tabBar = new("questListTabBar", [
new AcceptedQuestsTabItem(),
new UnacceptedQuestsTabItem(),
]);
public QuestListWindow() : base("HSMappy Quest List Window", new Vector2(300.0f, 500.0f))
{
AdditionalInfoTooltip = "Shows Quests for the zone you are currently in";
}
protected override void DrawContents()
{
using var child = ImRaii.Child("quest_list_scrollable", ImGui.GetContentRegionAvail());
if (!child) return;
tabBar.Draw();
}
public override void OnClose()
{
System.WindowManager.RemoveWindow(this);
}
}
public unsafe class UnacceptedQuestsTabItem : ITabItem
{
private const float ElementHeight = 48.0f;
public string Name => "Unaccepted Quests";
public bool Disabled => false;
public void Draw()
{
if (Map.Instance()->UnacceptedQuestMarkers.Count > 0) {
foreach (var quest in Map.Instance()->UnacceptedQuestMarkers) {
var questData = Service.DataManager.GetExcelSheet<Quest>().GetRow(quest.ObjectiveId + 65536u);
foreach (var marker in quest.MarkerData) {
var cursorStart = ImGui.GetCursorScreenPos();
if (ImGui.Selectable($"##{quest.ObjectiveId}_Selectable_{marker.LevelId}", false, ImGuiSelectableFlags.None,
new Vector2(ImGui.GetContentRegionAvail().X, ElementHeight * ImGuiHelpers.GlobalScale))) {
System.IntegrationsController.OpenMap(marker.MapId);
System.SystemConfig.FollowPlayer = false;
var mapOffsetVector = DrawHelpers.GetMapOffsetVector();
System.MapRenderer.DrawOffset = -marker.Position.AsMapVector() * AgentMap.Instance()->SelectedMapSizeFactorFloat + mapOffsetVector;
}
ImGui.SetCursorScreenPos(cursorStart);
ImGui.Image(Service.TextureProvider.GetFromGameIcon(marker.IconId).GetWrapOrEmpty().Handle, ImGuiHelpers.ScaledVector2(ElementHeight, ElementHeight));
ImGui.SameLine();
var text = $"Lv. {questData.ClassJobLevel.First()} {quest.Label}";
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ElementHeight * ImGuiHelpers.GlobalScale / 2.0f - ImGui.CalcTextSize(text).Y / 2.0f);
ImGui.Text(text);
}
}
}
else {
const string text = "No quests available";
var textSize = ImGui.CalcTextSize(text);
ImGui.SetCursorPosX(ImGui.GetContentRegionAvail().X / 2.0f - textSize.X / 2.0f);
ImGui.SetCursorPosY(ImGui.GetContentRegionAvail().Y / 2.0f - textSize.Y / 2.0f);
ImGui.TextColored(KnownColor.Orange.Vector(), text);
}
}
}
public unsafe class AcceptedQuestsTabItem : ITabItem
{
private const float ElementHeight = 48.0f;
public string Name => "Accepted Quests";
public bool Disabled => false;
public void Draw()
{
if (AnyActiveQuests()) {
foreach (var quest in Map.Instance()->QuestMarkers) {
if (quest.ObjectiveId is 0) continue;
var questData = Service.DataManager.GetExcelSheet<Quest>().GetRow(quest.ObjectiveId + 65536u);
var index = 0;
foreach (var marker in quest.MarkerData) {
var cursorStart = ImGui.GetCursorScreenPos();
if (ImGui.Selectable($"##{quest.ObjectiveId}_Selectable_{marker.LevelId}_{index++}", false, ImGuiSelectableFlags.None,
new Vector2(ImGui.GetContentRegionAvail().X, ElementHeight * ImGuiHelpers.GlobalScale))) {
System.IntegrationsController.OpenMap(marker.MapId);
System.SystemConfig.FollowPlayer = false;
System.MapRenderer.DrawOffset = -marker.Position.AsMapVector();
}
var iconId = marker.IconId switch
{
>= 60483 and <= 60494 => DrawHelpers.QuestionMarkIcon,
_ => marker.IconId,
};
ImGui.SetCursorScreenPos(cursorStart);
ImGui.Image(Service.TextureProvider.GetFromGameIcon(iconId).GetWrapOrEmpty().Handle, ImGuiHelpers.ScaledVector2(ElementHeight, ElementHeight));
ImGui.SameLine();
var text = $"Lv. {questData.ClassJobLevel.First()} {quest.Label}";
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ElementHeight * ImGuiHelpers.GlobalScale / 2.0f - ImGui.CalcTextSize(text).Y / 2.0f);
ImGui.Text(text);
}
}
}
else {
const string text = "No quests available";
var textSize = ImGui.CalcTextSize(text);
ImGui.SetCursorPosX(ImGui.GetContentRegionAvail().X / 2.0f - textSize.X / 2.0f);
ImGui.SetCursorPosY(ImGui.GetContentRegionAvail().Y / 2.0f - textSize.Y / 2.0f);
ImGui.TextColored(KnownColor.Orange.Vector(), text);
}
}
private static bool AnyActiveQuests()
{
foreach (var questMarker in Map.Instance()->QuestMarkers) {
if (questMarker.ObjectiveId is not 0) return true;
}
return false;
}
}