- The main menu bar's two empty recesses fill. The micro menu — the eight arched buttons: character, spellbook, talents, quest log, socials, world map, main menu, help — sits in the dark panel between the page arrows and the bags, transcribed from the reference's own layout: the character button wears your portrait, a button holds itself down while its panel is up, and the talent button appears at level 10. The latency meter fills the one slot the bar art leaves transparent for exactly it, tinted green/yellow/red on the reference's 300/600 ms thresholds and drawn UNDER the bar art, seen through its window, the way 1.12 actually does it. The socials and main-menu buttons find their panels one snapshot later; help still waits. - The UI explains itself on hover: 1.12 ships its detailed two-line tooltips ON — a FrameXML default, not the opt-in extra it is often remembered as — so the micro buttons, your own portrait, the XP bar, the five bag buttons and the ping meter all answer a hover with the reference's own explanations. - The PvP flag, end to end: crossed swords on the player, target and party frames, driven by the two descriptor bits the wire actually carries, and `/pvp` speaks — the yellow toast plus the system chat line, the OFF sentence being the one that warns the flag lingers for five minutes. It really does linger: turning it off waits out the server's timer, which looks like a bug and is the reference. - Quest markers stopped freezing. The grey `!` over a questgiver above your level goes gold the moment you ding, an NPC re-entering view is asked afresh instead of wearing a session-old answer, and the marker drops when the questgiver bit goes off — the re-ask triggers mirror the reference's own set, plus one named addition (your own level changing). - Water and heights: swimming downhill no longer bounces between swim and fall ten times a second — the surface line is a two-sided constraint, so the swimmer settles with the river instead of being left behind by it. And collision height is per-unit now, read from the unit's own model: a gnome used to swim with a third of a yard of water over her head because five depth lines were quoting a human's number. - A round of pixel honesty in text and chat: the newest chat line no longer wears a wider drop shadow than every line above it (shadows move in whole pixels now), keeps its descenders, and hover boxes stop painting over the chat body. The chat input's history browse survives programmatic edits, a failed script chunk warns instead of vanishing, and recall deepens to 64 lines. Idle animation got honest too: a model's idle is Stand resolved through its own lookup table — never file order, which had the duel flag bobbing on its spawn clip — and an armed idle is culled on the box its animation authors, not the bind pose, so the flag stops vanishing at most camera angles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
587 lines
33 KiB
XML
587 lines
33 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: FOLLOW (its CMSG
|
|
family is its own slice), 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. 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 = {
|
|
FOLLOW = 1, 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);
|
|
end
|
|
-- DEFERRED branches trimmed with their rows (header): TARGET/FOLLOW,
|
|
-- 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>
|