feat(payload-handler): wire DrawPlayerPopup + FindCharacterForPayload (E3)
E3 fills the player-payload right-click menu. DrawPlayerPopup migrates 1:1 from v1.5.6 with all §4.2/§6.9 substitutions: - Tell-prefix builds via _inputBar.SetPendingMessage (single string build) + _inputBar.Activate = true (replaces v1.5.6's 3x LogWindow.Chat incremental writes + LogWindow.Activate flip) - Channel-switch routes through _mainWindow.ActiveTab?.CurrentChannel? .SetChannel(channel) (per §6.3, ActiveTab is public getter on MainWindow per v1.7.0 refactor) - SendFriendRequest / AddToBlacklist / AddToMuteList / AddToTermsList / SetEurekaTellChannel all route through injected _functions - Player-name renderings route through _chunkRenderer.DrawChunks FindCharacterForPayload migrates 1:1 (pure helper, Plugin.ObjectTable static stays as-is). Replaces E2's TODO(E3) marker in DrawPopups' PlayerPayload case with real DrawPlayerPopup(chunk, player) call. E4 will wire Item/EventItem/Status/Uri popup bodies; E5 the Hover/Click bodies; E6 MoveTooltip.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
@@ -85,7 +86,7 @@ internal sealed class PayloadHandler
|
|||||||
switch (payload)
|
switch (payload)
|
||||||
{
|
{
|
||||||
case PlayerPayload player:
|
case PlayerPayload player:
|
||||||
// TODO(E3): DrawPlayerPopup(chunk, player);
|
DrawPlayerPopup(chunk, player);
|
||||||
drawn = true;
|
drawn = true;
|
||||||
break;
|
break;
|
||||||
case ItemPayload item:
|
case ItemPayload item:
|
||||||
@@ -217,6 +218,200 @@ internal sealed class PayloadHandler
|
|||||||
.Aggregate(string.Concat);
|
.Aggregate(string.Concat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawPlayerPopup(Chunk chunk, PlayerPayload player)
|
||||||
|
{
|
||||||
|
// Possible that GMs return a null payload
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
if (player == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var world = player.World;
|
||||||
|
if (chunk.Message?.Code.Type == ChatType.FreeCompanyLoginLogout)
|
||||||
|
if (Plugin.PlayerState.HomeWorld.IsValid)
|
||||||
|
world = Plugin.PlayerState.HomeWorld;
|
||||||
|
|
||||||
|
var name = new List<Chunk> { new TextChunk(ChunkSource.None, null, player.PlayerName) };
|
||||||
|
if (world.Value.IsPublic)
|
||||||
|
{
|
||||||
|
name.AddRange([
|
||||||
|
new IconChunk(ChunkSource.None, null, BitmapFontIcon.CrossWorld),
|
||||||
|
new TextChunk(ChunkSource.None, null, world.Value.Name.ExtractText()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_chunkRenderer.DrawChunks(name, false);
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
var validContentId = chunk.Message?.ContentId is not (null or 0);
|
||||||
|
if (ImGui.Selectable(Language.Context_SendTell))
|
||||||
|
{
|
||||||
|
// Eureka, Bozja and Occult need special handling as tells work different
|
||||||
|
if (!Sheets.IsInForay())
|
||||||
|
{
|
||||||
|
// §6.9: build as single string then hand off; replaces v1.5.6's
|
||||||
|
// incremental LogWindow.Chat += ... pattern
|
||||||
|
var builder = $"/tell {player.PlayerName}";
|
||||||
|
if (world.Value.IsPublic)
|
||||||
|
builder += $"@{world.Value.Name}";
|
||||||
|
|
||||||
|
builder += " ";
|
||||||
|
_inputBar.SetPendingMessage(builder);
|
||||||
|
}
|
||||||
|
else if (validContentId)
|
||||||
|
{
|
||||||
|
_functions.Chat.SetEurekaTellChannel(
|
||||||
|
player.PlayerName,
|
||||||
|
world.Value.Name.ToString(),
|
||||||
|
(ushort)world.RowId,
|
||||||
|
0,
|
||||||
|
chunk.Message!.ContentId,
|
||||||
|
0,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_inputBar.Activate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (world.Value.IsPublic)
|
||||||
|
{
|
||||||
|
var party = Plugin.PartyList;
|
||||||
|
var leader = party[(int)party.PartyLeaderIndex]?.ContentId;
|
||||||
|
var isLeader = party.Length == 0 || Plugin.PlayerState.ContentId == leader;
|
||||||
|
var member = party.FirstOrDefault(member =>
|
||||||
|
member.Name.TextValue == player.PlayerName && member.World.RowId == world.RowId
|
||||||
|
);
|
||||||
|
var isInParty = member != null;
|
||||||
|
var inInstance = GameFunctions.GameFunctions.IsInInstance();
|
||||||
|
var inPartyInstance =
|
||||||
|
Sheets
|
||||||
|
.TerritorySheet.GetRow(Plugin.ClientState.TerritoryType)
|
||||||
|
.TerritoryIntendedUse.RowId
|
||||||
|
is (41 or 47 or 48 or 52 or 53 or 61);
|
||||||
|
if (isLeader)
|
||||||
|
{
|
||||||
|
if (!isInParty)
|
||||||
|
{
|
||||||
|
if (inInstance && inPartyInstance)
|
||||||
|
{
|
||||||
|
if (validContentId && ImGui.Selectable(Language.Context_InviteToParty))
|
||||||
|
GameFunctions.Party.InviteInInstance(chunk.Message!.ContentId);
|
||||||
|
}
|
||||||
|
else if (!inInstance)
|
||||||
|
{
|
||||||
|
using var menu = ImRaii.Menu(Language.Context_InviteToParty);
|
||||||
|
if (menu.Success)
|
||||||
|
{
|
||||||
|
if (ImGui.Selectable(Language.Context_InviteToParty_SameWorld))
|
||||||
|
GameFunctions.Party.InviteSameWorld(
|
||||||
|
player.PlayerName,
|
||||||
|
(ushort)world.RowId,
|
||||||
|
chunk.Message?.ContentId ?? 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
validContentId
|
||||||
|
&& ImGui.Selectable(Language.Context_InviteToParty_DifferentWorld)
|
||||||
|
)
|
||||||
|
GameFunctions.Party.InviteOtherWorld(
|
||||||
|
chunk.Message!.ContentId,
|
||||||
|
(ushort)world.RowId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInParty && member != null && (!inInstance || (inInstance && inPartyInstance)))
|
||||||
|
{
|
||||||
|
if (ImGui.Selectable(Language.Context_Promote))
|
||||||
|
GameFunctions.Party.Promote(player.PlayerName, member.ContentId);
|
||||||
|
|
||||||
|
if (ImGui.Selectable(Language.Context_KickFromParty))
|
||||||
|
GameFunctions.Party.Kick(player.PlayerName, member.ContentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isFriend = GameFunctions
|
||||||
|
.GameFunctions.GetFriends()
|
||||||
|
.Any(friend =>
|
||||||
|
friend.NameString == player.PlayerName && friend.HomeWorld == world.RowId
|
||||||
|
);
|
||||||
|
if (!isFriend && ImGui.Selectable(Language.Context_SendFriendRequest))
|
||||||
|
_functions.SendFriendRequest(player.PlayerName, (ushort)world.RowId);
|
||||||
|
|
||||||
|
using (var menuBlockFunctions = ImRaii.Menu(Language.Context_BlockFunctions))
|
||||||
|
{
|
||||||
|
if (menuBlockFunctions.Success)
|
||||||
|
{
|
||||||
|
if (ImGui.Selectable(Language.Context_AddToBlacklist))
|
||||||
|
_functions.AddToBlacklist(player.PlayerName, (ushort)world.RowId);
|
||||||
|
|
||||||
|
if (chunk.Message != null)
|
||||||
|
{
|
||||||
|
var message = chunk.Message;
|
||||||
|
|
||||||
|
if (
|
||||||
|
message.AccountId != 0
|
||||||
|
&& ImGui.Selectable(Language.Context_AddToMuteList)
|
||||||
|
)
|
||||||
|
_functions.AddToMuteList(
|
||||||
|
message.AccountId,
|
||||||
|
message.ContentId,
|
||||||
|
player.PlayerName,
|
||||||
|
(short)world.RowId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ImGui.Selectable(Language.Context_AddToTermsFilter))
|
||||||
|
_functions.AddToTermsList(message.ContentSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
GameFunctions.GameFunctions.IsMentor()
|
||||||
|
&& ImGui.Selectable(Language.Context_InviteToNoviceNetwork)
|
||||||
|
)
|
||||||
|
GameFunctions.Context.InviteToNoviceNetwork(player.PlayerName, (ushort)world.RowId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputChannel = chunk.Message?.Code.Type.ToInputChannel();
|
||||||
|
if (inputChannel != null && ImGui.Selectable(Language.Context_ReplyInSelectedChatMode))
|
||||||
|
{
|
||||||
|
// §6.3: route channel-switch through MainWindow's active tab
|
||||||
|
_mainWindow.ActiveTab?.CurrentChannel?.SetChannel(inputChannel.Value);
|
||||||
|
_inputBar.Activate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.Selectable(Language.Context_Target) && FindCharacterForPayload(player) is { } obj)
|
||||||
|
Plugin.TargetManager.Target = obj;
|
||||||
|
|
||||||
|
if (validContentId && ImGui.Selectable(Language.Context_AdventurerPlate))
|
||||||
|
if (!GameFunctions.GameFunctions.TryOpenAdventurerPlate(chunk.Message!.ContentId))
|
||||||
|
WrapperUtil.AddNotification(
|
||||||
|
Language.Context_AdventurerPlateError,
|
||||||
|
NotificationType.Warning
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IPlayerCharacter? FindCharacterForPayload(PlayerPayload payload)
|
||||||
|
{
|
||||||
|
foreach (var obj in Plugin.ObjectTable)
|
||||||
|
{
|
||||||
|
if (obj is not IPlayerCharacter character)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (character.Name.TextValue != payload.PlayerName)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (payload.World.Value.IsPublic && character.HomeWorld.RowId != payload.World.RowId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return character;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
internal void Hover(Payload payload) { }
|
internal void Hover(Payload payload) { }
|
||||||
|
|
||||||
internal unsafe void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button) { }
|
internal unsafe void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button) { }
|
||||||
|
|||||||
Reference in New Issue
Block a user