режим предателя + UI референсы и DragonUI

This commit is contained in:
2026-04-09 02:05:29 +04:00
parent 1109d19bb5
commit e8f1ae4ab9
397 changed files with 202750 additions and 37 deletions
+507
View File
@@ -0,0 +1,507 @@
-----
--A note on nomenclature:
--LFD is used for Dungeon-specific functions and values
--LFR is used for Raid-specific functions and values
--LFG is used for for generic functions/values that may be used for LFD, LFR, and any other LF_ system we may implement in the future.
------
--DEBUG FIXME:
function LFGDebug(text, ...)
if ( GetCVarBool("lfgDebug") ) then
ConsolePrint("LFGLua: "..format(text, ...));
end
end
LFG_RETURN_VALUES = {
name = 1,
typeID = 2,
minLevel = 3,
maxLevel = 4,
recLevel = 5, --Recommended level
minRecLevel = 6, --Minimum recommended level
maxRecLevel = 7, --Maximum recommended level
expansionLevel = 8,
groupID = 9,
texture = 10,
difficulty = 11,
maxPlayers = 12,
}
LFG_INSTANCE_INVALID_CODES = { --Any other codes are unspecified conditions (e.g. attunements)
"EXPANSION_TOO_LOW",
"LEVEL_TOO_LOW",
"LEVEL_TOO_HIGH",
"GEAR_TOO_LOW",
"GEAR_TOO_HIGH",
"RAID_LOCKED",
[1001] = "LEVEL_TOO_LOW",
[1002] = "LEVEL_TOO_HIGH",
[1022] = "QUEST_NOT_COMPLETED",
[1025] = "MISSING_ITEM",
}
local tankIcon = "|TInterface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES.blp:16:16:0:%d:64:64:0:19:22:41|t";
local healerIcon = "|TInterface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES.blp:16:16:0:%d:64:64:20:39:1:20|t";
local damageIcon = "|TInterface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES.blp:16:16:0:%d:64:64:20:39:22:41|t";
--Variables to store dungeon info in Lua
--local LFDDungeonList, LFRRaidList, LFGDungeonInfo, LFGCollapseList, LFGEnabledList, LFDHiddenByCollapseList, LFGLockList;
function LFGEventFrame_OnLoad(self)
self:RegisterEvent("LFG_UPDATE");
self:RegisterEvent("PLAYER_ENTERING_WORLD");
self:RegisterEvent("LFG_LOCK_INFO_RECEIVED");
self:RegisterEvent("PARTY_MEMBERS_CHANGED");
self:RegisterEvent("LFG_OFFER_CONTINUE");
self:RegisterEvent("LFG_ROLE_CHECK_ROLE_CHOSEN");
--These just update states (roles changeable, buttons clickable, etc.)
self:RegisterEvent("LFG_PROPOSAL_UPDATE");
self:RegisterEvent("LFG_PROPOSAL_SHOW");
self:RegisterEvent("LFG_ROLE_CHECK_SHOW");
self:RegisterEvent("LFG_ROLE_CHECK_HIDE");
self:RegisterEvent("LFG_BOOT_PROPOSAL_UPDATE");
self:RegisterEvent("LFG_ROLE_UPDATE");
self:RegisterEvent("LFG_UPDATE_RANDOM_INFO");
self:RegisterEvent("LFG_PROPOSAL_FAILED");
end
LFGQueuedForList = {};
function LFGEventFrame_OnEvent(self, event, ...)
if ( event == "LFG_UPDATE" ) then
LFG_UpdateQueuedList();
elseif ( event == "PLAYER_ENTERING_WORLD" ) then
LFG_UpdateQueuedList();
LFG_UpdateRoleCheckboxes();
elseif ( event == "LFG_LOCK_INFO_RECEIVED" ) then
LFGLockList = GetLFDChoiceLockedState();
LFG_UpdateFramesIfShown();
elseif ( event == "PARTY_MEMBERS_CHANGED" ) then
LFG_UpdateQueuedList();
LFG_UpdateFramesIfShown();
if ( not CanPartyLFGBackfill() ) then
StaticPopup_Hide("LFG_OFFER_CONTINUE");
end
elseif ( event == "LFG_OFFER_CONTINUE" ) then
local displayName, lfgID, typeID = ...;
local dialog = StaticPopup_Show("LFG_OFFER_CONTINUE", NORMAL_FONT_COLOR_CODE..displayName.."|r");
if ( dialog ) then
dialog.data = lfgID;
dialog.data2 = typeID;
end
elseif ( event == "LFG_ROLE_CHECK_ROLE_CHOSEN" ) then
local player, isTank, isHealer, isDamage = ...;
--Yes, consecutive string concatenation == bad for garbage collection. But the alternative is either extremely unslightly or localization unfriendly. (Also, this happens fairly rarely)
local roleList;
--Horrible hack to deal with a bug in embedded font strings. FIXME
--The more icons with absolute sizes in a certain fontstring, the higher up the text goes. This offsets it to make the icons be in line with the text.
local numRoles = (isTank and 1 or 0) + (isHealer and 1 or 0) + (isDamage and 1 or 0);
local yOffset = 2*(numRoles-1)-2; --Formula derived through testing.
local tankIcon = format(tankIcon, yOffset);
local healerIcon = format(healerIcon, yOffset);
local damageIcon = format(damageIcon, yOffset);
if ( isTank ) then
roleList = tankIcon.." "..TANK;
end
if ( isHealer ) then
if ( roleList ) then
roleList = roleList..PLAYER_LIST_DELIMITER.." "..healerIcon.." "..HEALER;
else
roleList = healerIcon.." "..HEALER;
end
end
if ( isDamage ) then
if ( roleList ) then
roleList = roleList..PLAYER_LIST_DELIMITER.." "..damageIcon.." "..DAMAGER;
else
roleList = damageIcon.." "..DAMAGER;
end
end
assert(roleList);
ChatFrame_DisplayUsageError(string.format(LFG_ROLE_CHECK_ROLE_CHOSEN, player, roleList));
end
LFG_UpdateRolesChangeable();
LFG_UpdateFindGroupButtons();
LFG_UpdateLockedOutPanels();
LFDFrame_UpdateBackfill();
end
function LFG_UpdateLockedOutPanels()
local mode, submode = GetLFGMode();
if ( mode == "listed" ) then
LFDQueueFrameNoLFDWhileLFR:Show();
else
LFDQueueFrameNoLFDWhileLFR:Hide();
end
if ( mode == "queued" or mode == "proposal" or mode == "rolecheck" ) then
LFRQueueFrameNoLFRWhileLFD:Show();
else
LFRQueueFrameNoLFRWhileLFD:Hide();
end
end
function LFG_UpdateFindGroupButtons()
LFDQueueFrameFindGroupButton_Update();
LFRQueueFrameFindGroupButton_Update();
end
function LFG_UpdateQueuedList()
GetLFGQueuedList(LFGQueuedForList);
LFG_UpdateFramesIfShown();
MiniMapLFG_UpdateIsShown();
end
function LFG_UpdateFramesIfShown()
if ( LFDParentFrame:IsShown() ) then
LFDQueueFrame_Update();
end
if ( LFRParentFrame:IsShown() ) then
LFRQueueFrame_Update();
end
end
function LFG_PermanentlyDisableRoleButton(button)
button.permDisabled = true;
button:Disable();
SetDesaturation(button:GetNormalTexture(), true);
button.cover:Show();
button.cover:SetAlpha(0.7);
button.checkButton:Hide();
button.checkButton:Disable();
if ( button.background ) then
button.background:Hide();
end
end
function LFG_DisableRoleButton(button)
button:Disable();
button.cover:Show();
if ( not button.permDisabled ) then
button.cover:SetAlpha(0.5);
end
button.checkButton:Disable();
if ( button.background ) then
button.background:Hide();
end
end
function LFG_EnableRoleButton(button)
button.permDisabled = false;
button:Enable();
SetDesaturation(button:GetNormalTexture(), false);
button.cover:Hide();
button.checkButton:Show();
button.checkButton:Enable();
if ( button.background ) then
button.background:Show();
end
end
function LFG_UpdateAvailableRoles()
local canBeTank, canBeHealer, canBeDPS = GetAvailableRoles();
if ( canBeTank ) then
LFG_EnableRoleButton(LFDQueueFrameRoleButtonTank);
LFG_EnableRoleButton(LFRQueueFrameRoleButtonTank);
LFG_EnableRoleButton(LFDRoleCheckPopupRoleButtonTank);
else
LFG_PermanentlyDisableRoleButton(LFDQueueFrameRoleButtonTank);
LFG_PermanentlyDisableRoleButton(LFRQueueFrameRoleButtonTank);
LFG_PermanentlyDisableRoleButton(LFDRoleCheckPopupRoleButtonTank);
end
if ( canBeHealer ) then
LFG_EnableRoleButton(LFDQueueFrameRoleButtonHealer);
LFG_EnableRoleButton(LFRQueueFrameRoleButtonHealer);
LFG_EnableRoleButton(LFDRoleCheckPopupRoleButtonHealer);
else
LFG_PermanentlyDisableRoleButton(LFDQueueFrameRoleButtonHealer);
LFG_PermanentlyDisableRoleButton(LFRQueueFrameRoleButtonHealer);
LFG_PermanentlyDisableRoleButton(LFDRoleCheckPopupRoleButtonHealer);
end
if ( canBeDPS ) then
LFG_EnableRoleButton(LFDQueueFrameRoleButtonDPS);
LFG_EnableRoleButton(LFRQueueFrameRoleButtonDPS);
LFG_EnableRoleButton(LFDRoleCheckPopupRoleButtonDPS);
else
LFG_PermanentlyDisableRoleButton(LFDQueueFrameRoleButtonDPS);
LFG_PermanentlyDisableRoleButton(LFRQueueFrameRoleButtonDPS);
LFG_PermanentlyDisableRoleButton(LFDRoleCheckPopupRoleButtonDPS);
end
local canChangeLeader = (GetNumPartyMembers() == 0 or IsPartyLeader()) and (GetNumRaidMembers() == 0 or IsRaidLeader());
if ( canChangeLeader ) then
LFG_EnableRoleButton(LFDQueueFrameRoleButtonLeader);
else
LFG_PermanentlyDisableRoleButton(LFDQueueFrameRoleButtonLeader);
end
end
function LFG_UpdateRoleCheckboxes()
local leader, tank, healer, dps = GetLFGRoles();
LFDQueueFrameRoleButtonLeader.checkButton:SetChecked(leader);
LFDQueueFrameRoleButtonTank.checkButton:SetChecked(tank);
LFRQueueFrameRoleButtonTank.checkButton:SetChecked(tank);
LFDRoleCheckPopupRoleButtonTank.checkButton:SetChecked(tank);
LFDQueueFrameRoleButtonHealer.checkButton:SetChecked(healer);
LFRQueueFrameRoleButtonHealer.checkButton:SetChecked(healer);
LFDRoleCheckPopupRoleButtonHealer.checkButton:SetChecked(healer);
LFDQueueFrameRoleButtonDPS.checkButton:SetChecked(dps);
LFRQueueFrameRoleButtonDPS.checkButton:SetChecked(dps);
LFDRoleCheckPopupRoleButtonDPS.checkButton:SetChecked(dps);
end
function LFG_UpdateRolesChangeable()
local mode, subMode = GetLFGMode();
if ( mode == "queued" or mode == "listed" or mode == "rolecheck" or mode == "proposal" ) then
LFG_DisableRoleButton(LFDQueueFrameRoleButtonTank, true);
LFG_DisableRoleButton(LFRQueueFrameRoleButtonTank, true);
LFG_DisableRoleButton(LFDQueueFrameRoleButtonHealer, true);
LFG_DisableRoleButton(LFRQueueFrameRoleButtonHealer, true);
LFG_DisableRoleButton(LFDQueueFrameRoleButtonDPS, true);
LFG_DisableRoleButton(LFRQueueFrameRoleButtonDPS, true);
LFG_DisableRoleButton(LFDQueueFrameRoleButtonLeader, true);
else
LFG_UpdateAvailableRoles();
end
end
function LFGSpecificChoiceEnableButton_SetIsRadio(button, isRadio)
if ( isRadio ) then
button:SetSize(17, 17)
button:SetNormalTexture("Interface\\Buttons\\UI-RadioButton");
button:GetNormalTexture():SetTexCoord(0, 0.25, 0, 1);
button:SetHighlightTexture("Interface\\Buttons\\UI-RadioButton");
button:GetHighlightTexture():SetTexCoord(0.5, 0.75, 0, 1);
button:SetCheckedTexture("Interface\\Buttons\\UI-RadioButton");
button:GetCheckedTexture():SetTexCoord(0.25, 0.5, 0, 1);
button:SetPushedTexture("Interface\\Buttons\\UI-RadioButton");
button:GetPushedTexture():SetTexCoord(0, 0.25, 0, 1);
button:SetDisabledCheckedTexture("Interface\\Buttons\\UI-RadioButton");
button:GetDisabledCheckedTexture():SetTexCoord(0.75, 1, 0, 1);
else
button:SetSize(20, 20);
button:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up");
button:GetNormalTexture():SetTexCoord(0, 1, 0, 1);
button:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight");
button:GetHighlightTexture():SetTexCoord(0, 1, 0, 1);
button:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check");
button:GetCheckedTexture():SetTexCoord(0, 1, 0, 1);
button:SetPushedTexture("Interface\\Buttons\\UI-CheckBox-Down");
button:GetPushedTexture():SetTexCoord(0, 1, 0, 1);
button:SetDisabledCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check-Disabled");
button:GetDisabledCheckedTexture():SetTexCoord(0, 1, 0, 1);
end
end
--More functions
function GetTexCoordsForRole(role)
local textureHeight, textureWidth = 256, 256;
local roleHeight, roleWidth = 67, 67;
if ( role == "GUIDE" ) then
return GetTexCoordsByGrid(1, 1, textureWidth, textureHeight, roleWidth, roleHeight);
elseif ( role == "TANK" ) then
return GetTexCoordsByGrid(1, 2, textureWidth, textureHeight, roleWidth, roleHeight);
elseif ( role == "HEALER" ) then
return GetTexCoordsByGrid(2, 1, textureWidth, textureHeight, roleWidth, roleHeight);
elseif ( role == "DAMAGER" ) then
return GetTexCoordsByGrid(2, 2, textureWidth, textureHeight, roleWidth, roleHeight);
else
error("Unknown role: "..tostring(role));
end
end
function GetBackgroundTexCoordsForRole(role)
local textureHeight, textureWidth = 128, 256;
local roleHeight, roleWidth = 75, 75;
if ( role == "TANK" ) then
return GetTexCoordsByGrid(2, 1, textureWidth, textureHeight, roleWidth, roleHeight);
elseif ( role == "HEALER" ) then
return GetTexCoordsByGrid(1, 1, textureWidth, textureHeight, roleWidth, roleHeight);
elseif ( role == "DAMAGER" ) then
return GetTexCoordsByGrid(3, 1, textureWidth, textureHeight, roleWidth, roleHeight);
else
error("Role does not have background: "..tostring(role));
end
end
-------Utility functions-----------
function LFDGetNumDungeons()
return #LFDDungeonList;
end
function LFRGetNumDungeons()
return #LFRRaidList;
end
function LFGGetDungeonInfoByID(id)
return LFGDungeonInfo[id];
end
function LFGIsIDHeader(id)
return id < 0;
end
-------List filtering functions-----------
local hasSetUp = false;
function LFGDungeonList_Setup()
if ( not hasSetUp ) then
hasSetUp = true;
LFGDungeonInfo = GetLFDChoiceInfo(LFGDungeonInfo); --This will never change (without a patch).
LFGCollapseList = GetLFDChoiceCollapseState(LFGCollapseList); --We maintain this list in Lua
LFGEnabledList = GetLFDChoiceEnabledState(LFGEnabledList); --We maintain this list in Lua
LFGLockList = GetLFDChoiceLockedState(LFGLockList);
LFDQueueFrame_Update();
LFRQueueFrame_Update();
return true;
end
return false;
end
function LFGQueueFrame_UpdateLFGDungeonList(dungeonList, hiddenByCollapseList, lockList, dungeonInfo, enableList, collapseList, filter)
if ( LFGDungeonList_Setup() ) then
return;
end
table.wipe(hiddenByCollapseList);
--1. Remove all choices that don't match the filter.
LFGListFilterChoices(dungeonList, dungeonInfo, filter);
--2. Remove all headers that have no entries below them.
LFGListRemoveHeadersWithoutChildren(dungeonList);
--3. Update the enabled state of headers.
LFGListUpdateHeaderEnabledAndLockedStates(dungeonList, enableList, lockList, hiddenByCollapseList);
--4. Move the children of collapsed headers into the hiddenByCollapse list.
LFGListRemoveCollapsedChildren(dungeonList, collapseList, hiddenByCollapseList);
end
--filterFunc returns true if the object should be shown.
function LFGListFilterChoices(list, infoList, filterFunc)
local currentPosition = 1;
while ( currentPosition <= #list ) do
local id = list[currentPosition];
local isHeader = LFGIsIDHeader(id);
if ( isHeader or filterFunc(id) ) then
currentPosition = currentPosition + 1;
else
tremove(list, currentPosition);
end
end
end
function LFGListRemoveHeadersWithoutChildren(list)
--This relies on unparented children coming first.
local currentPosition = 1;
--The discrepency between nextObject>IsChild< and >isHeader< is due to the way we want to handle empty values.
local nextObjectIsChild = not LFGIsIDHeader(list[1] or 0);
while ( currentPosition <= #list ) do
local isHeader = not nextObjectIsChild;
nextObjectIsChild = currentPosition < #list and not LFGIsIDHeader(list[currentPosition+1]);
if ( isHeader and not nextObjectIsChild ) then
tremove(list, currentPosition);
else
currentPosition = currentPosition + 1;
end
end
end
--false = no children so far
--0 = all children unchecked
--1 = some children checked, some unchecked
--2 = all children checked
function LFGListUpdateHeaderEnabledAndLockedStates(dungeonList, enabledList, lockList, hiddenByCollapseList)
for i=1, #dungeonList do
local id = dungeonList[i];
if ( LFGIsIDHeader(id) ) then
enabledList[id] = false;
lockList[id] = true;
elseif ( not lockList[id] ) then
local groupID = LFGGetDungeonInfoByID(id)[LFG_RETURN_VALUES.groupID];
lockList[groupID] = false;
local idState = enabledList[id];
local groupState = enabledList[groupID];
if ( idState ) then
if ( not groupState or groupState == 2 ) then
enabledList[groupID] = 2;
elseif ( groupState == 0 or groupState == 1 ) then
enabledList[groupID] = 1;
end
else
if ( not groupState or groupState == 0 ) then
enabledList[groupID] = 0;
elseif ( groupState == 1 or groupState == 2 ) then
enabledList[groupID] = 1;
end
end
end
end
for i=1, #hiddenByCollapseList do
local id = hiddenByCollapseList[i];
if ( LFGIsIDHeader(id) ) then
enabledList[id] = false;
lockList[id] = true;
elseif ( not lockList[id] ) then
local groupID = LFGGetDungeonInfoByID(id)[LFG_RETURN_VALUES.groupID];
lockList[groupID] = false;
local idState = enabledList[id];
local groupState = enabledList[groupID];
if ( idState ) then
if ( not groupState or groupState == 2 ) then
enabledList[groupID] = 2;
elseif ( groupState == 0 or groupState == 1 ) then
enabledList[groupID] = 1;
end
else
if ( not groupState or groupState == 0 ) then
enabledList[groupID] = 0;
elseif ( groupState == 1 or groupState == 2 ) then
enabledList[groupID] = 1;
end
end
end
end
end
function LFGListRemoveCollapsedChildren(list, collapseStateList, hiddenByCollapseList)
local currentPosition = 1;
while ( currentPosition <= #list ) do
local id = list[currentPosition];
if ( not LFGIsIDHeader(id) and collapseStateList[id] ) then
tinsert(hiddenByCollapseList, tremove(list, currentPosition));
else
currentPosition = currentPosition + 1;
end
end
end