Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b30a3554cf | |||
| 595ed5091d | |||
| 2291ace9f7 | |||
| d5e2dce359 | |||
| 22ae941c4a | |||
| b0da9ec10b | |||
| d8d94f10b0 | |||
| 255485daca |
@@ -6,3 +6,7 @@ AWS_DEFAULT_REGION=
|
||||
AWS_BUCKET=
|
||||
AWS_ENDPOINT=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=
|
||||
|
||||
PRODUCTION_REALMLIST=
|
||||
PTR_REALMLIST=
|
||||
LOCAL_REALMLIST=
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
// boot phase, before GlueXML is loaded.
|
||||
|
||||
#include "core/Logger.hpp"
|
||||
#include "core/Hook.hpp"
|
||||
#include "core/Mem.hpp"
|
||||
#include "runtime/LuaBindings.hpp"
|
||||
#include "runtime/ModuleInstall.hpp"
|
||||
#include "offsets/engine/Gx.hpp"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
@@ -20,6 +23,199 @@ namespace moonwell
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using GxSetProjectionFn = wxl::offsets::engine::gx::GxSetProjectionFn;
|
||||
GxSetProjectionFn g_nextSetProjection = nullptr;
|
||||
|
||||
// FrameXML's stock SetCreature only accepts a creature entry and waits
|
||||
// for its client cache record. Encounter Journal already has the exact
|
||||
// CreatureDisplayInfo ID, so accept it as an optional second argument.
|
||||
constexpr uintptr_t kSetCreature = 0x00597960;
|
||||
constexpr uintptr_t kGetCurrentModelFrame = 0x004A81B0;
|
||||
constexpr uintptr_t kModelFrameTypeToken = 0x00C0E4D4;
|
||||
constexpr uintptr_t kApplyCreatureCacheRecord = 0x00597700;
|
||||
|
||||
using SetCreatureFn = int(__cdecl*)(void* state);
|
||||
using ApplyCreatureCacheRecordFn = void(__fastcall*)(void* frame, void* edx,
|
||||
const void* cacheRecord);
|
||||
SetCreatureFn g_nextSetCreature = nullptr;
|
||||
alignas(4) std::array<uint32_t, 10> g_encounterJournalCreatureRecord{};
|
||||
|
||||
void* GetCurrentModelFrame(void* state, uint32_t typeToken)
|
||||
{
|
||||
// FrameScript_GetObject takes the type token on the stack, but the
|
||||
// 3.3.5 client also expects lua_State in ESI (an internal calling
|
||||
// convention not expressible with a regular C function pointer).
|
||||
void* frame = nullptr;
|
||||
__asm
|
||||
{
|
||||
mov esi, state
|
||||
push typeToken
|
||||
mov eax, kGetCurrentModelFrame
|
||||
call eax
|
||||
add esp, 4
|
||||
mov frame, eax
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
int __cdecl SetCreatureDisplayInfoHook(void* state)
|
||||
{
|
||||
const int result = g_nextSetCreature ? g_nextSetCreature(state) : 0;
|
||||
if (!state || !wxl::runtime::lua::IsNumber(state, 3))
|
||||
return result;
|
||||
|
||||
const double requestedDisplayInfo = wxl::runtime::lua::ToNumber(state, 3);
|
||||
if (requestedDisplayInfo <= 0.0 || requestedDisplayInfo > 4294967295.0)
|
||||
return result;
|
||||
|
||||
const auto displayInfo = static_cast<uint32_t>(requestedDisplayInfo);
|
||||
__try
|
||||
{
|
||||
const uint32_t typeToken = *reinterpret_cast<const uint32_t*>(kModelFrameTypeToken);
|
||||
if (!typeToken)
|
||||
return result;
|
||||
|
||||
void* frame = GetCurrentModelFrame(state, typeToken);
|
||||
if (!frame)
|
||||
return result;
|
||||
|
||||
// Both the initial loader and the later character-appearance
|
||||
// pass read displayInfo at +0x24. Keep this record alive and
|
||||
// attach it to the frame: character models use it asynchronously
|
||||
// to resolve CreatureDisplayInfoExtra, baked skin and equipment.
|
||||
g_encounterJournalCreatureRecord.fill(0);
|
||||
g_encounterJournalCreatureRecord[9] = displayInfo;
|
||||
*reinterpret_cast<const void**>(static_cast<uint8_t*>(frame) + 0x378) =
|
||||
g_encounterJournalCreatureRecord.data();
|
||||
reinterpret_cast<ApplyCreatureCacheRecordFn>(kApplyCreatureCacheRecord)(
|
||||
frame, nullptr, g_encounterJournalCreatureRecord.data());
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
WLOG_ERROR("moonwell: SetCreature displayInfo bridge failed for %u", displayInfo);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void InstallEncounterJournalModelPreview()
|
||||
{
|
||||
if (!wxl::core::hook::Install("MoonWellSetCreatureDisplayInfo", kSetCreature,
|
||||
&SetCreatureDisplayInfoHook, &g_nextSetCreature))
|
||||
{
|
||||
WLOG_ERROR("moonwell: encounter journal model hook installation failed");
|
||||
return;
|
||||
}
|
||||
WLOG_INFO("moonwell: encounter journal displayInfo model bridge installed");
|
||||
}
|
||||
|
||||
struct CameraTransition
|
||||
{
|
||||
float zoom = 1.0f;
|
||||
float verticalOffset = 0.0f;
|
||||
float startZoom = 1.0f;
|
||||
float startVerticalOffset = 0.0f;
|
||||
float targetZoom = 1.0f;
|
||||
float targetVerticalOffset = 0.0f;
|
||||
DWORD startedAt = 0;
|
||||
DWORD duration = 0;
|
||||
} g_characterCreateCamera;
|
||||
|
||||
void UpdateCharacterCreateCamera(DWORD now)
|
||||
{
|
||||
auto& camera = g_characterCreateCamera;
|
||||
if (!camera.duration)
|
||||
{
|
||||
camera.zoom = camera.targetZoom;
|
||||
camera.verticalOffset = camera.targetVerticalOffset;
|
||||
return;
|
||||
}
|
||||
|
||||
const DWORD elapsed = now - camera.startedAt;
|
||||
float t = static_cast<float>(elapsed) / static_cast<float>(camera.duration);
|
||||
if (t >= 1.0f)
|
||||
{
|
||||
camera.zoom = camera.targetZoom;
|
||||
camera.verticalOffset = camera.targetVerticalOffset;
|
||||
camera.duration = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
t = t * t * (3.0f - 2.0f * t);
|
||||
camera.zoom = camera.startZoom + (camera.targetZoom - camera.startZoom) * t;
|
||||
camera.verticalOffset = camera.startVerticalOffset
|
||||
+ (camera.targetVerticalOffset - camera.startVerticalOffset) * t;
|
||||
}
|
||||
|
||||
int __cdecl SetCharacterCreateCamera(void* state)
|
||||
{
|
||||
const bool enabled = state && wxl::runtime::lua::IsNumber(state, 1)
|
||||
&& wxl::runtime::lua::ToNumber(state, 1) != 0.0;
|
||||
const float faceZoom = state && wxl::runtime::lua::IsNumber(state, 2)
|
||||
? static_cast<float>(wxl::runtime::lua::ToNumber(state, 2)) : 2.0f;
|
||||
const float faceVerticalOffset = state && wxl::runtime::lua::IsNumber(state, 3)
|
||||
? static_cast<float>(wxl::runtime::lua::ToNumber(state, 3)) : -0.65f;
|
||||
double requestedDuration = state && wxl::runtime::lua::IsNumber(state, 4)
|
||||
? wxl::runtime::lua::ToNumber(state, 4) : 500.0;
|
||||
if (requestedDuration < 0.0) requestedDuration = 0.0;
|
||||
if (requestedDuration > 2000.0) requestedDuration = 2000.0;
|
||||
|
||||
const DWORD now = GetTickCount();
|
||||
UpdateCharacterCreateCamera(now);
|
||||
auto& camera = g_characterCreateCamera;
|
||||
camera.startZoom = camera.zoom;
|
||||
camera.startVerticalOffset = camera.verticalOffset;
|
||||
camera.targetZoom = enabled && faceZoom > 1.0f ? faceZoom : 1.0f;
|
||||
camera.targetVerticalOffset = enabled ? faceVerticalOffset : 0.0f;
|
||||
camera.startedAt = now;
|
||||
camera.duration = static_cast<DWORD>(requestedDuration);
|
||||
UpdateCharacterCreateCamera(now);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __fastcall CharacterCreateProjectionHook(void* self, void* edx, const void* projection)
|
||||
{
|
||||
if (!g_nextSetProjection)
|
||||
return;
|
||||
|
||||
UpdateCharacterCreateCamera(GetTickCount());
|
||||
const auto& camera = g_characterCreateCamera;
|
||||
if (projection && (camera.zoom != 1.0f || camera.verticalOffset != 0.0f))
|
||||
{
|
||||
const float* source = static_cast<const float*>(projection);
|
||||
if (source[11] > 0.5f)
|
||||
{
|
||||
float adjusted[16];
|
||||
std::memcpy(adjusted, source, sizeof(adjusted));
|
||||
adjusted[0] *= camera.zoom;
|
||||
adjusted[5] *= camera.zoom;
|
||||
adjusted[9] += camera.verticalOffset;
|
||||
g_nextSetProjection(self, edx, adjusted);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
g_nextSetProjection(self, edx, projection);
|
||||
}
|
||||
|
||||
void InstallCharacterCreateCamera()
|
||||
{
|
||||
namespace gx = wxl::offsets::engine::gx;
|
||||
void** vtable = reinterpret_cast<void**>(gx::kGxDeviceVTable);
|
||||
void** slot = &vtable[gx::kGxSetProjectionSlot];
|
||||
if (*slot == reinterpret_cast<void*>(&CharacterCreateProjectionHook))
|
||||
return;
|
||||
|
||||
g_nextSetProjection = reinterpret_cast<GxSetProjectionFn>(*slot);
|
||||
void* replacement = reinterpret_cast<void*>(&CharacterCreateProjectionHook);
|
||||
if (!g_nextSetProjection || !wxl::core::mem::Patch(slot, &replacement, sizeof(replacement)))
|
||||
{
|
||||
g_nextSetProjection = nullptr;
|
||||
WLOG_ERROR("moonwell: character-create projection hook installation failed");
|
||||
return;
|
||||
}
|
||||
WLOG_INFO("moonwell: smooth character-create camera installed");
|
||||
}
|
||||
|
||||
struct Patch
|
||||
{
|
||||
const char* name;
|
||||
@@ -196,7 +392,13 @@ namespace moonwell
|
||||
{
|
||||
Registration()
|
||||
{
|
||||
wxl::runtime::lua::RegisterFunction(
|
||||
"MoonWellSetCharacterCreateCamera", &SetCharacterCreateCamera);
|
||||
wxl::runtime::modules::RegisterBoot("moonwell", &InstallBoot);
|
||||
wxl::runtime::modules::Register(
|
||||
"moonwell-character-create-camera", &InstallCharacterCreateCamera);
|
||||
wxl::runtime::modules::Register(
|
||||
"moonwell-encounter-journal-models", &InstallEncounterJournalModelPreview);
|
||||
wxl::runtime::modules::Register("moonwell-log-flush", &InstallRuntimeLogFlush);
|
||||
}
|
||||
} g_registration;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
param(
|
||||
[Parameter(Mandatory=$false)]
|
||||
[ValidateSet("production", "ptr", "local")]
|
||||
[string]$Env = "local"
|
||||
)
|
||||
|
||||
# Stop on errors
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
@@ -81,6 +87,21 @@ if ($LASTEXITCODE -ge 8) {
|
||||
|
||||
Write-Host "MPQ archives deployed to $WOW_HOME"
|
||||
|
||||
# --- Write realmlist.wtf based on selected environment
|
||||
$realmlist = switch ($Env) {
|
||||
"production" { $env:PRODUCTION_REALMLIST }
|
||||
"ptr" { $env:PTR_REALMLIST }
|
||||
"local" { $env:LOCAL_REALMLIST }
|
||||
}
|
||||
|
||||
if ($realmlist) {
|
||||
$realmlistPath = Join-Path $WOW_HOME "Data\ruRU\realmlist.wtf"
|
||||
Set-Content -Path $realmlistPath -Value "set realmlist $realmlist" -Encoding ASCII
|
||||
Write-Host "Realmlist ($Env): $realmlist"
|
||||
} else {
|
||||
Write-Warning "REALMLIST for '$Env' is not set in .env -- skipping realmlist.wtf"
|
||||
}
|
||||
|
||||
# --- Run WoW reload script
|
||||
Write-Host "Launching WoW..."
|
||||
cmd /c $RELOAD_SCRIPT
|
||||
|
||||
@@ -676,6 +676,44 @@ function CharacterCreate_UpdateGameModeVisibility()
|
||||
end
|
||||
end
|
||||
|
||||
-- The stock creation scene frames each race at a different height. Projection
|
||||
-- zoom therefore needs a per-race vertical target to keep the face in frame.
|
||||
-- Values are indexed by GetSelectedSex(): 2 male, 3 female.
|
||||
local CHARACTER_CREATE_CAMERA_PROFILES = {
|
||||
HUMAN = { [2] = -0.28, [3] = -0.20 },
|
||||
ORC = { [2] = -0.50, [3] = -0.30 },
|
||||
DWARF = { [2] = 0.30, [3] = 0.38 },
|
||||
NIGHTELF = { [2] = -0.65, [3] = -0.65 },
|
||||
SCOURGE = { [2] = 0.18, [3] = 0.27 },
|
||||
TAUREN = { [2] = -0.78, [3] = -0.62 },
|
||||
GNOME = { [2] = 0.64, [3] = 0.72 },
|
||||
TROLL = { [2] = -0.70, [3] = -0.55 },
|
||||
BLOODELF = { [2] = -0.30, [3] = -0.22 },
|
||||
DRAENEI = { [2] = -0.62, [3] = -0.55 },
|
||||
};
|
||||
|
||||
local function CharacterCreate_GetCameraProfile()
|
||||
local _, raceFile = GetNameForRace();
|
||||
local raceProfile = raceFile and CHARACTER_CREATE_CAMERA_PROFILES[strupper(raceFile)];
|
||||
local sex = GetSelectedSex();
|
||||
return 2.0, raceProfile and raceProfile[sex] or -0.20;
|
||||
end
|
||||
|
||||
local function CharacterCreate_UpdateCamera(immediate)
|
||||
local showFace = CharacterCreate.personalizationMode and not CharacterCreate.gameModeSelectionMode;
|
||||
local mode = showFace and 1 or 0;
|
||||
if CharacterCreate.cameraMode == mode and not immediate then
|
||||
return;
|
||||
end
|
||||
|
||||
CharacterCreate.cameraMode = mode;
|
||||
CharacterCreate:SetCamera(0);
|
||||
if MoonWellSetCharacterCreateCamera then
|
||||
local zoom, verticalOffset = CharacterCreate_GetCameraProfile();
|
||||
MoonWellSetCharacterCreateCamera(mode, zoom, verticalOffset, immediate and 0 or 500);
|
||||
end
|
||||
end
|
||||
|
||||
local function CharacterCreate_GetEnteredName()
|
||||
local text = "";
|
||||
|
||||
@@ -701,6 +739,8 @@ local function CharacterCreate_UpdatePersonalizationStep()
|
||||
CharacterCreate.gameModeSelectionMode = false;
|
||||
end
|
||||
|
||||
CharacterCreate_UpdateCamera();
|
||||
|
||||
local showGameModes = CharacterCreate.gameModeSelectionMode and not PAID_SERVICE_TYPE;
|
||||
|
||||
if showGameModes then
|
||||
@@ -943,6 +983,7 @@ function CharacterCreate_TogglePersonalization()
|
||||
CharCreatePersonalizeButton:Show();
|
||||
CharCreateOkayButton:Hide();
|
||||
CharacterCreate_UpdateGameModeVisibility();
|
||||
CharacterCreate_UpdateCamera();
|
||||
|
||||
for i=1, NUM_CHAR_CUSTOMIZATIONS do
|
||||
_G["CharacterCustomizationButtonFrame"..i]:Hide();
|
||||
@@ -1001,7 +1042,7 @@ function CharacterCreate_OnShow()
|
||||
CharacterChangeFixup();
|
||||
CharacterCreate_CreateGenderButtonTextures();
|
||||
CharacterCreate_UpdateButtonCheckedStates();
|
||||
CharacterCreate_ResetState();
|
||||
CharacterCreate_ResetState(true);
|
||||
|
||||
hideScheduled = true
|
||||
HideNameEditFrame:Show()
|
||||
@@ -1013,7 +1054,7 @@ end
|
||||
function CharacterCreate_OnHide()
|
||||
PAID_SERVICE_CHARACTER_ID = nil;
|
||||
PAID_SERVICE_TYPE = nil;
|
||||
CharacterCreate_ResetState();
|
||||
CharacterCreate_ResetState(true);
|
||||
|
||||
for button, tooltip in pairs(raceTooltips) do
|
||||
if tooltip then tooltip:Hide() end
|
||||
@@ -1518,6 +1559,7 @@ end
|
||||
|
||||
function CharacterCreate_UpdateModel(self)
|
||||
UpdateCustomizationScene();
|
||||
CharacterCreate_UpdateCamera();
|
||||
self:AdvanceTime();
|
||||
end
|
||||
|
||||
@@ -1644,9 +1686,10 @@ function SetCharacterGender(sex)
|
||||
CharacterCreate_UpdateButtonCheckedStates();
|
||||
end
|
||||
|
||||
function CharacterCreate_ResetState()
|
||||
function CharacterCreate_ResetState(immediate)
|
||||
CharacterCreate.personalizationMode = false;
|
||||
CharacterCreate.gameModeSelectionMode = false;
|
||||
CharacterCreate_UpdateCamera(immediate);
|
||||
CharacterCreateRaceButtonsContainer:Show();
|
||||
CharacterCreateClassButtonsContainer:Show();
|
||||
CharacterCreateGenderButtonsContainer:Show();
|
||||
|
||||
BIN
Binary file not shown.
@@ -69,25 +69,26 @@ local MainMenuBarBackpackButton = _G.MainMenuBarBackpackButton;
|
||||
local HelpMicroButton = _G.HelpMicroButton;
|
||||
local KeyRingButton = _G.KeyRingButton;
|
||||
|
||||
-- Button collections (dynamically set based on server)
|
||||
local MICRO_BUTTONS
|
||||
-- Button collections (rebuilt after addons create optional custom buttons).
|
||||
local function BuildMicroButtonList()
|
||||
if isAscensionServer then
|
||||
return {
|
||||
_G.CharacterMicroButton,
|
||||
_G.SpellbookMicroButton,
|
||||
_G.TalentMicroButton,
|
||||
_G.AchievementMicroButton,
|
||||
_G.QuestLogMicroButton,
|
||||
_G.SocialsMicroButton,
|
||||
_G.LFDMicroButton,
|
||||
_G.EncounterJournalMicroButton,
|
||||
_G.PathToAscensionMicroButton,
|
||||
_G.ChallengesMicroButton,
|
||||
_G.MainMenuMicroButton,
|
||||
_G.HelpMicroButton
|
||||
}
|
||||
end
|
||||
|
||||
if isAscensionServer then
|
||||
MICRO_BUTTONS = {
|
||||
_G.CharacterMicroButton,
|
||||
_G.SpellbookMicroButton,
|
||||
_G.TalentMicroButton,
|
||||
_G.AchievementMicroButton,
|
||||
_G.QuestLogMicroButton,
|
||||
_G.SocialsMicroButton,
|
||||
_G.LFDMicroButton,
|
||||
_G.PathToAscensionMicroButton,
|
||||
_G.ChallengesMicroButton,
|
||||
_G.MainMenuMicroButton,
|
||||
_G.HelpMicroButton
|
||||
}
|
||||
else
|
||||
MICRO_BUTTONS = {
|
||||
return {
|
||||
_G.CharacterMicroButton,
|
||||
_G.SpellbookMicroButton,
|
||||
_G.TalentMicroButton,
|
||||
@@ -95,6 +96,7 @@ else
|
||||
_G.QuestLogMicroButton,
|
||||
_G.SocialsMicroButton,
|
||||
_G.LFDMicroButton,
|
||||
_G.EncounterJournalMicroButton,
|
||||
_G.CollectionsMicroButton,
|
||||
_G.PVPMicroButton,
|
||||
_G.MainMenuMicroButton,
|
||||
@@ -102,6 +104,7 @@ else
|
||||
}
|
||||
end
|
||||
|
||||
local MICRO_BUTTONS = BuildMicroButtonList()
|
||||
|
||||
local bagslots = {_G.CharacterBag0Slot, _G.CharacterBag1Slot, _G.CharacterBag2Slot, _G.CharacterBag3Slot};
|
||||
|
||||
@@ -783,6 +786,8 @@ local function ApplyMicromenuSystem()
|
||||
return
|
||||
end
|
||||
|
||||
MICRO_BUTTONS = BuildMicroButtonList()
|
||||
|
||||
-- Store original states first
|
||||
StoreOriginalMicroButtonStates()
|
||||
|
||||
@@ -911,6 +916,8 @@ local function ApplyMicromenuSystem()
|
||||
or (_G.PVPParentFrame and _G.PVPParentFrame:IsVisible() and true or false)
|
||||
or (_G.BattlefieldFrame and _G.BattlefieldFrame:IsVisible() and true or false)
|
||||
or (_G.HonorFrame and _G.HonorFrame:IsVisible() and true or false)
|
||||
elseif buttonName == "EncounterJournal" then
|
||||
return (_G.EncounterJournal and _G.EncounterJournal:IsVisible() and true or false)
|
||||
end
|
||||
|
||||
return pressed
|
||||
@@ -919,6 +926,152 @@ local function ApplyMicromenuSystem()
|
||||
-- ============================================================================
|
||||
-- SECTION 5: SPECIALIZED BUTTON SETUP
|
||||
-- ============================================================================
|
||||
local function SetupEncounterJournalButton(button)
|
||||
local iconTexture = 'Interface\\EncounterJournal\\UI-EJ-PortraitIcon'
|
||||
local backgroundTexture = 'Interface\\AddOns\\DragonUI\\Textures\\Micromenu\\uimicromenu2x'
|
||||
local buttonWidth, buttonHeight = button:GetSize()
|
||||
local dx, dy = -1, 1
|
||||
local offX, offY = button:GetPushedTextOffset()
|
||||
|
||||
if not button.DragonUIEncounterJournalIcon then
|
||||
button.DragonUIEncounterJournalIcon = button:CreateTexture(nil, 'ARTWORK')
|
||||
end
|
||||
|
||||
local icon = button.DragonUIEncounterJournalIcon
|
||||
icon:SetTexture(iconTexture)
|
||||
icon:SetTexCoord(0, 1, 0, 1)
|
||||
icon:ClearAllPoints()
|
||||
icon:SetPoint('CENTER', button, 'CENTER', 0, 0)
|
||||
icon:SetSize(buttonWidth - 8, buttonWidth - 8)
|
||||
icon:SetAlpha(1)
|
||||
icon:Show()
|
||||
|
||||
local highlightTexture = button:GetHighlightTexture()
|
||||
if highlightTexture then
|
||||
highlightTexture:SetTexture(iconTexture)
|
||||
highlightTexture:SetTexCoord(0, 1, 0, 1)
|
||||
highlightTexture:ClearAllPoints()
|
||||
highlightTexture:SetAllPoints(icon)
|
||||
highlightTexture:SetBlendMode('ADD')
|
||||
highlightTexture:SetAlpha(0.55)
|
||||
end
|
||||
|
||||
if not button.DragonUIBackground then
|
||||
local bg = button:CreateTexture(nil, 'BACKGROUND')
|
||||
bg:SetTexture(backgroundTexture)
|
||||
bg:SetSize(buttonWidth, buttonHeight + 1)
|
||||
bg:SetTexCoord(0.0654297, 0.12793, 0.330078, 0.490234)
|
||||
bg:SetPoint('CENTER', dx, dy)
|
||||
button.DragonUIBackground = bg
|
||||
|
||||
local bgPushed = button:CreateTexture(nil, 'BACKGROUND')
|
||||
bgPushed:SetTexture(backgroundTexture)
|
||||
bgPushed:SetSize(buttonWidth, buttonHeight + 1)
|
||||
bgPushed:SetTexCoord(0.0654297, 0.12793, 0.494141, 0.654297)
|
||||
bgPushed:SetPoint('CENTER', dx + offX, dy + offY)
|
||||
bgPushed:Hide()
|
||||
button.DragonUIBackgroundPushed = bgPushed
|
||||
else
|
||||
button.DragonUIBackground:SetTexture(backgroundTexture)
|
||||
button.DragonUIBackground:SetTexCoord(0.0654297, 0.12793, 0.330078, 0.490234)
|
||||
button.DragonUIBackground:ClearAllPoints()
|
||||
button.DragonUIBackground:SetPoint('CENTER', dx, dy)
|
||||
button.DragonUIBackground:SetSize(buttonWidth, buttonHeight + 1)
|
||||
|
||||
if button.DragonUIBackgroundPushed then
|
||||
button.DragonUIBackgroundPushed:SetTexture(backgroundTexture)
|
||||
button.DragonUIBackgroundPushed:SetTexCoord(0.0654297, 0.12793, 0.494141, 0.654297)
|
||||
button.DragonUIBackgroundPushed:ClearAllPoints()
|
||||
button.DragonUIBackgroundPushed:SetPoint('CENTER', dx + offX, dy + offY)
|
||||
button.DragonUIBackgroundPushed:SetSize(buttonWidth, buttonHeight + 1)
|
||||
end
|
||||
end
|
||||
|
||||
button.dragonUIState = button.dragonUIState or {}
|
||||
button.dragonUIState.pushed = IsSpecialMicroButtonActive(button, "EncounterJournal")
|
||||
button.dragonUILastState = button.dragonUIState.pushed
|
||||
button.dragonUITimer = button.dragonUITimer or 0
|
||||
|
||||
button.HandleDragonUIState = function()
|
||||
local state = button.dragonUIState
|
||||
local hlTex = button:GetHighlightTexture()
|
||||
if state and state.pushed then
|
||||
if icon then
|
||||
icon:ClearAllPoints()
|
||||
icon:SetPoint('CENTER', button, 'CENTER', offX, offY)
|
||||
icon:SetAlpha(0.7)
|
||||
end
|
||||
if button.DragonUIBackground then
|
||||
button.DragonUIBackground:Hide()
|
||||
end
|
||||
if button.DragonUIBackgroundPushed then
|
||||
button.DragonUIBackgroundPushed:Show()
|
||||
end
|
||||
if hlTex then
|
||||
hlTex:ClearAllPoints()
|
||||
hlTex:SetPoint('TOPLEFT', icon, 'TOPLEFT', 0, 0)
|
||||
hlTex:SetPoint('BOTTOMRIGHT', icon, 'BOTTOMRIGHT', 0, 0)
|
||||
end
|
||||
else
|
||||
if icon then
|
||||
icon:ClearAllPoints()
|
||||
icon:SetPoint('CENTER', button, 'CENTER', 0, 0)
|
||||
icon:SetAlpha(1)
|
||||
end
|
||||
if button.DragonUIBackground then
|
||||
button.DragonUIBackground:Show()
|
||||
end
|
||||
if button.DragonUIBackgroundPushed then
|
||||
button.DragonUIBackgroundPushed:Hide()
|
||||
end
|
||||
if hlTex then
|
||||
hlTex:ClearAllPoints()
|
||||
hlTex:SetAllPoints(icon)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
button:SetScript('OnUpdate', function(self, elapsed)
|
||||
self.dragonUITimer = (self.dragonUITimer or 0) + elapsed
|
||||
if self.dragonUITimer >= 0.1 then
|
||||
self.dragonUITimer = 0
|
||||
local currentState = IsSpecialMicroButtonActive(self, "EncounterJournal")
|
||||
if currentState ~= self.dragonUILastState then
|
||||
self.dragonUILastState = currentState
|
||||
if self.dragonUIState then
|
||||
self.dragonUIState.pushed = currentState
|
||||
end
|
||||
if self.HandleDragonUIState then
|
||||
self.HandleDragonUIState()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if not button.DragonUIStateHooks then
|
||||
button:HookScript('OnMouseDown', function(self)
|
||||
if self.dragonUIState then
|
||||
self.dragonUIState.pushed = true
|
||||
end
|
||||
if self.HandleDragonUIState then
|
||||
self.HandleDragonUIState()
|
||||
end
|
||||
end)
|
||||
button:HookScript('OnMouseUp', function(self)
|
||||
local currentState = IsSpecialMicroButtonActive(self, "EncounterJournal")
|
||||
if self.dragonUIState then
|
||||
self.dragonUIState.pushed = currentState
|
||||
end
|
||||
if self.HandleDragonUIState then
|
||||
self.HandleDragonUIState()
|
||||
end
|
||||
end)
|
||||
button.DragonUIStateHooks = true
|
||||
end
|
||||
|
||||
button.HandleDragonUIState()
|
||||
end
|
||||
|
||||
local function SetupPVPButton(button)
|
||||
-- Mirror the Character button pattern:
|
||||
-- Instead of fighting WoW's internal NormalTexture alpha management,
|
||||
@@ -1582,6 +1735,8 @@ local function ApplyMicromenuSystem()
|
||||
end
|
||||
|
||||
local function setupMicroButtons(xOffset)
|
||||
MICRO_BUTTONS = BuildMicroButtonList()
|
||||
|
||||
local buttonxOffset = 0
|
||||
|
||||
local useGrayscale = addon.db.profile.micromenu.grayscale_icons
|
||||
@@ -1705,9 +1860,11 @@ local function ApplyMicromenuSystem()
|
||||
|
||||
local isCharacterButton = (buttonName == "Character")
|
||||
local isPVPButton = (buttonName == "PVP")
|
||||
local isEncounterJournalButton = (buttonName == "EncounterJournal")
|
||||
|
||||
local upCoords = not isCharacterButton and not isPVPButton and GetColoredTextureCoords(name, "Up") or nil
|
||||
local shouldUseGrayscale = useGrayscale or (not isPVPButton and not upCoords and not isCharacterButton)
|
||||
local upCoords = not isCharacterButton and not isPVPButton and not isEncounterJournalButton and GetColoredTextureCoords(name, "Up") or nil
|
||||
local shouldUseGrayscale = (useGrayscale and not isEncounterJournalButton) or
|
||||
(not isPVPButton and not isEncounterJournalButton and not upCoords and not isCharacterButton)
|
||||
|
||||
if shouldUseGrayscale then
|
||||
-- Grayscale icons
|
||||
@@ -1741,6 +1898,8 @@ local function ApplyMicromenuSystem()
|
||||
end
|
||||
elseif isPVPButton then
|
||||
SetupPVPButton(button)
|
||||
elseif isEncounterJournalButton then
|
||||
SetupEncounterJournalButton(button)
|
||||
elseif isCharacterButton then
|
||||
SetupCharacterButton(button)
|
||||
else
|
||||
@@ -2245,6 +2404,8 @@ end
|
||||
return
|
||||
end
|
||||
|
||||
MICRO_BUTTONS = BuildMicroButtonList()
|
||||
|
||||
local useGrayscale = addon.db.profile.micromenu.grayscale_icons
|
||||
local configMode = useGrayscale and "grayscale" or "normal"
|
||||
local config = addon.db.profile.micromenu[configMode]
|
||||
|
||||
+4172
File diff suppressed because it is too large
Load Diff
+3380
File diff suppressed because it is too large
Load Diff
+605
@@ -0,0 +1,605 @@
|
||||
local SHOW_RECOMMENDATIONS = true
|
||||
local DENSITY = "regular"
|
||||
local ACCENT = { r = 1.0, g = 0.84, b = 0.42 }
|
||||
|
||||
local ROMAN = { "I","II","III","IV","V","VI","VII","VIII","IX","X" }
|
||||
|
||||
local T = "Interface\\AddOns\\MoonWellClient\\EncounterJournal\\Textures\\PlayerGuide\\"
|
||||
|
||||
local BIOME_TEX = {
|
||||
fire = T .. "Biome-Fire",
|
||||
arcane = T .. "Biome-Arcane",
|
||||
storm = T .. "Biome-Storm",
|
||||
shadow = T .. "Biome-Shadow",
|
||||
nature = T .. "Biome-Nature",
|
||||
holy = T .. "Biome-Holy",
|
||||
}
|
||||
|
||||
local BADGE_TEX = {
|
||||
active = T .. "StageBadge-Active",
|
||||
done = T .. "StageBadge-Done",
|
||||
locked = T .. "StageBadge-Locked",
|
||||
}
|
||||
|
||||
local TYPE_ICON = {
|
||||
level = T .. "TypeIcon-Level",
|
||||
quest = T .. "TypeIcon-Quest",
|
||||
quest_chain = T .. "TypeIcon-QuestChain",
|
||||
dungeon = T .. "TypeIcon-Dungeon",
|
||||
raid = T .. "TypeIcon-Raid",
|
||||
boss = T .. "TypeIcon-Boss",
|
||||
achievement = T .. "TypeIcon-Achievement",
|
||||
reputation = T .. "TypeIcon-Reputation",
|
||||
profession = T .. "TypeIcon-Profession",
|
||||
item = T .. "TypeIcon-Item",
|
||||
currency = T .. "TypeIcon-Currency",
|
||||
custom = T .. "TypeIcon-Custom",
|
||||
done = T .. "TypeIcon-Done",
|
||||
locked = T .. "TypeIcon-Locked",
|
||||
}
|
||||
|
||||
local function showOrHide(frame, show)
|
||||
if not frame then return end
|
||||
if show then frame:Show() else frame:Hide() end
|
||||
end
|
||||
|
||||
local function getScrollBar(scrollFrame)
|
||||
if not scrollFrame then return nil end
|
||||
return scrollFrame.ScrollBar or _G[scrollFrame:GetName() .. "ScrollBar"]
|
||||
end
|
||||
|
||||
local function updateScrollBar(scrollFrame, contentHeight, contentWidth)
|
||||
if not scrollFrame then return end
|
||||
|
||||
local viewHeight = scrollFrame:GetHeight() or 0
|
||||
local viewWidth = scrollFrame:GetWidth() or 0
|
||||
local scrollBar = getScrollBar(scrollFrame)
|
||||
|
||||
if scrollFrame.ScrollChild and viewWidth > 0 then
|
||||
scrollFrame.ScrollChild:SetWidth(math.max(1, contentWidth or viewWidth))
|
||||
end
|
||||
|
||||
if scrollBar then
|
||||
if contentHeight and viewHeight > 0 and contentHeight > viewHeight + 2 then
|
||||
scrollBar:Show()
|
||||
else
|
||||
scrollBar:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function scrollFrameOnMouseWheel(scrollFrame, delta)
|
||||
if not scrollFrame then return end
|
||||
|
||||
if ScrollFrameTemplate_OnMouseWheel then
|
||||
ScrollFrameTemplate_OnMouseWheel(scrollFrame, delta)
|
||||
return
|
||||
end
|
||||
|
||||
local scrollBar = getScrollBar(scrollFrame)
|
||||
if scrollBar then
|
||||
local minValue, maxValue = scrollBar:GetMinMaxValues()
|
||||
local step = scrollBar:GetValueStep() or 20
|
||||
scrollBar:SetValue(math.min(maxValue, math.max(minValue, scrollBar:GetValue() - delta * step)))
|
||||
end
|
||||
end
|
||||
|
||||
local function hideScrollBar(scrollFrame)
|
||||
local scrollBar = getScrollBar(scrollFrame)
|
||||
if scrollBar then scrollBar:Hide() end
|
||||
end
|
||||
|
||||
local function getBodyChild()
|
||||
local body = PlayerGuideFrame and PlayerGuideFrame.BodyScroll
|
||||
return body and body.ScrollChild
|
||||
end
|
||||
|
||||
local resolveEncounterJournalInstanceID
|
||||
|
||||
local function getRecommendationArtwork(data)
|
||||
if type(data) ~= "table" then return nil end
|
||||
|
||||
local artwork = data.artwork or data.cover or data.coverImage or data.buttonImage or data.bgImage or data.icon
|
||||
if artwork and artwork ~= "" then return artwork end
|
||||
|
||||
if not resolveEncounterJournalInstanceID or not EJ_GetInstanceInfo then return nil end
|
||||
local instanceID = resolveEncounterJournalInstanceID(data)
|
||||
if not instanceID then return nil end
|
||||
|
||||
local ok, _, _, bgImage, buttonImage, loreImage, buttonSmallImage = pcall(EJ_GetInstanceInfo, instanceID)
|
||||
if ok then
|
||||
return buttonImage or bgImage or loreImage or buttonSmallImage
|
||||
end
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────── Цепочка этапов ──
|
||||
|
||||
local stageBadges = {}
|
||||
|
||||
local function buildStageChain(stages)
|
||||
if not PlayerGuideFrame then return end
|
||||
local container = PlayerGuideFrame.StageChain
|
||||
|
||||
for i = (#stages + 1), #stageBadges do stageBadges[i]:Hide() end
|
||||
if #stages == 0 then return end
|
||||
|
||||
local width = container:GetWidth()
|
||||
if width == 0 then width = 600 end
|
||||
local slot = width / #stages
|
||||
|
||||
for i, s in ipairs(stages) do
|
||||
local badge = stageBadges[i]
|
||||
if not badge then
|
||||
badge = CreateFrame("Frame", "PlayerGuideStageBadge"..i, container, "MW_PG_StageBadgeTemplate")
|
||||
stageBadges[i] = badge
|
||||
end
|
||||
badge:ClearAllPoints()
|
||||
badge:SetPoint("LEFT", container, "LEFT", (i - 0.5) * slot - 19, 0)
|
||||
badge:Show()
|
||||
|
||||
badge.Roman:SetText(ROMAN[s.id] or tostring(s.id))
|
||||
badge.Label:SetText(s.short or s.name or "")
|
||||
|
||||
local status = s.status or "locked"
|
||||
badge.BadgeBG:SetTexture(BADGE_TEX[status] or BADGE_TEX.locked)
|
||||
if status == "active" then
|
||||
badge.Roman:SetTextColor(1.0, 0.91, 0.63)
|
||||
badge.Label:SetTextColor(1.0, 0.84, 0.42)
|
||||
elseif status == "done" then
|
||||
badge.Roman:SetText("v")
|
||||
badge.Roman:SetTextColor(0.79, 0.65, 0.38)
|
||||
badge.Label:SetTextColor(0.79, 0.65, 0.38)
|
||||
else
|
||||
badge.Roman:SetTextColor(0.40, 0.34, 0.22)
|
||||
badge.Label:SetTextColor(0.40, 0.34, 0.22)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────── Карточки целей ──
|
||||
|
||||
local objectiveCards = {}
|
||||
local CARD_HEIGHT = { compact = 54, regular = 70, cozy = 82 }
|
||||
local CARD_GAP = { compact = 6, regular = 8, cozy = 10 }
|
||||
|
||||
local function renderObjectives()
|
||||
local section = PlayerGuideFrame.ObjectivesSection
|
||||
local parent = section.ScrollFrame.ScrollChild
|
||||
local n = C_PlayerGuide.GetNumObjectives()
|
||||
local width = 0
|
||||
if PlayerGuideFrame.BodyScroll then
|
||||
width = PlayerGuideFrame.BodyScroll:GetWidth()
|
||||
end
|
||||
if width == 0 then width = section:GetWidth() end
|
||||
if width == 0 then width = section.ScrollFrame:GetWidth() end
|
||||
if width == 0 then width = 860 end
|
||||
-- Body child right inset 8, section right inset 0, scrollframe left/right insets 6/8.
|
||||
-- The card then keeps 8px equal visual padding on both sides of the objective row.
|
||||
parent:SetWidth(math.max(1, width - 30))
|
||||
|
||||
for i = (n + 1), #objectiveCards do objectiveCards[i]:Hide() end
|
||||
|
||||
local h = CARD_HEIGHT[DENSITY] or 44
|
||||
local gap = CARD_GAP[DENSITY] or 4
|
||||
local done = 0
|
||||
|
||||
for i = 1, n do
|
||||
local o = C_PlayerGuide.GetObjectiveInfo(i)
|
||||
if not o then break end
|
||||
|
||||
local card = objectiveCards[i]
|
||||
if not card then
|
||||
card = CreateFrame("Frame", "PlayerGuideObjective"..i, parent, "MW_PG_ObjectiveCardTemplate")
|
||||
objectiveCards[i] = card
|
||||
end
|
||||
card:ClearAllPoints()
|
||||
card:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, -(i - 1) * (h + gap))
|
||||
card:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, -(i - 1) * (h + gap))
|
||||
card:SetHeight(h)
|
||||
card.objectiveData = o
|
||||
card:Show()
|
||||
if card.SetBackdropColor then
|
||||
card:SetBackdropColor(0, 0, 0, 0.72)
|
||||
card:SetBackdropBorderColor(0.64, 0.45, 0.15, 0.95)
|
||||
end
|
||||
|
||||
card.TypeLabel:SetText(_G["MW_PG_TYPE_" .. string.upper(o.type or "custom")] or string.upper(o.type or ""))
|
||||
card.Title:SetText(o.title or "")
|
||||
card.Subtitle:SetText(o.subtitle or o.description or "")
|
||||
|
||||
card.ProgressBar:SetMinMaxValues(0, o.required or 1)
|
||||
card.ProgressBar:SetValue(o.progress or 0)
|
||||
card.Count:SetText(string.format("%d / %d", o.progress or 0, o.required or 1))
|
||||
|
||||
if o.completed then
|
||||
done = done + 1
|
||||
card.Icon:SetTexture(TYPE_ICON.done)
|
||||
card.Icon:SetTexCoord(0, 1, 0, 1)
|
||||
card.Title:SetTextColor(0.62, 0.84, 0.47)
|
||||
card.TypeLabel:SetTextColor(0.49, 0.65, 0.35)
|
||||
card.Count:SetTextColor(0.62, 0.84, 0.47)
|
||||
card.ProgressBar:SetStatusBarColor(0.55, 0.85, 0.40)
|
||||
card.IconBorder:SetVertexColor(0.40, 0.65, 0.30, 0.7)
|
||||
if card.SetBackdropBorderColor then
|
||||
card:SetBackdropBorderColor(0.26, 0.52, 0.18, 0.9)
|
||||
end
|
||||
else
|
||||
card.Icon:SetTexture(TYPE_ICON[o.type] or TYPE_ICON.custom)
|
||||
card.Icon:SetTexCoord(0, 1, 0, 1)
|
||||
card.Title:SetTextColor(1.0, 0.91, 0.63)
|
||||
card.TypeLabel:SetTextColor(0.63, 0.46, 0.19)
|
||||
card.Count:SetTextColor(0.79, 0.65, 0.38)
|
||||
card.ProgressBar:SetStatusBarColor(ACCENT.r, ACCENT.g, ACCENT.b)
|
||||
card.IconBorder:SetVertexColor(ACCENT.r, ACCENT.g, ACCENT.b, 0.55)
|
||||
end
|
||||
|
||||
if o.required == 1 and (o.progress or 0) == 0 then
|
||||
card.ProgressBar:Hide()
|
||||
else
|
||||
card.ProgressBar:Show()
|
||||
end
|
||||
end
|
||||
|
||||
parent:SetHeight(math.max(1, n * h + math.max(0, n - 1) * gap))
|
||||
section:SetHeight(48 + parent:GetHeight())
|
||||
hideScrollBar(section.ScrollFrame)
|
||||
section.Count:SetText(string.format(" %d / %d", done, n))
|
||||
end
|
||||
|
||||
-- ─────────────────────────────── Карточки рекомендаций ──
|
||||
|
||||
local recCards = {}
|
||||
|
||||
local function renderRecommendations()
|
||||
local section = PlayerGuideFrame.RecommendationsSection
|
||||
if not SHOW_RECOMMENDATIONS then section:Hide(); return end
|
||||
|
||||
local parent = section.ScrollFrame.ScrollChild
|
||||
local n = C_PlayerGuide.GetNumRecommendations()
|
||||
if n == 0 then section:Hide(); return end
|
||||
section:Show()
|
||||
|
||||
for i = (n + 1), #recCards do recCards[i]:Hide() end
|
||||
|
||||
local cardW = 176
|
||||
local gap = 16
|
||||
|
||||
for i = 1, n do
|
||||
local r = C_PlayerGuide.GetRecommendationInfo(i)
|
||||
if not r then break end
|
||||
|
||||
local card = recCards[i]
|
||||
if not card then
|
||||
card = CreateFrame("Button", "PlayerGuideRec"..i, parent, "MW_PG_RecCardTemplate")
|
||||
recCards[i] = card
|
||||
end
|
||||
card:ClearAllPoints()
|
||||
card:SetPoint("TOPLEFT", parent, "TOPLEFT", (i - 1) * (cardW + gap), 0)
|
||||
card.recData = r
|
||||
card:Show()
|
||||
if card.SetBackdropColor then
|
||||
card:SetBackdropColor(0, 0, 0, 0.88)
|
||||
card:SetBackdropBorderColor(0.64, 0.45, 0.15, 0.95)
|
||||
end
|
||||
|
||||
local artwork = getRecommendationArtwork(r)
|
||||
card.Artwork:SetTexCoord(0, 0.68359375, 0, 0.7421875)
|
||||
card.Artwork:SetVertexColor(1, 1, 1, 1)
|
||||
if not artwork or not card.Artwork:SetTexture(artwork) then
|
||||
card.Artwork:SetTexture(BIOME_TEX[r.biome] or BIOME_TEX.fire)
|
||||
end
|
||||
card.Name:SetText(r.name or r.title or "")
|
||||
card.Loc:SetText(r.loc or r.description or "")
|
||||
card.Level:SetText(r.level or "")
|
||||
end
|
||||
|
||||
parent:SetWidth(math.max(1, n * cardW + math.max(0, n - 1) * gap))
|
||||
parent:SetHeight(116)
|
||||
section:SetHeight(162)
|
||||
hideScrollBar(section.ScrollFrame)
|
||||
section.Count:SetText(" " .. n)
|
||||
end
|
||||
|
||||
local function updateBodyContent()
|
||||
local F = PlayerGuideFrame
|
||||
local child = getBodyChild()
|
||||
if not F or not child then return end
|
||||
|
||||
local width = F.BodyScroll:GetWidth()
|
||||
if width == 0 then width = 860 end
|
||||
child:SetWidth(math.max(1, width - 8))
|
||||
|
||||
local y = 0
|
||||
F.ObjectivesSection:ClearAllPoints()
|
||||
F.ObjectivesSection:SetPoint("TOPLEFT", child, "TOPLEFT", 0, y)
|
||||
F.ObjectivesSection:SetPoint("TOPRIGHT", child, "TOPRIGHT", 0, y)
|
||||
y = y - F.ObjectivesSection:GetHeight() - 16
|
||||
|
||||
if F.RecommendationsSection:IsShown() then
|
||||
F.RecommendationsSection:ClearAllPoints()
|
||||
F.RecommendationsSection:SetPoint("TOPLEFT", child, "TOPLEFT", 0, y)
|
||||
F.RecommendationsSection:SetPoint("TOPRIGHT", child, "TOPRIGHT", 0, y)
|
||||
y = y - F.RecommendationsSection:GetHeight()
|
||||
end
|
||||
|
||||
local contentHeight = math.max(1, -y + 8)
|
||||
child:SetHeight(contentHeight)
|
||||
updateScrollBar(F.BodyScroll, contentHeight, child:GetWidth())
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────── Layout ──
|
||||
|
||||
local function applyLayout(self, hasChain)
|
||||
local chain = self.StageChain
|
||||
local hdr = self.Header
|
||||
local body = self.BodyScroll
|
||||
|
||||
chain:ClearAllPoints()
|
||||
chain:SetPoint("TOPLEFT", self, "TOPLEFT", 28, -8)
|
||||
chain:SetPoint("TOPRIGHT", self, "TOPRIGHT", -28, -8)
|
||||
|
||||
hdr:ClearAllPoints()
|
||||
if hasChain then
|
||||
hdr:SetPoint("TOPLEFT", chain, "BOTTOMLEFT", 0, -6)
|
||||
hdr:SetPoint("TOPRIGHT", chain, "BOTTOMRIGHT", 0, -6)
|
||||
else
|
||||
hdr:SetPoint("TOPLEFT", self, "TOPLEFT", 30, -68)
|
||||
hdr:SetPoint("TOPRIGHT", self, "TOPRIGHT", -30, -68)
|
||||
end
|
||||
|
||||
body:ClearAllPoints()
|
||||
body:SetPoint("TOPLEFT", hdr, "BOTTOMLEFT", -4, -8)
|
||||
body:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -30, 16)
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────── Show/hide ──
|
||||
|
||||
local function showState(state)
|
||||
local F = PlayerGuideFrame
|
||||
local hasData = (state == "data")
|
||||
-- StageChain visibility controlled in refresh() based on #stages
|
||||
showOrHide(F.Header, hasData)
|
||||
showOrHide(F.BodyScroll, hasData)
|
||||
showOrHide(F.ObjectivesSection, hasData)
|
||||
showOrHide(F.RecommendationsSection, hasData and SHOW_RECOMMENDATIONS)
|
||||
showOrHide(F.EmptyState, state == "empty")
|
||||
showOrHide(F.LoadingState, state == "loading")
|
||||
if not hasData then showOrHide(F.StageChain, false) end
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────── Render ──
|
||||
|
||||
local function refresh()
|
||||
if not PlayerGuideFrame then return end
|
||||
|
||||
local state = C_PlayerGuide.GetState()
|
||||
showState(state)
|
||||
if state ~= "data" then return end
|
||||
|
||||
local id, name, desc, progress = C_PlayerGuide.GetStageInfo()
|
||||
local stages = C_PlayerGuide.GetStages() or {}
|
||||
|
||||
local F = PlayerGuideFrame
|
||||
if #stages > 0 then
|
||||
F.Header.Pretitle:SetText(string.format(
|
||||
MW_PLAYER_GUIDE_STAGE_OF or "ЭТАП %d ИЗ %d", id or 0, #stages))
|
||||
else
|
||||
F.Header.Pretitle:SetText(string.format("ЭТАП %d", id or 0))
|
||||
end
|
||||
F.Header.Title:SetText(name or "")
|
||||
F.Header.Description:SetText(desc or "")
|
||||
F.Header.ProgressLabel:SetText(MW_PLAYER_GUIDE_STAGE_PROGRESS or "ПРОГРЕСС ЭТАПА")
|
||||
F.Header.ProgressPercent:SetText((progress or 0) .. "%")
|
||||
F.Header.ProgressBar:SetMinMaxValues(0, 100)
|
||||
F.Header.ProgressBar:SetValue(progress or 0)
|
||||
|
||||
local hasChain = #stages > 0
|
||||
showOrHide(F.StageChain, hasChain)
|
||||
applyLayout(F, hasChain)
|
||||
buildStageChain(stages)
|
||||
updateBodyContent()
|
||||
renderObjectives()
|
||||
renderRecommendations()
|
||||
updateBodyContent()
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────── XML callbacks ──
|
||||
|
||||
function MW_PlayerGuide_OnLoad(self)
|
||||
EncounterJournal.playerGuideFrame = self
|
||||
self:EnableMouse(true)
|
||||
self:EnableMouseWheel(true)
|
||||
|
||||
local bodyChild = self.BodyScroll and self.BodyScroll.ScrollChild
|
||||
if bodyChild then
|
||||
self.ObjectivesSection:SetParent(bodyChild)
|
||||
self.RecommendationsSection:SetParent(bodyChild)
|
||||
end
|
||||
|
||||
applyLayout(self, false)
|
||||
|
||||
local objScrollBar = getScrollBar(self.ObjectivesSection.ScrollFrame)
|
||||
if objScrollBar then objScrollBar:Hide() end
|
||||
local recScrollBar = getScrollBar(self.RecommendationsSection.ScrollFrame)
|
||||
if recScrollBar then recScrollBar:Hide() end
|
||||
hideScrollBar(self.BodyScroll)
|
||||
|
||||
C_PlayerGuide.RegisterListener(function() refresh() end)
|
||||
end
|
||||
|
||||
function MW_PlayerGuide_OnMouseWheel(self, delta)
|
||||
if self.BodyScroll and self.BodyScroll:IsShown() then
|
||||
scrollFrameOnMouseWheel(self.BodyScroll, delta)
|
||||
end
|
||||
end
|
||||
|
||||
function MW_PlayerGuide_OnShow()
|
||||
if not C_PlayerGuide.HasData() then
|
||||
C_PlayerGuide.SetLoading()
|
||||
C_PlayerGuide.RequestRefresh()
|
||||
end
|
||||
refresh()
|
||||
end
|
||||
|
||||
function MW_PlayerGuide_RefreshButton_OnClick()
|
||||
C_PlayerGuide.SetLoading()
|
||||
C_PlayerGuide.RequestRefresh()
|
||||
end
|
||||
|
||||
local function validEncounterJournalInstanceID(value)
|
||||
local instanceID = tonumber(value)
|
||||
if not instanceID or instanceID <= 0 or not EJ_GetInstanceInfo then return nil end
|
||||
|
||||
local ok, instanceName = pcall(EJ_GetInstanceInfo, instanceID)
|
||||
if ok and instanceName then return instanceID end
|
||||
end
|
||||
|
||||
local function mapIDToEncounterJournalInstanceID(value)
|
||||
local mapID = tonumber(value)
|
||||
if not mapID or mapID <= 0 then return nil end
|
||||
if not C_EncounterJournal or not C_EncounterJournal.GetInstanceIDByMapID then return nil end
|
||||
|
||||
local ok, instanceID = pcall(C_EncounterJournal.GetInstanceIDByMapID, mapID)
|
||||
if ok then
|
||||
return validEncounterJournalInstanceID(instanceID)
|
||||
end
|
||||
end
|
||||
|
||||
function resolveEncounterJournalInstanceID(data)
|
||||
if type(data) == "number" or type(data) == "string" then
|
||||
return validEncounterJournalInstanceID(data) or mapIDToEncounterJournalInstanceID(data)
|
||||
end
|
||||
if type(data) ~= "table" then return nil end
|
||||
|
||||
local instanceID =
|
||||
validEncounterJournalInstanceID(data.ej_instanceID) or
|
||||
validEncounterJournalInstanceID(data.ejInstanceID) or
|
||||
validEncounterJournalInstanceID(data.journalInstanceID) or
|
||||
validEncounterJournalInstanceID(data.instanceID) or
|
||||
validEncounterJournalInstanceID(data.dungeonID) or
|
||||
validEncounterJournalInstanceID(data.targetID)
|
||||
if instanceID then return instanceID end
|
||||
|
||||
return mapIDToEncounterJournalInstanceID(data.mapID) or
|
||||
mapIDToEncounterJournalInstanceID(data.worldMapAreaID) or
|
||||
mapIDToEncounterJournalInstanceID(data.instanceID) or
|
||||
mapIDToEncounterJournalInstanceID(data.dungeonID) or
|
||||
mapIDToEncounterJournalInstanceID(data.targetID)
|
||||
end
|
||||
|
||||
local function requestOpenTarget(data)
|
||||
if type(data) == "table" and data.id and C_PlayerGuide and C_PlayerGuide.RequestOpenTarget then
|
||||
C_PlayerGuide.RequestOpenTarget(data.id)
|
||||
end
|
||||
end
|
||||
|
||||
local function openEncounterJournalInstance(data, encounterID)
|
||||
if not EncounterJournal then return nil end
|
||||
|
||||
local instanceID = resolveEncounterJournalInstanceID(data)
|
||||
if not instanceID then
|
||||
requestOpenTarget(data)
|
||||
return nil
|
||||
end
|
||||
|
||||
if type(data) == "table" and not encounterID then
|
||||
encounterID = data.encounterID
|
||||
end
|
||||
|
||||
if not EncounterJournal:IsShown() then
|
||||
ShowUIPanel(EncounterJournal)
|
||||
end
|
||||
|
||||
if EncounterJournal.encounter then
|
||||
EncounterJournal.encounter:Show()
|
||||
end
|
||||
if EncounterJournal.instanceSelect then
|
||||
EJ_ContentTab_SelectAppropriateInstanceTab(instanceID)
|
||||
end
|
||||
|
||||
if NavBar_Reset and EncounterJournal.navBar then
|
||||
NavBar_Reset(EncounterJournal.navBar)
|
||||
end
|
||||
|
||||
EncounterJournal_DisplayInstance(instanceID)
|
||||
|
||||
if encounterID and encounterID > 0 then
|
||||
EncounterJournal_DisplayEncounter(encounterID)
|
||||
end
|
||||
end
|
||||
|
||||
function MW_PlayerGuide_Objective_OnClick(self)
|
||||
local o = self.objectiveData
|
||||
if not o then return end
|
||||
|
||||
if o.type == "dungeon" or o.type == "raid" then
|
||||
openEncounterJournalInstance(o)
|
||||
elseif o.type == "boss" then
|
||||
if o.encounterID and o.encounterID > 0 then
|
||||
if not EncounterJournal:IsShown() then ShowUIPanel(EncounterJournal) end
|
||||
EncounterJournal_DisplayEncounter(o.encounterID)
|
||||
end
|
||||
elseif o.type == "achievement" then
|
||||
if o.achievementID then
|
||||
if AchievementFrame_LoadUI then AchievementFrame_LoadUI() end
|
||||
if AchievementFrame_SelectAchievement then
|
||||
AchievementFrame_SelectAchievement(o.achievementID)
|
||||
end
|
||||
end
|
||||
elseif o.type == "item" then
|
||||
if o.itemID and o.itemID > 0 then
|
||||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetItemByID(o.itemID)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
elseif o.type == "quest" or o.type == "quest_chain" then
|
||||
local qid = o.questID or o.questChainID or o.targetID
|
||||
if qid and qid > 0 then
|
||||
pcall(SetItemRef, "quest:" .. qid .. ":-1", "", "LeftButton")
|
||||
end
|
||||
else
|
||||
C_PlayerGuide.RequestOpenTarget(o.id)
|
||||
end
|
||||
end
|
||||
|
||||
function MW_PlayerGuide_Objective_OnEnter(self)
|
||||
local o = self.objectiveData
|
||||
if not o then return end
|
||||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||||
GameTooltip:AddLine(o.title or "", 1, 0.91, 0.63)
|
||||
if o.subtitle and o.subtitle ~= "" then
|
||||
GameTooltip:AddLine(o.subtitle, 0.66, 0.59, 0.47, true)
|
||||
end
|
||||
if not o.completed then
|
||||
GameTooltip:AddLine(
|
||||
string.format("Прогресс: %d / %d", o.progress or 0, o.required or 1),
|
||||
0.79, 0.65, 0.38)
|
||||
end
|
||||
GameTooltip:Show()
|
||||
end
|
||||
|
||||
function MW_PlayerGuide_Objective_OnLeave() GameTooltip:Hide() end
|
||||
|
||||
function MW_PlayerGuide_Rec_OnClick(self)
|
||||
local r = self.recData
|
||||
if r then openEncounterJournalInstance(r) end
|
||||
end
|
||||
|
||||
function MW_PlayerGuide_Rec_OnEnter(self)
|
||||
local r = self.recData
|
||||
if not r then return end
|
||||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||||
GameTooltip:AddLine(r.name or r.title or "", 1, 0.91, 0.63)
|
||||
if r.loc then GameTooltip:AddLine(r.loc, 0.66, 0.59, 0.47) end
|
||||
if r.level then GameTooltip:AddLine("Уровень: " .. r.level, 0.79, 0.65, 0.38) end
|
||||
GameTooltip:Show()
|
||||
end
|
||||
|
||||
function MW_PlayerGuide_Rec_OnLeave() GameTooltip:Hide() end
|
||||
|
||||
-- ─────────────────────────────── Экспорт для отладки ──
|
||||
|
||||
MW_PlayerGuide = {
|
||||
Refresh = refresh,
|
||||
InjectDevData = function() C_PlayerGuide._InjectDevPayload() end,
|
||||
}
|
||||
+490
@@ -0,0 +1,490 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\..\FrameXML\UI.xsd">
|
||||
|
||||
<Script file="PlayerGuide.lua"/>
|
||||
|
||||
<!-- ───────── Шрифты ───────── -->
|
||||
|
||||
<Font name="MW_PG_Title" inherits="GameFontNormalHuge" virtual="true">
|
||||
<Color r="1.0" g="0.84" b="0.42"/>
|
||||
<Shadow><Color r="0" g="0" b="0"/><Offset><AbsDimension x="1" y="-1"/></Offset></Shadow>
|
||||
</Font>
|
||||
|
||||
<Font name="MW_PG_Section" inherits="QuestFont_Large" virtual="true">
|
||||
<Color r="1.0" g="0.84" b="0.42"/>
|
||||
<Shadow><Color r="0" g="0" b="0"/><Offset><AbsDimension x="1" y="-1"/></Offset></Shadow>
|
||||
</Font>
|
||||
|
||||
<Font name="MW_PG_Body" inherits="GameFontHighlight" virtual="true">
|
||||
<Color r="0.85" g="0.78" b="0.63"/>
|
||||
</Font>
|
||||
|
||||
<Font name="MW_PG_Caps" inherits="GameFontNormalSmall" virtual="true">
|
||||
<Color r="0.63" g="0.46" b="0.19"/>
|
||||
</Font>
|
||||
|
||||
<Font name="MW_PG_Italic" inherits="QuestFont" virtual="true">
|
||||
<Color r="0.66" g="0.59" b="0.47"/>
|
||||
</Font>
|
||||
|
||||
<!-- ───────── Карточка цели ───────── -->
|
||||
<!-- Высота = 60px (DENSITY "regular"). Позиции: TypeLabel+Title строка 1, Subtitle строка 2, бар внизу. -->
|
||||
|
||||
<Frame name="MW_PG_ObjectiveCardTemplate" virtual="true">
|
||||
<Size><AbsDimension x="0" y="70"/></Size>
|
||||
<Backdrop bgFile="Interface\Buttons\WHITE8X8"
|
||||
edgeFile="Interface\Buttons\WHITE8X8"
|
||||
tile="true">
|
||||
<BackgroundInsets><AbsInset left="0" right="0" top="0" bottom="0"/></BackgroundInsets>
|
||||
<EdgeSize><AbsValue val="1"/></EdgeSize>
|
||||
<TileSize><AbsValue val="64"/></TileSize>
|
||||
</Backdrop>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<!-- Рамка иконки — фоновый слой, центрируется по LEFT -->
|
||||
<Texture name="$parentIconBorder" parentKey="IconBorder" file="Interface\Buttons\UI-Quickslot2">
|
||||
<Size><AbsDimension x="50" y="50"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT"><Offset><AbsDimension x="12" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0.2" right="0.8" top="0.2" bottom="0.8"/>
|
||||
<Color r="1.0" g="0.84" b="0.42" a="0.55"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="ARTWORK">
|
||||
<Texture name="$parentIcon" parentKey="Icon" file="Interface\Icons\INV_Misc_QuestionMark">
|
||||
<Size><AbsDimension x="34" y="34"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT"><Offset><AbsDimension x="20" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="OVERLAY">
|
||||
<!-- Строка 1: TypeLabel (префикс) + Title (основной текст) на одной строке -->
|
||||
<FontString name="$parentTypeLabel" parentKey="TypeLabel" inherits="MW_PG_Caps" justifyH="LEFT">
|
||||
<Size><AbsDimension x="90" y="12"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="72" y="-16"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="$parentTitle" parentKey="Title" inherits="GameFontNormal" justifyH="LEFT">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="185" y="-16"/></Offset></Anchor>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-118" y="-16"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Color r="1.0" g="0.91" b="0.63"/>
|
||||
</FontString>
|
||||
<!-- Строка 2: Subtitle (описание/прогресс) -->
|
||||
<FontString name="$parentSubtitle" parentKey="Subtitle" inherits="MW_PG_Italic" justifyH="LEFT">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="72" y="-36"/></Offset></Anchor>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-118" y="-36"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<!-- Count: мелко снизу справа -->
|
||||
<FontString name="$parentCount" parentKey="Count" inherits="GameFontNormalSmall" justifyH="RIGHT">
|
||||
<Size><AbsDimension x="110" y="10"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-18" y="10"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Color r="0.63" g="0.46" b="0.19"/>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<!-- Прогресс-бар: тонкая полоска у самого низа карточки -->
|
||||
<StatusBar name="$parentProgressBar" parentKey="ProgressBar">
|
||||
<Size><AbsDimension x="0" y="4"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="72" y="8"/></Offset></Anchor>
|
||||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-118" y="8"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<BarTexture file="Interface\AddOns\MoonWellClient\EncounterJournal\Textures\PlayerGuide\ProgressBar-Fill"/>
|
||||
<BarColor r="1.0" g="0.79" b="0.29"/>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture setAllPoints="true"
|
||||
file="Interface\AddOns\MoonWellClient\EncounterJournal\Textures\PlayerGuide\ProgressBar-BG"/>
|
||||
</Layer>
|
||||
</Layers>
|
||||
</StatusBar>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnClick function="MW_PlayerGuide_Objective_OnClick"/>
|
||||
<OnEnter function="MW_PlayerGuide_Objective_OnEnter"/>
|
||||
<OnLeave function="MW_PlayerGuide_Objective_OnLeave"/>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
|
||||
<!-- ───────── Карточка рекомендации ───────── -->
|
||||
|
||||
<Button name="MW_PG_RecCardTemplate" virtual="true">
|
||||
<!-- Height 96: fits in the 100px visible area of RecommendationsSection scrollframe -->
|
||||
<Size><AbsDimension x="176" y="116"/></Size>
|
||||
<Backdrop bgFile="Interface\Buttons\WHITE8X8"
|
||||
edgeFile="Interface\Buttons\WHITE8X8"
|
||||
tile="true">
|
||||
<BackgroundInsets><AbsInset left="0" right="0" top="0" bottom="0"/></BackgroundInsets>
|
||||
<EdgeSize><AbsValue val="1"/></EdgeSize>
|
||||
<TileSize><AbsValue val="64"/></TileSize>
|
||||
</Backdrop>
|
||||
<Layers>
|
||||
<Layer level="ARTWORK">
|
||||
<!-- Artwork: 138x56, top offset 5 → bottom at y=61 from card top -->
|
||||
<Texture name="$parentArtwork" parentKey="Artwork" file="Interface\Buttons\WHITE8X8" setAllPoints="true">
|
||||
<Color r="1" g="1" b="1"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="OVERLAY">
|
||||
<!-- Level: top-right corner of artwork (artwork starts at y=-5 → level at TOPRIGHT+(-4,-9)) -->
|
||||
<Texture name="$parentTextBackdrop" parentKey="TextBackdrop" file="Interface\Buttons\WHITE8X8">
|
||||
<Size><AbsDimension x="176" y="42"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOM"/>
|
||||
</Anchors>
|
||||
<Color r="0" g="0" b="0" a="0.72"/>
|
||||
</Texture>
|
||||
<FontString name="$parentLevel" parentKey="Level" inherits="GameFontNormalSmall" justifyH="RIGHT">
|
||||
<Size><AbsDimension x="80" y="12"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-4" y="-9"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Color r="1.0" g="0.91" b="0.63"/>
|
||||
</FontString>
|
||||
<!-- Name: 4px below artwork (artwork bottom y=61, name at y=65) -->
|
||||
<FontString name="$parentName" parentKey="Name" inherits="GameFontNormal" justifyH="LEFT">
|
||||
<Size><AbsDimension x="162" y="16"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="8" y="-82"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Color r="1.0" g="0.91" b="0.63"/>
|
||||
</FontString>
|
||||
<!-- Loc: 3px below name (name bottom y=79, loc at y=82) -->
|
||||
<FontString name="$parentLoc" parentKey="Loc" inherits="MW_PG_Italic" justifyH="LEFT">
|
||||
<Size><AbsDimension x="162" y="16"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="8" y="-98"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnClick function="MW_PlayerGuide_Rec_OnClick"/>
|
||||
<OnEnter function="MW_PlayerGuide_Rec_OnEnter"/>
|
||||
<OnLeave function="MW_PlayerGuide_Rec_OnLeave"/>
|
||||
</Scripts>
|
||||
</Button>
|
||||
|
||||
<!-- ───────── Бейдж этапа ───────── -->
|
||||
|
||||
<Frame name="MW_PG_StageBadgeTemplate" virtual="true">
|
||||
<Size><AbsDimension x="38" y="38"/></Size>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture name="$parentBadgeBG" parentKey="BadgeBG"
|
||||
file="Interface\AddOns\MoonWellClient\EncounterJournal\Textures\PlayerGuide\StageBadge-Locked">
|
||||
<Size><AbsDimension x="60" y="60"/></Size>
|
||||
<Anchors><Anchor point="CENTER"/></Anchors>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="OVERLAY">
|
||||
<FontString name="$parentRoman" parentKey="Roman" inherits="GameFontNormalLarge" justifyH="CENTER">
|
||||
<Anchors><Anchor point="CENTER"/></Anchors>
|
||||
<Color r="1.0" g="0.91" b="0.63"/>
|
||||
</FontString>
|
||||
<FontString name="$parentLabel" parentKey="Label" inherits="MW_PG_Caps" justifyH="CENTER">
|
||||
<Size><AbsDimension x="100" y="10"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
</Frame>
|
||||
|
||||
<!-- ───────── Корневой фрейм ───────── -->
|
||||
|
||||
<Frame name="PlayerGuideFrame" hidden="true" parent="EncounterJournal">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="EncounterJournalInset" relativePoint="TOPLEFT">
|
||||
<Offset><AbsDimension x="5" y="-4"/></Offset>
|
||||
</Anchor>
|
||||
<Anchor point="BOTTOMRIGHT" relativeTo="EncounterJournalInset" relativePoint="BOTTOMRIGHT">
|
||||
<Offset><AbsDimension x="-3" y="0"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture parentKey="BackgroundPage"
|
||||
file="Interface\AddOns\MoonWellClient\EncounterJournal\Textures\PlayerGuide\Background-Page"
|
||||
setAllPoints="true">
|
||||
<TexCoords left="0" right="1" top="0" bottom="1"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="BORDER">
|
||||
<Texture parentKey="TabDivider"
|
||||
file="Interface\AddOns\MoonWellClient\EncounterJournal\Textures\PlayerGuide\Section-Divider">
|
||||
<Size><AbsDimension x="0" y="16"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="28" y="-52"/></Offset></Anchor>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-28" y="-52"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
|
||||
<Frames>
|
||||
|
||||
<!-- 1) Цепочка этапов — позиция задаётся Lua в OnLoad -->
|
||||
<Frame name="PlayerGuideStageChain" parentKey="StageChain">
|
||||
<Size><AbsDimension x="0" y="62"/></Size>
|
||||
</Frame>
|
||||
|
||||
<!-- 2) Заголовок этапа — позиция задаётся Lua в OnLoad -->
|
||||
<Frame name="PlayerGuideHeader" parentKey="Header">
|
||||
<Size><AbsDimension x="0" y="82"/></Size>
|
||||
<Layers>
|
||||
<Layer level="OVERLAY">
|
||||
<FontString name="PlayerGuideHeaderPretitle" parentKey="Pretitle" inherits="MW_PG_Caps" justifyH="LEFT">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="PlayerGuideHeaderTitle" parentKey="Title" inherits="MW_PG_Title" justifyH="LEFT">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="2" y="-18"/></Offset></Anchor>
|
||||
<Anchor point="RIGHT"><Offset><AbsDimension x="-260" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="PlayerGuideHeaderDesc" parentKey="Description" inherits="MW_PG_Body" justifyH="LEFT">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="4" y="-48"/></Offset></Anchor>
|
||||
<Anchor point="RIGHT"><Offset><AbsDimension x="-260" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="PlayerGuideHeaderProgLabel" parentKey="ProgressLabel" inherits="MW_PG_Caps" justifyH="RIGHT">
|
||||
<Anchors>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="PlayerGuideHeaderProgPct" parentKey="ProgressPercent" inherits="MW_PG_Title" justifyH="RIGHT">
|
||||
<Size><AbsDimension x="240" y="28"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-2" y="-14"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<StatusBar name="PlayerGuideHeaderProgBar" parentKey="ProgressBar">
|
||||
<Size><AbsDimension x="240" y="10"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-2" y="-52"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<BarTexture file="Interface\AddOns\MoonWellClient\EncounterJournal\Textures\PlayerGuide\ProgressBar-Fill"/>
|
||||
<BarColor r="1.0" g="0.79" b="0.29"/>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture setAllPoints="true"
|
||||
file="Interface\AddOns\MoonWellClient\EncounterJournal\Textures\PlayerGuide\ProgressBar-BG"/>
|
||||
</Layer>
|
||||
</Layers>
|
||||
</StatusBar>
|
||||
<Button name="PlayerGuideHeaderRefresh" parentKey="RefreshButton" inherits="UIPanelButtonTemplate2" text="MW_PLAYER_GUIDE_REFRESH">
|
||||
<Size><AbsDimension x="100" y="22"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-2" y="-72"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
local T = "Interface\\AddOns\\MoonWellClient\\EncounterJournal\\Textures\\PlayerGuide\\"
|
||||
self:SetNormalTexture(T.."Button-Up")
|
||||
self:SetPushedTexture(T.."Button-Down")
|
||||
self:SetHighlightTexture(T.."Button-Highlight")
|
||||
</OnLoad>
|
||||
<OnClick function="MW_PlayerGuide_RefreshButton_OnClick"/>
|
||||
</Scripts>
|
||||
</Button>
|
||||
</Frames>
|
||||
</Frame>
|
||||
|
||||
<!-- 3) Задачи этапа — позиция задаётся Lua в OnLoad -->
|
||||
<ScrollFrame name="PlayerGuideBodyScroll" parentKey="BodyScroll" inherits="UIPanelScrollFrameTemplate">
|
||||
<ScrollChild>
|
||||
<Frame parentKey="ScrollChild">
|
||||
<Size><AbsDimension x="860" y="1"/></Size>
|
||||
</Frame>
|
||||
</ScrollChild>
|
||||
</ScrollFrame>
|
||||
|
||||
<Frame name="PlayerGuideObjSection" parentKey="ObjectivesSection">
|
||||
<Layers>
|
||||
<Layer level="OVERLAY">
|
||||
<Texture name="PlayerGuideObjOrnament" parentKey="Ornament"
|
||||
file="Interface\AddOns\MoonWellClient\EncounterJournal\Textures\PlayerGuide\Corner-Ornament">
|
||||
<Size><AbsDimension x="20" y="20"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="2" y="-1"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Texture>
|
||||
<FontString name="PlayerGuideObjHeading" parentKey="Heading" inherits="MW_PG_Section" justifyH="LEFT" text="MW_PLAYER_GUIDE_OBJECTIVES">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="26" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="PlayerGuideObjCount" parentKey="Count" inherits="MW_PG_Caps" justifyH="LEFT">
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="PlayerGuideObjHeading" relativePoint="RIGHT">
|
||||
<Offset><AbsDimension x="6" y="-2"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<Texture name="PlayerGuideObjDivider" parentKey="Divider"
|
||||
file="Interface\AddOns\MoonWellClient\EncounterJournal\Textures\PlayerGuide\Section-Divider">
|
||||
<Size><AbsDimension x="0" y="16"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="-24"/></Offset></Anchor>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-4" y="-24"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<ScrollFrame name="PlayerGuideObjScroll" parentKey="ScrollFrame" inherits="UIPanelScrollFrameTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="6" y="-48"/></Offset></Anchor>
|
||||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-8" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<ScrollChild>
|
||||
<Frame parentKey="ScrollChild">
|
||||
<Size><AbsDimension x="600" y="200"/></Size>
|
||||
</Frame>
|
||||
</ScrollChild>
|
||||
</ScrollFrame>
|
||||
</Frames>
|
||||
</Frame>
|
||||
|
||||
<!-- 4) Подходящие подземелья — позиция задаётся Lua в OnLoad -->
|
||||
<Frame name="PlayerGuideRecsSection" parentKey="RecommendationsSection">
|
||||
<Size><AbsDimension x="0" y="150"/></Size>
|
||||
<Layers>
|
||||
<Layer level="OVERLAY">
|
||||
<Texture name="PlayerGuideRecsOrnament" parentKey="Ornament"
|
||||
file="Interface\AddOns\MoonWellClient\EncounterJournal\Textures\PlayerGuide\Corner-Ornament">
|
||||
<Size><AbsDimension x="20" y="20"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="2" y="-1"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Texture>
|
||||
<FontString name="PlayerGuideRecsHeading" parentKey="Heading" inherits="MW_PG_Section" justifyH="LEFT" text="MW_PLAYER_GUIDE_RECS">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="26" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="PlayerGuideRecsCount" parentKey="Count" inherits="MW_PG_Caps" justifyH="LEFT">
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="PlayerGuideRecsHeading" relativePoint="RIGHT">
|
||||
<Offset><AbsDimension x="6" y="-2"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<Texture name="PlayerGuideRecsDivider" parentKey="Divider"
|
||||
file="Interface\AddOns\MoonWellClient\EncounterJournal\Textures\PlayerGuide\Section-Divider">
|
||||
<Size><AbsDimension x="0" y="16"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="-24"/></Offset></Anchor>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-4" y="-24"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<ScrollFrame name="PlayerGuideRecScroll" parentKey="ScrollFrame" inherits="UIPanelScrollFrameTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="6" y="-46"/></Offset></Anchor>
|
||||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-36" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<ScrollChild>
|
||||
<Frame parentKey="ScrollChild">
|
||||
<Size><AbsDimension x="900" y="96"/></Size>
|
||||
</Frame>
|
||||
</ScrollChild>
|
||||
</ScrollFrame>
|
||||
</Frames>
|
||||
</Frame>
|
||||
|
||||
<!-- 5) Пустое состояние -->
|
||||
<Frame name="PlayerGuideEmptyState" parentKey="EmptyState" hidden="true">
|
||||
<Anchors><Anchor point="CENTER"><Offset><AbsDimension x="0" y="20"/></Offset></Anchor></Anchors>
|
||||
<Size><AbsDimension x="460" y="200"/></Size>
|
||||
<Layers>
|
||||
<Layer level="OVERLAY">
|
||||
<FontString name="PlayerGuideEmptyTitle" parentKey="Title" inherits="MW_PG_Title" justifyH="CENTER" text="MW_PLAYER_GUIDE_EMPTY_TITLE">
|
||||
<Anchors><Anchor point="TOP"><Offset><AbsDimension x="0" y="-20"/></Offset></Anchor></Anchors>
|
||||
<Size><AbsDimension x="460" y="28"/></Size>
|
||||
</FontString>
|
||||
<FontString name="PlayerGuideEmptyBody" parentKey="Body" inherits="MW_PG_Body" justifyH="CENTER" text="MW_PLAYER_GUIDE_EMPTY_BODY">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="PlayerGuideEmptyTitle" relativePoint="BOTTOM">
|
||||
<Offset><AbsDimension x="0" y="-10"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Size><AbsDimension x="420" y="60"/></Size>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Button name="PlayerGuideEmptyRefresh" parentKey="RefreshButton" inherits="UIPanelButtonTemplate2" text="MW_PLAYER_GUIDE_REFRESH">
|
||||
<Size><AbsDimension x="120" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="PlayerGuideEmptyBody" relativePoint="BOTTOM">
|
||||
<Offset><AbsDimension x="0" y="-14"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
local T = "Interface\\AddOns\\MoonWellClient\\EncounterJournal\\Textures\\PlayerGuide\\"
|
||||
self:SetNormalTexture(T.."Button-Up")
|
||||
self:SetPushedTexture(T.."Button-Down")
|
||||
self:SetHighlightTexture(T.."Button-Highlight")
|
||||
</OnLoad>
|
||||
<OnClick function="MW_PlayerGuide_RefreshButton_OnClick"/>
|
||||
</Scripts>
|
||||
</Button>
|
||||
</Frames>
|
||||
</Frame>
|
||||
|
||||
<!-- 6) Загрузка -->
|
||||
<Frame name="PlayerGuideLoadingState" parentKey="LoadingState" hidden="true">
|
||||
<Anchors><Anchor point="CENTER"/></Anchors>
|
||||
<Size><AbsDimension x="300" y="80"/></Size>
|
||||
<Layers>
|
||||
<Layer level="OVERLAY">
|
||||
<FontString name="PlayerGuideLoadingTitle" parentKey="Title" inherits="MW_PG_Section" justifyH="CENTER" text="MW_PLAYER_GUIDE_LOADING_TITLE">
|
||||
<Anchors><Anchor point="TOP"/></Anchors>
|
||||
<Size><AbsDimension x="300" y="20"/></Size>
|
||||
</FontString>
|
||||
<FontString name="PlayerGuideLoadingBody" parentKey="Body" inherits="MW_PG_Italic" justifyH="CENTER" text="MW_PLAYER_GUIDE_LOADING_BODY">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="PlayerGuideLoadingTitle" relativePoint="BOTTOM">
|
||||
<Offset><AbsDimension x="0" y="-6"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Size><AbsDimension x="300" y="16"/></Size>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
</Frame>
|
||||
|
||||
</Frames>
|
||||
|
||||
<Scripts>
|
||||
<OnLoad function="MW_PlayerGuide_OnLoad"/>
|
||||
<OnShow function="MW_PlayerGuide_OnShow"/>
|
||||
<OnMouseWheel function="MW_PlayerGuide_OnMouseWheel"/>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
|
||||
</Ui>
|
||||
+905
@@ -0,0 +1,905 @@
|
||||
-- Compatibility shims for the Sirus Encounter Journal backport on 3.3.5a.
|
||||
|
||||
if not tIndexOf then
|
||||
function tIndexOf(tbl, value)
|
||||
if not tbl then
|
||||
return nil
|
||||
end
|
||||
for index, entry in ipairs(tbl) do
|
||||
if entry == value then
|
||||
return index
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
if not CopyTable then
|
||||
function CopyTable(tbl)
|
||||
if type(tbl) ~= "table" then
|
||||
return tbl
|
||||
end
|
||||
local copy = {}
|
||||
for key, value in pairs(tbl) do
|
||||
copy[key] = CopyTable(value)
|
||||
end
|
||||
return copy
|
||||
end
|
||||
end
|
||||
|
||||
if not table.wipe then
|
||||
function table.wipe(tbl)
|
||||
for key in pairs(tbl) do
|
||||
tbl[key] = nil
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
end
|
||||
|
||||
if not tCompare then
|
||||
function tCompare(lhsTable, rhsTable, depth)
|
||||
depth = depth or 1
|
||||
for key, value in pairs(lhsTable) do
|
||||
if type(value) == "table" then
|
||||
local rhsValue = rhsTable[key]
|
||||
if type(rhsValue) ~= "table" then
|
||||
return false
|
||||
end
|
||||
if depth > 1 then
|
||||
if not tCompare(value, rhsValue, depth - 1) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
elseif value ~= rhsTable[key] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
for key in pairs(rhsTable) do
|
||||
if lhsTable[key] == nil then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
tsort = tsort or table.sort
|
||||
|
||||
bit = bit or {}
|
||||
if not bit.band then
|
||||
function bit.band(a, b)
|
||||
local result = 0
|
||||
local bitValue = 1
|
||||
a = a or 0
|
||||
b = b or 0
|
||||
while a > 0 or b > 0 do
|
||||
local aBit = a % 2
|
||||
local bBit = b % 2
|
||||
if aBit == 1 and bBit == 1 then
|
||||
result = result + bitValue
|
||||
end
|
||||
a = math.floor(a / 2)
|
||||
b = math.floor(b / 2)
|
||||
bitValue = bitValue * 2
|
||||
end
|
||||
return result
|
||||
end
|
||||
end
|
||||
bitband = bitband or bit.band
|
||||
|
||||
if not string.trim then
|
||||
function string.trim(value)
|
||||
return value and value:match("^%s*(.-)%s*$") or value
|
||||
end
|
||||
end
|
||||
|
||||
utf8 = utf8 or {}
|
||||
utf8.find = utf8.find or string.find
|
||||
utf8.upper = utf8.upper or string.upper
|
||||
|
||||
LE_EXPANSION_CLASSIC = LE_EXPANSION_CLASSIC or 0
|
||||
LE_EXPANSION_BURNING_CRUSADE = LE_EXPANSION_BURNING_CRUSADE or 1
|
||||
LE_EXPANSION_WRATH_OF_THE_LICH_KING = LE_EXPANSION_WRATH_OF_THE_LICH_KING or 2
|
||||
LE_EXPANSION_CATACLYSM = LE_EXPANSION_CATACLYSM or 3
|
||||
LE_EXPANSION_MISTS_OF_PANDARIA = LE_EXPANSION_MISTS_OF_PANDARIA or 4
|
||||
LE_EXPANSION_WARLORDS_OF_DRAENOR = LE_EXPANSION_WARLORDS_OF_DRAENOR or 5
|
||||
LE_EXPANSION_LEGION = LE_EXPANSION_LEGION or 6
|
||||
LE_EXPANSION_BATTLE_FOR_AZEROTH = LE_EXPANSION_BATTLE_FOR_AZEROTH or 7
|
||||
LE_EXPANSION_SHADOWLANDS = LE_EXPANSION_SHADOWLANDS or 8
|
||||
LE_EXPANSION_DRAGONFLIGHT = LE_EXPANSION_DRAGONFLIGHT or 9
|
||||
LE_EXPANSION_WAR_WITHIN = LE_EXPANSION_WAR_WITHIN or 10
|
||||
LE_EXPANSION_MIDNIGHT = LE_EXPANSION_MIDNIGHT or 11
|
||||
LE_EXPANSION_LEVEL_CURRENT = LE_EXPANSION_LEVEL_CURRENT or LE_EXPANSION_WRATH_OF_THE_LICH_KING
|
||||
|
||||
MAX_PLAYER_LEVEL_TABLE = MAX_PLAYER_LEVEL_TABLE or {}
|
||||
MAX_PLAYER_LEVEL_TABLE[LE_EXPANSION_CLASSIC] = MAX_PLAYER_LEVEL_TABLE[LE_EXPANSION_CLASSIC] or 60
|
||||
MAX_PLAYER_LEVEL_TABLE[LE_EXPANSION_BURNING_CRUSADE] = MAX_PLAYER_LEVEL_TABLE[LE_EXPANSION_BURNING_CRUSADE] or 70
|
||||
MAX_PLAYER_LEVEL_TABLE[LE_EXPANSION_WRATH_OF_THE_LICH_KING] = MAX_PLAYER_LEVEL_TABLE[LE_EXPANSION_WRATH_OF_THE_LICH_KING] or 80
|
||||
|
||||
PLAYER_FACTION_GROUP = PLAYER_FACTION_GROUP or {}
|
||||
PLAYER_FACTION_GROUP.Alliance = PLAYER_FACTION_GROUP.Alliance or 1
|
||||
PLAYER_FACTION_GROUP.Horde = PLAYER_FACTION_GROUP.Horde or 2
|
||||
PLAYER_FACTION_GROUP.Neutral = PLAYER_FACTION_GROUP.Neutral or 3
|
||||
PLAYER_FACTION_GROUP.Renegade = PLAYER_FACTION_GROUP.Renegade or 4
|
||||
|
||||
QUESTION_MARK_ICON = QUESTION_MARK_ICON or "Interface\\Icons\\INV_Misc_QuestionMark"
|
||||
|
||||
CLASS_ID_WARRIOR = CLASS_ID_WARRIOR or 1
|
||||
CLASS_ID_PALADIN = CLASS_ID_PALADIN or 2
|
||||
CLASS_ID_HUNTER = CLASS_ID_HUNTER or 3
|
||||
CLASS_ID_ROGUE = CLASS_ID_ROGUE or 4
|
||||
CLASS_ID_PRIEST = CLASS_ID_PRIEST or 5
|
||||
CLASS_ID_DEATHKNIGHT = CLASS_ID_DEATHKNIGHT or 6
|
||||
CLASS_ID_SHAMAN = CLASS_ID_SHAMAN or 7
|
||||
CLASS_ID_MAGE = CLASS_ID_MAGE or 8
|
||||
CLASS_ID_WARLOCK = CLASS_ID_WARLOCK or 9
|
||||
CLASS_ID_DRUID = CLASS_ID_DRUID or 11
|
||||
|
||||
if not SetParentFrameLevel then
|
||||
function SetParentFrameLevel(frame, offset)
|
||||
frame:SetFrameLevel(frame:GetParent():GetFrameLevel() + (offset or 0))
|
||||
end
|
||||
end
|
||||
|
||||
if not IsOnGlueScreen then
|
||||
function IsOnGlueScreen() return false end
|
||||
end
|
||||
|
||||
if not RegisterCustomEvent then
|
||||
function RegisterCustomEvent() end
|
||||
end
|
||||
|
||||
if not UnregisterCustomEvent then
|
||||
function UnregisterCustomEvent() end
|
||||
end
|
||||
|
||||
C_Timer = C_Timer or {}
|
||||
if not C_Timer.NewTimer then
|
||||
local timerFrame
|
||||
local timers = {}
|
||||
|
||||
local function EnsureTimerFrame()
|
||||
if timerFrame or not CreateFrame then
|
||||
return
|
||||
end
|
||||
|
||||
timerFrame = CreateFrame("Frame")
|
||||
timerFrame:SetScript("OnUpdate", function(_, elapsed)
|
||||
for timer in pairs(timers) do
|
||||
timer.remaining = timer.remaining - elapsed
|
||||
if timer.remaining <= 0 then
|
||||
timers[timer] = nil
|
||||
if not timer.cancelled then
|
||||
timer.callback()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function C_Timer.NewTimer(seconds, callback)
|
||||
local timer = {
|
||||
remaining = seconds or 0,
|
||||
callback = callback or function() end,
|
||||
}
|
||||
|
||||
function timer:Cancel()
|
||||
self.cancelled = true
|
||||
timers[self] = nil
|
||||
end
|
||||
|
||||
EnsureTimerFrame()
|
||||
timers[timer] = true
|
||||
return timer
|
||||
end
|
||||
end
|
||||
C_Timer.After = C_Timer.After or function(seconds, callback)
|
||||
return C_Timer.NewTimer(seconds, callback)
|
||||
end
|
||||
|
||||
if not IsInterfaceDevClient then
|
||||
function IsInterfaceDevClient()
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if not IsGMAccount then
|
||||
function IsGMAccount()
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if not FireCustomClientEvent then
|
||||
function FireCustomClientEvent(event, ...)
|
||||
if EventRegistry and EventRegistry.TriggerEvent then
|
||||
EventRegistry:TriggerEvent(event, ...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
EventRegistry = EventRegistry or {}
|
||||
EventRegistry.registry = EventRegistry.registry or {}
|
||||
EventRegistry.TriggerEvent = EventRegistry.TriggerEvent or function(self, event, ...)
|
||||
local callbacks = self.registry[event]
|
||||
if callbacks then
|
||||
for _, callback in ipairs(callbacks) do
|
||||
callback(...)
|
||||
end
|
||||
end
|
||||
end
|
||||
EventRegistry.RegisterCallback = EventRegistry.RegisterCallback or function(self, event, callback)
|
||||
self.registry[event] = self.registry[event] or {}
|
||||
table.insert(self.registry[event], callback)
|
||||
end
|
||||
|
||||
C_Service = C_Service or {}
|
||||
C_Service.GetRealmID = function() return 0 end
|
||||
C_Service.GetRealmStage = C_Service.GetRealmStage or function() return 0 end
|
||||
C_Service.GetCustomValue = C_Service.GetCustomValue or function() return 0 end
|
||||
C_Service.IsInGMMode = C_Service.IsInGMMode or IsGMAccount
|
||||
C_Service.IsGMAccount = C_Service.IsGMAccount or IsGMAccount
|
||||
C_Service.IsRenegadeRealm = C_Service.IsRenegadeRealm or function() return false end
|
||||
C_Service.IsHardcoreEnabledOnRealm = C_Service.IsHardcoreEnabledOnRealm or function() return false end
|
||||
|
||||
C_MythicPlus = C_MythicPlus or {}
|
||||
C_MythicPlus.IsMythicPlusActive = C_MythicPlus.IsMythicPlusActive or function() return false end
|
||||
|
||||
C_PvP = C_PvP or {}
|
||||
|
||||
C_LFGList = C_LFGList or {}
|
||||
C_LFGList.IsPremadeGroupFinderEnabled = C_LFGList.IsPremadeGroupFinderEnabled or function() return false end
|
||||
|
||||
if not MicroButtonPulseStop then
|
||||
function MicroButtonPulseStop() end
|
||||
end
|
||||
|
||||
if not EJSuggestFrame_OpenFrame then
|
||||
function EJSuggestFrame_OpenFrame() end
|
||||
end
|
||||
|
||||
if not EJSuggestTab_GetPlayerTierIndex then
|
||||
function EJSuggestTab_GetPlayerTierIndex()
|
||||
if EJ_GetCurrentTier then
|
||||
return EJ_GetCurrentTier()
|
||||
end
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
if not ChatEdit_TryInsertChatLink then
|
||||
function ChatEdit_TryInsertChatLink()
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if not SharedTooltip_SetBackdropStyle then
|
||||
function SharedTooltip_SetBackdropStyle(tooltip)
|
||||
if not tooltip then
|
||||
return
|
||||
end
|
||||
if tooltip.SetBackdropBorderColor and TOOLTIP_DEFAULT_COLOR then
|
||||
tooltip:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
|
||||
end
|
||||
if tooltip.SetBackdropColor and TOOLTIP_DEFAULT_BACKGROUND_COLOR then
|
||||
tooltip:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not HelpPlate_Hide then
|
||||
function HelpPlate_Hide() end
|
||||
end
|
||||
|
||||
if not HelpPlate_Show then
|
||||
function HelpPlate_Show() end
|
||||
end
|
||||
|
||||
-- 3.3.5 has no GetNumClasses/GetClassInfo (added in Cataclysm). Sirus EJ uses
|
||||
-- them to populate the loot class filter dropdown.
|
||||
local MOONWELL_CLASS_INFO = {
|
||||
{LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE.WARRIOR or "Warrior", "WARRIOR", CLASS_ID_WARRIOR},
|
||||
{LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE.PALADIN or "Paladin", "PALADIN", CLASS_ID_PALADIN},
|
||||
{LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE.HUNTER or "Hunter", "HUNTER", CLASS_ID_HUNTER},
|
||||
{LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE.ROGUE or "Rogue", "ROGUE", CLASS_ID_ROGUE},
|
||||
{LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE.PRIEST or "Priest", "PRIEST", CLASS_ID_PRIEST},
|
||||
{LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE.DEATHKNIGHT or "Death Knight", "DEATHKNIGHT", CLASS_ID_DEATHKNIGHT},
|
||||
{LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE.SHAMAN or "Shaman", "SHAMAN", CLASS_ID_SHAMAN},
|
||||
{LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE.MAGE or "Mage", "MAGE", CLASS_ID_MAGE},
|
||||
{LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE.WARLOCK or "Warlock", "WARLOCK", CLASS_ID_WARLOCK},
|
||||
{LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE.DRUID or "Druid", "DRUID", CLASS_ID_DRUID},
|
||||
}
|
||||
|
||||
if not GetNumClasses then
|
||||
function GetNumClasses()
|
||||
return #MOONWELL_CLASS_INFO
|
||||
end
|
||||
end
|
||||
|
||||
if not GetClassInfo then
|
||||
function GetClassInfo(index)
|
||||
local info = MOONWELL_CLASS_INFO[index]
|
||||
if info then
|
||||
return info[1], info[2], info[3]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
CLASS_ID_DEMONHUNTER = CLASS_ID_DEMONHUNTER or 12
|
||||
local function EnsureColorMethods(color)
|
||||
if type(color) ~= "table" then
|
||||
return color
|
||||
end
|
||||
|
||||
color.GetRGB = color.GetRGB or function(self)
|
||||
return self.r or 1, self.g or 1, self.b or 1
|
||||
end
|
||||
color.GetRGBA = color.GetRGBA or function(self)
|
||||
return self.r or 1, self.g or 1, self.b or 1, self.a or 1
|
||||
end
|
||||
color.WrapTextInColorCode = color.WrapTextInColorCode or function(self, text)
|
||||
if self.colorStr then
|
||||
return "|c" .. self.colorStr .. text .. "|r"
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
return color
|
||||
end
|
||||
|
||||
EnsureColorMethods(NORMAL_FONT_COLOR)
|
||||
EnsureColorMethods(HIGHLIGHT_FONT_COLOR)
|
||||
EnsureColorMethods(RED_FONT_COLOR)
|
||||
EnsureColorMethods(GREEN_FONT_COLOR)
|
||||
EnsureColorMethods(DISABLED_FONT_COLOR)
|
||||
LOOTJOURNAL_SOURCE_TOOLTIP_HEAD = LOOTJOURNAL_SOURCE_TOOLTIP_HEAD or "Способ получения:"
|
||||
RETURN_TO_DEFAULT = RETURN_TO_DEFAULT or "Настройки по умолчанию"
|
||||
GUIDEBOOK = GUIDEBOOK or "Путеводитель"
|
||||
INSTANCES = INSTANCES or "Подземелья"
|
||||
RAIDS = RAIDS or "Рейды"
|
||||
LOOT_JOURNAL_ITEM_SETS = LOOT_JOURNAL_ITEM_SETS or "Комплекты"
|
||||
|
||||
-- Format string used by EncounterJournal_SetLootButton to label the loot
|
||||
-- source ("Босс: <name>"). Sirus ships it via locale globals; default to a
|
||||
-- compact form on 3.3.5.
|
||||
BOSS_INFO_STRING = BOSS_INFO_STRING or "Босс: %s"
|
||||
|
||||
ITEM_CLASS_2 = ITEM_CLASS_2 or 2
|
||||
ITEM_CLASS_4 = ITEM_CLASS_4 or 4
|
||||
|
||||
for _, classID in ipairs({0, 2, 4, 5, 7, 9, 11, 12, 13, 14, 15}) do
|
||||
for subclassID = 0, 20 do
|
||||
local key = string.format("ITEM_SUB_CLASS_%d_%d", classID, subclassID)
|
||||
if _G[key] == nil then
|
||||
_G[key] = subclassID
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
LE_ITEM_FILTER_TYPE_HEAD = LE_ITEM_FILTER_TYPE_HEAD or 1
|
||||
LE_ITEM_FILTER_TYPE_NECK = LE_ITEM_FILTER_TYPE_NECK or 2
|
||||
LE_ITEM_FILTER_TYPE_SHOULDER = LE_ITEM_FILTER_TYPE_SHOULDER or 3
|
||||
LE_ITEM_FILTER_TYPE_CLOAK = LE_ITEM_FILTER_TYPE_CLOAK or 16
|
||||
LE_ITEM_FILTER_TYPE_CHEST = LE_ITEM_FILTER_TYPE_CHEST or 5
|
||||
LE_ITEM_FILTER_TYPE_WRIST = LE_ITEM_FILTER_TYPE_WRIST or 9
|
||||
LE_ITEM_FILTER_TYPE_HAND = LE_ITEM_FILTER_TYPE_HAND or 10
|
||||
LE_ITEM_FILTER_TYPE_WAIST = LE_ITEM_FILTER_TYPE_WAIST or 6
|
||||
LE_ITEM_FILTER_TYPE_LEGS = LE_ITEM_FILTER_TYPE_LEGS or 7
|
||||
LE_ITEM_FILTER_TYPE_FEET = LE_ITEM_FILTER_TYPE_FEET or 8
|
||||
LE_ITEM_FILTER_TYPE_MAIN_HAND = LE_ITEM_FILTER_TYPE_MAIN_HAND or 21
|
||||
LE_ITEM_FILTER_TYPE_OFF_HAND = LE_ITEM_FILTER_TYPE_OFF_HAND or 22
|
||||
LE_ITEM_FILTER_TYPE_2HWEAPON = LE_ITEM_FILTER_TYPE_2HWEAPON or 17
|
||||
LE_ITEM_FILTER_TYPE_HWEAPON = LE_ITEM_FILTER_TYPE_HWEAPON or 13
|
||||
LE_ITEM_FILTER_TYPE_RANGED = LE_ITEM_FILTER_TYPE_RANGED or 15
|
||||
LE_ITEM_FILTER_TYPE_FINGER = LE_ITEM_FILTER_TYPE_FINGER or 11
|
||||
LE_ITEM_FILTER_TYPE_TRINKET = LE_ITEM_FILTER_TYPE_TRINKET or 12
|
||||
|
||||
LE_ITEM_QUALITY_COMMON = LE_ITEM_QUALITY_COMMON or 1
|
||||
|
||||
BAG_ITEM_QUALITY_COLORS = BAG_ITEM_QUALITY_COLORS or {}
|
||||
local itemQualityColors = ITEM_QUALITY_COLORS or {}
|
||||
for quality = 0, 7 do
|
||||
local color = BAG_ITEM_QUALITY_COLORS[quality] or itemQualityColors[quality] or NORMAL_FONT_COLOR
|
||||
BAG_ITEM_QUALITY_COLORS[quality] = EnsureColorMethods(color or {})
|
||||
end
|
||||
|
||||
if not SetItemButtonQuality then
|
||||
function SetItemButtonQuality(button, quality)
|
||||
if not button or not button.IconBorder then
|
||||
return
|
||||
end
|
||||
|
||||
local color = quality and BAG_ITEM_QUALITY_COLORS[quality]
|
||||
if color then
|
||||
button.IconBorder:Show()
|
||||
button.IconBorder:SetVertexColor(color.r or 1, color.g or 1, color.b or 1)
|
||||
else
|
||||
button.IconBorder:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Enum = Enum or {}
|
||||
Enum.ItemQuality = Enum.ItemQuality or {
|
||||
Poor = 0,
|
||||
Common = 1,
|
||||
Uncommon = 2,
|
||||
Rare = 3,
|
||||
Epic = 4,
|
||||
Legendary = 5,
|
||||
Artifact = 6,
|
||||
Heirloom = 7,
|
||||
}
|
||||
Enum.ModelType = Enum.ModelType or {
|
||||
M2 = 0,
|
||||
Unit = 1,
|
||||
Creature = 2,
|
||||
Item = 3,
|
||||
ItemSet = 4,
|
||||
Illusion = 5,
|
||||
ItemTransmog = 6,
|
||||
Customization = 7,
|
||||
}
|
||||
Enum.HardcoreDeathReason = Enum.HardcoreDeathReason or {
|
||||
FAILED_DEATH = 1,
|
||||
RESTORE = 2,
|
||||
}
|
||||
local ITEM_CACHE_FIELD = {
|
||||
NAME_ENGB = 1,
|
||||
NAME_RURU = 2,
|
||||
RARITY = 3,
|
||||
ILEVEL = 4,
|
||||
MINLEVEL = 5,
|
||||
TYPE = 6,
|
||||
SUBTYPE = 7,
|
||||
STACKCOUNT = 8,
|
||||
EQUIPLOC = 9,
|
||||
TEXTURE = 10,
|
||||
VENDORPRICE = 11,
|
||||
VENDORPRIC = 11,
|
||||
}
|
||||
local ITEM_ID_FIELD = 0
|
||||
local ITEM_LINK_FIELD = -1
|
||||
|
||||
Enum.ItemCacheField = Enum.ItemCacheField or {}
|
||||
for key, value in pairs(ITEM_CACHE_FIELD) do
|
||||
Enum.ItemCacheField[key] = value
|
||||
end
|
||||
Enum.ItemCacheField.ITEM_ID = ITEM_ID_FIELD
|
||||
|
||||
SHARED_INVTYPE_BY_ID = SHARED_INVTYPE_BY_ID or {
|
||||
[0] = "",
|
||||
[1] = "INVTYPE_HEAD",
|
||||
[2] = "INVTYPE_NECK",
|
||||
[3] = "INVTYPE_SHOULDER",
|
||||
[4] = "INVTYPE_BODY",
|
||||
[5] = "INVTYPE_CHEST",
|
||||
[6] = "INVTYPE_WAIST",
|
||||
[7] = "INVTYPE_LEGS",
|
||||
[8] = "INVTYPE_FEET",
|
||||
[9] = "INVTYPE_WRIST",
|
||||
[10] = "INVTYPE_HAND",
|
||||
[11] = "INVTYPE_FINGER",
|
||||
[12] = "INVTYPE_TRINKET",
|
||||
[13] = "INVTYPE_WEAPON",
|
||||
[14] = "INVTYPE_SHIELD",
|
||||
[15] = "INVTYPE_RANGED",
|
||||
[16] = "INVTYPE_CLOAK",
|
||||
[17] = "INVTYPE_2HWEAPON",
|
||||
[18] = "INVTYPE_BAG",
|
||||
[19] = "INVTYPE_TABARD",
|
||||
[20] = "INVTYPE_ROBE",
|
||||
[21] = "INVTYPE_WEAPONMAINHAND",
|
||||
[22] = "INVTYPE_WEAPONOFFHAND",
|
||||
[23] = "INVTYPE_HOLDABLE",
|
||||
[24] = "INVTYPE_AMMO",
|
||||
[25] = "INVTYPE_THROWN",
|
||||
[26] = "INVTYPE_RANGEDRIGHT",
|
||||
[27] = "INVTYPE_QUIVER",
|
||||
[28] = "INVTYPE_RELIC",
|
||||
}
|
||||
|
||||
C_FactionManager = C_FactionManager or {}
|
||||
C_FactionManager.GetFactionInfoOriginal = C_FactionManager.GetFactionInfoOriginal or function()
|
||||
local faction = UnitFactionGroup and UnitFactionGroup("player")
|
||||
return PLAYER_FACTION_GROUP[faction] or PLAYER_FACTION_GROUP.Alliance
|
||||
end
|
||||
C_FactionManager.RegisterCallback = C_FactionManager.RegisterCallback or function(callback)
|
||||
if callback then
|
||||
callback()
|
||||
end
|
||||
end
|
||||
|
||||
C_Item = C_Item or {}
|
||||
|
||||
local ExistingCItemGetItemInfo = C_Item.GetItemInfo
|
||||
local ExistingCItemRequestServerCache = C_Item.RequestServerCache
|
||||
local ExistingCItemGetItemIDFromString = C_Item.GetItemIDFromString
|
||||
local ITEM_CACHE_CALLBACKS = {}
|
||||
local ITEM_QUALITY_HEX = {
|
||||
[0] = "ff9d9d9d",
|
||||
[1] = "ffffffff",
|
||||
[2] = "ff1eff00",
|
||||
[3] = "ff0070dd",
|
||||
[4] = "ffa335ee",
|
||||
[5] = "ffff8000",
|
||||
[6] = "ffe6cc80",
|
||||
[7] = "ffe6cc80",
|
||||
}
|
||||
local ITEMS_CACHE_INITIALIZED
|
||||
|
||||
local function GetItemIDFromAny(item)
|
||||
if type(item) == "number" then
|
||||
return item
|
||||
end
|
||||
if type(item) == "string" then
|
||||
local itemsCache = _G.ItemsCache
|
||||
if type(itemsCache) == "table" and type(itemsCache[item]) == "table" then
|
||||
return itemsCache[item][ITEM_ID_FIELD]
|
||||
end
|
||||
return tonumber(item:match("item:(%d+)") or item:match("^(%d+)$"))
|
||||
end
|
||||
end
|
||||
|
||||
local function CreateItemCacheLink(itemName, itemID, itemRarity, itemMinLevel)
|
||||
return string.format("|c%s|Hitem:%d:0:0:0:0:0:0:0:%d|h[%s]|h|r",
|
||||
ITEM_QUALITY_HEX[itemRarity] or "ffffffff",
|
||||
itemID or 0,
|
||||
itemMinLevel or 0,
|
||||
itemName or UNKNOWN or ""
|
||||
)
|
||||
end
|
||||
|
||||
local function EnsureItemsCache()
|
||||
if ITEMS_CACHE_INITIALIZED then
|
||||
return _G.ItemsCache
|
||||
end
|
||||
|
||||
local itemsCache = type(_G.ItemsCache) == "table" and _G.ItemsCache or nil
|
||||
local fileIndex = 1
|
||||
local itemCacheTable = _G["ItemsCache" .. fileIndex]
|
||||
|
||||
while type(itemCacheTable) == "table" do
|
||||
if not itemsCache then
|
||||
itemsCache = itemCacheTable
|
||||
_G.ItemsCache = itemsCache
|
||||
elseif itemCacheTable ~= itemsCache then
|
||||
for itemID, itemData in pairs(itemCacheTable) do
|
||||
itemsCache[itemID] = itemData
|
||||
end
|
||||
table.wipe(itemCacheTable)
|
||||
end
|
||||
|
||||
_G["ItemsCache" .. fileIndex] = nil
|
||||
fileIndex = fileIndex + 1
|
||||
itemCacheTable = _G["ItemsCache" .. fileIndex]
|
||||
end
|
||||
|
||||
if itemsCache then
|
||||
local localeIndex = GetLocale and GetLocale() == "ruRU" and ITEM_CACHE_FIELD.NAME_RURU or ITEM_CACHE_FIELD.NAME_ENGB
|
||||
local namedItems = {}
|
||||
|
||||
for itemID, itemData in pairs(itemsCache) do
|
||||
if type(itemID) == "number" and type(itemData) == "table" then
|
||||
itemData[ITEM_ID_FIELD] = itemID
|
||||
itemData[ITEM_LINK_FIELD] = nil
|
||||
|
||||
local itemName = itemData[localeIndex]
|
||||
if itemName and itemName ~= "" then
|
||||
namedItems[itemName] = itemData
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
setmetatable(itemsCache, {__index = namedItems})
|
||||
end
|
||||
|
||||
ITEMS_CACHE_INITIALIZED = true
|
||||
return itemsCache
|
||||
end
|
||||
|
||||
function C_Item.GetItemInfoCache(item)
|
||||
local itemsCache = EnsureItemsCache()
|
||||
if not itemsCache then
|
||||
return
|
||||
end
|
||||
|
||||
local itemID = GetItemIDFromAny(item)
|
||||
if not itemID then
|
||||
return
|
||||
end
|
||||
|
||||
local cacheData = itemsCache[itemID]
|
||||
if type(cacheData) ~= "table" then
|
||||
return
|
||||
end
|
||||
|
||||
local localeIndex = GetLocale and GetLocale() == "ruRU" and ITEM_CACHE_FIELD.NAME_RURU or ITEM_CACHE_FIELD.NAME_ENGB
|
||||
local itemName = cacheData[localeIndex] or cacheData[ITEM_CACHE_FIELD.NAME_ENGB]
|
||||
if not itemName or itemName == "" then
|
||||
return
|
||||
end
|
||||
|
||||
local itemRarity = cacheData[ITEM_CACHE_FIELD.RARITY] or LE_ITEM_QUALITY_COMMON
|
||||
local itemMinLevel = cacheData[ITEM_CACHE_FIELD.MINLEVEL] or 0
|
||||
local classID = cacheData[ITEM_CACHE_FIELD.TYPE]
|
||||
local subclassID = cacheData[ITEM_CACHE_FIELD.SUBTYPE]
|
||||
local equipLocID = cacheData[ITEM_CACHE_FIELD.EQUIPLOC] or 0
|
||||
|
||||
if not cacheData[ITEM_LINK_FIELD] then
|
||||
cacheData[ITEM_LINK_FIELD] = CreateItemCacheLink(itemName, itemID, itemRarity, itemMinLevel)
|
||||
end
|
||||
|
||||
return itemName,
|
||||
cacheData[ITEM_LINK_FIELD],
|
||||
itemRarity,
|
||||
cacheData[ITEM_CACHE_FIELD.ILEVEL],
|
||||
itemMinLevel,
|
||||
_G["ITEM_CLASS_" .. tostring(classID)] or classID,
|
||||
_G["ITEM_SUB_CLASS_" .. tostring(classID) .. "_" .. tostring(subclassID)] or subclassID,
|
||||
cacheData[ITEM_CACHE_FIELD.STACKCOUNT],
|
||||
SHARED_INVTYPE_BY_ID[equipLocID],
|
||||
"Interface\\Icons\\" .. tostring(cacheData[ITEM_CACHE_FIELD.TEXTURE] or "INV_Misc_QuestionMark"),
|
||||
cacheData[ITEM_CACHE_FIELD.VENDORPRICE],
|
||||
itemID,
|
||||
classID,
|
||||
subclassID,
|
||||
equipLocID
|
||||
end
|
||||
|
||||
local function GetItemInfoCompat(item)
|
||||
local name, link, quality, itemLevel, reqLevel, itemType, itemSubType, maxStack, equipSlot, icon, vendorPrice = GetItemInfo(item)
|
||||
if name then
|
||||
return name, link, quality, itemLevel, reqLevel, itemType, itemSubType, maxStack, equipSlot, icon, vendorPrice
|
||||
end
|
||||
|
||||
return C_Item.GetItemInfoCache(item)
|
||||
end
|
||||
|
||||
local function FireItemCacheCallbacks(itemID)
|
||||
local callbacks = ITEM_CACHE_CALLBACKS[itemID]
|
||||
if not callbacks then
|
||||
return
|
||||
end
|
||||
|
||||
local name, link, quality, itemLevel, reqLevel, itemType, itemSubType, maxStack, equipSlot, icon, vendorPrice = GetItemInfoCompat(itemID)
|
||||
if not name then
|
||||
return
|
||||
end
|
||||
|
||||
ITEM_CACHE_CALLBACKS[itemID] = nil
|
||||
for _, callback in ipairs(callbacks) do
|
||||
callback(itemID, name, link, quality, itemLevel, reqLevel, itemType, itemSubType, maxStack, equipSlot, icon, vendorPrice)
|
||||
end
|
||||
end
|
||||
|
||||
local function QueueItemCacheCallback(item, callback)
|
||||
if not callback then
|
||||
return
|
||||
end
|
||||
|
||||
local itemID = tonumber(item)
|
||||
if not itemID and type(item) == "string" then
|
||||
itemID = tonumber(item:match("item:(%d+)") or item:match("^(%d+)$"))
|
||||
end
|
||||
if not itemID then
|
||||
return
|
||||
end
|
||||
|
||||
ITEM_CACHE_CALLBACKS[itemID] = ITEM_CACHE_CALLBACKS[itemID] or {}
|
||||
table.insert(ITEM_CACHE_CALLBACKS[itemID], callback)
|
||||
end
|
||||
|
||||
local itemCacheFrame
|
||||
local function EnsureItemCacheFrame()
|
||||
if itemCacheFrame or not CreateFrame then
|
||||
return
|
||||
end
|
||||
|
||||
itemCacheFrame = CreateFrame("Frame")
|
||||
itemCacheFrame:RegisterEvent("GET_ITEM_INFO_RECEIVED")
|
||||
itemCacheFrame:SetScript("OnEvent", function(_, _, itemID)
|
||||
if itemID then
|
||||
FireItemCacheCallbacks(itemID)
|
||||
else
|
||||
for queuedItemID in pairs(ITEM_CACHE_CALLBACKS) do
|
||||
FireItemCacheCallbacks(queuedItemID)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
C_Item.GetItemInfo = function(item, skipClientCache, callback, ...)
|
||||
local name, link, quality, itemLevel, reqLevel, itemType, itemSubType, maxStack, equipSlot, icon, vendorPrice
|
||||
if ExistingCItemGetItemInfo then
|
||||
name, link, quality, itemLevel, reqLevel, itemType, itemSubType, maxStack, equipSlot, icon, vendorPrice = ExistingCItemGetItemInfo(item, skipClientCache, nil, ...)
|
||||
end
|
||||
if not name then
|
||||
name, link, quality, itemLevel, reqLevel, itemType, itemSubType, maxStack, equipSlot, icon, vendorPrice = GetItemInfoCompat(item)
|
||||
end
|
||||
|
||||
if callback then
|
||||
if name then
|
||||
callback(GetItemIDFromAny(item) or item, name, link, quality, itemLevel, reqLevel, itemType, itemSubType, maxStack, equipSlot, icon, vendorPrice)
|
||||
else
|
||||
QueueItemCacheCallback(item, callback)
|
||||
EnsureItemCacheFrame()
|
||||
end
|
||||
end
|
||||
return name, link, quality, itemLevel, reqLevel, itemType, itemSubType, maxStack, equipSlot, icon, vendorPrice
|
||||
end
|
||||
|
||||
C_Item.RequestServerCache = function(item, callback)
|
||||
local name, link, quality, itemLevel, reqLevel, itemType, itemSubType, maxStack, equipSlot, icon, vendorPrice = GetItemInfoCompat(item)
|
||||
if callback then
|
||||
if name then
|
||||
callback(GetItemIDFromAny(item) or item, name, link, quality, itemLevel, reqLevel, itemType, itemSubType, maxStack, equipSlot, icon, vendorPrice)
|
||||
elseif ExistingCItemRequestServerCache then
|
||||
return ExistingCItemRequestServerCache(item, callback)
|
||||
else
|
||||
QueueItemCacheCallback(item, callback)
|
||||
EnsureItemCacheFrame()
|
||||
end
|
||||
end
|
||||
return name, link, quality, itemLevel, reqLevel, itemType, itemSubType, maxStack, equipSlot, icon, vendorPrice
|
||||
end
|
||||
C_Item.GetItemIDFromString = function(item)
|
||||
local itemID = ExistingCItemGetItemIDFromString and ExistingCItemGetItemIDFromString(item)
|
||||
if itemID then
|
||||
return itemID
|
||||
end
|
||||
|
||||
EnsureItemsCache()
|
||||
return GetItemIDFromAny(item)
|
||||
end
|
||||
|
||||
EnsureItemsCache()
|
||||
|
||||
if not Mixin then
|
||||
function Mixin(object, ...)
|
||||
for i = 1, select("#", ...) do
|
||||
local mixin = select(i, ...)
|
||||
for key, value in pairs(mixin) do
|
||||
object[key] = value
|
||||
end
|
||||
end
|
||||
return object
|
||||
end
|
||||
end
|
||||
|
||||
if not CreateFromMixins then
|
||||
function CreateFromMixins(...)
|
||||
return Mixin({}, ...)
|
||||
end
|
||||
end
|
||||
|
||||
if not CreateAndInitFromMixin then
|
||||
function CreateAndInitFromMixin(mixin, ...)
|
||||
local object = CreateFromMixins(mixin)
|
||||
object:Init(...)
|
||||
return object
|
||||
end
|
||||
end
|
||||
|
||||
if not InlineHyperlinkFrame_SimpleHTMLAsFontString_OnLoad then
|
||||
function InlineHyperlinkFrame_SimpleHTMLAsFontString_OnLoad(self)
|
||||
if PKBT_SimpleHTMLAsFontStringMixin then
|
||||
Mixin(self, PKBT_SimpleHTMLAsFontStringMixin)
|
||||
if self.OnLoadSimpleHTMLAsFontString then
|
||||
self:OnLoadSimpleHTMLAsFontString()
|
||||
end
|
||||
elseif self.GetRegions and not self:GetRegions() then
|
||||
local meta = getmetatable(self)
|
||||
local methods = meta and meta.__index
|
||||
if methods and methods.SetText then
|
||||
methods.SetText(self, "")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function InstallTextureAtlasShim()
|
||||
if not CreateFrame then
|
||||
return
|
||||
end
|
||||
|
||||
local texture = CreateFrame("Frame"):CreateTexture(nil, "ARTWORK")
|
||||
local meta = getmetatable(texture)
|
||||
local methods = meta and meta.__index
|
||||
|
||||
local atlasToTexture = {
|
||||
["adventureguide-pane-large"] = {335, 337, 0.001953, 0.656250, 0.320312, 0.978516, "Interface\\EncounterJournal\\AdventureGuide"},
|
||||
["adventureguide-pane-small"] = {344, 161, 0.001953, 0.673828, 0.001953, 0.316406, "Interface\\EncounterJournal\\AdventureGuide"},
|
||||
["adventureguide-ring"] = {94, 95, 0.775391, 0.958984, 0.001953, 0.187500, "Interface\\EncounterJournal\\AdventureGuide"},
|
||||
["adventureguide-rewardring"] = {48, 48, 0.677734, 0.771484, 0.001953, 0.095703, "Interface\\EncounterJournal\\AdventureGuide"},
|
||||
["adventureguide-redx"] = {35, 34, 0.677734, 0.746094, 0.175781, 0.242188, "Interface\\EncounterJournal\\AdventureGuide"},
|
||||
|
||||
["UI-EJ-Classic"] = {786, 425, 0.000977, 0.768555, 0.417969, 0.833008, "Interface\\EncounterJournal\\DungeonJournalTierBackgrounds1"},
|
||||
["UI-EJ-BurningCrusade"] = {786, 425, 0.000977, 0.768555, 0.000977, 0.416016, "Interface\\EncounterJournal\\DungeonJournalTierBackgrounds1"},
|
||||
["UI-EJ-WrathoftheLichKing"] = {786, 425, 0.000977, 0.768555, 0.417969, 0.833008, "Interface\\EncounterJournal\\DungeonJournalTierBackgrounds2"},
|
||||
["UI-EJ-Cataclysm"] = {786, 425, 0.000977, 0.768555, 0.000977, 0.416016, "Interface\\EncounterJournal\\DungeonJournalTierBackgrounds2"},
|
||||
["UI-EJ-MistsofPandaria"] = {786, 425, 0.000977, 0.768555, 0.000977, 0.416016, "Interface\\EncounterJournal\\DungeonJournalTierBackgrounds3"},
|
||||
["UI-EJ-WarlordsofDraenor"] = {786, 425, 0.000977, 0.768555, 0.417969, 0.833008, "Interface\\EncounterJournal\\DungeonJournalTierBackgrounds3"},
|
||||
["UI-EJ-BattleforAzeroth"] = {786, 425, 0.000977, 0.768555, 0.000977, 0.416016, "Interface\\EncounterJournal\\DungeonJournalTierBackgrounds4"},
|
||||
["UI-EJ-Legion"] = {786, 425, 0.000977, 0.768555, 0.417969, 0.833008, "Interface\\EncounterJournal\\DungeonJournalTierBackgrounds4"},
|
||||
["UI-EJ-Dragonflight"] = {786, 425, 0.000977, 0.768555, 0.000977, 0.416016, "Interface\\EncounterJournal\\DungeonJournalTierBackgrounds5"},
|
||||
["UI-EJ-Shadowlands"] = {786, 425, 0.000977, 0.768555, 0.417969, 0.833008, "Interface\\EncounterJournal\\DungeonJournalTierBackgrounds5"},
|
||||
["UI-EJ-TheWarWithin"] = {786, 425, 0.000977, 0.768555, 0.000977, 0.416016, "Interface\\EncounterJournal\\DungeonJournalTierBackgrounds6"},
|
||||
["UI-EJ-Midnight"] = {786, 425, 0.000977, 0.768555, 0.417969, 0.833008, "Interface\\EncounterJournal\\DungeonJournalTierBackgrounds6"},
|
||||
}
|
||||
|
||||
if type(methods) == "table" and not methods.SetAtlas then
|
||||
methods.SetAtlas = function(self, atlas, useAtlasSize)
|
||||
local atlasInfo = atlasToTexture[atlas]
|
||||
if atlasInfo then
|
||||
self:SetTexture(atlasInfo[7])
|
||||
self:SetTexCoord(atlasInfo[3], atlasInfo[4], atlasInfo[5], atlasInfo[6])
|
||||
if useAtlasSize then
|
||||
self:SetSize(atlasInfo[1], atlasInfo[2])
|
||||
end
|
||||
elseif SetAtlasTexture then
|
||||
local ok = pcall(SetAtlasTexture, self, atlas)
|
||||
if not ok then
|
||||
self:SetTexture(nil)
|
||||
end
|
||||
else
|
||||
self:SetTexture(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
InstallTextureAtlasShim()
|
||||
|
||||
local function InstallFrameMethodShims()
|
||||
if not CreateFrame then
|
||||
return
|
||||
end
|
||||
|
||||
local frame = CreateFrame("Frame")
|
||||
local meta = getmetatable(frame)
|
||||
local methods = meta and meta.__index
|
||||
if type(methods) ~= "table" or methods.SetParentArray then
|
||||
return
|
||||
end
|
||||
|
||||
methods.SetParentArray = function(self, arrayName)
|
||||
local parent = self:GetParent()
|
||||
if not parent or not arrayName then
|
||||
return
|
||||
end
|
||||
|
||||
parent[arrayName] = parent[arrayName] or {}
|
||||
table.insert(parent[arrayName], self)
|
||||
end
|
||||
end
|
||||
|
||||
InstallFrameMethodShims()
|
||||
|
||||
if not EncounterJournal_SetSearchPreviewSelection then
|
||||
function EncounterJournal_SetSearchPreviewSelection(selectedIndex)
|
||||
if not EncounterJournal or not EncounterJournal.searchBox then
|
||||
return
|
||||
end
|
||||
|
||||
local searchBox = EncounterJournal.searchBox
|
||||
searchBox.selectedIndex = selectedIndex or 1
|
||||
|
||||
if searchBox.searchPreview then
|
||||
for index, button in ipairs(searchBox.searchPreview) do
|
||||
if button.selectedTexture then
|
||||
if index == searchBox.selectedIndex then
|
||||
button.selectedTexture:Show()
|
||||
else
|
||||
button.selectedTexture:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if searchBox.showAllResults and searchBox.showAllResults.selectedTexture then
|
||||
searchBox.showAllResults.selectedTexture:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
-- Optional AIO bridge: lets the server push fresh Encounter Journal data at runtime.
|
||||
-- The static fallback is loaded from Generated_EncounterJournal.lua, so the journal works
|
||||
-- without ever calling ApplyEncounterJournalData.
|
||||
|
||||
local function ApplyEncounterJournalData(data)
|
||||
if type(data) ~= "table" then
|
||||
return
|
||||
end
|
||||
|
||||
local function NormalizeTierInstances(tiers, tierInstances)
|
||||
if type(tiers) ~= "table" or type(tierInstances) ~= "table" then
|
||||
return tierInstances
|
||||
end
|
||||
|
||||
local tierIDs = {}
|
||||
for _, tierInfo in ipairs(tiers) do
|
||||
if type(tierInfo) == "table" then
|
||||
tierIDs[tierInfo[1]] = true
|
||||
end
|
||||
end
|
||||
|
||||
local hasTierKey = false
|
||||
for tierID in pairs(tierIDs) do
|
||||
if tierInstances[tierID] then
|
||||
hasTierKey = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not hasTierKey then
|
||||
return tierInstances
|
||||
end
|
||||
|
||||
local normalized = {}
|
||||
for tierID, instanceList in pairs(tierInstances) do
|
||||
if tierIDs[tierID] and type(instanceList) == "table" then
|
||||
for _, instanceID in ipairs(instanceList) do
|
||||
normalized[instanceID] = normalized[instanceID] or {}
|
||||
table.insert(normalized[instanceID], tierID)
|
||||
end
|
||||
else
|
||||
normalized[tierID] = instanceList
|
||||
end
|
||||
end
|
||||
|
||||
return normalized
|
||||
end
|
||||
|
||||
local tiers = data.JOURNALTIER or data.tiers or JOURNALTIER
|
||||
local tierInstances = data.JOURNALTIERXINSTANCE or data.tierInstances or JOURNALTIERXINSTANCE
|
||||
|
||||
JOURNALINSTANCE = data.JOURNALINSTANCE or data.instances or JOURNALINSTANCE
|
||||
JOURNALENCOUNTER = data.JOURNALENCOUNTER or data.encounters or JOURNALENCOUNTER
|
||||
JOURNALENCOUNTERCREATURE = data.JOURNALENCOUNTERCREATURE or data.creatures or JOURNALENCOUNTERCREATURE
|
||||
JOURNALENCOUNTERITEM = data.JOURNALENCOUNTERITEM or data.items or JOURNALENCOUNTERITEM
|
||||
JOURNALENCOUNTERSECTION = data.JOURNALENCOUNTERSECTION or data.sections or JOURNALENCOUNTERSECTION
|
||||
JOURNALTIER = tiers
|
||||
JOURNALTIERXINSTANCE = NormalizeTierInstances(tiers, tierInstances)
|
||||
JOURNAACTUALRAIDS = data.JOURNAACTUALRAIDS or data.actualRaids or JOURNAACTUALRAIDS
|
||||
|
||||
if C_EncounterJournal and C_EncounterJournal.ReloadData then
|
||||
C_EncounterJournal.ReloadData()
|
||||
end
|
||||
end
|
||||
|
||||
MoonWell_ApplyEncounterJournalData = ApplyEncounterJournalData
|
||||
|
||||
local staticData = MoonWellEncounterJournalData or EncounterJournalData
|
||||
if staticData then
|
||||
ApplyEncounterJournalData(staticData)
|
||||
end
|
||||
|
||||
if AIO then
|
||||
local Handler = AIO.AddHandlers("MoonWellEncounterJournal", {})
|
||||
|
||||
function Handler.SetData(_, data)
|
||||
ApplyEncounterJournalData(data)
|
||||
end
|
||||
end
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
-- Hide the Sirus Encounter Journal panels we don't ship: suggested content,
|
||||
-- LootJournal/ItemBrowser, search box, retail-era top tabs. Dungeons + Raids stay.
|
||||
-- The shipped Sirus tab layout assumes all four content tabs are visible; this
|
||||
-- skin repositions the two tabs we keep.
|
||||
|
||||
local function HideFrame(frame)
|
||||
if frame then
|
||||
frame:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function HideTabKeepEnabled(tab)
|
||||
if not tab then
|
||||
return
|
||||
end
|
||||
tab:Hide()
|
||||
-- Keep the tab enabled so Sirus EJ_OnShow does not fall back to the
|
||||
-- suggest-tier code path (EJSuggestTab_GetPlayerTierIndex etc).
|
||||
end
|
||||
|
||||
local function ConfigureTierTab(tab, text)
|
||||
if not tab then
|
||||
return
|
||||
end
|
||||
|
||||
tab:SetText(text or "")
|
||||
|
||||
if tab.SetNormalFontObject and GameFontNormal then
|
||||
tab:SetNormalFontObject(GameFontNormal)
|
||||
end
|
||||
if tab.SetHighlightFontObject and GameFontHighlight then
|
||||
tab:SetHighlightFontObject(GameFontHighlight)
|
||||
end
|
||||
if tab.SetDisabledFontObject and GameFontNormal then
|
||||
tab:SetDisabledFontObject(GameFontNormal)
|
||||
end
|
||||
|
||||
local fontString = tab:GetFontString()
|
||||
if fontString then
|
||||
fontString:SetText(text or "")
|
||||
fontString:ClearAllPoints()
|
||||
fontString:SetPoint("CENTER", tab, "CENTER", 0, -3)
|
||||
if fontString.SetDrawLayer then
|
||||
fontString:SetDrawLayer("OVERLAY", 7)
|
||||
end
|
||||
end
|
||||
|
||||
tab:SetWidth(math.max((tab:GetTextWidth() or 0) + 28, 92))
|
||||
end
|
||||
|
||||
local function PositionVisibleTierTabs(instanceSelect)
|
||||
local guidebookTab = instanceSelect and instanceSelect.guidebookTab
|
||||
local dungeonsTab = instanceSelect and instanceSelect.dungeonsTab
|
||||
local raidsTab = instanceSelect and instanceSelect.raidsTab
|
||||
if not dungeonsTab or not raidsTab then
|
||||
return
|
||||
end
|
||||
|
||||
ConfigureTierTab(guidebookTab, GUIDEBOOK or "Guidebook")
|
||||
ConfigureTierTab(dungeonsTab, INSTANCES or "Instances")
|
||||
ConfigureTierTab(raidsTab, RAIDS or "Raids")
|
||||
|
||||
dungeonsTab:ClearAllPoints()
|
||||
dungeonsTab:SetPoint("BOTTOMLEFT", instanceSelect, "TOPLEFT", 150, -45)
|
||||
|
||||
raidsTab:ClearAllPoints()
|
||||
raidsTab:SetPoint("BOTTOMLEFT", dungeonsTab, "BOTTOMRIGHT", 22, 0)
|
||||
end
|
||||
|
||||
local function GetVisibleSelectedTabID(instanceSelect)
|
||||
if not instanceSelect then
|
||||
return
|
||||
end
|
||||
|
||||
local selectedTab = instanceSelect.selectedTab
|
||||
local dungeonsTab = instanceSelect.dungeonsTab
|
||||
local raidsTab = instanceSelect.raidsTab
|
||||
|
||||
if raidsTab and selectedTab == raidsTab.id then
|
||||
return selectedTab
|
||||
end
|
||||
|
||||
if dungeonsTab then
|
||||
dungeonsTab.id = dungeonsTab.id or dungeonsTab:GetID()
|
||||
return dungeonsTab.id
|
||||
end
|
||||
end
|
||||
|
||||
local function SelectVisibleInstanceTab(journal)
|
||||
local instanceSelect = journal and journal.instanceSelect
|
||||
if not instanceSelect then
|
||||
return
|
||||
end
|
||||
|
||||
local selectedTab = GetVisibleSelectedTabID(instanceSelect)
|
||||
if selectedTab then
|
||||
instanceSelect.selectedTab = selectedTab
|
||||
end
|
||||
|
||||
if journal:IsShown()
|
||||
and instanceSelect:IsShown()
|
||||
and (not journal.encounter or not journal.encounter:IsShown())
|
||||
and selectedTab
|
||||
and EJ_ContentTab_Select
|
||||
then
|
||||
EJ_ContentTab_Select(selectedTab)
|
||||
end
|
||||
end
|
||||
|
||||
local function ApplyDungeonAndRaidOnlySkin()
|
||||
local journal = EncounterJournal
|
||||
if not journal then
|
||||
return
|
||||
end
|
||||
|
||||
-- Sirus EJ_OnLoad sets numTabs at the very end; if anything earlier in
|
||||
-- OnLoad bails out, PanelTemplates_UpdateTabs blows up later with a nil
|
||||
-- for-limit when OnShow calls PanelTemplates_SetTab. Guard it.
|
||||
if journal.numTabs == nil then
|
||||
journal.numTabs = 4
|
||||
end
|
||||
|
||||
HideFrame(journal.searchBox)
|
||||
HideFrame(journal.searchResults)
|
||||
HideFrame(journal.suggestFrame)
|
||||
HideFrame(journal.LootJournal)
|
||||
HideFrame(journal.LootJournalItems)
|
||||
HideFrame(journal.TutorialButton)
|
||||
|
||||
for index = 1, 4 do
|
||||
HideFrame(_G["EncounterJournalTab" .. index])
|
||||
end
|
||||
|
||||
local instanceSelect = journal.instanceSelect
|
||||
if instanceSelect then
|
||||
HideTabKeepEnabled(instanceSelect.suggestTab)
|
||||
HideTabKeepEnabled(instanceSelect.LootJournalTab)
|
||||
|
||||
-- Sirus EJ_OnShow forwards instanceSelect.selectedTab into
|
||||
-- EJ_ContentTab_Select; if it's nil the tab loop matches nothing and
|
||||
-- selectedTab.selectedGlow blows up. Default to the dungeons tab.
|
||||
local dungeonsTab = instanceSelect.dungeonsTab
|
||||
if dungeonsTab then
|
||||
dungeonsTab.id = dungeonsTab.id or dungeonsTab:GetID()
|
||||
end
|
||||
|
||||
PositionVisibleTierTabs(instanceSelect)
|
||||
SelectVisibleInstanceTab(journal)
|
||||
end
|
||||
end
|
||||
|
||||
MoonWell_EncounterJournalApplyDungeonMode = ApplyDungeonAndRaidOnlySkin
|
||||
|
||||
if EncounterJournal then
|
||||
ApplyDungeonAndRaidOnlySkin()
|
||||
EncounterJournal:HookScript("OnShow", ApplyDungeonAndRaidOnlySkin)
|
||||
end
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Font name="QuestFont_Super_Huge" font="Fonts\MORPHEUS.ttf" virtual="true">
|
||||
<FontHeight val="24"/>
|
||||
<Color r="1" g=".82" b="0"/>
|
||||
</Font>
|
||||
<Font name="QuestFont_Super_Huge2" font="Fonts\MORPHEUS.ttf" virtual="true">
|
||||
<FontHeight val="26"/>
|
||||
<Shadow>
|
||||
<Offset x="2" y="-2"/>
|
||||
<Color r="0" g="0" b="0"/>
|
||||
</Shadow>
|
||||
</Font>
|
||||
<Font name="GameFontNormalMed1" inherits="GameFontNormal" virtual="true">
|
||||
<FontHeight val="12"/>
|
||||
<Shadow>
|
||||
<Offset x="1" y="-1"/>
|
||||
<Color r="0" g="0" b="0"/>
|
||||
</Shadow>
|
||||
<Color r="1" g=".82" b="0"/>
|
||||
</Font>
|
||||
<Font name="QuestFont22" font="Fonts\MORPHEUS.ttf" virtual="true">
|
||||
<FontHeight val="22"/>
|
||||
<Color r="1" g=".82" b="0"/>
|
||||
</Font>
|
||||
<Font name="QuestFont_Enormous" font="Fonts\MORPHEUS.ttf" virtual="true">
|
||||
<FontHeight val="30"/>
|
||||
<Color r="1" g=".82" b="0"/>
|
||||
</Font>
|
||||
<Font name="DestinyFontHuge" font="Fonts\MORPHEUS.ttf" virtual="true">
|
||||
<FontHeight val="32"/>
|
||||
<Color r="1" g=".82" b="0"/>
|
||||
</Font>
|
||||
</Ui>
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
-- Minimal Frame metatable extensions used by Sirus EJ. Replaces Sirus
|
||||
-- SharedExtendedMethods.lua, which clobbers SetUnit/PlayerModel methods with
|
||||
-- retail-only API (ResetAllSpells, SetSpell, SetUnitRaw, GetDisplayIDs,
|
||||
-- C_CustomizationsCollection.*) and breaks TabardFrame/DressUpFrame on 3.3.5.
|
||||
|
||||
local function getMeta(objType)
|
||||
local frame = CreateFrame(objType)
|
||||
frame:Hide()
|
||||
return getmetatable(frame).__index
|
||||
end
|
||||
|
||||
local function RegisterCustomEventNoop() end
|
||||
local function UnregisterCustomEventNoop() end
|
||||
|
||||
local function SetShown(self, shown)
|
||||
if shown then
|
||||
self:Show()
|
||||
else
|
||||
self:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function SetParentArray(self, arrayName)
|
||||
local parent = self:GetParent()
|
||||
if not parent or not arrayName then
|
||||
return
|
||||
end
|
||||
parent[arrayName] = parent[arrayName] or {}
|
||||
table.insert(parent[arrayName], self)
|
||||
end
|
||||
|
||||
local function SetEnabled(self, enabled)
|
||||
if enabled then
|
||||
self:Enable()
|
||||
else
|
||||
self:Disable()
|
||||
end
|
||||
end
|
||||
|
||||
-- Sirus EJ calls Frame/Button:IsMouseOverEx() (Cataclysm+ retail-only).
|
||||
-- Map to the 3.3.5 IsMouseOver().
|
||||
local function IsMouseOverEx(self)
|
||||
return self:IsMouseOver()
|
||||
end
|
||||
|
||||
local metas = {
|
||||
getMeta("Frame"),
|
||||
getMeta("Button"),
|
||||
getMeta("CheckButton"),
|
||||
getMeta("EditBox"),
|
||||
getMeta("Slider"),
|
||||
getMeta("StatusBar"),
|
||||
getMeta("ScrollFrame"),
|
||||
getMeta("MessageFrame"),
|
||||
getMeta("ScrollingMessageFrame"),
|
||||
}
|
||||
|
||||
for _, meta in ipairs(metas) do
|
||||
if type(meta) == "table" then
|
||||
if not meta.RegisterCustomEvent then
|
||||
meta.RegisterCustomEvent = RegisterCustomEventNoop
|
||||
end
|
||||
if not meta.UnregisterCustomEvent then
|
||||
meta.UnregisterCustomEvent = UnregisterCustomEventNoop
|
||||
end
|
||||
if not meta.SetShown then
|
||||
meta.SetShown = SetShown
|
||||
end
|
||||
if not meta.SetParentArray then
|
||||
meta.SetParentArray = SetParentArray
|
||||
end
|
||||
if not meta.SetEnabled then
|
||||
meta.SetEnabled = SetEnabled
|
||||
end
|
||||
if not meta.IsMouseOverEx then
|
||||
meta.IsMouseOverEx = IsMouseOverEx
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Sirus EJ calls FontString:IsTruncated() (Cataclysm+) to detect overflow for
|
||||
-- header tooltips. Always return false on 3.3.5 — we just don't show the
|
||||
-- "expand" tooltip.
|
||||
do
|
||||
local frame = CreateFrame("Frame")
|
||||
local fs = frame:CreateFontString()
|
||||
local meta = getmetatable(fs)
|
||||
local methods = meta and meta.__index
|
||||
if type(methods) == "table" and not methods.IsTruncated then
|
||||
methods.IsTruncated = function() return false end
|
||||
end
|
||||
end
|
||||
|
||||
-- Sirus EJ description fields are SimpleHTML frames; the EJ logic does
|
||||
-- `self.description:GetText()` (Cataclysm+ on SimpleHTML). 3.3.5 SimpleHTML
|
||||
-- only has SetText, no GetText. Hook SetText to remember the last text and
|
||||
-- expose GetText that returns it.
|
||||
do
|
||||
local ok, simple = pcall(CreateFrame, "SimpleHTML")
|
||||
if ok and simple then
|
||||
local meta = getmetatable(simple)
|
||||
local methods = meta and meta.__index
|
||||
if type(methods) == "table" then
|
||||
local origSetText = methods.SetText
|
||||
if origSetText and not methods.GetText then
|
||||
methods.SetText = function(self, text, ...)
|
||||
self.__moonwellText = text
|
||||
return origSetText(self, text, ...)
|
||||
end
|
||||
methods.GetText = function(self)
|
||||
return self.__moonwellText
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Sirus EJ calls Texture:SetPortrait(displayID) for boss/creature portraits.
|
||||
-- 3.3.5 has no SetPortraitTextureFromCreatureDisplayID equivalent, so leave
|
||||
-- the texture blank — the static UI-EJ-BOSS-* icon set elsewhere is enough.
|
||||
do
|
||||
local portraitByDisplayInfo = {}
|
||||
local data = MoonWellEncounterJournalData
|
||||
if data and data.creatures then
|
||||
for _, creatureList in pairs(data.creatures) do
|
||||
for _, creature in ipairs(creatureList) do
|
||||
local displayInfo = creature[3]
|
||||
local icon = creature[4]
|
||||
if displayInfo and displayInfo > 0 and icon and icon ~= "" then
|
||||
portraitByDisplayInfo[displayInfo] = icon
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Ogom is a nested encounter variant and has no top-level portrait row.
|
||||
portraitByDisplayInfo[6709] = [[Interface\EncounterJournal\UI-EJ-BOSS-Jammalan the Prophet]]
|
||||
MoonWellEncounterPortraitByDisplayInfo = portraitByDisplayInfo
|
||||
|
||||
local frame = CreateFrame("Frame")
|
||||
local texture = frame:CreateTexture()
|
||||
local meta = getmetatable(texture)
|
||||
local methods = meta and meta.__index
|
||||
if type(methods) == "table" and not methods.SetPortrait then
|
||||
methods.SetPortrait = function(self, displayInfo)
|
||||
self:SetTexture(portraitByDisplayInfo[displayInfo] or QUESTION_MARK_ICON)
|
||||
self:SetTexCoord(0, 1, 0, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
-- Force C_EncounterJournal to copy the freshly loaded JOURNAL* globals into its
|
||||
-- internal locals. Without this the EJ data API returns nil and the instance
|
||||
-- list comes up empty.
|
||||
|
||||
if C_EncounterJournal and C_EncounterJournal.ReloadData then
|
||||
C_EncounterJournal.ReloadData()
|
||||
end
|
||||
|
||||
-- EJ_GetLootFilter returns nil until a class filter is set; Sirus
|
||||
-- EncounterJournal_UpdateFilterString does `if classID > 0 then`, which blows
|
||||
-- up when classID is nil. Coerce to 0.
|
||||
if EJ_GetLootFilter then
|
||||
local _GetLootFilter = EJ_GetLootFilter
|
||||
function EJ_GetLootFilter()
|
||||
return _GetLootFilter() or 0
|
||||
end
|
||||
end
|
||||
|
||||
-- Sirus C_AdventureJournal sometimes fails to fully initialize on 3.3.5
|
||||
-- because PRIVATE.Initialize() inside Utils/C_AdventureJournal.lua hits an
|
||||
-- unsupported API mid-way and aborts before `C_AdventureJournal = {}` is
|
||||
-- reached. Provide a no-op stub so suggestFrame OnShow doesn't blow up.
|
||||
C_AdventureJournal = C_AdventureJournal or {}
|
||||
local function noop() end
|
||||
C_AdventureJournal.UpdateSuggestions = C_AdventureJournal.UpdateSuggestions or noop
|
||||
C_AdventureJournal.GetPrimaryOffset = C_AdventureJournal.GetPrimaryOffset or function() return 0 end
|
||||
C_AdventureJournal.SetPrimaryOffset = C_AdventureJournal.SetPrimaryOffset or noop
|
||||
C_AdventureJournal.GetNumAvailableSuggestions = C_AdventureJournal.GetNumAvailableSuggestions or function() return 0 end
|
||||
C_AdventureJournal.GetSuggestion = C_AdventureJournal.GetSuggestion or function() end
|
||||
C_AdventureJournal.CanBeShown = C_AdventureJournal.CanBeShown or function() return true end
|
||||
C_AdventureJournal.ReloadData = C_AdventureJournal.ReloadData or noop
|
||||
C_AdventureJournal.ActivateEntry = C_AdventureJournal.ActivateEntry or noop
|
||||
C_AdventureJournal.GetSuggestions = C_AdventureJournal.GetSuggestions or function(out)
|
||||
if type(out) == "table" then
|
||||
for k in pairs(out) do out[k] = nil end
|
||||
end
|
||||
return out or {}
|
||||
end
|
||||
C_AdventureJournal.GetReward = C_AdventureJournal.GetReward or function() end
|
||||
C_AdventureJournal.GetPrimaryReward = C_AdventureJournal.GetPrimaryReward or function() end
|
||||
C_AdventureJournal.GetSecondaryReward = C_AdventureJournal.GetSecondaryReward or function() end
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
-- Explicit entry point for 3.3.5 clients, which do not have the retail
|
||||
-- EncounterJournal micro button.
|
||||
|
||||
local function MoonWell_ToggleEncounterJournal()
|
||||
if not EncounterJournal then
|
||||
return
|
||||
end
|
||||
|
||||
if EncounterJournal:IsShown() then
|
||||
HideUIPanel(EncounterJournal)
|
||||
return
|
||||
end
|
||||
|
||||
if MoonWell_EncounterJournalApplyDungeonMode then
|
||||
MoonWell_EncounterJournalApplyDungeonMode()
|
||||
end
|
||||
|
||||
ShowUIPanel(EncounterJournal)
|
||||
|
||||
if EJPlayerGuide_OpenFrame then
|
||||
EJPlayerGuide_OpenFrame()
|
||||
elseif EncounterJournal.instanceSelect and EncounterJournal.instanceSelect.guideTab and EJ_ContentTab_Select then
|
||||
EJ_ContentTab_Select(EncounterJournal.instanceSelect.guideTab:GetID())
|
||||
end
|
||||
end
|
||||
|
||||
SLASH_MOONWELLENCOUNTERJOURNAL1 = "/ej"
|
||||
SLASH_MOONWELLENCOUNTERJOURNAL2 = "/journal"
|
||||
SLASH_MOONWELLENCOUNTERJOURNAL3 = "/encounterjournal"
|
||||
SlashCmdList.MOONWELLENCOUNTERJOURNAL = MoonWell_ToggleEncounterJournal
|
||||
|
||||
MoonWell_ToggleEncounterJournal = MoonWell_ToggleEncounterJournal
|
||||
|
||||
local function MoonWell_EncounterJournalMicroButton_OnEnter(self)
|
||||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||||
GameTooltip:AddLine("Путеводитель по приключениям", 1, 1, 1)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
|
||||
local function MoonWell_CreateEncounterJournalMicroButton()
|
||||
local button = EncounterJournalMicroButton
|
||||
if not button then
|
||||
local iconTexture = "Interface\\EncounterJournal\\UI-EJ-PortraitIcon"
|
||||
button = CreateFrame("Button", "EncounterJournalMicroButton", MainMenuBarArtFrame or UIParent)
|
||||
button:SetSize(32, 40)
|
||||
button:SetPoint("BOTTOMLEFT", LFDMicroButton or MainMenuMicroButton or HelpMicroButton or UIParent, "BOTTOMRIGHT", 0, 0)
|
||||
button:RegisterForClicks("LeftButtonUp")
|
||||
button:SetNormalTexture(iconTexture)
|
||||
button:SetPushedTexture(iconTexture)
|
||||
button:SetDisabledTexture(iconTexture)
|
||||
button:SetHighlightTexture(iconTexture)
|
||||
button:SetPushedTextOffset(1, -1)
|
||||
|
||||
local highlight = button:GetHighlightTexture()
|
||||
if highlight then
|
||||
highlight:SetBlendMode("ADD")
|
||||
highlight:SetAlpha(0.55)
|
||||
end
|
||||
end
|
||||
|
||||
button:SetScript("OnClick", MoonWell_ToggleEncounterJournal)
|
||||
button:SetScript("OnEnter", MoonWell_EncounterJournalMicroButton_OnEnter)
|
||||
button:SetScript("OnLeave", GameTooltip_Hide)
|
||||
button:Show()
|
||||
|
||||
return button
|
||||
end
|
||||
|
||||
MoonWell_CreateEncounterJournalMicroButton()
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<!--
|
||||
Sirus EncounterJournalScrollBarTemplate inherits MinimalScrollBarTemplate
|
||||
and references self.trackBG inside its inline OnLoad. On 3.3.5 the
|
||||
parentKey="trackBG" attribute on the inherited template does not
|
||||
propagate, leaving self.trackBG nil and crashing OnLoad. Define the
|
||||
template here ourselves with trackBG as a direct child texture, before
|
||||
Sirus Custom_EncounterJournal.xml gets a chance to register its own.
|
||||
-->
|
||||
<Slider name="EncounterJournalScrollBarTemplate" inherits="MinimalScrollBarTemplate" parentKey="ScrollBar" virtual="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPRIGHT" x="0" y="-17"/>
|
||||
<Anchor point="BOTTOMRIGHT" x="0" y="17"/>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="ARTWORK">
|
||||
<Texture parentKey="trackBG" setAllPoints="true">
|
||||
<Color r="0.2" g="0.13" b="0.08" a="0.25"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
</Slider>
|
||||
</Ui>
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
-- Sirus EJ XML for lootScroll/searchResults.scrollFrame omits the scrollUp /
|
||||
-- scrollDown buttons that the stock 3.3.5 HybridScrollFrame implementation
|
||||
-- requires (HybridScrollFrame_Update / HybridScrollFrame_UpdateButtonStates
|
||||
-- index them directly). Wire up no-op buttons so the stock helpers don't
|
||||
-- index nil and the rest of the EJ flow works.
|
||||
|
||||
local function AttachFakeScrollButtons(scrollFrame)
|
||||
if not scrollFrame then
|
||||
return
|
||||
end
|
||||
if not scrollFrame.scrollUp then
|
||||
local btn = CreateFrame("Button", nil, scrollFrame)
|
||||
btn:Hide()
|
||||
btn:SetSize(1, 1)
|
||||
btn:SetPoint("TOPLEFT")
|
||||
scrollFrame.scrollUp = btn
|
||||
end
|
||||
if not scrollFrame.scrollDown then
|
||||
local btn = CreateFrame("Button", nil, scrollFrame)
|
||||
btn:Hide()
|
||||
btn:SetSize(1, 1)
|
||||
btn:SetPoint("BOTTOMLEFT")
|
||||
scrollFrame.scrollDown = btn
|
||||
end
|
||||
if scrollFrame.scrollBar and not scrollFrame.scrollBar.thumbTexture then
|
||||
scrollFrame.scrollBar.thumbTexture = scrollFrame.scrollBar:GetThumbTexture()
|
||||
or scrollFrame.scrollBar:CreateTexture(nil, "ARTWORK")
|
||||
end
|
||||
if scrollFrame.scrollBar and not scrollFrame.scrollBar.trackBG then
|
||||
scrollFrame.scrollBar.trackBG = scrollFrame.scrollBar:CreateTexture(nil, "BACKGROUND")
|
||||
end
|
||||
end
|
||||
|
||||
if EncounterJournal then
|
||||
AttachFakeScrollButtons(EncounterJournal.encounter
|
||||
and EncounterJournal.encounter.info
|
||||
and EncounterJournal.encounter.info.lootScroll)
|
||||
AttachFakeScrollButtons(EncounterJournal.searchResults
|
||||
and EncounterJournal.searchResults.scrollFrame)
|
||||
end
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
-- Restrict Sirus EJ data tables to expansions that exist on 3.3.5a (Classic, TBC, WotLK).
|
||||
-- Must run AFTER Generated_EncounterJournal.lua (where the globals are populated) and BEFORE
|
||||
-- Utils/C_EncounterJournal.lua reads them into locals on first ReloadData.
|
||||
|
||||
local ALLOWED_TIER_IDS = {
|
||||
[68] = true, -- Classic
|
||||
[70] = true, -- Burning Crusade
|
||||
[72] = true, -- Wrath of the Lich King
|
||||
}
|
||||
|
||||
local TIER_FIELD_ID = 1
|
||||
|
||||
if type(JOURNALTIER) == "table" then
|
||||
local filtered = {}
|
||||
for _, tierInfo in ipairs(JOURNALTIER) do
|
||||
if ALLOWED_TIER_IDS[tierInfo[TIER_FIELD_ID]] then
|
||||
filtered[#filtered + 1] = tierInfo
|
||||
end
|
||||
end
|
||||
JOURNALTIER = filtered
|
||||
end
|
||||
|
||||
if type(JOURNALTIERXINSTANCE) == "table" then
|
||||
for instanceID, tierList in pairs(JOURNALTIERXINSTANCE) do
|
||||
local kept
|
||||
for _, tierID in ipairs(tierList) do
|
||||
if ALLOWED_TIER_IDS[tierID] then
|
||||
kept = kept or {}
|
||||
kept[#kept + 1] = tierID
|
||||
end
|
||||
end
|
||||
if kept then
|
||||
JOURNALTIERXINSTANCE[instanceID] = kept
|
||||
else
|
||||
JOURNALTIERXINSTANCE[instanceID] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- Keep tier tables available for the 3.3.5a dungeon-list shim.
|
||||
-- C_EncounterJournal imports Sirus globals into locals and clears the globals.
|
||||
|
||||
MoonWell_EncounterJournalTiers = CopyTable(JOURNALTIER or {})
|
||||
MoonWell_EncounterJournalTierInstances = CopyTable(JOURNALTIERXINSTANCE or {})
|
||||
|
||||
+15193
File diff suppressed because it is too large
Load Diff
+75
@@ -0,0 +1,75 @@
|
||||
ADVENTURE_JOURNAL = "Путеводитель по приключениям"
|
||||
AJ_ACTION_TEXT_JOIN_BATTLE = "Вступить в бой"
|
||||
AJ_ACTION_TEXT_JOIN_GROUP = "Вступить в группу"
|
||||
AJ_ACTION_TEXT_OPEN_EJ = "Открыть журнал"
|
||||
AJ_ACTION_TEXT_SHOW_QUEST = "Открыть задания"
|
||||
AJ_ACTION_TEXT_START_HUNT = "Открыть охоту"
|
||||
AJ_ACTION_TEXT_START_QUEST = "Начать задание"
|
||||
AJ_LFG_REWARD_DEFAULT_IRANGE_TEXT = "%s: здесь можно получить предметы экипировки %i - %i уровней."
|
||||
AJ_LFG_REWARD_DEFAULT_TEXT = "%s: здесь можно получить предметы экипировки %i-го уровня."
|
||||
AJ_LFG_REWARD_DIFFICULTY_IRANGE_TEXT = "%s (%s): здесь можно получить предметы экипировки %i - %i уровней."
|
||||
AJ_LFG_REWARD_DIFFICULTY_TEXT = "%s (%s): здесь можно получить предметы экипировки %i-го уровня."
|
||||
AJ_PRIMARY_REWARD_TEXT = "Награда:"
|
||||
AJ_REWARD_CLICK_TEXT = "Щелкните для просмотра всех наград."
|
||||
AJ_SAMPLE_REWARD_TEXT = "\n\nПример награды:"
|
||||
AJ_SUGGESTED_CONTENT_TAB = "Рекомендуемый контент"
|
||||
EJ_CLASS_FILTER = "Фильтр по классу: %s"
|
||||
EJ_FILTER_ALL_CLASS = "Все классы"
|
||||
EJ_INSTANCE_REQUIREMENT_ACHIEVEMENTS = "Достижения:"
|
||||
EJ_INSTANCE_REQUIREMENT_DIFFICULTY = "Сложность: %s"
|
||||
EJ_INSTANCE_REQUIREMENT_ITEM_LEVEL = "Средний уровень предметов: %s"
|
||||
EJ_INSTANCE_REQUIREMENT_LABLE = "Требования:"
|
||||
EJ_INSTANCE_REQUIREMENT_LEVEL = "Уровень: %s"
|
||||
EJ_INSTANCE_REQUIREMENT_LEVEL_RANGE = "Уровень: %s-%s"
|
||||
EJ_INSTANCE_REQUIREMENT_NONE = "Вход свободный"
|
||||
EJ_INSTANCE_REQUIREMENT_QUESTS = "Задания:"
|
||||
EJ_INSTANCE_REQUIREMENT_RAID_GROUP = "Для входа требуется быть участником рейдовой группы"
|
||||
EJ_SET_ITEM_LEVEL = "|cffcc4040[%s]|r Уровень предмета: %d"
|
||||
ENCOUNTER_JOURNAL_ABILITY = "Способность"
|
||||
ENCOUNTER_JOURNAL_ENCOUNTER = "Босс"
|
||||
ENCOUNTER_JOURNAL_ENCOUNTER_ADD = "Помощник"
|
||||
ENCOUNTER_JOURNAL_FIND_GROUP = "Найти\nгруппу"
|
||||
ENCOUNTER_JOURNAL_FIND_RAID = "Найти\nрейд"
|
||||
ENCOUNTER_JOURNAL_INSTANCE = "Подземелье"
|
||||
ENCOUNTER_JOURNAL_ITEM = "Предмет"
|
||||
ENCOUNTER_JOURNAL_SEARCH_RESULTS = "Результаты поиска для \"%s\" (%d)"
|
||||
ENCOUNTER_JOURNAL_SHOW_MAP = "Показать\nкарту"
|
||||
ENCOUNTER_JOURNAL_SHOW_SEARCH_RESULTS = "Показать все результаты: %d"
|
||||
LOOT_JOURNAL_ITEM_SETS = "Комплекты"
|
||||
ENCOUNTER_JOURNAL_NAVIGATION_HOME = "Главная"
|
||||
|
||||
-- Путеводитель — вкладка
|
||||
MW_PLAYER_GUIDE = "Путеводитель"
|
||||
MW_PLAYER_GUIDE_REFRESH = "Обновить"
|
||||
|
||||
-- Заголовки секций
|
||||
MW_PLAYER_GUIDE_OBJECTIVES = "Задачи этапа"
|
||||
MW_PLAYER_GUIDE_RECS = "Подходящие подземелья"
|
||||
MW_PLAYER_GUIDE_REWARDS = "Награды за этап"
|
||||
|
||||
-- Шапка этапа
|
||||
MW_PLAYER_GUIDE_STAGE_OF = "ЭТАП %d ИЗ %d"
|
||||
MW_PLAYER_GUIDE_STAGE_PROGRESS = "ПРОГРЕСС ЭТАПА"
|
||||
|
||||
-- Пустое состояние
|
||||
MW_PLAYER_GUIDE_EMPTY_TITLE = "Путеводитель ещё не открыт"
|
||||
MW_PLAYER_GUIDE_EMPTY_BODY = "Данные о вашем прогрессе ещё не получены с сервера. " ..
|
||||
"Это может занять несколько секунд после входа в игру."
|
||||
|
||||
-- Загрузка
|
||||
MW_PLAYER_GUIDE_LOADING_TITLE = "ЗАПРАШИВАЕМ ХРОНИКИ"
|
||||
MW_PLAYER_GUIDE_LOADING_BODY = "Ожидание данных от Путеводителя"
|
||||
|
||||
-- Caps-плашки типов целей
|
||||
MW_PG_TYPE_LEVEL = "УРОВЕНЬ"
|
||||
MW_PG_TYPE_QUEST = "ЗАДАНИЕ"
|
||||
MW_PG_TYPE_QUEST_CHAIN = "ЦЕПОЧКА"
|
||||
MW_PG_TYPE_DUNGEON = "ПОДЗЕМЕЛЬЕ"
|
||||
MW_PG_TYPE_RAID = "РЕЙД"
|
||||
MW_PG_TYPE_BOSS = "БОСС"
|
||||
MW_PG_TYPE_ACHIEVEMENT = "ДОСТИЖЕНИЕ"
|
||||
MW_PG_TYPE_REPUTATION = "РЕПУТАЦИЯ"
|
||||
MW_PG_TYPE_PROFESSION = "ПРОФЕССИЯ"
|
||||
MW_PG_TYPE_ITEM = "ПРЕДМЕТ"
|
||||
MW_PG_TYPE_CURRENCY = "ВАЛЮТА"
|
||||
MW_PG_TYPE_CUSTOM = "ЗАДАЧА"
|
||||
+427
@@ -0,0 +1,427 @@
|
||||
--[[-----------------------------------------------------------------------------------------------
|
||||
For a hybrid scroll frame with buttons of varying size, set .dynamic on the scroll frame
|
||||
to be a function which will take the offset and return:
|
||||
1. how many buttons the offset is completely past
|
||||
2. how many pixels the offset is into the topmost button
|
||||
So with buttons of size 20, .dynamic(0) should return 0,0 and .dynamic(34) should return 1,14
|
||||
-----------------------------------------------------------------------------------------------]]--
|
||||
|
||||
local round = function (num) return math.floor(num + .5); end
|
||||
|
||||
local function HybridScrollFrame_SetVerticalScrollOffset(self, offset)
|
||||
if self.SetVerticalScroll then
|
||||
self:SetVerticalScroll(offset);
|
||||
end
|
||||
end
|
||||
|
||||
local function HybridScrollFrame_UpdateScrollChildRectSafe(self)
|
||||
if self.UpdateScrollChildRect then
|
||||
self:UpdateScrollChildRect();
|
||||
end
|
||||
end
|
||||
|
||||
local function HybridScrollFrame_GetScrollFrame(self)
|
||||
if not self then
|
||||
return self;
|
||||
end
|
||||
if self.SetVerticalScroll then
|
||||
return self;
|
||||
end
|
||||
|
||||
local name = self.GetName and self:GetName();
|
||||
local scrollFrame = self.Container or (name and _G[name.."Container"]);
|
||||
return scrollFrame or self;
|
||||
end
|
||||
|
||||
local function HybridScrollFrame_GetScrollBar(self)
|
||||
if self.scrollBar then
|
||||
return self.scrollBar;
|
||||
end
|
||||
|
||||
local name = self.GetName and self:GetName();
|
||||
local scrollBar = self.ScrollBar or (name and (_G[name.."ScrollBar"] or _G[name.."ContainerScrollBar"]));
|
||||
if scrollBar then
|
||||
self.scrollBar = scrollBar;
|
||||
end
|
||||
return scrollBar;
|
||||
end
|
||||
|
||||
function HybridScrollFrame_OnLoad (self)
|
||||
self:EnableMouse(true);
|
||||
end
|
||||
|
||||
function HybridScrollFrameScrollUp_OnLoad (self)
|
||||
self:GetParent():GetParent().scrollUp = self;
|
||||
self:Disable();
|
||||
self:RegisterForClicks("LeftButtonUp", "LeftButtonDown");
|
||||
self.direction = 1;
|
||||
end
|
||||
|
||||
function HybridScrollFrameScrollDown_OnLoad (self)
|
||||
self:GetParent():GetParent().scrollDown = self;
|
||||
self:Disable();
|
||||
self:RegisterForClicks("LeftButtonUp", "LeftButtonDown");
|
||||
self.direction = -1;
|
||||
end
|
||||
|
||||
function HybridScrollFrame_OnValueChanged (self, value)
|
||||
local scrollFrame = self;
|
||||
if self.SetMinMaxValues and self.GetParent then
|
||||
scrollFrame = self:GetParent();
|
||||
end
|
||||
scrollFrame = HybridScrollFrame_GetScrollFrame(scrollFrame);
|
||||
HybridScrollFrame_SetOffset(scrollFrame, value);
|
||||
HybridScrollFrame_UpdateButtonStates(scrollFrame, value);
|
||||
end
|
||||
|
||||
function HybridScrollFrame_UpdateButtonStates (self, currValue)
|
||||
local scrollBar = HybridScrollFrame_GetScrollBar(self);
|
||||
if ( not scrollBar ) then
|
||||
return;
|
||||
end
|
||||
|
||||
local scrollBarName = scrollBar.GetName and scrollBar:GetName();
|
||||
local scrollUp = self.scrollUp or scrollBar.ScrollUpButton or (scrollBarName and _G[scrollBarName.."ScrollUpButton"]);
|
||||
local scrollDown = self.scrollDown or scrollBar.ScrollDownButton or (scrollBarName and _G[scrollBarName.."ScrollDownButton"]);
|
||||
|
||||
if ( not currValue ) then
|
||||
currValue = scrollBar:GetValue();
|
||||
end
|
||||
|
||||
if ( scrollUp ) then
|
||||
scrollUp:Enable();
|
||||
end
|
||||
if ( scrollDown ) then
|
||||
scrollDown:Enable();
|
||||
end
|
||||
|
||||
local minVal, maxVal = scrollBar:GetMinMaxValues();
|
||||
if ( currValue >= maxVal ) then
|
||||
if ( scrollBar.thumbTexture ) then
|
||||
scrollBar.thumbTexture:Show();
|
||||
end
|
||||
if ( scrollDown ) then
|
||||
scrollDown:Disable()
|
||||
end
|
||||
end
|
||||
if ( currValue <= minVal ) then
|
||||
if ( scrollBar.thumbTexture ) then
|
||||
scrollBar.thumbTexture:Show();
|
||||
end
|
||||
if ( scrollUp ) then
|
||||
scrollUp:Disable();
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function HybridScrollFrame_OnMouseWheel (self, delta, stepSize)
|
||||
self = HybridScrollFrame_GetScrollFrame(self);
|
||||
local scrollBar = HybridScrollFrame_GetScrollBar(self);
|
||||
if ( not scrollBar or not scrollBar:IsVisible() ) then
|
||||
return;
|
||||
end
|
||||
local isEnabled = not scrollBar.IsEnabled or scrollBar:IsEnabled();
|
||||
if ( not isEnabled or isEnabled == 0 ) then
|
||||
return;
|
||||
end
|
||||
|
||||
local minVal, maxVal = scrollBar:GetMinMaxValues();
|
||||
minVal = minVal or 0;
|
||||
maxVal = maxVal or self.range or 0;
|
||||
stepSize = stepSize or self.stepSize or self.buttonHeight or 1;
|
||||
if ( delta == 1 ) then
|
||||
scrollBar:SetValue(max(minVal, scrollBar:GetValue() - stepSize));
|
||||
else
|
||||
scrollBar:SetValue(min(maxVal, scrollBar:GetValue() + stepSize));
|
||||
end
|
||||
end
|
||||
|
||||
function HybridScrollFrameScrollButton_OnUpdate (self, elapsed)
|
||||
self.timeSinceLast = self.timeSinceLast + elapsed;
|
||||
if ( self.timeSinceLast >= ( self.updateInterval or 0.08 ) ) then
|
||||
if ( not IsMouseButtonDown("LeftButton") ) then
|
||||
self:SetScript("OnUpdate", nil);
|
||||
elseif ( self:IsMouseOver() ) then
|
||||
local parent = self.parent or self:GetParent():GetParent();
|
||||
HybridScrollFrame_OnMouseWheel (parent, self.direction, (self.stepSize or parent.buttonHeight/3));
|
||||
self.timeSinceLast = 0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function HybridScrollFrameScrollButton_OnClick (self, button, down)
|
||||
local parent = self.parent or self:GetParent():GetParent();
|
||||
|
||||
if ( down ) then
|
||||
if IsMouseButtonDown then
|
||||
self.timeSinceLast = (self.timeToStart or -0.2);
|
||||
self:SetScript("OnUpdate", HybridScrollFrameScrollButton_OnUpdate);
|
||||
end
|
||||
HybridScrollFrame_OnMouseWheel (parent, self.direction);
|
||||
PlaySound("UChatScrollButton");
|
||||
else
|
||||
self:SetScript("OnUpdate", nil);
|
||||
end
|
||||
end
|
||||
|
||||
function HybridScrollFrame_SetPercentageHeight(self, percentageHeight)
|
||||
local offset = percentageHeight * (self.totalHeight or 0);
|
||||
HybridScrollFrame_SetOffset(self, offset);
|
||||
end
|
||||
|
||||
function HybridScrollFrame_GetVisiblePercentage(self)
|
||||
local totalHeight = (self.totalHeight or 0);
|
||||
if totalHeight == 0 then
|
||||
return 0;
|
||||
end
|
||||
|
||||
return self.scrollChild:GetHeight() / totalHeight;
|
||||
end
|
||||
|
||||
function HybridScrollFrame_GetScrollPercentage(self)
|
||||
local scrollableHeight = (self.totalHeight or 0) - self.scrollChild:GetHeight();
|
||||
if scrollableHeight == 0 then
|
||||
return 0;
|
||||
end
|
||||
|
||||
return (self.offset or 0) / scrollableHeight;
|
||||
end
|
||||
|
||||
function HybridScrollFrame_Update (self, totalHeight, displayedHeight)
|
||||
local range = floor(totalHeight - self:GetHeight() + 0.5);
|
||||
local scrollBar = HybridScrollFrame_GetScrollBar(self);
|
||||
if ( range > 0 and scrollBar ) then
|
||||
local minVal, maxVal = scrollBar:GetMinMaxValues();
|
||||
if ( math.floor(scrollBar:GetValue()) >= math.floor(maxVal) ) then
|
||||
scrollBar:SetMinMaxValues(0, range);
|
||||
if ( math.floor(scrollBar:GetValue()) ~= math.floor(range) ) then
|
||||
scrollBar:SetValue(range);
|
||||
else
|
||||
HybridScrollFrame_SetOffset(self, range); -- If we've scrolled to the bottom, we need to recalculate the offset.
|
||||
end
|
||||
else
|
||||
scrollBar:SetMinMaxValues(0, range)
|
||||
end
|
||||
scrollBar:Enable();
|
||||
HybridScrollFrame_UpdateButtonStates(self);
|
||||
scrollBar:Show();
|
||||
elseif ( scrollBar ) then
|
||||
scrollBar:SetValue(0);
|
||||
if ( scrollBar.doNotHide ) then
|
||||
scrollBar:Disable();
|
||||
if ( self.scrollUp ) then
|
||||
self.scrollUp:Disable();
|
||||
end
|
||||
if ( self.scrollDown ) then
|
||||
self.scrollDown:Disable();
|
||||
end
|
||||
if ( scrollBar.thumbTexture ) then
|
||||
scrollBar.thumbTexture:Hide();
|
||||
end
|
||||
else
|
||||
scrollBar:Hide();
|
||||
end
|
||||
end
|
||||
|
||||
self.range = range;
|
||||
self.totalHeight = totalHeight;
|
||||
self.scrollChild:SetHeight(displayedHeight);
|
||||
HybridScrollFrame_UpdateScrollChildRectSafe(self);
|
||||
end
|
||||
|
||||
function HybridScrollFrame_GetOffset (self)
|
||||
return math.floor(self.offset or 0), (self.offset or 0);
|
||||
end
|
||||
|
||||
function HybridScrollFrameScrollChild_OnLoad (self)
|
||||
self:GetParent().scrollChild = self;
|
||||
end
|
||||
|
||||
function HybridScrollFrame_ExpandButton (self, offset, height)
|
||||
self.largeButtonTop = round(offset);
|
||||
self.largeButtonHeight = round(height)
|
||||
local scrollBar = HybridScrollFrame_GetScrollBar(self);
|
||||
HybridScrollFrame_SetOffset(self, scrollBar and scrollBar:GetValue() or 0);
|
||||
end
|
||||
|
||||
function HybridScrollFrame_CollapseButton (self)
|
||||
self.largeButtonTop = nil;
|
||||
self.largeButtonHeight = nil;
|
||||
end
|
||||
|
||||
function HybridScrollFrame_SetOffset (self, offset)
|
||||
local buttons = self.buttons
|
||||
local buttonHeight = self.buttonHeight;
|
||||
local element, overflow;
|
||||
|
||||
if ( not buttonHeight and buttons and buttons[1] ) then
|
||||
buttonHeight = round(buttons[1]:GetHeight());
|
||||
self.buttonHeight = buttonHeight;
|
||||
end
|
||||
if ( not buttonHeight or buttonHeight == 0 ) then
|
||||
self.offset = 0;
|
||||
HybridScrollFrame_SetVerticalScrollOffset(self, 0);
|
||||
return;
|
||||
end
|
||||
|
||||
local scrollHeight = 0;
|
||||
|
||||
local largeButtonTop = self.largeButtonTop
|
||||
if ( self.dynamic ) then --This is for frames where buttons will have different heights
|
||||
if ( offset < buttonHeight ) then
|
||||
-- a little optimization
|
||||
element = 0;
|
||||
scrollHeight = offset;
|
||||
else
|
||||
element, scrollHeight = self.dynamic(offset);
|
||||
end
|
||||
elseif ( largeButtonTop and offset >= largeButtonTop ) then
|
||||
local largeButtonHeight = self.largeButtonHeight;
|
||||
-- Initial offset...
|
||||
element = largeButtonTop / buttonHeight;
|
||||
|
||||
if ( offset >= (largeButtonTop + largeButtonHeight) ) then
|
||||
element = element + 1;
|
||||
|
||||
local leftovers = (offset - (largeButtonTop + largeButtonHeight) );
|
||||
|
||||
element = element + ( leftovers / buttonHeight );
|
||||
overflow = element - math.floor(element);
|
||||
scrollHeight = overflow * buttonHeight;
|
||||
else
|
||||
scrollHeight = math.abs(offset - largeButtonTop);
|
||||
end
|
||||
else
|
||||
element = offset / buttonHeight;
|
||||
overflow = element - math.floor(element);
|
||||
scrollHeight = overflow * buttonHeight;
|
||||
end
|
||||
|
||||
if ( math.floor(self.offset or 0) ~= math.floor(element) and self.update ) then
|
||||
self.offset = element;
|
||||
self:update();
|
||||
else
|
||||
self.offset = element;
|
||||
end
|
||||
|
||||
HybridScrollFrame_SetVerticalScrollOffset(self, scrollHeight);
|
||||
end
|
||||
|
||||
function HybridScrollFrame_CreateButtons (self, buttonTemplate, initialOffsetX, initialOffsetY, initialPoint, initialRelative, offsetX, offsetY, point, relativePoint)
|
||||
local scrollChild = self.scrollChild;
|
||||
local button, buttonHeight, buttons, numButtons;
|
||||
|
||||
local parentName = self:GetName();
|
||||
local buttonName = parentName and (parentName .. "Button") or nil;
|
||||
|
||||
initialPoint = initialPoint or "TOPLEFT";
|
||||
initialRelative = initialRelative or "TOPLEFT";
|
||||
point = point or "TOPLEFT";
|
||||
relativePoint = relativePoint or "BOTTOMLEFT";
|
||||
offsetX = offsetX or 0;
|
||||
offsetY = offsetY or 0;
|
||||
|
||||
if ( self.buttons ) then
|
||||
buttons = self.buttons;
|
||||
buttonHeight = buttons[1]:GetHeight();
|
||||
else
|
||||
button = CreateFrame("BUTTON", buttonName and (buttonName .. 1) or nil, scrollChild, buttonTemplate);
|
||||
buttonHeight = button:GetHeight();
|
||||
button:SetPoint(initialPoint, scrollChild, initialRelative, initialOffsetX, initialOffsetY);
|
||||
buttons = {}
|
||||
tinsert(buttons, button);
|
||||
end
|
||||
|
||||
self.buttonHeight = round(buttonHeight) - offsetY;
|
||||
|
||||
numButtons = math.ceil(self:GetHeight() / buttonHeight) + 1;
|
||||
|
||||
for i = #buttons + 1, numButtons do
|
||||
button = CreateFrame("BUTTON", buttonName and (buttonName .. i) or nil, scrollChild, buttonTemplate);
|
||||
button:SetPoint(point, buttons[i-1], relativePoint, offsetX, offsetY);
|
||||
tinsert(buttons, button);
|
||||
end
|
||||
|
||||
scrollChild:SetWidth(self:GetWidth())
|
||||
scrollChild:SetHeight(numButtons * buttonHeight);
|
||||
HybridScrollFrame_SetVerticalScrollOffset(self, 0);
|
||||
HybridScrollFrame_UpdateScrollChildRectSafe(self);
|
||||
|
||||
self.buttons = buttons;
|
||||
local scrollBar = HybridScrollFrame_GetScrollBar(self);
|
||||
if scrollBar then
|
||||
scrollBar:SetMinMaxValues(0, numButtons * buttonHeight)
|
||||
scrollBar.buttonHeight = buttonHeight;
|
||||
scrollBar:SetValueStep(.005);
|
||||
scrollBar:SetValue(0);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function HybridScrollFrame_GetButtonIndex(self, button)
|
||||
return tIndexOf(self.buttons, button);
|
||||
end
|
||||
|
||||
function HybridScrollFrame_GetButtons (self)
|
||||
return self.buttons;
|
||||
end
|
||||
|
||||
function HybridScrollFrame_SetDoNotHideScrollBar (self, doNotHide)
|
||||
local scrollBar = HybridScrollFrame_GetScrollBar(self);
|
||||
if not scrollBar or scrollBar.doNotHide == doNotHide then
|
||||
return;
|
||||
end
|
||||
|
||||
scrollBar.doNotHide = doNotHide;
|
||||
HybridScrollFrame_Update(self, self.totalHeight or 0, self.scrollChild:GetHeight());
|
||||
end
|
||||
|
||||
function HybridScrollFrame_ScrollToIndex(self, index, getHeightFunc)
|
||||
local scrollBar = HybridScrollFrame_GetScrollBar(self);
|
||||
if not scrollBar then
|
||||
return;
|
||||
end
|
||||
|
||||
local totalHeight = 0;
|
||||
local scrollFrameHeight = self:GetHeight();
|
||||
for i = 1, index do
|
||||
local entryHeight = getHeightFunc(i);
|
||||
if i == index then
|
||||
local offset = 0;
|
||||
-- we don't need to do anything if the entry is fully displayed with the scroll all the way up
|
||||
if ( totalHeight + entryHeight > scrollFrameHeight ) then
|
||||
if ( entryHeight > scrollFrameHeight ) then
|
||||
-- this entry is larger than the entire scrollframe, put it at the top
|
||||
offset = totalHeight;
|
||||
else
|
||||
-- otherwise place it in the center
|
||||
local diff = scrollFrameHeight - entryHeight;
|
||||
offset = totalHeight - diff / 2;
|
||||
end
|
||||
-- because of valuestep our positioning might change
|
||||
-- we'll do the adjustment ourselves to make sure the entry ends up above the center rather than below
|
||||
local valueStep = scrollBar:GetValueStep();
|
||||
offset = offset + valueStep - mod(offset, valueStep);
|
||||
-- but if we ended up moving the entry so high up that its top is not visible, move it back down
|
||||
if ( offset > totalHeight ) then
|
||||
offset = offset - valueStep;
|
||||
end
|
||||
end
|
||||
scrollBar:SetValue(offset);
|
||||
break;
|
||||
end
|
||||
totalHeight = totalHeight + entryHeight;
|
||||
end
|
||||
end
|
||||
|
||||
function HybridScrollBar_Disable(scrollBar)
|
||||
scrollBar:Disable();
|
||||
local scrollDownButton = scrollBar.ScrollDownButton or _G[scrollBar:GetName().."ScrollDownButton"];
|
||||
if scrollDownButton then
|
||||
scrollDownButton:Disable();
|
||||
end
|
||||
local scrollUpButton = scrollBar.ScrollUpButton or _G[scrollBar:GetName().."ScrollUpButton"];
|
||||
if scrollUpButton then
|
||||
scrollUpButton:Disable();
|
||||
end
|
||||
end
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Script file="HybridScrollFrame.lua"/>
|
||||
|
||||
<Texture name="HybridScrollBarButton" virtual="true">
|
||||
<TexCoords left="0.25" right="0.75" top="0.25" bottom="0.75"/>
|
||||
</Texture>
|
||||
|
||||
<Slider name="HybridScrollBarBackgroundTemplate" parentKey="scrollBar" virtual="true">
|
||||
<Size x="20" y="0"/>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativePoint="TOPRIGHT" x="0" y="-18"/>
|
||||
<Anchor point="BOTTOMLEFT" relativePoint="BOTTOMRIGHT" x="0" y="16"/>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture name="$parentBG" setAllPoints="true" hidden="true" parentKey="trackBG">
|
||||
<Color r="0" g="0" b="0" a=".85"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="ARTWORK">
|
||||
<Texture name="$parentTop" parentKey="ScrollBarTop" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
|
||||
<Size x="27" y="48"/>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" x="-4" y="17"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.484375" top="0" bottom=".20"/>
|
||||
</Texture>
|
||||
<Texture name="$parentBottom" parentKey="ScrollBarBottom" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
|
||||
<Size x="27" y="64"/>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMLEFT" x="-4" y="-15"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0.515625" right="1.0" top="0.1440625" bottom="0.4140625"/>
|
||||
</Texture>
|
||||
<Texture name="$parentMiddle" parentKey="ScrollBarMiddle" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parentTop" relativePoint="BOTTOMLEFT"/>
|
||||
<Anchor point="BOTTOMRIGHT" relativeTo="$parentBottom" relativePoint="TOPRIGHT"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.484375" top="0.1640625" bottom="1"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<ThumbTexture name="$parentThumbTexture" file="Interface\Buttons\UI-ScrollBar-Knob" parentKey="thumbTexture">
|
||||
<Size x="18" y="24"/>
|
||||
<TexCoords left="0.20" right="0.80" top="0.125" bottom="0.875"/>
|
||||
</ThumbTexture>
|
||||
</Slider>
|
||||
|
||||
<Slider name="HybridScrollBarTemplate" inherits="HybridScrollBarBackgroundTemplate" virtual="true">
|
||||
<Frames>
|
||||
<Button name="$parentScrollUpButton" parentKey="ScrollUpButton" inherits="UIPanelScrollUpButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOM" relativePoint="TOP" x="0" y="-2"/>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad function="HybridScrollFrameScrollUp_OnLoad"/>
|
||||
<OnClick function ="HybridScrollFrameScrollButton_OnClick"/>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="$parentScrollDownButton" parentKey="ScrollDownButton" inherits="UIPanelScrollDownButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativePoint="BOTTOM" x="0" y="2"/>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad function="HybridScrollFrameScrollDown_OnLoad"/>
|
||||
<OnClick function="HybridScrollFrameScrollButton_OnClick"/>
|
||||
</Scripts>
|
||||
</Button>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnValueChanged function="HybridScrollFrame_OnValueChanged"/>
|
||||
</Scripts>
|
||||
</Slider>
|
||||
|
||||
<Slider name="HybridScrollBarTrimTemplate" parentKey="scrollBar" virtual="true">
|
||||
<Size x="20" y="0"/>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture name="$parentBG" setAllPoints="true" hidden="true" parentKey="trackBG">
|
||||
<Color r="0" g="0" b="0" a=".85"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="ARTWORK">
|
||||
<Texture name="$parentTop" parentKey="Top" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
|
||||
<Size x="24" y="48"/>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" x="-4" y="17"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.45" top="0" bottom=".20"/>
|
||||
</Texture>
|
||||
<Texture name="$parentBottom" parentKey="Bottom" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
|
||||
<Size x="24" y="64"/>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMLEFT" x="-4" y="-15"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0.515625" right="0.97" top="0.1440625" bottom="0.4140625"/>
|
||||
</Texture>
|
||||
<Texture name="$parentMiddle" parentKey="Middle" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parentTop" relativePoint="BOTTOMLEFT"/>
|
||||
<Anchor point="BOTTOMRIGHT" relativeTo="$parentBottom" relativePoint="TOPRIGHT"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.45" top="0.1640625" bottom="1"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Button name="$parentScrollUpButton" parentKey="UpButton" inherits="UIPanelScrollUpButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOM" relativePoint="TOP" x="0" y="-2"/>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad function="HybridScrollFrameScrollUp_OnLoad"/>
|
||||
<OnClick function="HybridScrollFrameScrollButton_OnClick"/>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="$parentScrollDownButton" parentKey="DownButton" inherits="UIPanelScrollDownButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativePoint="BOTTOM" x="0" y="2"/>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad function="HybridScrollFrameScrollDown_OnLoad"/>
|
||||
<OnClick function="HybridScrollFrameScrollButton_OnClick"/>
|
||||
</Scripts>
|
||||
</Button>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnValueChanged function="HybridScrollFrame_OnValueChanged"/>
|
||||
</Scripts>
|
||||
<ThumbTexture name="$parentThumbTexture" inherits="HybridScrollBarButton" file="Interface\Buttons\UI-ScrollBar-Knob" parentKey="thumbTexture">
|
||||
<Size x="18" y="24"/>
|
||||
<TexCoords left="0.20" right="0.80" top="0.125" bottom="0.875"/>
|
||||
</ThumbTexture>
|
||||
</Slider>
|
||||
|
||||
<Slider name="MinimalHybridScrollBarTemplate" parentKey="scrollBar" virtual="true">
|
||||
<Size x="22" y="0"/>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture name="$parentTrack" parentKey="trackBG">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" x="0" y="17"/>
|
||||
<Anchor point="BOTTOMRIGHT" x="0" y="-17"/>
|
||||
</Anchors>
|
||||
<Color r="0" g="0" b="0" a="1"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Button name="$parentScrollUpButton" inherits="UIPanelScrollUpButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" x="0" y="15"/>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad function="HybridScrollFrameScrollUp_OnLoad"/>
|
||||
<OnClick function="HybridScrollFrameScrollButton_OnClick"/>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="$parentScrollDownButton" inherits="UIPanelScrollDownButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOM" x="0" y="-15"/>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad function="HybridScrollFrameScrollDown_OnLoad"/>
|
||||
<OnClick function="HybridScrollFrameScrollButton_OnClick"/>
|
||||
</Scripts>
|
||||
</Button>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnValueChanged function="HybridScrollFrame_OnValueChanged"/>
|
||||
</Scripts>
|
||||
<ThumbTexture name="$parentThumbTexture" inherits="HybridScrollBarButton" file="Interface\Buttons\UI-ScrollBar-Knob" parentKey="thumbTexture">
|
||||
<Size x="18" y="24"/>
|
||||
<TexCoords left="0.20" right="0.80" top="0.125" bottom="0.875"/>
|
||||
</ThumbTexture>
|
||||
</Slider>
|
||||
|
||||
<ScrollFrame name="HybridScrollFrameTemplate" virtual="true">
|
||||
<Scripts>
|
||||
<OnLoad function="HybridScrollFrame_OnLoad"/>
|
||||
<OnMouseWheel function="HybridScrollFrame_OnMouseWheel"/>
|
||||
</Scripts>
|
||||
<ScrollChild>
|
||||
<Frame name="$parentScrollChild" parentKey="ScrollChild">
|
||||
<Scripts>
|
||||
<OnLoad function="HybridScrollFrameScrollChild_OnLoad"/>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
</ScrollChild>
|
||||
</ScrollFrame>
|
||||
|
||||
<ScrollFrame name="BasicHybridScrollFrameTemplate" inherits="HybridScrollFrameTemplate" virtual="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"/>
|
||||
<Anchor point="BOTTOMRIGHT"/>
|
||||
</Anchors>
|
||||
<Frames>
|
||||
<Slider name="$parentScrollBar" parentKey="ScrollBar" inherits="HybridScrollBarTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativePoint="TOPRIGHT" x="0" y="-17"/>
|
||||
<Anchor point="BOTTOMLEFT" relativePoint="BOTTOMRIGHT" x="0" y="12"/>
|
||||
</Anchors>
|
||||
</Slider>
|
||||
</Frames>
|
||||
</ScrollFrame>
|
||||
|
||||
<!-- This is a scrollframe with no border and a black texture for a track -->
|
||||
<ScrollFrame name="MinimalHybridScrollFrameTemplate" inherits="HybridScrollFrameTemplate" virtual="true">
|
||||
<Frames>
|
||||
<Slider name="$parentScrollBar" inherits="MinimalHybridScrollBarTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativePoint="TOPRIGHT" x="0" y="-17"/>
|
||||
<Anchor point="BOTTOMLEFT" relativePoint="BOTTOMRIGHT" x="0" y="17"/>
|
||||
</Anchors>
|
||||
</Slider>
|
||||
</Frames>
|
||||
</ScrollFrame>
|
||||
</Ui>
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
|
||||
NAVBAR_WIDTHBUFFER = 20;
|
||||
|
||||
|
||||
function NavBar_Initialize(self, template, homeData, homeButton, overflowButton)
|
||||
self.template = template;
|
||||
self.freeButtons = {};
|
||||
self.navList = {};
|
||||
self.widthBuffer = NAVBAR_WIDTHBUFFER;
|
||||
|
||||
local name = self:GetName();
|
||||
|
||||
if not self.dropDown then
|
||||
local dropDownName = name and name.."DropDown" or nil;
|
||||
self.dropDown = CreateFrame("Frame", dropDownName, self, "UIDropDownMenuTemplate");
|
||||
UIDropDownMenu_Initialize(self.dropDown, NavBar_DropDown_Initialize, "MENU");
|
||||
end
|
||||
|
||||
if not homeButton then
|
||||
local homeButtonName = name and name.."HomeButton" or nil;
|
||||
homeButton = CreateFrame("BUTTON", homeButtonName, self, self.template);
|
||||
homeButton:SetWidth(homeButton.text:GetStringWidth()+30);
|
||||
end
|
||||
homeButton:SetText(homeData.name or HOME);
|
||||
|
||||
if not overflowButton then
|
||||
local overflowButtonName = name and name.."OverflowButton" or nil;
|
||||
overflowButton = CreateFrame("BUTTON", overflowButtonName, self, self.template);
|
||||
overflowButton:SetWidth(30);
|
||||
|
||||
-- LOOK AT CLICK
|
||||
end
|
||||
|
||||
|
||||
if not homeButton:GetScript("OnEnter") then
|
||||
homeButton:SetScript("OnEnter", NavBar_ButtonOnEnter);
|
||||
end
|
||||
|
||||
if not homeButton:GetScript("OnLeave") then
|
||||
homeButton:SetScript("OnLeave", NavBar_ButtonOnLeave);
|
||||
end
|
||||
|
||||
if ( self.oldStyle ) then
|
||||
homeButton:RegisterForClicks("LeftButtonUp", "RightButtonUp");
|
||||
overflowButton:RegisterForClicks("LeftButtonUp", "RightButtonUp");
|
||||
else
|
||||
homeButton:RegisterForClicks("LeftButtonUp");
|
||||
end
|
||||
overflowButton.listFunc = NavBar_ListOverFlowButtons;
|
||||
homeButton:ClearAllPoints();
|
||||
overflowButton:ClearAllPoints();
|
||||
homeButton:SetPoint("LEFT", self, "LEFT", 0, 0);
|
||||
overflowButton:SetPoint("LEFT", self, "LEFT", 0, 0);
|
||||
overflowButton:Hide();
|
||||
|
||||
homeButton.oldClick = homeButton:GetScript("OnClick");
|
||||
homeButton:SetScript("OnClick", NavBar_ButtonOnClick);
|
||||
overflowButton:SetScript("OnMouseDown", NavBar_ToggleMenu);
|
||||
self.homeButton = homeButton;
|
||||
self.overflowButton = overflowButton;
|
||||
|
||||
|
||||
self.navList[#self.navList+1] = homeButton;
|
||||
homeButton.myclick = homeData.OnClick;
|
||||
homeButton.listFunc = homeData.listFunc;
|
||||
homeButton.data = homeData;
|
||||
homeButton:Show();
|
||||
end
|
||||
|
||||
|
||||
|
||||
function NavBar_Reset(self)
|
||||
for index=2,#self.navList do
|
||||
self.navList[index]:Hide();
|
||||
tinsert(self.freeButtons, self.navList[index])
|
||||
self.navList[index] = nil;
|
||||
end
|
||||
NavBar_CheckLength(self);
|
||||
end
|
||||
|
||||
|
||||
function NavBar_AddButton(self, buttonData)
|
||||
local navButton = self.freeButtons[#self.freeButtons];
|
||||
if navButton then
|
||||
self.freeButtons[#self.freeButtons] = nil;
|
||||
end
|
||||
|
||||
if not navButton then
|
||||
local name = self:GetName();
|
||||
local buttonName = name and name.."Button"..(#self.navList+1);
|
||||
navButton = CreateFrame("BUTTON", buttonName, self, self.template);
|
||||
navButton.oldClick = navButton:GetScript("OnClick");
|
||||
navButton:SetScript("OnClick", NavBar_ButtonOnClick);
|
||||
if ( self.oldStyle ) then
|
||||
navButton:RegisterForClicks("LeftButtonUp", "RightButtonUp");
|
||||
else
|
||||
navButton:RegisterForClicks("LeftButtonUp");
|
||||
end
|
||||
|
||||
if not navButton:GetScript("OnEnter") then
|
||||
navButton:SetScript("OnEnter", NavBar_ButtonOnEnter);
|
||||
end
|
||||
|
||||
if not navButton:GetScript("OnLeave") then
|
||||
navButton:SetScript("OnLeave", NavBar_ButtonOnLeave);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--Set up the button
|
||||
local navParent = self.navList[#self.navList];
|
||||
self.navList[#self.navList+1] = navButton;
|
||||
navButton.navParent = navParent;
|
||||
|
||||
navButton:SetText(buttonData.name);
|
||||
local buttonExtraWidth;
|
||||
navButton.MenuArrowButton:Hide();
|
||||
buttonExtraWidth = 30;
|
||||
navButton.text:SetWidth(self.textMaxWidth or 0);
|
||||
navButton:SetWidth(navButton.text:GetStringWidth() + buttonExtraWidth);
|
||||
navButton.myclick = buttonData.OnClick;
|
||||
navButton.listFunc = buttonData.listFunc;
|
||||
navButton.id = buttonData.id;
|
||||
navButton.data = buttonData;
|
||||
|
||||
navButton:Show();
|
||||
NavBar_CheckLength(self);
|
||||
|
||||
navButton.MenuArrowButton:SetEnabled(not buttonData.isDisabled)
|
||||
end
|
||||
|
||||
function NavBar_ClearTrailingButtons(list, freeList, button)
|
||||
for index=#list,1,-1 do
|
||||
if not list[index] or button == list[index] then
|
||||
break
|
||||
end
|
||||
|
||||
list[index]:Hide();
|
||||
tinsert(freeList, list[index])
|
||||
list[index] = nil;
|
||||
end
|
||||
NavBar_CheckLength(button:GetParent());
|
||||
end
|
||||
|
||||
function NavBar_OpenTo(self, id)
|
||||
local button;
|
||||
local found = false;
|
||||
for i=1, #self.navList do
|
||||
button = self.navList[i];
|
||||
if (button.id and button.id == id) then
|
||||
found = true;
|
||||
break;
|
||||
end
|
||||
end
|
||||
|
||||
if (found) then
|
||||
NavBar_ClearTrailingButtons(self.navList, self.freeButtons, button)
|
||||
end
|
||||
end
|
||||
|
||||
function NavBar_ButtonOnClick(self, button)
|
||||
local parent = self:GetParent()
|
||||
CloseDropDownMenus();
|
||||
if button == "LeftButton" then
|
||||
NavBar_ClearTrailingButtons(parent.navList, parent.freeButtons, self);
|
||||
|
||||
if self.oldClick then
|
||||
self:oldClick(button);
|
||||
end
|
||||
|
||||
if self.myclick then
|
||||
self:myclick(button);
|
||||
end
|
||||
elseif button == "RightButton" then
|
||||
NavBar_ToggleMenu(self);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function NavBar_ButtonOnEnter(self)
|
||||
if self.text:IsTruncated() then
|
||||
GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT");
|
||||
GameTooltip:AddLine(self.text:GetText(), NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, true);
|
||||
GameTooltip:Show();
|
||||
end
|
||||
end
|
||||
|
||||
function NavBar_ButtonOnLeave(self)
|
||||
if self.text:IsTruncated() then
|
||||
GameTooltip:Hide();
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function NavBar_CheckLength(self)
|
||||
local width = 0;
|
||||
local collapsedWidth;
|
||||
local maxWidth = self:GetWidth() - self.widthBuffer;
|
||||
local xoffset;
|
||||
|
||||
local lastShown;
|
||||
local collapsed = false;
|
||||
|
||||
for i=#self.navList,1,-1 do
|
||||
local currentWidth = width;
|
||||
width = width + self.navList[i]:GetWidth();
|
||||
|
||||
if width > maxWidth then
|
||||
self.navList[i]:Hide();
|
||||
collapsed = true;
|
||||
if not collapsedWidth then -- store the width for adding the offset button
|
||||
collapsedWidth = currentWidth;
|
||||
end
|
||||
else
|
||||
self.navList[i]:Show();
|
||||
if lastShown then
|
||||
local lastButton = self.navList[lastShown];
|
||||
xoffset = self.navList[i].xoffset or 0
|
||||
lastButton:SetPoint("LEFT", self.navList[i], "RIGHT", xoffset, 0);
|
||||
self.navList[i]:SetFrameLevel(lastButton:GetFrameLevel()+1);
|
||||
else
|
||||
self.navList[i]:SetFrameLevel(self:GetFrameLevel()+1);
|
||||
end
|
||||
lastShown = i;
|
||||
end
|
||||
|
||||
if i<#self.navList then
|
||||
if self.navList[i].selected then
|
||||
self.navList[i].selected:Hide();
|
||||
end
|
||||
self.navList[i]:Enable();
|
||||
else
|
||||
if self.navList[i].selected then
|
||||
self.navList[i].selected:Show();
|
||||
end
|
||||
|
||||
self.navList[i]:SetButtonState("NORMAL");
|
||||
self.navList[i]:Disable();
|
||||
end
|
||||
end
|
||||
|
||||
if self.navList[1] then
|
||||
local frameLevel = self.navList[1]:GetFrameLevel()
|
||||
if frameLevel >= self.overlay:GetFrameLevel() then
|
||||
self.overlay:SetFrameLevel(frameLevel + 1)
|
||||
end
|
||||
end
|
||||
|
||||
if collapsed then
|
||||
if collapsedWidth + self.overflowButton:GetWidth() > maxWidth then
|
||||
--No room for the overflow button
|
||||
self.navList[lastShown]:Hide();
|
||||
lastShown = lastShown + 1;
|
||||
end
|
||||
|
||||
self.overflowButton:Show();
|
||||
|
||||
--There should only ever be no lastShown if a single button is longer than maxWidth by itself.
|
||||
if ( lastShown ) then
|
||||
local lastButton = self.navList[lastShown];
|
||||
|
||||
--There should only ever be no lastButton when there is lastShown if a single button is less than maxWidth
|
||||
--but it's width plus the width of the overflow button is longer than maxWidth. In this case the single button
|
||||
--is hidden to make room for the overflowButton.
|
||||
if ( lastButton ) then
|
||||
xoffset = self.overflowButton.xoffset or 0
|
||||
lastButton:SetPoint("LEFT", self.overflowButton, "RIGHT", xoffset, 0);
|
||||
self.overflowButton:SetFrameLevel(lastButton:GetFrameLevel()+1);
|
||||
end
|
||||
end
|
||||
else
|
||||
self.overflowButton:Hide();
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
function NavBar_ListOverFlowButtons(self, index)
|
||||
local navBar = self:GetParent();
|
||||
|
||||
local button = navBar.navList[index];
|
||||
if not button:IsShown() then
|
||||
return button:GetText(), NavBar_OverflowItemOnClick;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function NavBar_ToggleMenu(self, button)
|
||||
if ( self:GetParent().dropDown.buttonOwner ~= self ) then
|
||||
CloseDropDownMenus();
|
||||
end
|
||||
self:GetParent().dropDown.buttonOwner = self;
|
||||
ToggleDropDownMenu(nil, nil, self:GetParent().dropDown, self, 20, 3);
|
||||
end
|
||||
|
||||
|
||||
function NavBar_DropDown_Initialize(self, level)
|
||||
local navButton = self.buttonOwner;
|
||||
if not navButton or not navButton.listFunc then
|
||||
return;
|
||||
end
|
||||
|
||||
local info = UIDropDownMenu_CreateInfo();
|
||||
info.func = NavBar_DropDown_Click;
|
||||
info.owner = navButton;
|
||||
info.notCheckable = true;
|
||||
local index = 1;
|
||||
local text, func = navButton:listFunc(index);
|
||||
while text do
|
||||
info.text = text;
|
||||
|
||||
info.arg1 = index;
|
||||
info.arg2 = func;
|
||||
UIDropDownMenu_AddButton(info, level);
|
||||
|
||||
index = index + 1;
|
||||
text, func = navButton:listFunc(index);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function NavBar_DropDown_Click(self, index, func)
|
||||
local navButton = self.owner;
|
||||
local navBar = navButton:GetParent();
|
||||
|
||||
if func ~= NavBar_OverflowItemOnClick then
|
||||
NavBar_ClearTrailingButtons(navBar.navList, navBar.freeButtons, navButton);
|
||||
end
|
||||
if type(func) == "function" then
|
||||
func(self, index, navBar);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function NavBar_OverflowItemOnClick(junk, index, navBar)
|
||||
local button = navBar.navList[index];
|
||||
if button then
|
||||
button:Click();
|
||||
end
|
||||
end
|
||||
|
||||
function NavBar_ReconsultationSize( self )
|
||||
self.xoffset = -15
|
||||
local newWidth = min(128, self.text:GetStringWidth()+50)
|
||||
local texCoordoffsetX = (newWidth/128)*0.25
|
||||
|
||||
self:GetNormalTexture():SetTexCoord(0.70312500-texCoordoffsetX, 0.70312500, 0.00781250, 0.24218750)
|
||||
self:GetPushedTexture():SetTexCoord(0.70312500-texCoordoffsetX, 0.70312500, 0.25781250, 0.49218750)
|
||||
self:GetHighlightTexture():SetTexCoord(0.70312500-texCoordoffsetX, 0.71312500, 0.50781250, 0.74218750)
|
||||
|
||||
self:SetWidth(newWidth)
|
||||
end
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Script file="NavigationBar.lua"/>
|
||||
|
||||
<Button name="NavButtonTemplate" motionScriptsWhileDisabled="true" virtual="true">
|
||||
<Size x="80" y="30"/>
|
||||
<Layers>
|
||||
<Layer level="OVERLAY">
|
||||
<Texture file="Interface\HelpFrame\CS_HelpTextures" parentKey="arrowUp">
|
||||
<Size x="21" y="30"/>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativePoint="RIGHT" x="0" y="0"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0.88867188" right="0.92968750" top="0.29687500" bottom="0.53125000"/>
|
||||
</Texture>
|
||||
<Texture file="Interface\HelpFrame\CS_HelpTextures" parentKey="arrowDown">
|
||||
<Size x="21" y="30"/>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativePoint="RIGHT" x="0" y="0"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0.63281250" right="0.67382813" top="0.75781250" bottom="0.99218750"/>
|
||||
</Texture>
|
||||
<Texture file="Interface\HelpFrame\CS_HelpTextures" parentKey="selected" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" x="0" y="0"/>
|
||||
<Anchor point="BOTTOMRIGHT" x="0" y="0"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0.00195313" right="0.25195313" top="0.37500000" bottom="0.64062500"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Button parentKey="MenuArrowButton" hidden="true">
|
||||
<Size x="27" y="31"/>
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT" relativePoint="TOPRIGHT" x="-2" y="-15"/>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="OVERLAY">
|
||||
<Texture parentKey="Art" file="Interface\Buttons\SquareButtonTextures">
|
||||
<Size x="12" y="12"/>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER" x="0" y="-1"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0.45312500" right="0.64062500" bottom="0.01562500" top="0.20312500"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<NormalTexture file="Interface\Buttons\UI-SquareButton-Up" parentKey="NormalTexture" alpha="0">
|
||||
<Size x="32" y="32"/>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"/>
|
||||
</Anchors>
|
||||
</NormalTexture>
|
||||
<PushedTexture file="Interface\Buttons\UI-SquareButton-Down" parentKey="PushedTexture" alpha="0">
|
||||
<Size x="32" y="32"/>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"/>
|
||||
</Anchors>
|
||||
</PushedTexture>
|
||||
<HighlightTexture file="Interface\Buttons\UI-Common-MouseHilight" alphaMode="ADD">
|
||||
<Size x="32" y="32"/>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"/>
|
||||
</Anchors>
|
||||
</HighlightTexture>
|
||||
<Scripts>
|
||||
<OnMouseDown>
|
||||
if self:IsEnabled() == 1 then
|
||||
self.Art:SetPoint("CENTER", -1, -2);
|
||||
end
|
||||
</OnMouseDown>
|
||||
<OnMouseUp>
|
||||
if self:IsEnabled() == 1 then
|
||||
self.Art:SetPoint("CENTER", 0, -1);
|
||||
PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON);
|
||||
NavBar_ToggleMenu(self:GetParent());
|
||||
end
|
||||
</OnMouseUp>
|
||||
<OnEnter>
|
||||
self.NormalTexture:SetAlpha(1);
|
||||
self.PushedTexture:SetAlpha(1);
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
self.NormalTexture:SetAlpha(0);
|
||||
self.PushedTexture:SetAlpha(0);
|
||||
</OnLeave>
|
||||
<OnEnable>
|
||||
self.NormalTexture:SetDesaturated(false)
|
||||
self.PushedTexture:SetDesaturated(false)
|
||||
self.Art:SetDesaturated(false)
|
||||
</OnEnable>
|
||||
<OnDisable>
|
||||
self.NormalTexture:SetDesaturated(true)
|
||||
self.PushedTexture:SetDesaturated(true)
|
||||
self.Art:SetDesaturated(true)
|
||||
</OnDisable>
|
||||
</Scripts>
|
||||
</Button>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnMouseDown>
|
||||
if self:IsEnabled() == 1 then
|
||||
self.arrowUp:Hide();
|
||||
self.arrowDown:Show();
|
||||
end
|
||||
</OnMouseDown>
|
||||
<OnMouseUp>
|
||||
self.arrowDown:Hide();
|
||||
self.arrowUp:Show();
|
||||
</OnMouseUp>
|
||||
<OnClick>
|
||||
PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON);
|
||||
</OnClick>
|
||||
<OnEnable>
|
||||
self.arrowDown:Hide();
|
||||
self.arrowUp:Show();
|
||||
</OnEnable>
|
||||
<OnDisable>
|
||||
self.arrowDown:Hide();
|
||||
self.arrowUp:Show();
|
||||
</OnDisable>
|
||||
</Scripts>
|
||||
<NormalTexture file="Interface\HelpFrame\CS_HelpTextures_Tile" parentKey="NormalTexture" horizTile="true">
|
||||
<TexCoords top="0.06250000" bottom="0.12109375"/>
|
||||
</NormalTexture>
|
||||
<PushedTexture file="Interface\HelpFrame\CS_HelpTextures_Tile" parentKey="PushedTexture" horizTile="true">
|
||||
<TexCoords top="0.12500000" bottom="0.18359375"/>
|
||||
</PushedTexture>
|
||||
<HighlightTexture file="Interface\HelpFrame\CS_HelpTextures" parentKey="HighlightTexture" alphaMode="ADD">
|
||||
<TexCoords left="0.00195313" right="0.25195313" top="0.65625000" bottom="0.92187500"/>
|
||||
</HighlightTexture>
|
||||
<ButtonText name="$parentText" inherits="GameFontNormal" justifyH="LEFT" parentKey="text">
|
||||
<Size x="0" y="12"/>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" x="20" y="0"/>
|
||||
</Anchors>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
|
||||
<Frame name="NavBarTemplate" parentKey="navBar" virtual="true">
|
||||
<Size x="300" y="34"/>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture file="Interface\HelpFrame\CS_HelpTextures_Tile" horizTile="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativePoint="TOPLEFT" x="0" y="0"/>
|
||||
<Anchor point="BOTTOMRIGHT" relativePoint="BOTTOMRIGHT" x="0" y="0"/>
|
||||
</Anchors>
|
||||
<TexCoords top="0.18750000" bottom="0.25390625"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Frame name="$parentOverlay" parentKey="overlay">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" x="0" y="0"/>
|
||||
<Anchor point="BOTTOMRIGHT" x="0" y="0"/>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="OVERLAY">
|
||||
<Texture file="Interface\HelpFrame\CS_HelpTextures_Tile" horizTile="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" x="0" y="0"/>
|
||||
<Anchor point="BOTTOMRIGHT" x="0" y="0"/>
|
||||
</Anchors>
|
||||
<TexCoords top="0.25781250" bottom="0.32421875"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
self:SetFrameLevel(50);
|
||||
</OnLoad>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
<Button name="$parentOverflowButton" parentKey="overflow">
|
||||
<Size x="44" y="30"/>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
self.xoffset = -18
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
<NormalTexture file="Interface\HelpFrame\CS_HelpTextures">
|
||||
<TexCoords left="0.54296875" right="0.62890625" top="0.75781250" bottom="0.99218750"/>
|
||||
</NormalTexture>
|
||||
<PushedTexture file="Interface\HelpFrame\CS_HelpTextures">
|
||||
<TexCoords left="0.45312500" right="0.53906250" top="0.75781250" bottom="0.99218750"/>
|
||||
</PushedTexture>
|
||||
<HighlightTexture file="Interface\HelpFrame\CS_HelpTextures" alphaMode="ADD" alpha="0.4">
|
||||
<TexCoords left="0.54296875" right="0.62890625" top="0.75781250" bottom="0.99218750"/>
|
||||
</HighlightTexture>
|
||||
</Button>
|
||||
<Button name="$parentHomeButton" parentKey="home" text="NAVIGATIONBAR_HOME">
|
||||
<Size x="128" y="30"/>
|
||||
<Layers>
|
||||
<Layer level="OVERLAY">
|
||||
<Texture name="$parentLeft" file="Interface\Common\ShadowOverlay-Left">
|
||||
<Size x="30" y="30"/>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" x="0" y="0"/>
|
||||
</Anchors>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
self.xoffset = -15;
|
||||
local newWidth = min(128, self.text:GetStringWidth()+50)
|
||||
local texCoordoffsetX = (newWidth/128)*0.25;
|
||||
|
||||
self:GetNormalTexture():SetTexCoord(0.70312500-texCoordoffsetX, 0.70312500, 0.00781250, 0.24218750);
|
||||
self:GetPushedTexture():SetTexCoord(0.70312500-texCoordoffsetX, 0.70312500, 0.25781250, 0.49218750);
|
||||
self:GetHighlightTexture():SetTexCoord(0.70312500-texCoordoffsetX, 0.71312500, 0.50781250, 0.74218750);
|
||||
|
||||
self:SetWidth(newWidth);
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
<NormalTexture file="Interface\HelpFrame\CS_HelpTextures">
|
||||
</NormalTexture>
|
||||
<PushedTexture file="Interface\HelpFrame\CS_HelpTextures">
|
||||
</PushedTexture>
|
||||
<HighlightTexture file="Interface\HelpFrame\CS_HelpTextures" alphaMode="ADD">
|
||||
</HighlightTexture>
|
||||
<ButtonText name="$parentText" inherits="GameFontNormal" justifyH="LEFT" parentKey="text">
|
||||
<Size x="0" y="12"/>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" x="10" y="0"/>
|
||||
<Anchor point="RIGHT" x="-30" y="0"/>
|
||||
</Anchors>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
</Frames>
|
||||
</Frame>
|
||||
</Ui>
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
local SetPortraitTexture = SetPortraitTexture
|
||||
local SetBagPortraitTexture = SetBagPortraitTexture
|
||||
|
||||
TitledPanelMixin = {};
|
||||
|
||||
function TitledPanelMixin:GetTitleText()
|
||||
return self.TitleContainer.TitleText;
|
||||
end
|
||||
|
||||
function TitledPanelMixin:SetTitleColor(color)
|
||||
self:GetTitleText():SetTextColor(color:GetRGBA());
|
||||
end
|
||||
|
||||
function TitledPanelMixin:SetTitle(title)
|
||||
self:GetTitleText():SetText(title);
|
||||
end
|
||||
|
||||
function TitledPanelMixin:SetTitleFormatted(fmt, ...)
|
||||
self:GetTitleText():SetFormattedText(fmt, ...);
|
||||
end
|
||||
|
||||
function TitledPanelMixin:SetTitleMaxLinesAndHeight(maxLines, height)
|
||||
self:GetTitleText():SetMaxLines(maxLines);
|
||||
self:GetTitleText():SetHeight(height);
|
||||
end
|
||||
|
||||
function TitledPanelMixin:SetTitleOffsets(leftOffset, rightOffset)
|
||||
self.TitleContainer:SetPoint("TOPLEFT", self, "TOPLEFT", leftOffset or 58, -1);
|
||||
self.TitleContainer:SetPoint("TOPRIGHT", self, "TOPRIGHT", rightOffset or -24, -1);
|
||||
end
|
||||
|
||||
DefaultPanelMixin = CreateFromMixins(TitledPanelMixin);
|
||||
|
||||
PortraitFrameMixin = CreateFromMixins(TitledPanelMixin);
|
||||
|
||||
function PortraitFrameMixin:GetPortrait()
|
||||
return self.portrait;
|
||||
end
|
||||
|
||||
function PortraitFrameMixin:GetTitleText()
|
||||
return self.TitleText;
|
||||
end
|
||||
|
||||
function PortraitFrameMixin:SetBorder(layoutName)
|
||||
local layout = NineSliceUtil.GetLayout(layoutName);
|
||||
NineSliceUtil.ApplyLayout(self.NineSlice, layout);
|
||||
end
|
||||
|
||||
function PortraitFrameMixin:SetPortraitToAsset(texture)
|
||||
SetPortraitToTexture(self:GetPortrait(), texture);
|
||||
end
|
||||
|
||||
function PortraitFrameMixin:SetPortraitToUnit(unit)
|
||||
SetPortraitTexture(self:GetPortrait(), unit);
|
||||
end
|
||||
|
||||
function PortraitFrameMixin:SetPortraitToBag(bagID)
|
||||
SetBagPortraitTexture(self:GetPortrait(), bagID);
|
||||
end
|
||||
|
||||
function PortraitFrameMixin:SetPortraitTextureRaw(texture)
|
||||
self:GetPortrait():SetTexture(texture);
|
||||
end
|
||||
|
||||
function PortraitFrameMixin:SetPortraitAtlasRaw(atlas, ...)
|
||||
self:GetPortrait():SetAtlas(atlas, ...);
|
||||
end
|
||||
|
||||
function PortraitFrameMixin:SetPortraitToClassIcon(classFilename)
|
||||
self:SetPortraitToAsset("Interface/TargetingFrame/UI-Classes-Circles");
|
||||
local left, right, bottom, top = unpack(CLASS_ICON_TCOORDS[string.upper(classFilename)]);
|
||||
self:SetPortraitTexCoord(left, right, bottom, top);
|
||||
end
|
||||
|
||||
function PortraitFrameMixin:SetPortraitTexCoord(...)
|
||||
self:GetPortrait():SetTexCoord(...);
|
||||
end
|
||||
|
||||
function PortraitFrameMixin:SetPortraitShown(shown)
|
||||
self:GetPortrait():SetShown(shown);
|
||||
end
|
||||
|
||||
function PortraitFrameMixin:SetPortraitTextureSizeAndOffset(size, offsetX, offsetY)
|
||||
local portrait = self:GetPortrait();
|
||||
portrait:SetSize(size, size);
|
||||
portrait:SetPoint("TOPLEFT", self, "TOPLEFT", offsetX, offsetY);
|
||||
end
|
||||
|
||||
do
|
||||
local function SetFrameLevelInternal(frame, level)
|
||||
if frame then
|
||||
-- assertsafe(level < 10000, "Base level needs to be low enough to accomodate the existing layering"); -- TODO: Should become a constant
|
||||
frame:SetFrameLevel(level);
|
||||
end
|
||||
end
|
||||
|
||||
function PortraitFrameMixin:SetFrameLevelsFromBaseLevel(baseLevel)
|
||||
SetFrameLevelInternal(self.NineSlice, baseLevel + 2);
|
||||
SetFrameLevelInternal(self.PortraitContainer, baseLevel + 1);
|
||||
SetFrameLevelInternal(self.TitleContainer, baseLevel + 3);
|
||||
SetFrameLevelInternal(self.CloseButton, baseLevel + 3);
|
||||
end
|
||||
end
|
||||
|
||||
PortraitFrameFlatBaseMixin = {};
|
||||
|
||||
function PortraitFrameFlatBaseMixin:SetBackgroundColor(color)
|
||||
if self.Bg then
|
||||
local bg = self.Bg;
|
||||
color = color or PANEL_BACKGROUND_COLOR;
|
||||
local r, g, b, a = color:GetRGBA();
|
||||
bg.BottomLeft:SetVertexColor(r, g, b, a);
|
||||
bg.BottomRight:SetVertexColor(r, g, b, a);
|
||||
bg.BottomEdge:SetColorTexture(r, g, b, a);
|
||||
bg.TopSection:SetColorTexture(r, g, b, a);
|
||||
end
|
||||
end
|
||||
+1536
File diff suppressed because it is too large
Load Diff
+2443
File diff suppressed because it is too large
Load Diff
+365
@@ -0,0 +1,365 @@
|
||||
-- SOUND KITS
|
||||
SOUNDKIT = {
|
||||
LOOT_WINDOW_COIN_SOUND = 120,
|
||||
INTERFACE_SOUND_LOST_TARGET_UNIT = 684,
|
||||
GS_TITLE_OPTIONS = 778,
|
||||
GS_TITLE_CREDITS = 790,
|
||||
GS_TITLE_OPTION_OK = 798,
|
||||
GS_TITLE_OPTION_EXIT = 799,
|
||||
GS_LOGIN = 800,
|
||||
GS_LOGIN_NEW_ACCOUNT = 801,
|
||||
GS_LOGIN_CHANGE_REALM_OK = 805,
|
||||
GS_LOGIN_CHANGE_REALM_CANCEL = 807,
|
||||
GS_CHARACTER_SELECTION_ENTER_WORLD = 809,
|
||||
GS_CHARACTER_SELECTION_DEL_CHARACTER = 810,
|
||||
GS_CHARACTER_SELECTION_ACCT_OPTIONS = 811,
|
||||
GS_CHARACTER_SELECTION_EXIT = 812,
|
||||
GS_CHARACTER_SELECTION_CREATE_NEW = 813,
|
||||
GS_CHARACTER_CREATION_CLASS = 814,
|
||||
GS_CHARACTER_CREATION_LOOK = 817,
|
||||
GS_CHARACTER_CREATION_CREATE_CHAR = 818,
|
||||
GS_CHARACTER_CREATION_CANCEL = 819,
|
||||
IG_MINIMAP_OPEN = 821,
|
||||
IG_MINIMAP_CLOSE = 822,
|
||||
IG_MINIMAP_ZOOM_IN = 823,
|
||||
IG_MINIMAP_ZOOM_OUT = 824,
|
||||
IG_CHAT_EMOTE_BUTTON = 825,
|
||||
IG_CHAT_SCROLL_UP = 826,
|
||||
IG_CHAT_SCROLL_DOWN = 827,
|
||||
IG_CHAT_BOTTOM = 828,
|
||||
IG_SPELLBOOK_OPEN = 829,
|
||||
IG_SPELLBOOK_CLOSE = 830,
|
||||
IG_ABILITY_OPEN = 834,
|
||||
IG_ABILITY_CLOSE = 835,
|
||||
IG_ABILITY_PAGE_TURN = 836,
|
||||
IG_ABILITY_ICON_DROP = 838,
|
||||
IG_CHARACTER_INFO_OPEN = 839,
|
||||
IG_CHARACTER_INFO_CLOSE = 840,
|
||||
IG_CHARACTER_INFO_TAB = 841,
|
||||
IG_QUEST_LOG_OPEN = 844,
|
||||
IG_QUEST_LOG_CLOSE = 845,
|
||||
IG_QUEST_LOG_ABANDON_QUEST = 846,
|
||||
IG_MAINMENU_OPEN = 850,
|
||||
IG_MAINMENU_CLOSE = 851,
|
||||
IG_MAINMENU_OPTION = 852,
|
||||
IG_MAINMENU_LOGOUT = 853,
|
||||
IG_MAINMENU_QUIT = 854,
|
||||
IG_MAINMENU_CONTINUE = 855,
|
||||
IG_MAINMENU_OPTION_CHECKBOX_ON = 856,
|
||||
IG_MAINMENU_OPTION_CHECKBOX_OFF = 857,
|
||||
IG_MAINMENU_OPTION_FAER_TAB = 858,
|
||||
IG_INVENTORY_ROTATE_CHARACTER = 861,
|
||||
IG_BACKPACK_OPEN = 862,
|
||||
IG_BACKPACK_CLOSE = 863,
|
||||
IG_BACKPACK_COIN_SELECT = 864,
|
||||
IG_BACKPACK_COIN_OK = 865,
|
||||
IG_BACKPACK_COIN_CANCEL = 866,
|
||||
IG_CHARACTER_NPC_SELECT = 867,
|
||||
IG_CREATURE_NEUTRAL_SELECT = 871,
|
||||
IG_CREATURE_AGGRO_SELECT = 873,
|
||||
IG_QUEST_LIST_OPEN = 875,
|
||||
IG_QUEST_LIST_CLOSE = 876,
|
||||
IG_QUEST_LIST_SELECT = 877,
|
||||
IG_QUEST_LIST_COMPLETE = 878,
|
||||
IG_QUEST_CANCEL = 879,
|
||||
IG_PLAYER_INVITE = 880,
|
||||
MONEY_FRAME_OPEN = 891,
|
||||
MONEY_FRAME_CLOSE = 892,
|
||||
U_CHAT_SCROLL_BUTTON = 1115,
|
||||
PUT_DOWN_SMALL_CHAIN = 1212,
|
||||
LOOT_WINDOW_OPEN_EMPTY = 1264,
|
||||
TELL_MESSAGE = 3081,
|
||||
MAP_PING = 3175,
|
||||
FISHING_REEL_IN = 3407,
|
||||
IG_PVP_UPDATE = 4574,
|
||||
AUCTION_WINDOW_OPEN = 5274,
|
||||
AUCTION_WINDOW_CLOSE = 5275,
|
||||
TUTORIAL_POPUP = 7355,
|
||||
ITEM_REPAIR = 7994,
|
||||
PVP_ENTER_QUEUE = 8458,
|
||||
PVP_THROUGH_QUEUE = 8459,
|
||||
KEY_RING_OPEN = 8938,
|
||||
KEY_RING_CLOSE = 8939,
|
||||
RAID_WARNING = 8959,
|
||||
READY_CHECK = 8960,
|
||||
GLUESCREEN_INTRO = 9902,
|
||||
AMB_GLUESCREEN_HUMAN = 9903,
|
||||
AMB_GLUESCREEN_ORC = 9905,
|
||||
AMB_GLUESCREEN_TAUREN = 9906,
|
||||
AMB_GLUESCREEN_DWARF = 9907,
|
||||
AMB_GLUESCREEN_NIGHTELF = 9908,
|
||||
AMB_GLUESCREEN_UNDEAD = 9909,
|
||||
AMB_GLUESCREEN_BLOODELF = 9910,
|
||||
AMB_GLUESCREEN_DRAENEI = 9911,
|
||||
JEWEL_CRAFTING_FINALIZE = 10590,
|
||||
MENU_CREDITS01 = 10763,
|
||||
MENU_CREDITS02 = 10804,
|
||||
GUILD_VAULT_OPEN = 12188,
|
||||
GUILD_VAULT_CLOSE = 12189,
|
||||
RAID_BOSS_EMOTE_WARNING = 12197,
|
||||
GUILD_BANK_OPEN_BAG = 12206,
|
||||
GS_LICH_KING = 12765,
|
||||
ALARM_CLOCK_WARNING_2 = 12867,
|
||||
ALARM_CLOCK_WARNING_3 = 12889,
|
||||
MENU_CREDITS03 = 13822,
|
||||
ACHIEVEMENT_MENU_OPEN = 13832,
|
||||
ACHIEVEMENT_MENU_CLOSE = 13833,
|
||||
BARBERSHOP_HAIRCUT = 13873,
|
||||
BARBERSHOP_SIT = 14148,
|
||||
GM_CHAT_WARNING = 15273,
|
||||
LFG_REWARDS = 17316,
|
||||
LFG_ROLE_CHECK = 17317,
|
||||
LFG_DENIED = 17341,
|
||||
UI_BNET_TOAST = 18019,
|
||||
ALARM_CLOCK_WARNING_1 = 18871,
|
||||
AMB_GLUESCREEN_WORGEN = 20169,
|
||||
AMB_GLUESCREEN_GOBLIN = 20170,
|
||||
AMB_GLUESCREEN_TROLL = 21136,
|
||||
AMB_GLUESCREEN_GNOME = 21137,
|
||||
UI_POWER_AURA_GENERIC = 23287,
|
||||
UI_REFORGING_REFORGE = 23291,
|
||||
UI_AUTO_QUEST_COMPLETE = 23404,
|
||||
GS_CATACLYSM = 23640,
|
||||
MENU_CREDITS04 = 23812,
|
||||
UI_BATTLEGROUND_COUNTDOWN_TIMER = 25477,
|
||||
UI_BATTLEGROUND_COUNTDOWN_FINISHED = 25478,
|
||||
UI_VOID_STORAGE_UNLOCK = 25711,
|
||||
UI_VOID_STORAGE_DEPOSIT = 25712,
|
||||
UI_VOID_STORAGE_WITHDRAW = 25713,
|
||||
UI_TRANSMOGRIFY_UNDO = 25715,
|
||||
UI_ETHEREAL_WINDOW_OPEN = 25716,
|
||||
UI_ETHEREAL_WINDOW_CLOSE = 25717,
|
||||
UI_TRANSMOGRIFY_REDO = 25738,
|
||||
UI_VOID_STORAGE_BOTH = 25744,
|
||||
AMB_GLUESCREEN_PANDAREN = 25848,
|
||||
MUS_50_HEART_OF_PANDARIA_MAINTITLE = 28509,
|
||||
UI_PET_BATTLES_TRAP_READY = 28814,
|
||||
UI_EPICLOOT_TOAST = 31578,
|
||||
UI_BONUS_LOOT_ROLL_START = 31579,
|
||||
UI_BONUS_LOOT_ROLL_LOOP = 31580,
|
||||
UI_BONUS_LOOT_ROLL_END = 31581,
|
||||
UI_PET_BATTLE_START = 31584,
|
||||
UI_SCENARIO_ENDING = 31754,
|
||||
UI_SCENARIO_STAGE_END = 31757,
|
||||
MENU_CREDITS05 = 32015,
|
||||
UI_PET_BATTLE_CAMERA_MOVE_IN = 32047,
|
||||
UI_PET_BATTLE_CAMERA_MOVE_OUT = 32052,
|
||||
AMB_50_GLUESCREEN_ALLIANCE = 32412,
|
||||
AMB_50_GLUESCREEN_HORDE = 32413,
|
||||
AMB_50_GLUESCREEN_PANDAREN_NEUTRAL = 32414,
|
||||
UI_CHALLENGES_NEW_RECORD = 33338,
|
||||
MENU_CREDITS06 = 34020,
|
||||
UI_LOSS_OF_CONTROL_START = 34468,
|
||||
UI_PET_BATTLES_PVP_THROUGH_QUEUE = 36609,
|
||||
AMB_GLUESCREEN_DEATHKNIGHT = 37056,
|
||||
UI_RAID_BOSS_WHISPER_WARNING = 37666,
|
||||
UI_DIG_SITE_COMPLETION_TOAST = 38326,
|
||||
UI_IG_STORE_PAGE_NAV_BUTTON = 39511,
|
||||
UI_IG_STORE_WINDOW_OPEN_BUTTON = 39512,
|
||||
UI_IG_STORE_WINDOW_CLOSE_BUTTON = 39513,
|
||||
UI_IG_STORE_CANCEL_BUTTON = 39514,
|
||||
UI_IG_STORE_BUY_BUTTON = 39515,
|
||||
UI_IG_STORE_CONFIRM_PURCHASE_BUTTON = 39516,
|
||||
UI_IG_STORE_PURCHASE_DELIVERED_TOAST_01 = 39517,
|
||||
MUS_60_MAIN_TITLE = 40169,
|
||||
UI_GARRISON_MISSION_COMPLETE_ENCOUNTER_FAIL = 43501,
|
||||
UI_GARRISON_MISSION_COMPLETE_MISSION_SUCCESS = 43502,
|
||||
UI_GARRISON_MISSION_COMPLETE_MISSION_FAIL_STINGER = 43503,
|
||||
UI_GARRISON_MISSION_THREAT_COUNTERED = 43505,
|
||||
UI_GARRISON_MISSION_100_PERCENT_CHANCE_REACHED_NOT_USED = 43507,
|
||||
UI_QUEST_ROLLING_FORWARD_01 = 43936,
|
||||
UI_BAG_SORTING_01 = 43937,
|
||||
UI_TOYBOX_TABS = 43938,
|
||||
UI_GARRISON_TOAST_INVASION_ALERT = 44292,
|
||||
UI_GARRISON_TOAST_MISSION_COMPLETE = 44294,
|
||||
UI_GARRISON_TOAST_BUILDING_COMPLETE = 44295,
|
||||
UI_GARRISON_TOAST_FOLLOWER_GAINED = 44296,
|
||||
UI_GARRISON_NAV_TABS = 44297,
|
||||
UI_GARRISON_GARRISON_REPORT_OPEN = 44298,
|
||||
UI_GARRISON_GARRISON_REPORT_CLOSE = 44299,
|
||||
UI_GARRISON_ARCHITECT_TABLE_OPEN = 44300,
|
||||
UI_GARRISON_ARCHITECT_TABLE_CLOSE = 44301,
|
||||
UI_GARRISON_ARCHITECT_TABLE_UPGRADE = 44302,
|
||||
UI_GARRISON_ARCHITECT_TABLE_UPGRADE_CANCEL = 44304,
|
||||
UI_GARRISON_ARCHITECT_TABLE_UPGRADE_START = 44305,
|
||||
UI_GARRISON_ARCHITECT_TABLE_PLOT_SELECT = 44306,
|
||||
UI_GARRISON_ARCHITECT_TABLE_BUILDING_SELECT = 44307,
|
||||
UI_GARRISON_ARCHITECT_TABLE_BUILDING_PLACEMENT = 44308,
|
||||
UI_GARRISON_COMMAND_TABLE_OPEN = 44311,
|
||||
UI_GARRISON_COMMAND_TABLE_CLOSE = 44312,
|
||||
UI_GARRISON_COMMAND_TABLE_MISSION_CLOSE = 44313,
|
||||
UI_GARRISON_COMMAND_TABLE_NAV_NEXT = 44314,
|
||||
UI_GARRISON_COMMAND_TABLE_SELECT_MISSION = 44315,
|
||||
UI_GARRISON_COMMAND_TABLE_SELECT_FOLLOWER = 44316,
|
||||
UI_GARRISON_COMMAND_TABLE_FOLLOWER_ABILITY_OPEN = 44317,
|
||||
UI_GARRISON_COMMAND_TABLE_FOLLOWER_ABILITY_CLOSE = 44318,
|
||||
UI_GARRISON_COMMAND_TABLE_ASSIGN_FOLLOWER = 44319,
|
||||
UI_GARRISON_COMMAND_TABLE_UNASSIGN_FOLLOWER = 44320,
|
||||
UI_GARRISON_COMMAND_TABLE_SLOT_CHAMPION = 72546,
|
||||
UI_GARRISON_COMMAND_TABLE_SLOT_TROOP = 72547,
|
||||
UI_GARRISON_COMMAND_TABLE_REDUCED_SUCCESS_CHANCE = 44321,
|
||||
UI_GARRISON_COMMAND_TABLE_100_SUCCESS = 44322,
|
||||
UI_GARRISON_COMMAND_TABLE_MISSION_START = 44323,
|
||||
UI_GARRISON_COMMAND_TABLE_VIEW_MISSION_REPORT = 44324,
|
||||
UI_GARRISON_COMMAND_TABLE_MISSION_SUCCESS_STINGER = 44330,
|
||||
UI_GARRISON_COMMAND_TABLE_CHEST_UNLOCK = 44331,
|
||||
UI_GARRISON_COMMAND_TABLE_CHEST_UNLOCK_GOLD_SUCCESS = 44332,
|
||||
UI_GARRISON_MONUMENTS_OPEN = 44344,
|
||||
UI_BONUS_EVENT_SYSTEM_VIGNETTES = 45142,
|
||||
UI_GARRISON_COMMAND_TABLE_FOLLOWER_LEVEL_UP = 46893,
|
||||
UI_GARRISON_ARCHITECT_TABLE_BUILDING_PLACEMENT_ERROR = 47355,
|
||||
UI_GARRISON_MONUMENTS_CLOSE = 47373,
|
||||
AMB_GLUESCREEN_WARLORDS_OF_DRAENOR = 47544,
|
||||
MUS_1_0_MAINTITLE_ORIGINAL = 47598,
|
||||
UI_GROUP_FINDER_RECEIVE_APPLICATION = 47615,
|
||||
UI_GARRISON_MISSION_ENCOUNTER_ANIMATION_GENERIC = 47704,
|
||||
UI_GARRISON_START_WORK_ORDER = 47972,
|
||||
UI_GARRISON_SHIPMENTS_WINDOW_OPEN = 48191,
|
||||
UI_GARRISON_SHIPMENTS_WINDOW_CLOSE = 48192,
|
||||
UI_GARRISON_MONUMENTS_NAV = 48942,
|
||||
UI_RAID_BOSS_DEFEATED = 50111,
|
||||
UI_PERSONAL_LOOT_BANNER = 50893,
|
||||
UI_GARRISON_FOLLOWER_LEARN_TRAIT = 51324,
|
||||
UI_GARRISON_SHIPYARD_PLACE_CARRIER = 51385,
|
||||
UI_GARRISON_SHIPYARD_PLACE_GALLEON = 51387,
|
||||
UI_GARRISON_SHIPYARD_PLACE_DREADNOUGHT = 51388,
|
||||
UI_GARRISON_SHIPYARD_PLACE_SUBMARINE = 51389,
|
||||
UI_GARRISON_SHIPYARD_PLACE_LANDING_CRAFT = 51390,
|
||||
UI_GARRISON_SHIPYARD_START_MISSION = 51401,
|
||||
UI_RAID_LOOT_TOAST_LESSER_ITEM_WON = 51402,
|
||||
UI_WARFORGED_ITEM_LOOT_TOAST = 51561,
|
||||
UI_GARRISON_COMMAND_TABLE_INCREASED_SUCCESS_CHANCE = 51570,
|
||||
UI_GARRISON_SHIPYARD_DECOMISSION_SHIP = 51871,
|
||||
UI_70_ARTIFACT_FORGE_TRAIT_FIRST_TRAIT = 54126,
|
||||
UI_70_ARTIFACT_FORGE_RELIC_PLACE = 54128,
|
||||
UI_70_ARTIFACT_FORGE_APPEARANCE_COLOR_SELECT = 54130,
|
||||
UI_70_ARTIFACT_FORGE_APPEARANCE_LOCKED = 54131,
|
||||
UI_70_ARTIFACT_FORGE_APPEARANCE_APPEARANCE_CHANGE = 54132,
|
||||
UI_70_ARTIFACT_FORGE_TOAST_TRAIT_AVAILABLE = 54133,
|
||||
UI_70_ARTIFACT_FORGE_APPEARANCE_APPEARANCE_UNLOCK = 54139,
|
||||
UI_70_ARTIFACT_FORGE_TRAIT_GOLD_TRAIT = 54125,
|
||||
UI_72_ARTIFACT_FORGE_FINAL_TRAIT_UNLOCKED = 83682,
|
||||
UI_70_ARTIFACT_FORGE_TRAIT_FINALRANK = 54127,
|
||||
UI_70_ARTIFACT_FORGE_TRAIT_RANKUP = 54129,
|
||||
AMB_GLUESCREEN_DEMONHUNTER = 56352,
|
||||
MUS_70_MAIN_TITLE = 56353,
|
||||
MENU_CREDITS07 = 56354,
|
||||
UI_TRANSMOG_ITEM_CLICK = 62538,
|
||||
UI_TRANSMOG_PAGE_TURN = 62539,
|
||||
UI_TRANSMOG_GEAR_SLOT_CLICK = 62540,
|
||||
UI_TRANSMOG_REVERTING_GEAR_SLOT = 62541,
|
||||
UI_TRANSMOG_APPLY = 62542,
|
||||
UI_TRANSMOG_CLOSE_WINDOW = 62543,
|
||||
UI_TRANSMOG_OPEN_WINDOW = 62544,
|
||||
UI_LEGENDARY_LOOT_TOAST = 63971,
|
||||
UI_STORE_UNWRAP = 64329,
|
||||
AMB_GLUESCREEN_LEGION = 71535,
|
||||
UI_MISSION_200_PERCENT = 72548,
|
||||
UI_MISSION_MAP_ZOOM = 72549,
|
||||
UI_70_BOOST_THANKSFORPLAYING_SMALLER = 72978,
|
||||
UI_70_BOOST_THANKSFORPLAYING = 72977,
|
||||
UI_WORLDQUEST_START = 73275,
|
||||
UI_WORLDQUEST_MAP_SELECT = 73276,
|
||||
UI_WORLDQUEST_COMPLETE = 73277,
|
||||
UI_ORDERHALL_TALENT_SELECT = 73279,
|
||||
UI_ORDERHALL_TALENT_READY_TOAST = 73280,
|
||||
UI_ORDERHALL_TALENT_READY_CHECK = 73281,
|
||||
UI_ORDERHALL_TALENT_NUKE_FROM_ORBIT = 73282,
|
||||
UI_ORDERHALL_TALENT_WINDOW_OPEN = 73914,
|
||||
UI_ORDERHALL_TALENT_WINDOW_CLOSE = 73915,
|
||||
UI_PROFESSIONS_WINDOW_OPEN = 73917,
|
||||
UI_PROFESSIONS_WINDOW_CLOSE = 73918,
|
||||
UI_PROFESSIONS_NEW_RECIPE_LEARNED_TOAST = 73919,
|
||||
UI_70_CHALLENGE_MODE_SOCKET_PAGE_OPEN = 74421,
|
||||
UI_70_CHALLENGE_MODE_SOCKET_PAGE_CLOSE = 74423,
|
||||
UI_70_CHALLENGE_MODE_SOCKET_PAGE_SOCKET = 74431,
|
||||
UI_70_CHALLENGE_MODE_SOCKET_PAGE_ACTIVATE_BUTTON = 74432,
|
||||
UI_70_CHALLENGE_MODE_KEYSTONE_UPGRADE = 74437,
|
||||
UI_70_CHALLENGE_MODE_NEW_RECORD = 74438,
|
||||
UI_70_CHALLENGE_MODE_SOCKET_PAGE_REMOVE_KEYSTONE = 74525,
|
||||
UI_70_CHALLENGE_MODE_COMPLETE_NO_UPGRADE = 74526,
|
||||
UI_MISSION_SUCCESS_CHEERS = 74702,
|
||||
UI_PVP_HONOR_PRESTIGE_OPEN_WINDOW = 76995,
|
||||
UI_PVP_HONOR_PRESTIGE_WINDOW_CLOSE = 77002,
|
||||
UI_PVP_HONOR_PRESTIGE_RANK_UP = 77003,
|
||||
UI_71_SOCIAL_QUEUEING_TOAST = 79739,
|
||||
UI_72_ARTIFACT_FORGE_ACTIVATE_FINAL_TIER = 83681,
|
||||
UI_72_BUILDINGS_CONTRIBUTE_POWER_MENU_CLICK = 84240,
|
||||
UI_72_BUILDING_CONTRIBUTION_TABLE_OPEN = 84368,
|
||||
UI_72_BUILDINGS_CONTRIBUTION_TABLE_CLOSE = 84369,
|
||||
UI_72_BUILDINGS_CONTRIBUTE_RESOURCES = 84378,
|
||||
UI_72_ARTIFACT_FORGE_FINAL_TRAIT_REFUND_START = 83684,
|
||||
UI_72_ARTIFACT_FORGE_FINAL_TRAIT_REFUND_LOOP = 83685,
|
||||
UI_73_ARTIFACT_RELICS_TRAIT_SELECT_AND_REVEAL = 89685,
|
||||
UI_73_ARTIFACT_RELICS_TRAIT_SELECT_ONLY = 89686,
|
||||
UI_73_ARTIFACT_RELICS_TRAIT_REVEAL_ONLY = 90080,
|
||||
PUT_DOWN_GEMS = 1204,
|
||||
PICK_UP_GEMS = 1221,
|
||||
UI_73_ARTIFACT_OVERLOADED_HIGHEST = 97301,
|
||||
UI_73_ARTIFACT_OVERLOADED_HIGH = 97471,
|
||||
UI_73_ARTIFACT_OVERLOADED_MEDIUM = 97470,
|
||||
UI_73_ARTIFACT_OVERLOADED_LOW = 97469,
|
||||
UI_73_ARTIFACT_OVERLOADED_ORB_HIGHEST = 97559,
|
||||
UI_73_ARTIFACT_OVERLOADED_ORB_HIGH = 97558,
|
||||
UI_73_ARTIFACT_OVERLOADED_ORB_MEDIUM = 97557,
|
||||
UI_73_ARTIFACT_OVERLOADED_ORB_LOW = 97556,
|
||||
UI_73_ARTIFACT_OVERLOADED_ORB_IMPACT_MEDIUM = 97597,
|
||||
UI_73_ARTIFACT_OVERLOADED_ORB_IMPACT_LOW = 97598,
|
||||
AMB_GLUESCREEN_VOIDELF = 97324,
|
||||
AMB_GLUESCREEN_LIGHTFORGEDDRAENEI = 97325,
|
||||
AMB_GLUESCREEN_NIGHTBORNE = 97326,
|
||||
AMB_GLUESCREEN_HIGHMOUNTAINTAUREN = 97327,
|
||||
UI_VOICECHAT_DEAFENOFF = 110990,
|
||||
UI_VOICECHAT_DEAFENON = 110989,
|
||||
UI_VOICECHAT_MUTEOFF = 110988,
|
||||
UI_VOICECHAT_MUTEON = 110987,
|
||||
UI_VOICECHAT_MUTEOTHEROFF = 111080,
|
||||
UI_VOICECHAT_MUTEOTHERON = 111081,
|
||||
UI_VOICECHAT_MEMBERLEAVECHANNEL = 110986,
|
||||
UI_VOICECHAT_MEMBERJOINCHANNEL = 110985,
|
||||
UI_VOICECHAT_STOPTALK = 110984,
|
||||
UI_VOICECHAT_TALKSTART = 110983,
|
||||
UI_VOICECHAT_LEAVECHANNEL = 110982,
|
||||
UI_VOICECHAT_JOINCHANNEL = 110981,
|
||||
AMB_GLUESCREEN_BATTLE_FOR_AZEROTH = 113894,
|
||||
AMB_GLUESCREEN_MAGHARORC = 113556,
|
||||
AMB_GLUESCREEN_DARKIRONDWARF = 113557,
|
||||
MUS_80_MAIN_TITLE = 113559,
|
||||
MENU_CREDITS08 = 113560,
|
||||
UI_AZERITE_EMPOWERED_ITEM_LOOT_TOAST = 118238,
|
||||
|
||||
UI_80_AZERITEARMOR_SELECTBUFF = 114993,
|
||||
UI_80_AZERITEARMOR_BUFFAVAILABLE = 116657,
|
||||
UI_80_AZERITEARMOR_ROTATION_LOOP = 114994,
|
||||
UI_80_AZERITEARMOR_ROTATIONENDS = 114995,
|
||||
UI_80_AZERITEARMOR_ROTATIONENDS_FINALTRAIT = 116233,
|
||||
UI_80_AZERITEARMOR_ROTATIONENDCLICKS = 115314,
|
||||
UI_80_AZERITEARMOR_ROTATIONSTARTCLICKS = 115358,
|
||||
UI_80_AZERITEARMOR_FIRSTTIMEFLOURISH = 118264,
|
||||
|
||||
UI_80_AZERITEARMOR_REFORGE_ETHEREALWINDOW_OPEN = 114990,
|
||||
UI_80_AZERITEARMOR_REFORGE_ETHEREALWINDOW_CLOSE = 114991,
|
||||
UI_80_AZERITEARMOR_REFORGE = 114992,
|
||||
UI_80_ISLANDS_AZERITECOLLECTION_START = 115003,
|
||||
UI_80_ISLANDS_AZERITECOLLECTION_LOOP = 115004,
|
||||
UI_80_ISLANDS_AZERITECOLLECTION_STOP = 115005,
|
||||
UI_80_SCRAPPING_WINDOW_CLOSE = 115487,
|
||||
UI_80_SCRAPPING_WINDOW_OPEN = 115488,
|
||||
UI_WARMODE_ACTIVATE = 118563,
|
||||
UI_WARMODE_DECTIVATE = 118564,
|
||||
|
||||
UI_80_ISLANDS_TABLE_OPEN = 118610,
|
||||
UI_80_ISLANDS_TABLE_CLOSE = 118611,
|
||||
UI_80_ISLANDS_TABLE_SELECT_DIFFICULTY = 118612,
|
||||
UI_80_ISLANDS_TABLE_FIND_GROUP = 118613,
|
||||
UI_80_ISLANDS_TABLE_FIND_GROUP_PVP = 118668,
|
||||
UI_80_ISLANDS_TUTORIAL_CLOSE = 119726,
|
||||
|
||||
UI_GARRISON_MISSION_COMPLETE_ENCOUNTER_CHANCE = 0, -- missing SoundKit entry!
|
||||
|
||||
UI_COUNTDOWN_BAR_STATE_STARTS = 158958,
|
||||
UI_COUNTDOWN_BAR_STATE_FINISHED = 158959,
|
||||
UI_COUNTDOWN_MEDIUM_NUMBER_FINISHED = 158960,
|
||||
UI_COUNTDOWN_TIMER = 191352,
|
||||
UI_COUNTDOWN_FINISHED = 158566,
|
||||
};
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+2950
File diff suppressed because it is too large
Load Diff
+436
@@ -0,0 +1,436 @@
|
||||
C_PlayerGuide = C_PlayerGuide or {}
|
||||
local L = C_PlayerGuide
|
||||
|
||||
local PREFIX = "MWPG"
|
||||
local SEP = "\t"
|
||||
|
||||
local currentData = nil
|
||||
local lastError = nil
|
||||
local uiState = "loading" -- "data" | "empty" | "loading"
|
||||
local listeners = {}
|
||||
local inbox = {} -- [cmd] = { total, got, chunks }
|
||||
|
||||
-- ===== minimal JSON decoder =====
|
||||
|
||||
local function jsonDecode(s)
|
||||
if type(s) ~= "string" then return nil end
|
||||
local pos = 1
|
||||
|
||||
local parseValue, parseString, parseNumber, parseObject, parseArray
|
||||
|
||||
local function skipWS()
|
||||
local _, e = s:find("^%s*", pos)
|
||||
if e then pos = e + 1 end
|
||||
end
|
||||
|
||||
parseString = function()
|
||||
pos = pos + 1
|
||||
local result = {}
|
||||
while pos <= #s do
|
||||
local c = s:sub(pos, pos)
|
||||
if c == '"' then
|
||||
pos = pos + 1
|
||||
return table.concat(result)
|
||||
elseif c == '\\' then
|
||||
pos = pos + 1
|
||||
local esc = s:sub(pos, pos)
|
||||
pos = pos + 1
|
||||
if esc == '"' then result[#result+1] = '"'
|
||||
elseif esc == '\\' then result[#result+1] = '\\'
|
||||
elseif esc == '/' then result[#result+1] = '/'
|
||||
elseif esc == 'n' then result[#result+1] = '\n'
|
||||
elseif esc == 'r' then result[#result+1] = '\r'
|
||||
elseif esc == 't' then result[#result+1] = '\t'
|
||||
elseif esc == 'u' then
|
||||
local hex = s:sub(pos, pos + 3)
|
||||
pos = pos + 4
|
||||
local code = tonumber(hex, 16) or 0
|
||||
if code < 0x80 then
|
||||
result[#result+1] = string.char(code)
|
||||
elseif code < 0x800 then
|
||||
result[#result+1] = string.char(
|
||||
0xC0 + math.floor(code / 64),
|
||||
0x80 + (code % 64))
|
||||
else
|
||||
result[#result+1] = string.char(
|
||||
0xE0 + math.floor(code / 4096),
|
||||
0x80 + math.floor((code % 4096) / 64),
|
||||
0x80 + (code % 64))
|
||||
end
|
||||
else
|
||||
result[#result+1] = esc
|
||||
end
|
||||
else
|
||||
result[#result+1] = c
|
||||
pos = pos + 1
|
||||
end
|
||||
end
|
||||
return table.concat(result)
|
||||
end
|
||||
|
||||
parseNumber = function()
|
||||
local numStr = s:match("^-?%d+%.?%d*[eE]?[+-]?%d*", pos)
|
||||
if not numStr then return 0 end
|
||||
pos = pos + #numStr
|
||||
return tonumber(numStr) or 0
|
||||
end
|
||||
|
||||
parseArray = function()
|
||||
pos = pos + 1
|
||||
local arr = {}
|
||||
skipWS()
|
||||
if s:sub(pos, pos) == ']' then pos = pos + 1; return arr end
|
||||
while pos <= #s do
|
||||
arr[#arr+1] = parseValue()
|
||||
skipWS()
|
||||
local c = s:sub(pos, pos)
|
||||
if c == ']' then pos = pos + 1; break
|
||||
elseif c == ',' then pos = pos + 1 end
|
||||
end
|
||||
return arr
|
||||
end
|
||||
|
||||
parseObject = function()
|
||||
pos = pos + 1
|
||||
local obj = {}
|
||||
skipWS()
|
||||
if s:sub(pos, pos) == '}' then pos = pos + 1; return obj end
|
||||
while pos <= #s do
|
||||
skipWS()
|
||||
local key = parseString()
|
||||
skipWS()
|
||||
pos = pos + 1
|
||||
local val = parseValue()
|
||||
if key ~= nil then obj[key] = val end
|
||||
skipWS()
|
||||
local c = s:sub(pos, pos)
|
||||
if c == '}' then pos = pos + 1; break
|
||||
elseif c == ',' then pos = pos + 1 end
|
||||
end
|
||||
return obj
|
||||
end
|
||||
|
||||
parseValue = function()
|
||||
skipWS()
|
||||
local c = s:sub(pos, pos)
|
||||
if c == '"' then return parseString()
|
||||
elseif c == '{' then return parseObject()
|
||||
elseif c == '[' then return parseArray()
|
||||
elseif c == 't' then pos = pos + 4; return true
|
||||
elseif c == 'f' then pos = pos + 5; return false
|
||||
elseif c == 'n' then pos = pos + 4; return nil
|
||||
else return parseNumber()
|
||||
end
|
||||
end
|
||||
|
||||
local ok, result = pcall(parseValue)
|
||||
if ok then return result end
|
||||
geterrorhandler()("[PlayerGuide] JSON parse error: " .. tostring(result))
|
||||
return nil
|
||||
end
|
||||
|
||||
-- ===== internal =====
|
||||
|
||||
local function notify()
|
||||
for _, fn in ipairs(listeners) do
|
||||
pcall(fn, uiState, currentData)
|
||||
end
|
||||
end
|
||||
|
||||
local function isTable(v) return type(v) == "table" end
|
||||
|
||||
local function validate(data)
|
||||
if not isTable(data) then return false, "payload is not a table" end
|
||||
if data.stageID and type(data.stageID) ~= "number" then return false, "stageID is not a number" end
|
||||
if data.progress and type(data.progress) ~= "number" then return false, "progress is not a number" end
|
||||
if data.objectives and not isTable(data.objectives) then return false, "objectives is not a table" end
|
||||
if isTable(data.objectives) then
|
||||
local seen = {}
|
||||
for i, o in ipairs(data.objectives) do
|
||||
if not isTable(o) then return false, "objective #" .. i .. " is not a table" end
|
||||
if o.id and seen[o.id] then return false, "duplicate objective id: " .. tostring(o.id) end
|
||||
if o.id then seen[o.id] = true end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- ===== send =====
|
||||
|
||||
local function sendCmd(cmd, arg)
|
||||
local body = cmd
|
||||
if arg ~= nil then body = body .. SEP .. tostring(arg) end
|
||||
local player = UnitName("player")
|
||||
if player then SendAddonMessage(PREFIX, body, "WHISPER", player) end
|
||||
end
|
||||
|
||||
-- ===== receive dispatch =====
|
||||
|
||||
local function onAddonMessage(prefix, message, channel)
|
||||
if prefix ~= PREFIX or channel ~= "WHISPER" then return end
|
||||
|
||||
local cmd, seq, total, body =
|
||||
message:match("^([^\t]+)\t(%d+)\t(%d+)\t(.*)$")
|
||||
if not cmd then return end
|
||||
seq = tonumber(seq) or 0
|
||||
total = tonumber(total) or 0
|
||||
if seq == 0 or total == 0 then return end
|
||||
|
||||
local box = inbox[cmd]
|
||||
if not box or box.total ~= total then
|
||||
box = { total = total, got = 0, chunks = {} }
|
||||
inbox[cmd] = box
|
||||
end
|
||||
if box.chunks[seq] == nil then
|
||||
box.chunks[seq] = body
|
||||
box.got = box.got + 1
|
||||
end
|
||||
if box.got < total then return end
|
||||
|
||||
local full = table.concat(box.chunks)
|
||||
inbox[cmd] = nil
|
||||
|
||||
if cmd == "SetData" then
|
||||
local data = jsonDecode(full)
|
||||
L.SetData(data)
|
||||
|
||||
elseif cmd == "SetStage" then
|
||||
local stage = jsonDecode(full)
|
||||
if isTable(stage) and L.HasData() then
|
||||
currentData.stageID = stage.stageID or currentData.stageID
|
||||
currentData.stageName = stage.stageName or currentData.stageName
|
||||
currentData.stageDescription = stage.stageDescription or currentData.stageDescription
|
||||
currentData.progress = stage.progress or currentData.progress
|
||||
if stage.stages then currentData.stages = stage.stages end
|
||||
if stage.objectives then currentData.objectives = stage.objectives end
|
||||
if stage.recommendations then currentData.recommendations = stage.recommendations end
|
||||
notify()
|
||||
end
|
||||
|
||||
elseif cmd == "UpdateObjective" then
|
||||
local obj = jsonDecode(full)
|
||||
if isTable(obj) and obj.id and L.HasData() then
|
||||
local objectives = currentData.objectives or {}
|
||||
for i, existing in ipairs(objectives) do
|
||||
if existing.id == obj.id then
|
||||
objectives[i] = obj
|
||||
break
|
||||
end
|
||||
end
|
||||
notify()
|
||||
end
|
||||
|
||||
elseif cmd == "Notify" then
|
||||
if DEFAULT_CHAT_FRAME and full ~= "" then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffd76a[Путеводитель]|r " .. full)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local _frame = CreateFrame("Frame")
|
||||
_frame:RegisterEvent("CHAT_MSG_ADDON")
|
||||
_frame:RegisterEvent("PLAYER_LOGIN")
|
||||
_frame:SetScript("OnEvent", function(_, event, ...)
|
||||
if event == "CHAT_MSG_ADDON" then
|
||||
onAddonMessage(...)
|
||||
elseif event == "PLAYER_LOGIN" then
|
||||
L.RequestRefresh()
|
||||
end
|
||||
end)
|
||||
|
||||
-- ===== public API =====
|
||||
|
||||
function L.RegisterListener(fn)
|
||||
if type(fn) == "function" then table.insert(listeners, fn) end
|
||||
end
|
||||
|
||||
function L.GetState() return uiState end
|
||||
function L.GetLastError() return lastError end
|
||||
function L.HasData() return uiState == "data" and currentData ~= nil end
|
||||
|
||||
function L.SetData(data)
|
||||
local ok, err = validate(data)
|
||||
if not ok then
|
||||
lastError = err
|
||||
uiState = "empty"
|
||||
notify()
|
||||
return false, err
|
||||
end
|
||||
currentData = data
|
||||
lastError = nil
|
||||
uiState = "data"
|
||||
notify()
|
||||
return true
|
||||
end
|
||||
|
||||
function L.Clear()
|
||||
currentData = nil
|
||||
lastError = nil
|
||||
uiState = "empty"
|
||||
notify()
|
||||
end
|
||||
|
||||
function L.SetLoading()
|
||||
if uiState ~= "data" then
|
||||
uiState = "loading"
|
||||
notify()
|
||||
end
|
||||
end
|
||||
|
||||
function L.GetStageInfo()
|
||||
local d = currentData
|
||||
if not d then return nil end
|
||||
return d.stageID, d.stageName, d.stageDescription, d.progress or 0
|
||||
end
|
||||
|
||||
function L.GetStages()
|
||||
local d = currentData
|
||||
if not d then return nil end
|
||||
return d.stages or {}
|
||||
end
|
||||
|
||||
function L.GetNumObjectives()
|
||||
local d = currentData
|
||||
if not d or not d.objectives then return 0 end
|
||||
return #d.objectives
|
||||
end
|
||||
|
||||
function L.GetObjectiveInfo(index)
|
||||
local d = currentData
|
||||
if not d or not d.objectives then return nil end
|
||||
local o = d.objectives[index]
|
||||
if not o then return nil end
|
||||
return {
|
||||
id = o.id,
|
||||
type = o.type or "custom",
|
||||
title = o.title or "",
|
||||
description = o.description or o.subtitle or "",
|
||||
subtitle = o.subtitle,
|
||||
completed = o.completed and true or false,
|
||||
progress = o.progress or 0,
|
||||
required = o.required or 1,
|
||||
targetID = o.targetID,
|
||||
questID = o.questID,
|
||||
questChainID = o.questChainID,
|
||||
instanceID = o.instanceID,
|
||||
ej_instanceID = o.ej_instanceID or o.ejInstanceID,
|
||||
journalInstanceID = o.journalInstanceID,
|
||||
mapID = o.mapID,
|
||||
worldMapAreaID = o.worldMapAreaID,
|
||||
dungeonID = o.dungeonID,
|
||||
encounterID = o.encounterID,
|
||||
achievementID = o.achievementID,
|
||||
factionID = o.factionID,
|
||||
itemID = o.itemID,
|
||||
currencyID = o.currencyID,
|
||||
order = o.order or index,
|
||||
}
|
||||
end
|
||||
|
||||
function L.GetNumRecommendations()
|
||||
local d = currentData
|
||||
if not d or not d.recommendations then return 0 end
|
||||
return #d.recommendations
|
||||
end
|
||||
|
||||
function L.GetRecommendationInfo(index)
|
||||
local d = currentData
|
||||
if not d or not d.recommendations then return nil end
|
||||
local r = d.recommendations[index]
|
||||
if not r then return nil end
|
||||
return {
|
||||
id = r.id,
|
||||
type = r.type or "dungeon",
|
||||
title = r.title or r.name or "",
|
||||
name = r.name or r.title or "",
|
||||
description = r.description or r.loc or "",
|
||||
loc = r.loc,
|
||||
level = r.level,
|
||||
biome = r.biome,
|
||||
artwork = r.artwork,
|
||||
cover = r.cover,
|
||||
coverImage = r.coverImage,
|
||||
bgImage = r.bgImage,
|
||||
buttonImage = r.buttonImage,
|
||||
icon = r.icon,
|
||||
instanceID = r.instanceID,
|
||||
ej_instanceID = r.ej_instanceID or r.ejInstanceID,
|
||||
journalInstanceID = r.journalInstanceID,
|
||||
mapID = r.mapID,
|
||||
worldMapAreaID = r.worldMapAreaID,
|
||||
targetID = r.targetID,
|
||||
dungeonID = r.dungeonID,
|
||||
encounterID = r.encounterID,
|
||||
priority = r.priority or 0,
|
||||
}
|
||||
end
|
||||
|
||||
function L.GetNumRewards()
|
||||
local d = currentData
|
||||
if not d or not d.rewards then return 0 end
|
||||
return #d.rewards
|
||||
end
|
||||
|
||||
function L.GetRewardInfo(index)
|
||||
local d = currentData
|
||||
if not d or not d.rewards then return nil end
|
||||
return d.rewards[index]
|
||||
end
|
||||
|
||||
function L.RequestRefresh()
|
||||
L.SetLoading()
|
||||
sendCmd("RequestRefresh")
|
||||
end
|
||||
|
||||
function L.RequestTrackObjective(objectiveID)
|
||||
sendCmd("RequestTrackObjective", objectiveID)
|
||||
end
|
||||
|
||||
function L.RequestOpenTarget(objectiveID)
|
||||
sendCmd("RequestOpenTarget", objectiveID)
|
||||
end
|
||||
|
||||
function L._InjectDevPayload()
|
||||
L.SetData({
|
||||
version = 1,
|
||||
stageID = 5,
|
||||
stageName = "Подготовка к Нордсколу",
|
||||
stageDescription =
|
||||
"Завершите ключевые цепочки заданий в Запределье и докажите готовность к походу на север.",
|
||||
progress = 62,
|
||||
stages = {
|
||||
{ id = 1, name = "Начало пути", short = "Начало пути", status = "done" },
|
||||
{ id = 2, name = "Огненные Недра", short = "Огн. Недра", status = "done" },
|
||||
{ id = 3, name = "Тёмный портал", short = "Тёмный портал", status = "done" },
|
||||
{ id = 4, name = "Покорение Запределья", short = "Запределье", status = "done" },
|
||||
{ id = 5, name = "Подготовка к Нордсколу",short = "К Нордсколу", status = "active" },
|
||||
{ id = 6, name = "Заря Нордскола", short = "Нордскол", status = "locked" },
|
||||
{ id = 7, name = "Цитадель Ледяной Короны",short = "Ледяная Корона",status = "locked" },
|
||||
},
|
||||
objectives = {
|
||||
{ id = 1, type = "level", title = "Достигнуть 68 уровня",
|
||||
subtitle = "Можно отправляться в Нордскол.",
|
||||
completed = true, progress = 68, required = 68, order = 1 },
|
||||
{ id = 2, type = "quest_chain", title = "Цепочка «Поиск виверны» в Награнде",
|
||||
subtitle = "Кисть Шамана · Награнд",
|
||||
completed = false, progress = 5, required = 8, order = 2 },
|
||||
{ id = 3, type = "dungeon", title = "Пройти Кузню Крови",
|
||||
subtitle = "Цитадель Адского Пламени · 61–64",
|
||||
completed = false, progress = 0, required = 1, order = 3, instanceID = 256 },
|
||||
{ id = 4, type = "dungeon", title = "Пройти Подземелья Аукенай",
|
||||
subtitle = "Аукиндон · 64–66",
|
||||
completed = false, progress = 0, required = 1, order = 4, instanceID = 257 },
|
||||
{ id = 5, type = "reputation", title = "Дружелюбие с Кенарийской экспедицией",
|
||||
subtitle = "Нейтрально сейчас · Долина Призрачной Луны",
|
||||
completed = false, progress = 1200, required = 3000, order = 5, factionID = 942 },
|
||||
},
|
||||
recommendations = {
|
||||
{ id = 1, type = "dungeon", name = "Кузня Крови", loc = "Цитадель Адского Пламени", level = "61–64", biome = "fire" },
|
||||
{ id = 2, type = "dungeon", name = "Подземелья Аукенай", loc = "Аукиндон", level = "64–66", biome = "arcane" },
|
||||
{ id = 3, type = "dungeon", name = "Сетеккские залы", loc = "Награнд", level = "67–69", biome = "storm" },
|
||||
{ id = 4, type = "dungeon", name = "Тёмный Лабиринт", loc = "Аукиндон", level = "68–70", biome = "shadow" },
|
||||
{ id = 5, type = "dungeon", name = "Ботаника", loc = "Долина Призрачной Луны", level = "69–70", biome = "nature" },
|
||||
},
|
||||
rewards = {},
|
||||
})
|
||||
end
|
||||
@@ -9,3 +9,23 @@
|
||||
## X-Hidden: 1
|
||||
|
||||
MoonWellClient.lua
|
||||
EncounterJournal\EncounterJournalCompat.lua
|
||||
EncounterJournal\SharedXML\SoundKitConstants.lua
|
||||
EncounterJournal\Generated_EncounterJournalData.lua
|
||||
EncounterJournal\EncounterJournalData.lua
|
||||
EncounterJournal\EncounterJournalTierFilter.lua
|
||||
EncounterJournal\EncounterJournalTierSnapshot.lua
|
||||
EncounterJournal\GlobalStrings.lua
|
||||
EncounterJournal\Utils\C_EncounterJournal.lua
|
||||
EncounterJournal\Utils\C_PlayerGuide.lua
|
||||
EncounterJournal\EncounterJournalInit.lua
|
||||
EncounterJournal\EncounterJournalFrameMethods.lua
|
||||
EncounterJournal\EncounterJournalFonts.xml
|
||||
EncounterJournal\SharedXML\SharedUIPanelTemplates.xml
|
||||
EncounterJournal\SharedXML\HybridScrollFrame.xml
|
||||
EncounterJournal\SharedXML\NavigationBar.xml
|
||||
EncounterJournal\Custom_EncounterJournal\Custom_EncounterJournal.xml
|
||||
EncounterJournal\Custom_EncounterJournal\PlayerGuide.xml
|
||||
EncounterJournal\EncounterJournalScrollFix.lua
|
||||
EncounterJournal\EncounterJournalDungeonMode.lua
|
||||
EncounterJournal\EncounterJournalOpen.lua
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user