Files
Modernization-Tool/Payload/Data/Interface/GlueXML/AutoLogin.lua
T
2026-06-09 11:23:40 +02:00

1020 lines
34 KiB
Lua

--------
-- setup
--------
-- toto, make cancelling a login clear LoginManager.auto_char_button_pressed = true
local _G = _G or getfenv(0)
local PASSWORD_MASK = "***************"
local L = {}
L["enUS"] = {
-- localize the Left value
class = {
["Druid"] = "Druid",
["Hunter"] = "Hunter",
["Mage"] = "Mage",
["Paladin"] = "Paladin",
["Priest"] = "Priest",
["Rogue"] = "Rogue",
["Shaman"] = "Shaman",
["Warrior"] = "Warrior",
["Warlock"] = "Warlock",
},
-- localize the Right value
["SelectAccount"] = "Select account",
["RemoveAccount"] = "Remove account",
["LockAccounts"] = "Lock",
["StreamerMode"] = "Streamer",
["EncryptMode"] = "Encrypt",
["LockCharacters"] = "Lock Characters",
["NoDeps"] = "|cff77ff00Turtle AutoLogin|r requires Nampower 3.2.0+ or SuperWoW 1.4+ to operate.",
}
L["ruRU"] = {
-- localize the Left value
class = {
["Друид"] = "Druid",
["Охотник"] = "Hunter",
["Маг"] = "Mage",
["Паладин"] = "Paladin",
["Жрец"] = "Priest",
["Разбойник"] = "Rogue",
["Шаман"] = "Shaman",
["Воин"] = "Warrior",
["Чернокнижник"] = "Warlock",
},
-- localize the Right value
["SelectAccount"] = "Выберите аккаунт",
["RemoveAccount"] = "Удалить аккаунт",
["LockAccounts"] = "Блок.",
["StreamerMode"] = "Стример",
["EncryptMode"] = "Шифр.",
["LockCharacters"] = "Заблокировать персонажей",
["NoDeps"] = "|cff77ff00Turtle AutoLogin|r требует Nampower 3.2.0+ или SuperWoW 1.4+ для работы.",
}
L = L[GetLocale()] or L["enUS"]
local has_nampower = EncryptPassword and EncryptedServerLogin
local has_superwow = (ImportFile and ExportFile) or (SUPERWOW_VERSION and tonumber(SUPERWOW_VERSION) >= 1.4)
local has_encryption = false
local encryption_missing_key = false
if has_nampower then
local ok, err = pcall(EncryptPassword, "test")
if ok then
has_encryption = true
elseif string.find(err or "", "WOW_ENCRYPTION_KEY is not set") then
encryption_missing_key = true
end
end
if not has_nampower and not has_superwow then
GlueDialogTypes["AL_NO_DEPS"] = {
text = L["NoDeps"],
button1 = TEXT(OKAY),
showAlert = 1,
}
GlueDialog_Show("AL_NO_DEPS")
return
end
GlueDialogTypes["AL_CANT_DECRYPT"] = {
text = "This account's password is encrypted but decryption is not available.\nPlease re-enter your password to save it again.",
button1 = TEXT(OKAY),
showAlert = 1,
}
local CLASS_COLORS = {
["Druid"] = { r = 1.00, g = 0.49, b = 0.04, colorStr = "ffFF7C0A" },
["Mage"] = { r = 0.25, g = 0.78, b = 0.92, colorStr = "ff3FC7EB" },
["Hunter"] = { r = 0.67, g = 0.83, b = 0.45, colorStr = "ffAAD372" },
["Paladin"] = { r = 0.96, g = 0.55, b = 0.73, colorStr = "ffF48CBA" },
["Priest"] = { r = 1.00, g = 1.00, b = 1.00, colorStr = "ffFFFFFF" },
["Rogue"] = { r = 1.00, g = 0.96, b = 0.41, colorStr = "ffFFF468" },
["Shaman"] = { r = 0.00, g = 0.44, b = 0.87, colorStr = "ff0070DD" },
["Warlock"] = { r = 0.53, g = 0.53, b = 0.93, colorStr = "ff8788EE" },
["Warrior"] = { r = 0.78, g = 0.61, b = 0.43, colorStr = "ffC69B6D" },
}
LoginManager = {}
LoginManager.State = {}
LoginManager.SelectedAcct = nil
LoginManager.CurrentPage = 0
LoginManager.PageSize = 9
LoginManager.LimitReached = false
LoginManager.from_login_screen = false
LoginManager.has_nampower = has_nampower
LoginManager.has_superwow = has_superwow
--------
-------
-- util
-------
-- Move the element at 'index' upward by one position.
local function moveIndexUp(list, index)
if index > 1 then
local item = table.remove(list, index)
table.insert(list, index - 1, item)
end
end
-- Move the element at 'index' downward by one position.
local function moveIndexDown(list, index)
if list[index] and list[index+1] then
local item = table.remove(list, index)
table.insert(list, index + 1, item)
end
end
-------
---------
-- serial
---------
local Serialize = {
serializeValue = function (self,val,indent)
local t = type(val)
if t == "string" then
return string.format("%q", val)
elseif t == "number" or t == "boolean" then
return tostring(val)
elseif t == "table" then
return self:serializeTable(val,indent)
else
return '"<unsupported type>"'
end
end,
serializeTable = function (self,tbl,indent)
indent = indent or ""
local nextIndent = indent .. " "
local result = {}
table.insert(result, "{\n")
for k, v in pairs(tbl) do
local key
if type(k) == "string" and string.find(k, "^[_%a][_%w]*$") then
key = k
else
key = "[" .. self:serializeValue(k, nextIndent) .. "]"
end
table.insert(result, nextIndent .. key .. " = " .. self:serializeValue(v, nextIndent) .. ",\n")
end
table.insert(result, indent .. "}")
return table.concat(result)
end,
}
---------
---------------
-- LoginManager
---------------
function LoginManager:ToFile(file,value)
local v = Serialize:serializeValue(value)
if v ~= '"<unsupported type>"' then
ExportFile(file, Serialize:serializeValue(value," "))
return true
end
return false
end
function LoginManager:FromFile(file)
local contents = ImportFile(file)
if contents then
local chunk,err = loadstring("return " .. contents)
if not err then
return chunk()
end
end
return false
end
function LoginManager:HideSideButtons()
local hide_frames = {
"AccountLoginTurtleWebsite",
"AccountLoginTurtleArmory",
"AccountLoginTurtleKnowledgeDatabase",
"AccountLoginTurtleCommunityForum",
"AccountLoginTurtleDiscord",
"AccountLoginTurtleReddit",
"AccountLoginCinematicsButton",
-- "AccountLoginCreditsButton",
}
for _,frame in ipairs(hide_frames) do
if _G[frame] then _G[frame]:Hide() end
end
end
function LoginManager:MakeExtraAccountButtons()
-- make account up/down/lock buttons
local button_size = 26
for i=1,self.PageSize do
local acctButton = _G["AutologinAccountButton"..i]
if not acctButton.up then
-- Create the Up button
local upButton = CreateFrame("Button", acctButton:GetName().."Up", acctButton)
upButton:SetWidth(button_size)
upButton:SetHeight(button_size)
upButton:SetPoint("TOPRIGHT", acctButton, "TOPRIGHT", -30, 1)
upButton:SetHitRectInsets(1, 1, 1, 1)
upButton:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollUp-Up")
upButton:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollUp-Down")
upButton:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD")
upButton:SetScript("OnClick", function()
moveIndexUp(self.State.accounts, this:GetParent():GetID())
LoginManager:UpdateUI()
end)
upButton:SetScript("OnEnter", function()
this:GetParent():LockHighlight()
end)
upButton:SetScript("OnLeave", function()
if this:GetParent():GetID() ~= self.SelectedAcct then
this:GetParent():UnlockHighlight()
end
end)
acctButton.up = upButton
end
if not acctButton.down then
-- Create the Down button
local downButton = CreateFrame("Button", acctButton:GetName().."Down", acctButton)
downButton:SetWidth(button_size)
downButton:SetHeight(button_size)
downButton:SetPoint("TOP", acctButton.up, "BOTTOM", 0, 8)
downButton:SetHitRectInsets(1, 1, 1, 1)
downButton:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up")
downButton:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Down")
downButton:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD")
downButton:SetScript("OnClick", function()
moveIndexDown(self.State.accounts, this:GetParent():GetID())
LoginManager:UpdateUI()
end)
downButton:SetScript("OnEnter", function()
this:GetParent():LockHighlight()
end)
downButton:SetScript("OnLeave", function()
if this:GetParent():GetID() ~= self.SelectedAcct then
this:GetParent():UnlockHighlight()
end
end)
acctButton.down = downButton
end
if not acctButton.char then
-- Create the Char button
local charButton = CreateFrame("Button", acctButton:GetName().."Char", acctButton, "GlueButtonSmallTemplate")
charButton:SetWidth(40)
charButton:SetHeight(35)
charButton:SetPoint("RIGHT", acctButton, "RIGHT", -50, 8)
charButton:SetHitRectInsets(1, 1, 1, 1)
charButton:SetScript("OnClick", function()
LoginManager:SelectAccount(this:GetParent():GetID())
LoginManager.auto_char_button_pressed = true
AccountLogin_Login()
end)
charButton:SetScript("OnEnter", function()
this:GetParent():LockHighlight()
end)
charButton:SetScript("OnLeave", function()
if this:GetParent():GetID() ~= self.SelectedAcct then
this:GetParent():UnlockHighlight()
end
end)
acctButton.char = charButton
end
end
local quitButton = _G["AccountLoginExitButton"]
if has_nampower and not quitButton.encrypt then
local encryptButton = CreateFrame("Button", "ButtonAccountButtonsEncrypt", quitButton, "GlueButtonSmallTemplate")
encryptButton:SetWidth(150)
encryptButton:SetHeight(35)
encryptButton:SetPoint("RIGHT", quitButton, "LEFT", 4, 0)
encryptButton:SetText(L["EncryptMode"])
encryptButton:SetWidth(encryptButton:GetFontString():GetStringWidth() + 50)
encryptButton:SetScript("OnClick", function()
if encryption_missing_key then return end
LoginManager.State.encrypt_passwords = not LoginManager.State.encrypt_passwords
this:Show()
LoginManager:SaveAccounts()
LoginManager:UpdateUI()
end)
if encryption_missing_key then
encryptButton:SetScript("OnEnter", function()
GlueTooltip_SetOwner(nil, encryptButton, 0, 0)
GlueTooltip:ClearAllPoints()
GlueTooltip:SetPoint("BOTTOM", encryptButton, "TOP", 0, 4)
GlueTooltip_SetText("Set environment variable\n|cffffd100WOW_ENCRYPTION_KEY|r to enable.", nil, 1, 1, 1)
end)
encryptButton:SetScript("OnLeave", function()
GlueTooltip:Hide()
end)
end
quitButton.encrypt = encryptButton
end
if not quitButton.lock then
local lockButton = CreateFrame("Button", "ButtonAccountButtonsLock", quitButton, "GlueButtonSmallTemplate")
lockButton:SetWidth(150)
lockButton:SetHeight(35)
lockButton:SetPoint("RIGHT", (quitButton.encrypt or quitButton), "LEFT", 4, 0)
lockButton:SetText(L["LockAccounts"])
lockButton:SetWidth(lockButton:GetFontString():GetStringWidth() + 50)
lockButton:SetScript("OnClick", function()
LoginManager.State.account_buttons_locked = not LoginManager.State.account_buttons_locked
this:Show()
LoginManager:SaveAccounts()
LoginManager:UpdateUI()
end)
quitButton.lock = lockButton
end
if not quitButton.streamer then
local streamerButton = CreateFrame("Button", "ButtonAccountButtonsStreamer", quitButton, "GlueButtonSmallTemplate")
streamerButton:SetWidth(150)
streamerButton:SetHeight(35)
streamerButton:SetPoint("RIGHT", quitButton.lock, "LEFT", 4, 0)
streamerButton:SetText(L["StreamerMode"])
streamerButton:SetWidth(streamerButton:GetFontString():GetStringWidth() + 50)
streamerButton:SetScript("OnClick", function()
LoginManager.State.account_buttons_streamer = not LoginManager.State.account_buttons_streamer
this:Show()
LoginManager:UpdateUI()
end)
quitButton.streamer = streamerButton
end
if not AccountLoginAccountEdit.cover then
-- Create the account input cover for streaming mode
local cover = CreateFrame("Frame", nil, AccountLoginAccountEdit)
cover:SetFrameLevel(AccountLoginAccountEdit:GetFrameLevel() + 10)
cover:SetWidth(AccountLoginAccountEdit:GetWidth()-15)
cover:SetHeight(AccountLoginAccountEdit:GetHeight()-15)
cover:SetPoint("TOPLEFT",AccountLoginAccountEdit,"TOPLEFT", 10, -6)
local tex = cover:CreateTexture()
tex:SetAllPoints(cover)
tex:SetTexture(0, 0, 0, 1) -- RGBA: Black, 80% opaque
AccountLoginAccountEdit.cover = cover
end
end
-- Strangely when you login and cancel the program scrubs the string you used for the password from _anywhere_ it finds it.
-- Essentially it deletes the immutable string itself. We avoid this by storing the password string in memory with a
-- leading prefix and clip it at use-time. : = plaintext, :encrypted: = encrypted (nampower).
function LoginManager:LoadAccounts()
self.State.accounts = {}
self.State.last = nil
local login_data = ImportFile("logins")
local file = self:FromFile("logins")
if file then
if file.accounts then self.State.accounts = file.accounts end
if file.last then self.State.last = file.last end
if file.account_buttons_locked then self.State.account_buttons_locked = file.account_buttons_locked end
if file.account_buttons_streamer then self.State.account_buttons_streamer = file.account_buttons_streamer end
if has_encryption and file.encrypt_passwords ~= nil then
self.State.encrypt_passwords = file.encrypt_passwords
elseif has_encryption then
self.State.encrypt_passwords = true
end
return true
elseif login_data then -- old format
for label,account,password,character,auto,last in string.gfind(login_data, "label:(%S*) account:(%S+) password(:%S+) character:(%S*) auto:(%S*) last:(%S*)\n") do
if auto == "true" then auto = true else auto = false end
table.insert(self.State.accounts, { --[[ label = label, ]] account = account, password = password, character = character, auto = auto })
end
return true
end
-- no saved passwords? check the old style account string storage
if not next(self.State.accounts) then
local val = GetSavedAccountName()
for n, p, c in string.gfind(val, "(%S+) (%S+) *(%d*);") do
if (c == "") then c = "-" end
-- Decompress duplicate passwords
if (string.find(p, "~%d") == 1) then
p = self.State.accounts[tonumber(string.sub(p, 2, 3))].password
end
table.insert(self.State.accounts, { account = n, password = p })
end
return true
end
return false
end
function LoginManager:SaveAccounts(by_login)
if by_login then
local account = AccountLoginAccountEdit:GetText()
local password = AccountLoginPasswordEdit:GetText()
-- only try to overwrite a password when there is one to overwrite!
if (account and account ~= "" and password and password ~= "" and password ~= PASSWORD_MASK) then
local stored = ":"..password
if self.State.encrypt_passwords then
local ok, encrypted = pcall(EncryptPassword, password)
if ok then stored = encrypted end
end
local exists = false
for i = 1, table.getn(self.State.accounts) do
if (self.State.accounts[i].account == account) then
exists = true
self.State.accounts[i].password = stored
break
end
end
if (not exists) then
self.pending_account = { account = account, password = stored }
end
end
end
return self:ToFile("logins", self.State)
end
function LoginManager:SaveLabel()
self:SaveAccounts(true)
self:UpdateUI()
end
function LoginManager:SelectAccount(idx)
local i = self.CurrentPage * self.PageSize + idx;
local act = self.State.accounts[i].account
local pwd = self.State.accounts[i].password
AccountLoginAccountEdit:SetText(act)
if string.find(pwd, "^:encrypted:") then
AccountLoginPasswordEdit:SetText(PASSWORD_MASK)
else
AccountLoginPasswordEdit:SetText(string.sub(pwd, 2))
end
LoginManager:OnNameUpdate(act)
end
function LoginManager:OnNameUpdate(name)
self.SelectedAcct = nil
for i = 1, table.getn(self.State.accounts) do
if (self.State.accounts[i].account == name) then self.SelectedAcct = i end
end
if (self.SelectedAcct) then
self.CurrentPage = math.floor((self.SelectedAcct - 1) / self.PageSize)
end
self:UpdateUI()
end
function LoginManager:UpdateUI()
if AccountLoginUI:IsVisible() then
self:MakeExtraAccountButtons()
self:HideSideButtons()
AccountLoginSaveAccountName:Hide()
self:UpdateLoginUI()
elseif CharacterSelectUI:IsVisible() then
self:UpdateCharacterUI()
end
end
function LoginManager:UpdateLoginUI()
local total = table.getn(self.State.accounts)
local pageIndices = {}
local skip = self.CurrentPage * self.PageSize;
AutologinSelectAccountText:SetText(L["SelectAccount"])
AutologinRemoveAccountButton:SetText(L["RemoveAccount"])
local streamer = self.State.account_buttons_streamer
if AccountLoginAccountEdit.cover then
if streamer then
AccountLoginAccountEdit.cover:Show()
else
AccountLoginAccountEdit.cover:Hide()
end
end
for i = 1, self.PageSize do
local button = _G["AutologinAccountButton" .. i]
local acct_id = skip + i
button:UnlockHighlight()
if (acct_id > total) then
button:Hide()
else
local r = self.State.accounts[acct_id]
local selected = self.SelectedAcct == acct_id
local acc_str = r.account
if streamer then
acc_str = string.sub(acc_str,1,1) .. string.rep('*',5) .. string.sub(acc_str,-2)
end
_G["AutologinAccountButton" .. i .. "ButtonTextName"]:SetText(acc_str)
-- get save class, show character login name using class info and whether we're in streamer mode
local class_str = r.class or "-----"
local chr_str = streamer and class_str or r.character
local class_clr = r.class and L.class[class_str] and CLASS_COLORS[L.class[class_str]].colorStr
if class_clr then
chr_str = class_clr and ("|c" .. class_clr .. chr_str .. "|r")
end
button.char:SetText(chr_str or "")
button.char:SetWidth(button.char:GetFontString():GetStringWidth() + 20)
if self.State.account_buttons_locked or acct_id == table.getn(self.State.accounts) then
button.down:Hide()
else
button.down:Show()
end
if self.State.account_buttons_locked or acct_id == 1 then
button.up:Hide()
else
button.up:Show()
end
if not r.character or r.character == "" then
button.char:Hide()
else
button.char:Show()
end
if selected then
button:LockHighlight()
end
button:Show()
end
end
local lockButton = _G["ButtonAccountButtonsLock"]
if lockButton then
if self.State.account_buttons_locked then
lockButton:GetNormalTexture():SetVertexColor(0.5,0.5,0.5)
lockButton:SetTextColor(0.5,0.5,0.5)
else
lockButton:GetNormalTexture():SetVertexColor(1,1,1)
lockButton:SetTextColor(1,0.78,0)
end
end
local streamerButton = _G["ButtonAccountButtonsStreamer"]
if streamerButton then
if self.State.account_buttons_streamer then
streamerButton:GetNormalTexture():SetVertexColor(0.5,0.5,0.5)
streamerButton:SetTextColor(0.5,0.5,0.5)
else
streamerButton:GetNormalTexture():SetVertexColor(1,1,1)
streamerButton:SetTextColor(1,0.78,0)
end
end
local encryptButton = _G["ButtonAccountButtonsEncrypt"]
if encryptButton then
if encryption_missing_key then
encryptButton:GetNormalTexture():SetVertexColor(0.3,0.3,0.3)
encryptButton:SetTextColor(0.7,0.15,0.15)
elseif self.State.encrypt_passwords then
encryptButton:GetNormalTexture():SetVertexColor(0.5,0.5,0.5)
encryptButton:SetTextColor(0.5,0.5,0.5)
else
encryptButton:GetNormalTexture():SetVertexColor(1,1,1)
encryptButton:SetTextColor(1,0.78,0)
end
end
local removeButton = _G["AutologinRemoveAccountButton"]
if removeButton then
if self.State.account_buttons_locked then
removeButton:Hide()
else
removeButton:Show()
end
end
end
function LoginManager:UpdateCharacterUI()
local numChars = GetNumCharacters()
local index = 1
for _,char in ipairs(LoginManager.realm_chars) do
local zone = char.zone
local button = _G["CharSelectCharacterButton"..index]
if ( not char.name ) then
button:SetText("ERROR - Tell Jeremy")
else
button:UnlockHighlight()
if ( not char.zone ) then
zone = ""
end
local classColor = L.class[char.class] and CLASS_COLORS[L.class[char.class]].colorStr or "ffFFFFFF"
_G["CharSelectCharacterButton"..index.."ButtonTextName"]:SetText(char.name)
_G["CharSelectCharacterButton"..index.."ButtonTextInfo"]:SetText(
format(TEXT(char.ghost and CHARACTER_SELECT_INFO_GHOST or CHARACTER_SELECT_INFO), char.level, "|c" .. classColor .. char.class .. "|r")
)
_G["CharSelectCharacterButton"..index.."ButtonTextLocation"]:SetText(zone)
end
if (char.id == CharacterSelect.selectedIndex) then
button:LockHighlight()
end
if LoginManager.char_buttons_locked then
button.up:Hide()
button.down:Hide()
else
button.up:Show()
button.down:Show()
end
index = index + 1
if ( index > MAX_CHARACTERS_DISPLAYED ) then
break
end
end
local lockButton = _G["ButtonCharButtonsLock"]
if lockButton then
if self.char_buttons_locked then
lockButton:GetNormalTexture():SetVertexColor(0.5,0.5,0.5)
lockButton:SetTextColor(0.5,0.5,0.5)
else
lockButton:GetNormalTexture():SetVertexColor(1,1,1)
lockButton:SetTextColor(1,0.78,0)
end
end
end
function LoginManager:OnLogin()
local name = AccountLoginAccountEdit:GetText()
local password = AccountLoginPasswordEdit:GetText()
self.State.last = self.SelectedAcct
self:SaveAccounts(true)
self:OnNameUpdate(name)
self.from_login_screen = true
if self.SelectedAcct and self.State.accounts[self.SelectedAcct] then
local stored = self.State.accounts[self.SelectedAcct].password
if string.find(stored, "^:encrypted:") then
if not has_encryption then
GlueDialog_Show("AL_CANT_DECRYPT")
return
end
EncryptedServerLogin(name, stored)
return
end
end
DefaultServerLogin(name, password)
end
function LoginManager:RemoveAccount()
if not next(LoginManager.State.accounts) or not LoginManager.SelectedAcct then return end
table.remove(LoginManager.State.accounts, LoginManager.SelectedAcct)
LoginManager:SaveAccounts()
AccountLoginAccountEdit:SetText("")
AccountLoginPasswordEdit:SetText("")
if (LoginManager.CurrentPage > 0 and LoginManager.CurrentPage * LoginManager.PageSize >
table.getn(LoginManager.State) - 1) then
LoginManager.CurrentPage = LoginManager.CurrentPage - 1
end
LoginManager:UpdateUI()
end
function LoginManager:NextPage()
if ((self.CurrentPage + 1) * self.PageSize >
table.getn(self.State.accounts) - 1) then return end
self.CurrentPage = self.CurrentPage + 1
self:UpdateUI()
end
function LoginManager:PrevPage()
if (self.CurrentPage == 0) then return end
self.CurrentPage = self.CurrentPage - 1
self:UpdateUI()
end
function LoginManager:OnCharactersLoad()
self:LoadAccounts()
if self.pending_account and self.pending_account.account and self.pending_account.account ~= ""
and self.pending_account.password and self.pending_account.password ~= "" then
table.insert(self.State.accounts, self.pending_account)
self:SaveAccounts()
end
self.pending_account = nil
-- make up/down/auto buttons
for i=1,10 do
local charButton = _G["CharSelectCharacterButton"..i]
if not charButton.up then
-- Create the Up button
local upButton = CreateFrame("Button", charButton:GetName().."Up", charButton)
upButton:SetWidth(32)
upButton:SetHeight(32)
upButton:SetPoint("TOPRIGHT", charButton, "TOPRIGHT", -30, 0)
upButton:SetHitRectInsets(1, 1, 1, 1)
upButton:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollUp-Up")
upButton:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollUp-Down")
upButton:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD")
upButton:SetScript("OnClick", function()
moveIndexUp(LoginManager.realm_chars, this:GetParent():GetID())
LoginManager:UpdateUI()
end)
upButton:SetScript("OnEnter", function()
this:GetParent():LockHighlight()
end)
upButton:SetScript("OnLeave", function()
if this:GetParent():GetID() ~= CharacterSelect.selectedIndex then
this:GetParent():UnlockHighlight()
end
end)
charButton.up = upButton
end
if not charButton.down then
-- Create the Down button
local downButton = CreateFrame("Button", charButton:GetName().."Down", charButton)
downButton:SetWidth(32)
downButton:SetHeight(32)
downButton:SetPoint("TOP", charButton.up, "BOTTOM", 0, 8)
downButton:SetHitRectInsets(1, 1, 1, 1)
downButton:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up")
downButton:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Down")
downButton:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD")
downButton:SetScript("OnClick", function()
moveIndexDown(LoginManager.realm_chars, this:GetParent():GetID())
LoginManager:UpdateUI()
end)
downButton:SetScript("OnEnter", function()
this:GetParent():LockHighlight()
end)
downButton:SetScript("OnLeave", function()
if this:GetParent():GetID() ~= CharacterSelect.selectedIndex then
this:GetParent():UnlockHighlight()
end
end)
charButton.down = downButton
end
end
local addonsButton = _G["CharacterSelectAddonsButton"]
if not addonsButton.lock then
-- Create the Lock button
local lockButton = CreateFrame("Button", "ButtonCharButtonsLock", addonsButton, "GlueButtonSmallTemplate")
lockButton:SetWidth(150)
lockButton:SetHeight(35)
lockButton:SetPoint("LEFT", addonsButton, "RIGHT", 4, 0)
lockButton:SetText(L["LockCharacters"])
lockButton:SetWidth(lockButton:GetFontString():GetStringWidth() + 50)
lockButton:SetScript("OnClick", function()
LoginManager.char_buttons_locked = not LoginManager.char_buttons_locked
this:Show()
LoginManager:SaveAccounts()
LoginManager:UpdateUI()
end)
addonsButton.lock = lockButton
end
self.realm = GetServerName()
self.realm_chars = {}
for c = 1, GetNumCharacters() do
local name, race, class, level, zone, fileString, gender, ghost = GetCharacterInfo(c)
self.realm_chars[c] = { id = c, name = name, race = race, class = class, level = level, zone = zone, fileString = fileString, gender = gender, ghost = ghost }
end
if not self.from_login_screen then
-- handle case that we came from a char logout, iow determine what account we're using based on characters
-- this matters when we're multiboxing or have auto-login enabled
local function SetIdx()
for i,account in ipairs(self.State.accounts) do
for saved_realm,saved_chars in pairs(account.characters or {}) do
if saved_realm == self.realm then
for _,schar in pairs(saved_chars.order) do
for _,rchar in pairs(self.realm_chars) do
if schar.name == rchar.name then
self.SelectedAcct = i
return
end
end
end
end
end
end
end
SetIdx()
end
-- reconcile given chars with saved chars
local acct = self.State.accounts[self.SelectedAcct]
if acct and acct.characters then
for saved_realm,saved_chars in pairs(acct.characters or {}) do
if saved_realm == self.realm then
self.char_buttons_locked = saved_chars.char_buttons_locked
local orderRank = {}
local rank = 1
for _, item in ipairs(saved_chars.order or {}) do
orderRank[item.id] = rank
rank = rank + 1
end
-- Reorder primaryList in place using table.sort with a custom comparator.
table.sort(LoginManager.realm_chars, function(a, b)
local ra = orderRank[a.id] or 10000
local rb = orderRank[b.id] or 10000
return ra < rb
end)
local numChars = GetNumCharacters()
if numChars > 0 then
CharacterSelect_SelectCharacter(math.min(saved_chars.last or 1, numChars))
if self.auto_char_button_pressed then
self.auto_char_button_pressed = false
EnterWorld()
end
end
break
end
end
end
self:UpdateUI()
end
function LoginManager:EnterWorld()
-- save live state
if self.realm and self.SelectedAcct and self.State.accounts[self.SelectedAcct] then
local acct = self.State.accounts[self.SelectedAcct]
acct.characters = acct.characters or {}
acct.characters[self.realm] = acct.characters[self.realm] or {}
local chars = acct.characters[self.realm]
chars.last = CharacterSelect.selectedIndex or 1
chars.order = {}
for i,char in ipairs(LoginManager.realm_chars) do
table.insert(chars.order, { id = char.id, name = char.name })
end
chars.char_buttons_locked = self.char_buttons_locked or nil
local name, race, class, _level, zone, _race2, _gender, _isGhost = GetCharacterInfo(chars.last)
acct.character = name
acct.class = class
acct.race = race
acct.zone = zone
end
self:SaveAccounts()
EnterWorld()
end
---------------
----------
-- buttons
----------
function AutologinAccountButton_OnClick(button)
if button == "LeftButton" then
LoginManager:SelectAccount(this:GetID())
end
end
function AutologinAccountButton_OnDoubleClick()
LoginManager:SelectAccount(this:GetID())
AccountLogin_Login()
end
function AutologinRemoveAccountButton_OnClick()
LoginManager:RemoveAccount()
end
----------
--------
-- hooks
--------
local orig_AccountLogin_OnLoad = AccountLogin_OnLoad
AccountLogin_OnLoad = function (a1,a2,a3,a4,a5,a6,a7,a8,a9)
if orig_AccountLogin_OnLoad then orig_AccountLogin_OnLoad(a1,a2,a3,a4,a5,a6,a7,a8,a9) end
LoginManager:LoadAccounts()
end
local orig_AccountLogin_OnShow = AccountLogin_OnShow
AccountLogin_OnShow = function (a1,a2,a3,a4,a5,a6,a7,a8,a9)
if orig_AccountLogin_OnShow then orig_AccountLogin_OnShow(a1,a2,a3,a4,a5,a6,a7,a8,a9) end
if not LoginManager.State.accounts then LoginManager:LoadAccounts() end
if LoginManager.State.last and LoginManager.State.accounts[LoginManager.State.last] then
LoginManager:SelectAccount(LoginManager.State.last)
end
LoginManager:UpdateUI()
end
local orig_AccountLoginAccountEdit_OnTextChanged = AccountLoginAccountEdit:GetScript("OnTextChanged")
AccountLoginAccountEdit:SetScript("OnTextChanged", function (a1,a2,a3,a4,a5,a6,a7,a8,a9)
orig_AccountLoginAccountEdit_OnTextChanged(a1,a2,a3,a4,a5,a6,a7,a8,a9)
LoginManager:OnNameUpdate(this:GetText())
end)
local orig_AccountLogin_Login = AccountLogin_Login
AccountLogin_Login = function (a1,a2,a3,a4,a5,a6,a7,a8,a9)
LoginManager:OnLogin()
-- if we got this far it's our show, take over
-- if orig_AccountLogin_Login then orig_AccountLogin_Login(a1,a2,a3,a4,a5,a6,a7,a8,a9) end
end
--------
local orig_CharacterSelectButton_OnClick = CharacterSelectButton_OnClick
CharacterSelectButton_OnClick = function (a1,a2,a3,a4,a5,a6,a7,a8,a9)
-- orig_CharacterSelectButton_OnClick(a1,a2,a3,a4,a5,a6,a7,a8,a9)
local id = this:GetID()
local sorted_id = LoginManager.realm_chars[id].id
if ( sorted_id ~= CharacterSelect.selectedIndex ) then
CharacterSelect_SelectCharacter(sorted_id)
end
LoginManager:UpdateUI()
end
local orig_CharacterSelectButton_OnDoubleClick = CharacterSelectButton_OnDoubleClick
CharacterSelectButton_OnDoubleClick = function (a1,a2,a3,a4,a5,a6,a7,a8,a9)
-- orig_CharacterSelectButton_OnDoubleClick(a1,a2,a3,a4,a5,a6,a7,a8,a9)
local id = this:GetID()
local sorted_id = LoginManager.realm_chars[id].id
if ( sorted_id ~= CharacterSelect.selectedIndex ) then
CharacterSelect_SelectCharacter(sorted_id)
end
CharacterSelect_EnterWorld()
end
local orig_CharacterSelect_OnKeyDown = CharacterSelect_OnKeyDown
CharacterSelect_OnKeyDown = function (a1,a2,a3,a4,a5,a6,a7,a8,a9)
local numChars = GetNumCharacters()
local button_index
for i,char in ipairs(LoginManager.realm_chars) do
if char.id == this.selectedIndex then
button_index = i
break
end
end
if ( arg1 == "UP" or arg1 == "LEFT" ) then
if ( numChars > 1 ) then
if ( button_index > 1 ) then
_G["CharSelectCharacterButton" .. (button_index - 1)]:Click()
else
_G["CharSelectCharacterButton" .. numChars]:Click()
end
end
elseif ( arg1 == "DOWN" or arg1 == "RIGHT" ) then
if ( numChars > 1 ) then
if ( button_index < GetNumCharacters() ) then
_G["CharSelectCharacterButton" .. (button_index + 1)]:Click()
else
_G["CharSelectCharacterButton" .. 1]:Click()
end
end
else
orig_CharacterSelect_OnKeyDown(a1,a2,a3,a4,a5,a6,a7,a8,a9)
end
end
local orig_CharacterSelect_OnEvent = CharacterSelect_OnEvent
CharacterSelect_OnEvent = function (a1,a2,a3,a4,a5,a6,a7,a8,a9)
if orig_CharacterSelect_OnEvent then orig_CharacterSelect_OnEvent(a1,a2,a3,a4,a5,a6,a7,a8,a9) end
if event == "CHARACTER_LIST_UPDATE" then
LoginManager:OnCharactersLoad()
end
end
local orig_CharacterSelect_EnterWorld = CharacterSelect_EnterWorld
CharacterSelect_EnterWorld = function (a1,a2,a3,a4,a5,a6,a7,a8,a9)
LoginManager:EnterWorld()
end
--------