- follow a player and the screen names who you're following, the right-click menu grows its Follow row, and your own movement is what cancels it — creatures are refused like the real client - a locked lockbox click asks the server instead of refusing silently, a quest-starter chest offers its quest before opening, and an item on cooldown hides its green "open" line - a parried swing plays its clang like the real client keys it - the README's What works / Not built yet caught up on a week of shipping Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
596 lines
34 KiB
XML
596 lines
34 KiB
XML
<!--
|
|
The unit right-click popups (decision 0434 phase 5) — transcribed from the 1.12 FrameXML
|
|
UnitPopup.lua (the whole file: the button/menu tables, ShowMenu, HideButtons, the OnUpdate
|
|
enable pass, OnClick) + the UIParent.xml OnUpdate hook (a driver frame here — the DeathFrame
|
|
driver pattern; benilla's UIParent carries no OnUpdate).
|
|
|
|
The menus ride the dropdown kit's MENU displayMode + level-2 submenus (UIDropDownMenu.xml).
|
|
The owner frames (TargetFrameDropDown, PlayerFrameDropDown, PartyMemberFrameNDropDown) live
|
|
with their frames in UnitFrames.xml / PartyFrame.xml — this file is the shared engine, like
|
|
the ref's load split.
|
|
|
|
DEVIATIONS, deliberate (each waits for its arc):
|
|
- NO ADDED ROWS. A "PVP" row briefly sat at the head of SELF (decision 0646 §3); decision 0652
|
|
took it back out. 1.12.1 has no PvP toggle in any menu — `TogglePVP()`'s single caller in the
|
|
whole shipped UI is `SlashCmdList["PVP"]`, and `UnitPopup.lua` contains the string "PVP"
|
|
exactly zero times — so the row was a deviation that made this menu read wrong against the
|
|
reference. `/pvp` is the toggle the client has.
|
|
- DEFERRED ENTRIES: rows whose backing system doesn't exist yet are HIDDEN outright (the
|
|
BENILLA_UNITPOPUP_DEFERRED set in HideButtons) rather than shown-dead: TARGET (the friends
|
|
list), GUILD_PROMOTE/GUILD_LEAVE (the guild arc), RESET_INSTANCES (instances). TRADE came out
|
|
with decision 0592 P1, DUEL with 0633, INSPECT with 0631, FOLLOW with 0890's follow-up — and
|
|
that last entry's stated reason ("its CMSG family is its own slice") was simply WRONG: 0890
|
|
verified auto-follow sends nothing at all, it is pure client-side movement, so the row was
|
|
waiting on a wire slice that does not exist. It was the motion law it really needed.
|
|
Everything a menu SHOWS works. The PET menu and the RAID_* rank rows stay
|
|
in the tables but are unreachable (no "pet" token, no raid roster) — their OnClick branches
|
|
are trimmed with the rest.
|
|
- Lua 5.1: the ref's 5.0 `for i, v in t do` iteration is ipairs/pairs here; strsub → the
|
|
TEXT stub below keeps the table lines verbatim (the ref's TEXT() is the localization
|
|
identity).
|
|
- The newbie tooltips on rows are OUT with the kit's (default-off cvar look).
|
|
- CheckInteractDistance now really gates (decision 0631): the binding landed with the inspect
|
|
row, off wow-re's §5-VERIFIED `check_interact_dist2` table {10², 11.1111², 10², 30²}. So every
|
|
SHOWN row with a non-zero `dist` grays out of range now — INSPECT (dist 1, 10 yd), TRADE
|
|
(dist 2, 11.11 yd) and DUEL (dist 3, 10 yd) — where TRADE and DUEL shipped ungated only
|
|
because the binding was nil when they landed. The nil-guard in the OnUpdate pass below is
|
|
kept as-is.
|
|
-->
|
|
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
|
<Script><![CDATA[
|
|
-- The ref's localization identity (UIParent.lua) — keeps the table lines verbatim.
|
|
if ( not TEXT ) then
|
|
TEXT = function(s) return s end
|
|
end
|
|
|
|
UNITPOPUP_TIMEOUT = 5;
|
|
|
|
UnitPopupButtons = { };
|
|
UnitPopupButtons["CANCEL"] = { text = TEXT(CANCEL), dist = 0, space = 1 };
|
|
UnitPopupButtons["TRADE"] = { text = TEXT(TRADE), dist = 2 };
|
|
UnitPopupButtons["INSPECT"] = { text = TEXT(INSPECT), dist = 1 };
|
|
UnitPopupButtons["TARGET"] = { text = TEXT(TARGET), dist = 0 };
|
|
UnitPopupButtons["DUEL"] = { text = TEXT(DUEL), dist = 3, space = 1 };
|
|
UnitPopupButtons["WHISPER"] = { text = TEXT(WHISPER), dist = 0 };
|
|
UnitPopupButtons["INVITE"] = { text = TEXT(PARTY_INVITE), dist = 0 };
|
|
UnitPopupButtons["UNINVITE"] = { text = TEXT(PARTY_UNINVITE), dist = 0 };
|
|
UnitPopupButtons["PROMOTE"] = { text = TEXT(PARTY_PROMOTE), dist = 0 };
|
|
UnitPopupButtons["GUILD_PROMOTE"] = { text = TEXT(GUILD_PROMOTE), dist = 0 };
|
|
UnitPopupButtons["GUILD_LEAVE"] = { text = TEXT(GUILD_LEAVE), dist = 0 };
|
|
UnitPopupButtons["LEAVE"] = { text = TEXT(PARTY_LEAVE), dist = 0 };
|
|
UnitPopupButtons["FOLLOW"] = { text = TEXT(FOLLOW), dist = 4 };
|
|
UnitPopupButtons["PET_PASSIVE"] = { text = TEXT(PET_PASSIVE), dist = 0 };
|
|
UnitPopupButtons["PET_DEFENSIVE"] = { text = TEXT(PET_DEFENSIVE), dist = 0 };
|
|
UnitPopupButtons["PET_AGGRESSIVE"] = { text = TEXT(PET_AGGRESSIVE), dist = 0 };
|
|
UnitPopupButtons["PET_WAIT"] = { text = TEXT(PET_WAIT), dist = 0 };
|
|
UnitPopupButtons["PET_FOLLOW"] = { text = TEXT(PET_FOLLOW), dist = 0 };
|
|
UnitPopupButtons["PET_ATTACK"] = { text = TEXT(PET_ATTACK), dist = 0 };
|
|
UnitPopupButtons["PET_DISMISS"] = { text = TEXT(PET_DISMISS), dist = 0 };
|
|
UnitPopupButtons["PET_ABANDON"] = { text = TEXT(PET_ABANDON), dist = 0 };
|
|
UnitPopupButtons["PET_PAPERDOLL"] = { text = TEXT(PET_PAPERDOLL), dist = 0 };
|
|
UnitPopupButtons["PET_RENAME"] = { text = TEXT(PET_RENAME), dist = 0 };
|
|
|
|
UnitPopupButtons["LOOT_METHOD"] = { text = TEXT(LOOT_METHOD), dist = 0, nested = 1 };
|
|
UnitPopupButtons["FREE_FOR_ALL"] = { text = TEXT(LOOT_FREE_FOR_ALL), dist = 0 };
|
|
UnitPopupButtons["ROUND_ROBIN"] = { text = TEXT(LOOT_ROUND_ROBIN), dist = 0 };
|
|
UnitPopupButtons["MASTER_LOOTER"] = { text = TEXT(LOOT_MASTER_LOOTER), dist = 0 };
|
|
UnitPopupButtons["GROUP_LOOT"] = { text = TEXT(LOOT_GROUP_LOOT), dist = 0 };
|
|
UnitPopupButtons["NEED_BEFORE_GREED"] = { text = TEXT(LOOT_NEED_BEFORE_GREED), dist = 0 };
|
|
UnitPopupButtons["RESET_INSTANCES"] = { text = TEXT(RESET_INSTANCES), dist = 0 };
|
|
|
|
UnitPopupButtons["LOOT_THRESHOLD"] = { text = TEXT(LOOT_THRESHOLD), dist = 0, nested = 1 };
|
|
UnitPopupButtons["LOOT_PROMOTE"] = { text = TEXT(LOOT_PROMOTE), dist = 0 };
|
|
UnitPopupButtons["ITEM_QUALITY2_DESC"] = { text = TEXT(ITEM_QUALITY2_DESC), dist = 0, color = ITEM_QUALITY_COLORS[2] };
|
|
UnitPopupButtons["ITEM_QUALITY3_DESC"] = { text = TEXT(ITEM_QUALITY3_DESC), dist = 0, color = ITEM_QUALITY_COLORS[3] };
|
|
UnitPopupButtons["ITEM_QUALITY4_DESC"] = { text = TEXT(ITEM_QUALITY4_DESC), dist = 0, color = ITEM_QUALITY_COLORS[4] };
|
|
|
|
UnitPopupButtons["RAID_LEADER"] = { text = TEXT(NEW_LEADER), dist = 0 };
|
|
UnitPopupButtons["RAID_PROMOTE"] = { text = TEXT(PROMOTE), dist = 0 };
|
|
UnitPopupButtons["RAID_DEMOTE"] = { text = TEXT(DEMOTE), dist = 0 };
|
|
UnitPopupButtons["RAID_REMOVE"] = { text = TEXT(REMOVE), dist = 0 };
|
|
|
|
UnitPopupButtons["RAID_TARGET_ICON"] = { text = TEXT(RAID_TARGET_ICON), dist = 0, nested = 1 };
|
|
UnitPopupButtons["RAID_TARGET_1"] = { text = TEXT(RAID_TARGET_1), dist = 0, checkable = 1, color = {r = 1.0, g = 0.92, b = 0}, icon = "Interface\\TargetingFrame\\UI-RaidTargetingIcons", tCoordLeft = 0, tCoordRight = 0.25, tCoordTop = 0, tCoordBottom = 0.25 };
|
|
UnitPopupButtons["RAID_TARGET_2"] = { text = TEXT(RAID_TARGET_2), dist = 0, checkable = 1, color = {r = 0.98, g = 0.57, b = 0}, icon = "Interface\\TargetingFrame\\UI-RaidTargetingIcons", tCoordLeft = 0.25, tCoordRight = 0.5, tCoordTop = 0, tCoordBottom = 0.25 };
|
|
UnitPopupButtons["RAID_TARGET_3"] = { text = TEXT(RAID_TARGET_3), dist = 0, checkable = 1, color = {r = 0.83, g = 0.22, b = 0.9}, icon = "Interface\\TargetingFrame\\UI-RaidTargetingIcons", tCoordLeft = 0.5, tCoordRight = 0.75, tCoordTop = 0, tCoordBottom = 0.25 };
|
|
UnitPopupButtons["RAID_TARGET_4"] = { text = TEXT(RAID_TARGET_4), dist = 0, checkable = 1, color = {r = 0.04, g = 0.95, b = 0}, icon = "Interface\\TargetingFrame\\UI-RaidTargetingIcons", tCoordLeft = 0.75, tCoordRight = 1, tCoordTop = 0, tCoordBottom = 0.25 };
|
|
UnitPopupButtons["RAID_TARGET_5"] = { text = TEXT(RAID_TARGET_5), dist = 0, checkable = 1, color = {r = 0.7, g = 0.82, b = 0.875}, icon = "Interface\\TargetingFrame\\UI-RaidTargetingIcons", tCoordLeft = 0, tCoordRight = 0.25, tCoordTop = 0.25, tCoordBottom = 0.5 };
|
|
UnitPopupButtons["RAID_TARGET_6"] = { text = TEXT(RAID_TARGET_6), dist = 0, checkable = 1, color = {r = 0, g = 0.71, b = 1}, icon = "Interface\\TargetingFrame\\UI-RaidTargetingIcons", tCoordLeft = 0.25, tCoordRight = 0.5, tCoordTop = 0.25, tCoordBottom = 0.5 };
|
|
UnitPopupButtons["RAID_TARGET_7"] = { text = TEXT(RAID_TARGET_7), dist = 0, checkable = 1, color = {r = 1.0, g = 0.24, b = 0.168}, icon = "Interface\\TargetingFrame\\UI-RaidTargetingIcons", tCoordLeft = 0.5, tCoordRight = 0.75, tCoordTop = 0.25, tCoordBottom = 0.5 };
|
|
UnitPopupButtons["RAID_TARGET_8"] = { text = TEXT(RAID_TARGET_8), dist = 0, checkable = 1, color = {r = 0.98, g = 0.98, b = 0.98}, icon = "Interface\\TargetingFrame\\UI-RaidTargetingIcons", tCoordLeft = 0.75, tCoordRight = 1, tCoordTop = 0.25, tCoordBottom = 0.5 };
|
|
UnitPopupButtons["RAID_TARGET_NONE"] = { text = TEXT(NONE), dist = 0, checkable = 1, };
|
|
|
|
-- First level menus
|
|
UnitPopupMenus = { };
|
|
UnitPopupMenus["SELF"] = { "LOOT_METHOD", "LOOT_THRESHOLD", "LOOT_PROMOTE", "LEAVE", "RESET_INSTANCES", "RAID_TARGET_ICON", "CANCEL" };
|
|
UnitPopupMenus["PET"] = { "PET_PAPERDOLL", "PET_RENAME", "PET_ABANDON", "PET_DISMISS", "CANCEL" };
|
|
UnitPopupMenus["PARTY"] = { "WHISPER", "PROMOTE", "LOOT_PROMOTE", "UNINVITE", "INSPECT", "TRADE", "FOLLOW", "DUEL", "RAID_TARGET_ICON", "CANCEL" };
|
|
UnitPopupMenus["PLAYER"] = { "WHISPER", "INSPECT", "INVITE", "TRADE", "FOLLOW", "DUEL", "RAID_TARGET_ICON", "CANCEL" };
|
|
UnitPopupMenus["RAID"] = { "RAID_LEADER", "RAID_PROMOTE", "RAID_DEMOTE", "RAID_REMOVE", "CANCEL" };
|
|
UnitPopupMenus["FRIEND"] = { "WHISPER", "INVITE", "TARGET", "GUILD_PROMOTE", "GUILD_LEAVE", "CANCEL" };
|
|
UnitPopupMenus["RAID_TARGET_ICON"] = { "RAID_TARGET_1", "RAID_TARGET_2", "RAID_TARGET_3", "RAID_TARGET_4", "RAID_TARGET_5", "RAID_TARGET_6", "RAID_TARGET_7", "RAID_TARGET_8", "RAID_TARGET_NONE" };
|
|
|
|
-- Second level menus
|
|
UnitPopupMenus["LOOT_METHOD"] = { "FREE_FOR_ALL", "ROUND_ROBIN", "MASTER_LOOTER", "GROUP_LOOT", "NEED_BEFORE_GREED", "CANCEL" };
|
|
UnitPopupMenus["LOOT_THRESHOLD"] = { "ITEM_QUALITY2_DESC", "ITEM_QUALITY3_DESC", "ITEM_QUALITY4_DESC", "CANCEL" };
|
|
|
|
UnitPopupShown = { 1, 1, 1, 1, 1, 1, 1, 1, 1 };
|
|
|
|
UnitLootMethod = {};
|
|
UnitLootMethod["freeforall"] = { text = LOOT_FREE_FOR_ALL };
|
|
UnitLootMethod["roundrobin"] = { text = LOOT_ROUND_ROBIN };
|
|
UnitLootMethod["master"] = { text = LOOT_MASTER_LOOTER };
|
|
UnitLootMethod["group"] = { text = LOOT_GROUP_LOOT };
|
|
UnitLootMethod["needbeforegreed"] = { text = LOOT_NEED_BEFORE_GREED };
|
|
|
|
UnitPopupFrames = {
|
|
"PlayerFrameDropDown",
|
|
"TargetFrameDropDown",
|
|
"PartyMemberFrame1DropDown",
|
|
"PartyMemberFrame2DropDown",
|
|
"PartyMemberFrame3DropDown",
|
|
"PartyMemberFrame4DropDown",
|
|
};
|
|
|
|
-- The DEFERRED set (header): entries hidden until their backing arc lands.
|
|
BENILLA_UNITPOPUP_DEFERRED = {
|
|
TARGET = 1,
|
|
GUILD_PROMOTE = 1, GUILD_LEAVE = 1, RESET_INSTANCES = 1,
|
|
};
|
|
|
|
function UnitPopup_ShowMenu(dropdownMenu, which, unit, name, userData)
|
|
-- Init variables
|
|
dropdownMenu.which = which;
|
|
dropdownMenu.unit = unit;
|
|
local server = nil;
|
|
if ( unit and not name ) then
|
|
name = UnitName(unit);
|
|
end
|
|
dropdownMenu.name = name;
|
|
dropdownMenu.userData = userData;
|
|
dropdownMenu.server = server;
|
|
|
|
-- Determine which buttons should be shown or hidden
|
|
UnitPopup_HideButtons();
|
|
|
|
-- If only one menu item (the cancel button) then don't show the menu
|
|
local count = 0;
|
|
for index, value in ipairs(UnitPopupMenus[which]) do
|
|
if ( UnitPopupShown[index] == 1 and value ~= "CANCEL" ) then
|
|
count = count + 1;
|
|
end
|
|
end
|
|
if ( count < 1 ) then
|
|
return;
|
|
end
|
|
|
|
-- Reflect the live loot method/threshold into the nested rows' labels (ref).
|
|
local lootMethod = GetLootMethod();
|
|
dropdownMenu.selectedLootMethod = UnitLootMethod[lootMethod].text;
|
|
UnitPopupButtons["LOOT_METHOD"].text = dropdownMenu.selectedLootMethod;
|
|
dropdownMenu.selectedLootThreshold = getglobal("ITEM_QUALITY"..GetLootThreshold().."_DESC");
|
|
UnitPopupButtons["LOOT_THRESHOLD"].text = dropdownMenu.selectedLootThreshold;
|
|
-- A non-leader can VIEW the loot settings but not open the submenus (ref).
|
|
if ( ((GetNumPartyMembers() > 0) or (GetNumRaidMembers() > 0)) and IsPartyLeader() ) then
|
|
UnitPopupButtons["LOOT_METHOD"].nested = 1;
|
|
UnitPopupButtons["LOOT_THRESHOLD"].nested = 1;
|
|
else
|
|
UnitPopupButtons["LOOT_METHOD"].nested = nil;
|
|
UnitPopupButtons["LOOT_THRESHOLD"].nested = nil;
|
|
end
|
|
|
|
-- If level 2 dropdown
|
|
local info;
|
|
local color;
|
|
if ( UIDROPDOWNMENU_MENU_LEVEL == 2 ) then
|
|
dropdownMenu.which = UIDROPDOWNMENU_MENU_VALUE;
|
|
OPEN_DROPDOWNMENUS[UIDROPDOWNMENU_MENU_LEVEL] = {which = dropdownMenu.which, unit = dropdownMenu.unit};
|
|
for index, value in ipairs(UnitPopupMenus[UIDROPDOWNMENU_MENU_VALUE]) do
|
|
info = {};
|
|
info.text = UnitPopupButtons[value].text;
|
|
info.owner = UIDROPDOWNMENU_MENU_VALUE;
|
|
color = UnitPopupButtons[value].color;
|
|
if ( color ) then
|
|
info.textR = color.r;
|
|
info.textG = color.g;
|
|
info.textB = color.b;
|
|
end
|
|
info.icon = UnitPopupButtons[value].icon;
|
|
info.tCoordLeft = UnitPopupButtons[value].tCoordLeft;
|
|
info.tCoordRight = UnitPopupButtons[value].tCoordRight;
|
|
info.tCoordTop = UnitPopupButtons[value].tCoordTop;
|
|
info.tCoordBottom = UnitPopupButtons[value].tCoordBottom;
|
|
-- Checked conditions
|
|
if ( info.text == dropdownMenu.selectedLootMethod ) then
|
|
info.checked = 1;
|
|
elseif ( info.text == dropdownMenu.selectedLootThreshold ) then
|
|
info.checked = 1;
|
|
elseif ( string.sub(value, 1, 12) == "RAID_TARGET_" ) then
|
|
local raidTargetIndex = GetRaidTargetIndex(unit);
|
|
if ( raidTargetIndex == index ) then
|
|
info.checked = 1;
|
|
end
|
|
end
|
|
info.value = value;
|
|
info.func = UnitPopup_OnClick;
|
|
UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);
|
|
end
|
|
return;
|
|
end
|
|
|
|
-- Add dropdown title
|
|
if ( unit or name ) then
|
|
info = {};
|
|
if ( name ) then
|
|
info.text = name;
|
|
else
|
|
info.text = TEXT(UNKNOWN);
|
|
end
|
|
info.isTitle = 1;
|
|
info.notCheckable = 1;
|
|
UIDropDownMenu_AddButton(info);
|
|
end
|
|
|
|
-- Set which menu is being opened
|
|
OPEN_DROPDOWNMENUS[UIDROPDOWNMENU_MENU_LEVEL] = {which = dropdownMenu.which, unit = dropdownMenu.unit};
|
|
-- Show the buttons which are used by this menu
|
|
for index, value in ipairs(UnitPopupMenus[which]) do
|
|
if ( UnitPopupShown[index] == 1 ) then
|
|
info = {};
|
|
info.text = UnitPopupButtons[value].text;
|
|
info.value = value;
|
|
info.owner = which;
|
|
info.func = UnitPopup_OnClick;
|
|
if ( not UnitPopupButtons[value].checkable ) then
|
|
info.notCheckable = 1;
|
|
end
|
|
-- Text color
|
|
if ( value == "LOOT_THRESHOLD" ) then
|
|
color = ITEM_QUALITY_COLORS[GetLootThreshold()];
|
|
if ( color ) then
|
|
info.textR = color.r;
|
|
info.textG = color.g;
|
|
info.textB = color.b;
|
|
end
|
|
else
|
|
color = UnitPopupButtons[value].color;
|
|
if ( color ) then
|
|
info.textR = color.r;
|
|
info.textG = color.g;
|
|
info.textB = color.b;
|
|
end
|
|
end
|
|
-- Icons
|
|
info.icon = UnitPopupButtons[value].icon;
|
|
info.tCoordLeft = UnitPopupButtons[value].tCoordLeft;
|
|
info.tCoordRight = UnitPopupButtons[value].tCoordRight;
|
|
info.tCoordTop = UnitPopupButtons[value].tCoordTop;
|
|
info.tCoordBottom = UnitPopupButtons[value].tCoordBottom;
|
|
-- Checked conditions
|
|
if ( string.sub(value, 1, 12) == "RAID_TARGET_" ) then
|
|
local raidTargetIndex = GetRaidTargetIndex("target");
|
|
if ( raidTargetIndex == index ) then
|
|
info.checked = 1;
|
|
end
|
|
end
|
|
if ( UnitPopupButtons[value].nested ) then
|
|
info.hasArrow = 1;
|
|
end
|
|
UIDropDownMenu_AddButton(info);
|
|
end
|
|
end
|
|
PlaySound("igMainMenuOpen");
|
|
end
|
|
|
|
function UnitPopup_HideButtons()
|
|
local dropdownMenu = getglobal(UIDROPDOWNMENU_INIT_MENU);
|
|
local inParty = 0;
|
|
if ( (GetNumPartyMembers() > 0) or (GetNumRaidMembers() > 0) ) then
|
|
inParty = 1;
|
|
end
|
|
|
|
local isLeader = 0;
|
|
if ( inParty == 1 and IsPartyLeader() ) then
|
|
isLeader = 1;
|
|
end
|
|
|
|
local isAssistant = 0;
|
|
if ( IsRaidOfficer() ) then
|
|
isAssistant = 1;
|
|
end
|
|
|
|
local canCoop = 0;
|
|
if ( dropdownMenu.unit and UnitCanCooperate("player", dropdownMenu.unit) ) then
|
|
canCoop = 1;
|
|
end
|
|
for index, value in ipairs(UnitPopupMenus[dropdownMenu.which]) do
|
|
UnitPopupShown[index] = 1;
|
|
|
|
if ( BENILLA_UNITPOPUP_DEFERRED[value] ) then
|
|
-- Hidden until its arc lands (header) — checked FIRST so the ref chain
|
|
-- below never resurrects one.
|
|
UnitPopupShown[index] = 0;
|
|
elseif ( value == "DUEL" ) then
|
|
-- Ref UnitPopup.lua l.311: shown only for a unit we can cooperate with — an
|
|
-- enemy-faction player is already attackable, so there is nothing to arrange.
|
|
if ( canCoop == 0 ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "TRADE" ) then
|
|
-- Only shown for a unit we can cooperate with (ref UnitPopup.lua l.285) — you
|
|
-- cannot trade an enemy-faction player, so the row is hidden, not just doomed.
|
|
if ( canCoop == 0 ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "INVITE" ) then
|
|
if ( dropdownMenu.unit ) then
|
|
if ( canCoop == 0 ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
else
|
|
if ( dropdownMenu.name == UnitName("party1") or
|
|
dropdownMenu.name == UnitName("party2") or
|
|
dropdownMenu.name == UnitName("party3") or
|
|
dropdownMenu.name == UnitName("party4") or
|
|
dropdownMenu.name == UnitName("player")) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
end
|
|
elseif ( value == "WHISPER" ) then
|
|
if ( (dropdownMenu.unit and canCoop == 0) or (dropdownMenu.name == UnitName("player")) ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "PROMOTE" ) then
|
|
if ( (inParty == 0) or (isLeader == 0) ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "UNINVITE" ) then
|
|
if ( (inParty == 0) or (isLeader == 0) ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "LEAVE" ) then
|
|
if ( inParty == 0 ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "FREE_FOR_ALL" ) then
|
|
if ( (inParty == 0) or ((isLeader == 0) and (GetLootMethod() ~= "freeforall")) ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "ROUND_ROBIN" ) then
|
|
if ( (inParty == 0) or ((isLeader == 0) and (GetLootMethod() ~= "roundrobin")) ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "MASTER_LOOTER" ) then
|
|
if ( (inParty == 0) or ((isLeader == 0) and (GetLootMethod() ~= "master")) ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "GROUP_LOOT" ) then
|
|
if ( (inParty == 0) or ((isLeader == 0) and (GetLootMethod() ~= "group")) ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "NEED_BEFORE_GREED" ) then
|
|
if ( (inParty == 0) or ((isLeader == 0) and (GetLootMethod() ~= "needbeforegreed")) ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "LOOT_THRESHOLD" ) then
|
|
if ( inParty == 0 ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "LOOT_PROMOTE" ) then
|
|
local lootMethod, lootMaster = GetLootMethod();
|
|
if ( (inParty == 0) or (isLeader == 0) or (lootMethod ~= "master") or (lootMaster and (dropdownMenu.unit == "party"..lootMaster)) or ((dropdownMenu.unit == "player") and lootMaster and (lootMaster == 0)) ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "LOOT_METHOD" ) then
|
|
if ( inParty == 0 ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
elseif ( value == "RAID_LEADER" or value == "RAID_PROMOTE" or value == "RAID_DEMOTE" or value == "RAID_REMOVE" ) then
|
|
-- The raid roster (GetRaidRosterInfo) is a later arc — no raid menus open yet.
|
|
UnitPopupShown[index] = 0;
|
|
elseif ( value == "PET_PAPERDOLL" or value == "PET_RENAME" or value == "PET_ABANDON" or value == "PET_DISMISS" ) then
|
|
-- The pet system is a later arc — the PET menu never opens (no "pet" token).
|
|
UnitPopupShown[index] = 0;
|
|
elseif ( string.sub(value, 1, 12) == "RAID_TARGET_" ) then
|
|
if ( (inParty == 0) or ((isLeader == 0) and (isAssistant == 0)) ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
if ( not (dropdownMenu.which == "SELF") ) then
|
|
if ( UnitExists("target") and not UnitInParty("target") ) then
|
|
if ( UnitIsPlayer("target") and (not UnitCanCooperate("player", "target") and not UnitIsUnit("target", "player")) ) then
|
|
UnitPopupShown[index] = 0;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- The per-frame enable/disable pass over every open unit-popup menu (ref; driven by the
|
|
-- BenillaUnitPopupDriver below — the ref hooks it off UIParent's OnUpdate).
|
|
function UnitPopup_OnUpdate(elapsed)
|
|
if ( not DropDownList1:IsVisible() ) then
|
|
return;
|
|
else
|
|
-- If none of the unitpopup frames own the open menu then return
|
|
local found = nil;
|
|
for index, value in ipairs(UnitPopupFrames) do
|
|
if ( UIDROPDOWNMENU_OPEN_MENU == value ) then
|
|
found = 1;
|
|
break;
|
|
end
|
|
end
|
|
if ( not found ) then
|
|
return;
|
|
end
|
|
end
|
|
|
|
local inParty = 0;
|
|
if ( (GetNumPartyMembers() > 0) or (GetNumRaidMembers() > 0) ) then
|
|
inParty = 1;
|
|
end
|
|
|
|
local isLeader = 0;
|
|
if ( inParty == 1 and IsPartyLeader() ) then
|
|
isLeader = 1;
|
|
end
|
|
local isAssistant = 0;
|
|
if ( IsRaidOfficer() ) then
|
|
isAssistant = 1;
|
|
end
|
|
-- Loop through all menus and enable/disable their buttons appropriately
|
|
local count;
|
|
for level, dropdownFrame in pairs(OPEN_DROPDOWNMENUS) do
|
|
if ( dropdownFrame ) then
|
|
count = 0;
|
|
for index, value in ipairs(UnitPopupMenus[dropdownFrame.which]) do
|
|
if ( UnitPopupShown[index] == 1 ) then
|
|
count = count + 1;
|
|
local enable = 1;
|
|
if ( UnitPopupButtons[value].dist > 0 and CheckInteractDistance ) then
|
|
if ( not CheckInteractDistance(dropdownFrame.unit, UnitPopupButtons[value].dist) ) then
|
|
enable = 0;
|
|
end
|
|
end
|
|
if ( value == "LEAVE" ) then
|
|
if ( inParty == 0 ) then
|
|
enable = 0;
|
|
end
|
|
elseif ( value == "INVITE" ) then
|
|
if ( inParty == 1 and (isLeader == 0 and isAssistant == 0) ) then
|
|
enable = 0;
|
|
end
|
|
elseif ( value == "UNINVITE" or value == "PROMOTE" ) then
|
|
if ( inParty == 0 or isLeader == 0 ) then
|
|
enable = 0;
|
|
end
|
|
elseif ( value == "DUEL" ) then
|
|
-- Ref UnitPopup.lua l.502-505, verbatim: greyed while either
|
|
-- party is dead or a ghost, or while we lack full control
|
|
-- (feared/charmed) — `HasFullControl` is nil-guarded like
|
|
-- CheckInteractDistance above: benilla models no loss of control
|
|
-- yet, so the clause is inert rather than an error. The row also
|
|
-- carries dist = 3, so the shared range leg gates it too.
|
|
if ( UnitIsDeadOrGhost("player") or (HasFullControl and not HasFullControl()) or UnitIsDeadOrGhost(dropdownFrame.unit) ) then
|
|
enable = 0;
|
|
end
|
|
elseif ( value == "WHISPER" ) then
|
|
if ( dropdownFrame.unit and not UnitIsConnected(dropdownFrame.unit) ) then
|
|
enable = 0;
|
|
end
|
|
elseif ( value == "LOOT_PROMOTE" ) then
|
|
local lootMethod, lootMaster = GetLootMethod();
|
|
if ( (inParty == 0) or (isLeader == 0) or (lootMethod ~= "master") ) then
|
|
enable = 0;
|
|
else
|
|
local masterName = 0;
|
|
if ( not lootMaster or (lootMaster == 0) ) then
|
|
masterName = "player";
|
|
else
|
|
masterName = "party"..lootMaster;
|
|
end
|
|
if ( dropdownFrame.unit and UnitIsUnit(dropdownFrame.unit, masterName) ) then
|
|
enable = 0;
|
|
end
|
|
end
|
|
end
|
|
|
|
if ( enable == 1 ) then
|
|
UIDropDownMenu_EnableButton(level, count+1);
|
|
else
|
|
UIDropDownMenu_DisableButton(level, count+1);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function UnitPopup_OnClick()
|
|
local dropdownFrame = getglobal(UIDROPDOWNMENU_INIT_MENU);
|
|
local button = this.value;
|
|
local unit = dropdownFrame.unit;
|
|
local name = dropdownFrame.name;
|
|
|
|
if ( button == "WHISPER" ) then
|
|
ChatFrame_SendTell(name);
|
|
elseif ( button == "INVITE" ) then
|
|
if ( unit ) then
|
|
InviteToParty(unit);
|
|
else
|
|
InviteByName(name);
|
|
end
|
|
elseif ( button == "UNINVITE" ) then
|
|
UninviteFromParty(unit);
|
|
elseif ( button == "PROMOTE" ) then
|
|
PromoteToPartyLeader(unit);
|
|
elseif ( button == "LEAVE" ) then
|
|
LeaveParty();
|
|
elseif ( button == "FREE_FOR_ALL" ) then
|
|
SetLootMethod("freeforall");
|
|
UIDropDownMenu_SetButtonText(1, 2, UnitPopupButtons[button].text);
|
|
UIDropDownMenu_Refresh(dropdownFrame, nil, 1);
|
|
elseif ( button == "ROUND_ROBIN" ) then
|
|
SetLootMethod("roundrobin");
|
|
UIDropDownMenu_SetButtonText(1, 2, UnitPopupButtons[button].text);
|
|
UIDropDownMenu_Refresh(dropdownFrame, nil, 1);
|
|
elseif ( button == "MASTER_LOOTER" ) then
|
|
SetLootMethod("master", name);
|
|
UIDropDownMenu_SetButtonText(1, 2, UnitPopupButtons[button].text);
|
|
UIDropDownMenu_Refresh(dropdownFrame, nil, 1);
|
|
elseif ( button == "GROUP_LOOT" ) then
|
|
SetLootMethod("group");
|
|
UIDropDownMenu_SetButtonText(1, 2, UnitPopupButtons[button].text);
|
|
UIDropDownMenu_Refresh(dropdownFrame, nil, 1);
|
|
elseif ( button == "NEED_BEFORE_GREED" ) then
|
|
SetLootMethod("needbeforegreed");
|
|
UIDropDownMenu_SetButtonText(1, 2, UnitPopupButtons[button].text);
|
|
UIDropDownMenu_Refresh(dropdownFrame, nil, 1);
|
|
elseif ( button == "LOOT_PROMOTE" ) then
|
|
SetLootMethod("master", name);
|
|
elseif ( button == "ITEM_QUALITY2_DESC" or button == "ITEM_QUALITY3_DESC" or button == "ITEM_QUALITY4_DESC" ) then
|
|
SetLootThreshold(this:GetID()+1);
|
|
local color = ITEM_QUALITY_COLORS[this:GetID()+1];
|
|
UIDropDownMenu_SetButtonText(1, 3, UnitPopupButtons[button].text, color.r, color.g, color.b);
|
|
elseif ( string.sub(button, 1, 12) == "RAID_TARGET_" and button ~= "RAID_TARGET_ICON" ) then
|
|
local raidTargetIndex = string.sub(button, 13);
|
|
if ( raidTargetIndex == "NONE" ) then
|
|
raidTargetIndex = 0;
|
|
end
|
|
SetRaidTargetIcon(unit, tonumber(raidTargetIndex));
|
|
elseif ( button == "DUEL" ) then
|
|
-- Challenge to a duel (decision 0633): the app resolves the unit token → player
|
|
-- guid → a cast of the duel spell (ref UnitPopup_OnClick l.559, StartDuelUnit).
|
|
StartDuelUnit(unit);
|
|
elseif ( button == "TRADE" ) then
|
|
-- Offer to trade (decision 0592 P1): the app resolves the unit token → player guid
|
|
-- → CMSG_INITIATE_TRADE (ref UnitPopup_OnClick's TRADE arm, InitiateTrade(unit)).
|
|
InitiateTrade(unit);
|
|
elseif ( button == "INSPECT" ) then
|
|
-- Open the inspect window (decision 0631) — the ref UnitPopup_OnClick's INSPECT arm
|
|
-- verbatim: InspectUnit(unit), which gates on CanInspect and sends CMSG_INSPECT.
|
|
InspectUnit(unit);
|
|
elseif ( button == "FOLLOW" ) then
|
|
-- Begin auto-following — the ref UnitPopup_OnClick's FOLLOW arm verbatim, by NAME
|
|
-- and EXACT. That `1` is the resolver's exact-only flag, and it matters: this menu
|
|
-- already knows the unit's name to the letter, so unlike `/follow rag` it must not
|
|
-- prefix-match its way onto a bystander. Client-side movement only, no wire.
|
|
FollowByName(name, 1);
|
|
end
|
|
-- DEFERRED branches trimmed with their rows (header): TARGET,
|
|
-- GUILD_*, RESET_INSTANCES, PET_*, RAID_LEADER/PROMOTE/DEMOTE/REMOVE.
|
|
PlaySound("UChatScrollButton");
|
|
end
|
|
]]></Script>
|
|
|
|
<!-- The OnUpdate driver (ref UIParent.xml l.21 runs UnitPopup_OnUpdate from UIParent's own
|
|
OnUpdate; ours has none — the DeathFrame hidden-driver pattern instead). -->
|
|
<Frame name="BenillaUnitPopupDriver" parent="UIParent" hidden="false">
|
|
<Size><AbsDimension x="1" y="1"/></Size>
|
|
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
|
<Scripts>
|
|
<OnUpdate>UnitPopup_OnUpdate(arg1)</OnUpdate>
|
|
</Scripts>
|
|
</Frame>
|
|
</Ui>
|