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 Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
@@ -85,7 +86,7 @@ internal sealed class PayloadHandler
|
||||
switch (payload)
|
||||
{
|
||||
case PlayerPayload player:
|
||||
// TODO(E3): DrawPlayerPopup(chunk, player);
|
||||
DrawPlayerPopup(chunk, player);
|
||||
drawn = true;
|
||||
break;
|
||||
case ItemPayload item:
|
||||
@@ -217,6 +218,200 @@ internal sealed class PayloadHandler
|
||||
.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 unsafe void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button) { }
|
||||
|
||||
Reference in New Issue
Block a user