Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9fdb7ed718 | |||
| 698de84762 | |||
| cddaf2c515 |
@@ -7,7 +7,9 @@
|
||||
|
||||
#include "core/Logger.hpp"
|
||||
#include "core/Mem.hpp"
|
||||
#include "runtime/LuaBindings.hpp"
|
||||
#include "runtime/ModuleInstall.hpp"
|
||||
#include "offsets/engine/Gx.hpp"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
@@ -20,6 +22,138 @@ namespace moonwell
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr uint32_t kTraitorFlag = 0x40000000u;
|
||||
bool g_loginCharacterIsTraitor = false;
|
||||
|
||||
using GxSetProjectionFn = wxl::offsets::engine::gx::GxSetProjectionFn;
|
||||
GxSetProjectionFn g_nextSetProjection = nullptr;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Smoothstep avoids a visible jerk at both ends of the move.
|
||||
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");
|
||||
}
|
||||
|
||||
int __cdecl SetLoginCharacterFlags(void* state)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
if (state && wxl::runtime::lua::IsNumber(state, 1))
|
||||
flags = static_cast<uint32_t>(wxl::runtime::lua::ToNumber(state, 1));
|
||||
|
||||
g_loginCharacterIsTraitor = (flags & kTraitorFlag) != 0;
|
||||
wxl::runtime::lua::PushBoolean(state, g_loginCharacterIsTraitor ? 1 : 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __cdecl IsTraitor(void* state)
|
||||
{
|
||||
wxl::runtime::lua::PushBoolean(state, g_loginCharacterIsTraitor ? 1 : 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct Patch
|
||||
{
|
||||
const char* name;
|
||||
@@ -196,7 +330,14 @@ namespace moonwell
|
||||
{
|
||||
Registration()
|
||||
{
|
||||
wxl::runtime::lua::RegisterFunction(
|
||||
"MoonWellSetLoginCharacterFlags", &SetLoginCharacterFlags);
|
||||
wxl::runtime::lua::RegisterFunction("MoonWellIsTraitor", &IsTraitor);
|
||||
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-log-flush", &InstallRuntimeLogFlush);
|
||||
}
|
||||
} g_registration;
|
||||
|
||||
@@ -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 the same
|
||||
-- part of the screen. 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();
|
||||
|
||||
@@ -1139,6 +1139,12 @@ end
|
||||
function CharacterSelect_EnterWorld()
|
||||
PlaySound("gsCharacterSelectionEnterWorld");
|
||||
StopGlueAmbience();
|
||||
-- Persist the server-provided game-mode bit across the GlueXML -> FrameXML
|
||||
-- Lua-state transition. WarcraftXL owns the value for this Wow.exe process.
|
||||
if ( type(MoonWellSetLoginCharacterFlags) == "function" ) then
|
||||
local info = { GetCharacterInfo(CharacterSelect.selectedIndex) };
|
||||
MoonWellSetLoginCharacterFlags(info[11] or 0);
|
||||
end
|
||||
EnterWorld();
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
-- The MoonWell Achievement.dbc intentionally exposes both faction variants.
|
||||
-- Restore the 3.3.5a client-side faction filter and make traitors use the
|
||||
-- opposite faction. IDs come from the stock ruRU build 12340 DBC.
|
||||
|
||||
local achievementFaction = {}
|
||||
|
||||
local function AddFaction(ids, faction)
|
||||
for id in string.gmatch(ids, "%d+") do
|
||||
achievementFaction[tonumber(id)] = faction
|
||||
end
|
||||
end
|
||||
|
||||
AddFaction([[
|
||||
33 34 35 37 41 58 202 203 206 220 225 230 246 388 433 434 435 436 437 438
|
||||
439 440 441 442 470 471 472 473 604 610 611 612 613 614 701 707 709 711 713
|
||||
764 899 907 908 942 948 963 966 969 970 1012 1022 1023 1024 1028 1029 1030
|
||||
1034 1035 1038 1040 1151 1167 1169 1172 1184 1189 1191 1192 1255 1262 1279
|
||||
1466 1563 1656 1676 1678 1681 1684 1686 1692 1697 1707 1737 1752 1757 1762
|
||||
1782 2016 2144 2194 2419 2421 2536 2760 2761 2762 2763 2764 2770 2777 2778
|
||||
2779 2780 2781 2782 2797 2817 3356 3478 3556 3576 3580 3596 3676 3846 3851
|
||||
3856 3857 4156 4296 4298 4436 4784 4786
|
||||
]], "Alliance")
|
||||
|
||||
AddFaction([[
|
||||
224 259 443 444 445 446 447 448 449 450 451 452 453 454 468 469 593 603 615
|
||||
616 617 618 619 700 706 708 710 712 714 762 763 873 901 909 926 943 965 967
|
||||
968 971 1005 1006 1011 1025 1026 1027 1031 1032 1033 1036 1037 1039 1041
|
||||
1164 1168 1170 1173 1175 1203 1251 1252 1271 1272 1273 1274 1280 1356 1357
|
||||
1358 1359 1360 1502 1657 1677 1680 1682 1683 1685 1691 1693 1698 1783 1784
|
||||
2017 2145 2192 2195 2200 2420 2476 2497 2537 2765 2766 2767 2768 2769 2771
|
||||
2776 2783 2784 2785 2786 2787 2788 2798 2816 3357 3557 3577 3581 3597 3656
|
||||
3677 3778 3957 4079 4176 4177 4256 4297 4437 4785 4790
|
||||
]], "Horde")
|
||||
|
||||
local NativeGetAchievementInfo = GetAchievementInfo
|
||||
local NativeGetCategoryNumAchievements = GetCategoryNumAchievements
|
||||
local NativeGetLatestCompletedAchievements = GetLatestCompletedAchievements
|
||||
local NativeGetTrackedAchievements = GetTrackedAchievements
|
||||
local NativeGetNumCompletedAchievements = GetNumCompletedAchievements
|
||||
local NativeGetTotalAchievementPoints = GetTotalAchievementPoints
|
||||
|
||||
local categoryCache = {}
|
||||
local summaryCache = {}
|
||||
|
||||
local function GetDisplayFaction()
|
||||
local faction = UnitFactionGroup("player")
|
||||
if MoonWell_IsTraitor then
|
||||
if faction == "Alliance" then
|
||||
return "Horde"
|
||||
elseif faction == "Horde" then
|
||||
return "Alliance"
|
||||
end
|
||||
end
|
||||
return faction
|
||||
end
|
||||
|
||||
local function IsAchievementVisible(id, faction)
|
||||
local requiredFaction = achievementFaction[id]
|
||||
return not requiredFaction or not faction or requiredFaction == faction
|
||||
end
|
||||
|
||||
local function BuildCategory(categoryID, showAll)
|
||||
local faction = GetDisplayFaction()
|
||||
local key = tostring(categoryID) .. ":" .. tostring(showAll and 1 or 0)
|
||||
.. ":" .. tostring(faction) .. ":" .. tostring(MoonWell_IsTraitor)
|
||||
local cached = categoryCache[key]
|
||||
if cached then
|
||||
return cached
|
||||
end
|
||||
|
||||
local nativeCount = NativeGetCategoryNumAchievements(categoryID, showAll)
|
||||
local indices = {}
|
||||
local completed = 0
|
||||
|
||||
for nativeIndex = 1, nativeCount do
|
||||
local id, _, _, isCompleted = NativeGetAchievementInfo(categoryID, nativeIndex)
|
||||
if id and IsAchievementVisible(id, faction) then
|
||||
indices[#indices + 1] = nativeIndex
|
||||
if isCompleted then
|
||||
completed = completed + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
cached = { indices = indices, completed = completed }
|
||||
categoryCache[key] = cached
|
||||
return cached
|
||||
end
|
||||
|
||||
function GetCategoryNumAchievements(categoryID, showAll)
|
||||
local category = BuildCategory(categoryID, showAll)
|
||||
return #category.indices, category.completed
|
||||
end
|
||||
|
||||
function GetAchievementInfo(idOrCategory, achievementIndex)
|
||||
if achievementIndex == nil then
|
||||
return NativeGetAchievementInfo(idOrCategory)
|
||||
end
|
||||
|
||||
local category = BuildCategory(idOrCategory, false)
|
||||
local nativeIndex = category.indices[achievementIndex]
|
||||
if not nativeIndex then
|
||||
return nil
|
||||
end
|
||||
return NativeGetAchievementInfo(idOrCategory, nativeIndex)
|
||||
end
|
||||
|
||||
local function BuildSummary()
|
||||
local faction = GetDisplayFaction()
|
||||
local key = tostring(faction) .. ":" .. tostring(MoonWell_IsTraitor)
|
||||
local cached = summaryCache[key]
|
||||
if cached then
|
||||
return cached
|
||||
end
|
||||
|
||||
local total, completed = NativeGetNumCompletedAchievements()
|
||||
local points = NativeGetTotalAchievementPoints()
|
||||
local statisticFlag = ACHIEVEMENT_FLAGS_STATISTIC or 0x00000001
|
||||
local hiddenFlag = ACHIEVEMENT_FLAGS_HIDDEN or 0x00000002
|
||||
|
||||
for id, requiredFaction in pairs(achievementFaction) do
|
||||
if faction and requiredFaction ~= faction then
|
||||
local _, _, achievementPoints, isCompleted, _, _, _, _, flags =
|
||||
NativeGetAchievementInfo(id)
|
||||
if flags and bit.band(flags, statisticFlag) == 0 then
|
||||
local isHidden = bit.band(flags, hiddenFlag) ~= 0
|
||||
if not isHidden or isCompleted then
|
||||
total = total - 1
|
||||
end
|
||||
if isCompleted then
|
||||
completed = completed - 1
|
||||
points = points - (achievementPoints or 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
cached = { total = total, completed = completed, points = points }
|
||||
summaryCache[key] = cached
|
||||
return cached
|
||||
end
|
||||
|
||||
function GetNumCompletedAchievements()
|
||||
local summary = BuildSummary()
|
||||
return summary.total, summary.completed
|
||||
end
|
||||
|
||||
function GetTotalAchievementPoints()
|
||||
return BuildSummary().points
|
||||
end
|
||||
|
||||
local function FilterAchievementIDs(getter)
|
||||
local faction = GetDisplayFaction()
|
||||
local source = { getter() }
|
||||
local filtered = {}
|
||||
for i = 1, #source do
|
||||
if IsAchievementVisible(source[i], faction) then
|
||||
filtered[#filtered + 1] = source[i]
|
||||
end
|
||||
end
|
||||
return unpack(filtered)
|
||||
end
|
||||
|
||||
if NativeGetLatestCompletedAchievements then
|
||||
function GetLatestCompletedAchievements()
|
||||
return FilterAchievementIDs(NativeGetLatestCompletedAchievements)
|
||||
end
|
||||
end
|
||||
|
||||
if NativeGetTrackedAchievements then
|
||||
function GetTrackedAchievements()
|
||||
return FilterAchievementIDs(NativeGetTrackedAchievements)
|
||||
end
|
||||
end
|
||||
|
||||
function MoonWellAchievementFaction_Refresh()
|
||||
categoryCache = {}
|
||||
summaryCache = {}
|
||||
if AchievementFrame and AchievementFrame:IsShown() then
|
||||
if AchievementFrameCategories_Update then
|
||||
AchievementFrameCategories_Update()
|
||||
end
|
||||
if AchievementFrameAchievements_ForceUpdate then
|
||||
AchievementFrameAchievements_ForceUpdate()
|
||||
elseif AchievementFrameAchievements_Update then
|
||||
AchievementFrameAchievements_Update()
|
||||
end
|
||||
if AchievementFrameSummary_Update then
|
||||
AchievementFrameSummary_Update()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local events = CreateFrame("Frame")
|
||||
events:RegisterEvent("ACHIEVEMENT_EARNED")
|
||||
events:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
events:RegisterEvent("UNIT_FACTION")
|
||||
events:SetScript("OnEvent", function(self, event, unit)
|
||||
if event ~= "UNIT_FACTION" or unit == "player" then
|
||||
MoonWellAchievementFaction_Refresh()
|
||||
end
|
||||
end)
|
||||
@@ -1,62 +1,85 @@
|
||||
-- ============================================================================
|
||||
-- MoonWell Client
|
||||
-- Server integration: game modes, traitor faction display
|
||||
-- ============================================================================
|
||||
-- MoonWell client game-mode integration.
|
||||
-- WarcraftXL retains the selected character's server-provided charFlags while
|
||||
-- WoW replaces the GlueXML Lua state with the in-world FrameXML state.
|
||||
|
||||
-- Global flag read by FrameXML files to decide which faction icon to show.
|
||||
-- true = show opposite faction icon (traitor)
|
||||
-- false = show real faction icon (normal)
|
||||
MoonWell_IsTraitor = false
|
||||
|
||||
-- ============================================================================
|
||||
-- AIO handler — called by server on login
|
||||
-- Server: AIO:Handle(player, "MoonWellClient", "Init", gameMode)
|
||||
-- gameMode: 0 = normal, 1 = traitor
|
||||
-- ============================================================================
|
||||
|
||||
if not AIO then
|
||||
return
|
||||
end
|
||||
|
||||
local Handler = AIO.AddHandlers("MoonWellClient", {})
|
||||
|
||||
local function MoonWell_RefreshFactionIcons()
|
||||
-- Portrait PvP icon
|
||||
local function RefreshFactionIcons()
|
||||
if PlayerFrame_UpdatePvPStatus then
|
||||
PlayerFrame_UpdatePvPStatus()
|
||||
end
|
||||
-- DragonUI PvP micro button: SetupPVPButton() runs on load before AIO responds,
|
||||
-- so we refresh its DragonUIPVPIcon texture directly after Init.
|
||||
|
||||
if PVPFrame and PVPFrame:IsShown() and PVPFrame_Update then
|
||||
PVPFrame_Update()
|
||||
end
|
||||
if BattlefieldFrame and BattlefieldFrame:IsShown() and BattlefieldFrame_Update then
|
||||
BattlefieldFrame_Update()
|
||||
end
|
||||
|
||||
if PVPMicroButton and PVPMicroButton.DragonUIPVPIcon then
|
||||
local factionGroup = UnitFactionGroup("player")
|
||||
if factionGroup then
|
||||
local displayFaction = MoonWell_IsTraitor
|
||||
and (factionGroup == "Alliance" and "Horde" or "Alliance")
|
||||
or factionGroup
|
||||
local microTexture = "Interface\\AddOns\\DragonUI\\Textures\\Micromenu\\micropvp"
|
||||
local texture = "Interface\\AddOns\\DragonUI\\Textures\\Micromenu\\micropvp"
|
||||
local icon = PVPMicroButton.DragonUIPVPIcon
|
||||
local highlight = PVPMicroButton:GetHighlightTexture()
|
||||
|
||||
if displayFaction == "Alliance" then
|
||||
icon:SetTexture(microTexture)
|
||||
icon:SetTexture(texture)
|
||||
icon:SetTexCoord(0, 118/256, 0, 151/256)
|
||||
if highlight then
|
||||
highlight:SetTexture(texture)
|
||||
highlight:SetTexCoord(0, 118/256, 0, 151/256)
|
||||
end
|
||||
elseif displayFaction == "Horde" then
|
||||
icon:SetTexture(microTexture)
|
||||
icon:SetTexture(texture)
|
||||
icon:SetTexCoord(118/256, 236/256, 0, 151/256)
|
||||
end
|
||||
local hl = PVPMicroButton:GetHighlightTexture()
|
||||
if hl then
|
||||
if displayFaction == "Alliance" then
|
||||
hl:SetTexture(microTexture)
|
||||
hl:SetTexCoord(0, 118/256, 0, 151/256)
|
||||
elseif displayFaction == "Horde" then
|
||||
hl:SetTexture(microTexture)
|
||||
hl:SetTexCoord(118/256, 236/256, 0, 151/256)
|
||||
if highlight then
|
||||
highlight:SetTexture(texture)
|
||||
highlight:SetTexCoord(118/256, 236/256, 0, 151/256)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Handler.Init(_, mode)
|
||||
MoonWell_IsTraitor = (tonumber(mode) == 1)
|
||||
MoonWell_RefreshFactionIcons()
|
||||
local function ReadNativeGameMode()
|
||||
if type(MoonWellIsTraitor) ~= "function" then
|
||||
return false
|
||||
end
|
||||
|
||||
local wasTraitor = MoonWell_IsTraitor
|
||||
MoonWell_IsTraitor = MoonWellIsTraitor() and true or false
|
||||
if wasTraitor ~= MoonWell_IsTraitor and MoonWellAchievementFaction_Refresh then
|
||||
MoonWellAchievementFaction_Refresh()
|
||||
end
|
||||
RefreshFactionIcons()
|
||||
return true
|
||||
end
|
||||
|
||||
local events = CreateFrame("Frame")
|
||||
events:RegisterEvent("PLAYER_LOGIN")
|
||||
events:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
events:RegisterEvent("UNIT_FACTION")
|
||||
events:SetScript("OnEvent", function(self, event, unit)
|
||||
if event ~= "UNIT_FACTION" or unit == "player" then
|
||||
ReadNativeGameMode()
|
||||
end
|
||||
end)
|
||||
|
||||
-- WarcraftXL may publish its Lua globals shortly after addons are loaded.
|
||||
-- Lua 5.1/WotLK has no C_Timer, so retry cheaply until the native API appears.
|
||||
local elapsedTotal = 0
|
||||
events:SetScript("OnUpdate", function(self, elapsed)
|
||||
elapsedTotal = elapsedTotal + elapsed
|
||||
if elapsedTotal < 0.25 then
|
||||
return
|
||||
end
|
||||
elapsedTotal = 0
|
||||
|
||||
if ReadNativeGameMode() then
|
||||
self:SetScript("OnUpdate", nil)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
## Interface: 30300
|
||||
## Title: MoonWell Client
|
||||
## Notes: MoonWell server integration — game modes, traitor faction display
|
||||
## Version: 1.0.0
|
||||
## Notes: Native WarcraftXL game-mode and traitor faction display
|
||||
## Version: 2.0.0
|
||||
## Author: MoonWell
|
||||
## SavedVariables: MoonWellClientDB
|
||||
## Dependencies: AIO_Client
|
||||
## DefaultState: Enabled
|
||||
## X-Hidden: 1
|
||||
|
||||
AchievementFactions.lua
|
||||
MoonWellClient.lua
|
||||
|
||||
Reference in New Issue
Block a user