Files
SnowcloakClient/MareSynchronos/UI/CharaDataHubUi.NearbyPoses.cs

208 lines
12 KiB
C#
Raw Normal View History

2025-08-22 02:19:48 +01:00
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using System.Numerics;
namespace MareSynchronos.UI;
internal partial class CharaDataHubUi
{
private void DrawNearbyPoses()
{
_uiSharedService.BigText("Poses Nearby");
DrawHelpFoldout("This tab will show you all Shared World Poses nearby you." + Environment.NewLine + Environment.NewLine
+ "Shared World Poses are poses in character data that have world data attached to them and are set to shared. "
+ "This means that all data that is in 'Shared with You' that has a pose with world data attached to it will be shown here if you are nearby." + Environment.NewLine
+ "By default all poses that are shared will be shown. Poses taken in housing areas will by default only be shown on the correct world and location." + Environment.NewLine + Environment.NewLine
+ "Shared World Poses will appear in the world as floating wisps, as well as in the list below. You can mouse over a Shared World Pose in the list for it to get highlighted in the world." + Environment.NewLine + Environment.NewLine
+ "You can apply Shared World Poses to yourself or spawn the associated character to pose with them." + Environment.NewLine + Environment.NewLine
+ "You can adjust the filter and change further settings in the 'Settings & Filter' foldout.");
UiSharedService.DrawTree("Settings & Filters", () =>
{
string filterByUser = _charaDataNearbyManager.UserNoteFilter;
if (ImGui.InputTextWithHint("##filterbyuser", "Filter by User", ref filterByUser, 50))
{
_charaDataNearbyManager.UserNoteFilter = filterByUser;
}
bool onlyCurrent = _configService.Current.NearbyOwnServerOnly;
if (ImGui.Checkbox("Only show Poses on current world", ref onlyCurrent))
{
_configService.Current.NearbyOwnServerOnly = onlyCurrent;
_configService.Save();
}
_uiSharedService.DrawHelpText("Show the location of shared Poses with World Data from current world only");
bool showOwn = _configService.Current.NearbyShowOwnData;
if (ImGui.Checkbox("Also show your own data", ref showOwn))
{
_configService.Current.NearbyShowOwnData = showOwn;
_configService.Save();
}
_uiSharedService.DrawHelpText("Show your own Poses as well");
bool ignoreHousing = _configService.Current.NearbyIgnoreHousingLimitations;
if (ImGui.Checkbox("Ignore Housing Limitations", ref ignoreHousing))
{
_configService.Current.NearbyIgnoreHousingLimitations = ignoreHousing;
_configService.Save();
}
_uiSharedService.DrawHelpText("Display all poses in their location regardless of housing limitations. (Ignoring Ward, Plot, Room)" + UiSharedService.TooltipSeparator
+ "Note: Poses that utilize housing props, furniture, etc. will not be displayed correctly if not spawned in the right location.");
bool showWisps = _configService.Current.NearbyDrawWisps;
if (ImGui.Checkbox("Show Pose Wisps in the overworld", ref showWisps))
{
_configService.Current.NearbyDrawWisps = showWisps;
_configService.Save();
}
_uiSharedService.DrawHelpText("Draw floating wisps where other's poses are in the world.");
int poseDetectionDistance = _configService.Current.NearbyDistanceFilter;
ImGui.SetNextItemWidth(100);
if (ImGui.SliderInt("Detection Distance", ref poseDetectionDistance, 5, 1000))
{
_configService.Current.NearbyDistanceFilter = poseDetectionDistance;
_configService.Save();
}
_uiSharedService.DrawHelpText("Maximum distance in which poses will be shown. Set it to the maximum if you want to see all poses on the current map.");
bool alwaysShow = _configService.Current.NearbyShowAlways;
if (ImGui.Checkbox("Keep active outside Poses Nearby tab", ref alwaysShow))
{
_configService.Current.NearbyShowAlways = alwaysShow;
_configService.Save();
}
_uiSharedService.DrawHelpText("Continue the calculation of position of wisps etc. active outside of the 'Poses Nearby' tab." + UiSharedService.TooltipSeparator
+ "Note: The wisps etc. will disappear during combat and performing.");
});
if (!_uiSharedService.IsInGpose)
{
ImGuiHelpers.ScaledDummy(5);
UiSharedService.DrawGroupedCenteredColorText("Spawning and applying pose data is only available in GPose.", ImGuiColors.DalamudYellow);
ImGuiHelpers.ScaledDummy(5);
}
DrawUpdateSharedDataButton();
UiSharedService.DistanceSeparator();
using var child = ImRaii.Child("nearbyPosesChild", new(0, 0), false, ImGuiWindowFlags.AlwaysAutoResize);
ImGuiHelpers.ScaledDummy(3f);
using var indent = ImRaii.PushIndent(5f);
if (_charaDataNearbyManager.NearbyData.Count == 0)
{
UiSharedService.DrawGroupedCenteredColorText("No Shared World Poses found nearby.", ImGuiColors.DalamudYellow);
}
bool wasAnythingHovered = false;
int i = 0;
foreach (var pose in _charaDataNearbyManager.NearbyData.OrderBy(v => v.Value.Distance))
{
using var poseId = ImRaii.PushId("nearbyPose" + (i++));
var pos = ImGui.GetCursorPos();
var circleDiameter = 60f;
var circleOriginX = ImGui.GetWindowContentRegionMax().X - circleDiameter - pos.X;
float circleOffsetY = 0;
UiSharedService.DrawGrouped(() =>
{
string? userNote = _serverConfigurationManager.GetNoteForUid(pose.Key.MetaInfo.Uploader.UID);
var noteText = pose.Key.MetaInfo.IsOwnData ? "YOU" : (userNote == null ? pose.Key.MetaInfo.Uploader.AliasOrUID : $"{userNote} ({pose.Key.MetaInfo.Uploader.AliasOrUID})");
ImGui.TextUnformatted("Pose by");
ImGui.SameLine();
UiSharedService.ColorText(noteText, ImGuiColors.ParsedGreen);
using (ImRaii.Group())
{
UiSharedService.ColorText("Character Data Description", ImGuiColors.DalamudGrey);
ImGui.SameLine();
_uiSharedService.IconText(FontAwesomeIcon.ExternalLinkAlt, ImGuiColors.DalamudGrey);
}
UiSharedService.AttachToolTip(pose.Key.MetaInfo.Description);
UiSharedService.ColorText("Description", ImGuiColors.DalamudGrey);
ImGui.SameLine();
UiSharedService.TextWrapped(pose.Key.Description ?? "No Pose Description was set", circleOriginX);
var posAfterGroup = ImGui.GetCursorPos();
var groupHeightCenter = (posAfterGroup.Y - pos.Y) / 2;
circleOffsetY = (groupHeightCenter - circleDiameter / 2);
if (circleOffsetY < 0) circleOffsetY = 0;
ImGui.SetCursorPos(new Vector2(circleOriginX, pos.Y));
ImGui.Dummy(new Vector2(circleDiameter, circleDiameter));
UiSharedService.AttachToolTip("Click to open corresponding map and set map marker" + UiSharedService.TooltipSeparator
+ pose.Key.WorldDataDescriptor);
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
{
_dalamudUtilService.SetMarkerAndOpenMap(pose.Key.Position, pose.Key.Map);
}
ImGui.SetCursorPos(posAfterGroup);
if (_uiSharedService.IsInGpose)
{
GposePoseAction(() =>
{
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowRight, "Apply Pose"))
{
_charaDataManager.ApplyFullPoseDataToGposeTarget(pose.Key);
}
}, $"Apply pose and position to {CharaName(_gposeTarget)}", _hasValidGposeTarget);
ImGui.SameLine();
GposeMetaInfoAction((_) =>
{
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Spawn and Pose"))
{
_charaDataManager.SpawnAndApplyWorldTransform(pose.Key.MetaInfo, pose.Key);
}
}, "Spawn actor and apply pose and position", pose.Key.MetaInfo, _hasValidGposeTarget, true);
}
});
if (ImGui.IsItemHovered())
{
wasAnythingHovered = true;
_nearbyHovered = pose.Key;
}
var drawList = ImGui.GetWindowDrawList();
var circleRadius = circleDiameter / 2f;
var windowPos = ImGui.GetWindowPos();
var scrollX = ImGui.GetScrollX();
var scrollY = ImGui.GetScrollY();
var circleCenter = new Vector2(windowPos.X + circleOriginX + circleRadius - scrollX, windowPos.Y + pos.Y + circleRadius + circleOffsetY - scrollY);
var rads = pose.Value.Direction * (Math.PI / 180);
float halfConeAngleRadians = 15f * (float)Math.PI / 180f;
Vector2 baseDir1 = new Vector2((float)Math.Sin(rads - halfConeAngleRadians), -(float)Math.Cos(rads - halfConeAngleRadians));
Vector2 baseDir2 = new Vector2((float)Math.Sin(rads + halfConeAngleRadians), -(float)Math.Cos(rads + halfConeAngleRadians));
Vector2 coneBase1 = circleCenter + baseDir1 * circleRadius;
Vector2 coneBase2 = circleCenter + baseDir2 * circleRadius;
// Draw the cone as a filled triangle
drawList.AddTriangleFilled(circleCenter, coneBase1, coneBase2, UiSharedService.Color(ImGuiColors.ParsedGreen));
drawList.AddCircle(circleCenter, circleDiameter / 2, UiSharedService.Color(ImGuiColors.DalamudWhite), 360, 2);
var distance = pose.Value.Distance.ToString("0.0") + "y";
var textSize = ImGui.CalcTextSize(distance);
drawList.AddText(new Vector2(circleCenter.X - textSize.X / 2, circleCenter.Y + textSize.Y / 3f), UiSharedService.Color(ImGuiColors.DalamudWhite), distance);
ImGuiHelpers.ScaledDummy(3);
}
if (!wasAnythingHovered) _nearbyHovered = null;
_charaDataNearbyManager.SetHoveredVfx(_nearbyHovered);
}
private void DrawUpdateSharedDataButton()
{
using (ImRaii.Disabled(_charaDataManager.GetAllDataTask != null
|| (_charaDataManager.GetSharedWithYouTimeoutTask != null && !_charaDataManager.GetSharedWithYouTimeoutTask.IsCompleted)))
{
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleDown, "Update Data Shared With You"))
{
_ = _charaDataManager.GetAllSharedData(_disposalCts.Token).ContinueWith(u => UpdateFilteredItems());
}
}
if (_charaDataManager.GetSharedWithYouTimeoutTask != null && !_charaDataManager.GetSharedWithYouTimeoutTask.IsCompleted)
{
UiSharedService.AttachToolTip("You can only refresh all character data from server every minute. Please wait.");
}
}
}