mirror of
https://github.com/mehah/otclient
synced 2026-08-15 16:29:06 -04:00
567 lines
16 KiB
Lua
567 lines
16 KiB
Lua
CharacterList = {}
|
|
|
|
-- private variables
|
|
local charactersWindow
|
|
local loadBox
|
|
local characterList
|
|
local errorBox
|
|
local waitingWindow
|
|
local updateWaitEvent
|
|
local resendWaitEvent
|
|
local loginEvent
|
|
local outfitCreatureBox
|
|
|
|
local autoReconnectButton
|
|
local autoReconnectEvent
|
|
local lastLogout = 0
|
|
local function removeAutoReconnectEvent() --prevent
|
|
if autoReconnectEvent then
|
|
removeEvent(autoReconnectEvent)
|
|
autoReconnectEvent = nil
|
|
end
|
|
end
|
|
|
|
-- private functions
|
|
local function tryLogin(charInfo, tries)
|
|
tries = tries or 1
|
|
|
|
if tries > 50 then
|
|
return
|
|
end
|
|
|
|
if g_game.isOnline() then
|
|
if tries == 1 then
|
|
g_game.safeLogout()
|
|
if loginEvent then
|
|
removeEvent(loginEvent)
|
|
loginEvent = nil
|
|
end
|
|
end
|
|
loginEvent = scheduleEvent(function()
|
|
tryLogin(charInfo, tries + 1)
|
|
end, 100)
|
|
return
|
|
end
|
|
|
|
CharacterList.hide()
|
|
|
|
g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort,
|
|
charInfo.characterName, G.authenticatorToken, G.sessionKey)
|
|
|
|
loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to game server...'))
|
|
connect(loadBox, {
|
|
onCancel = function()
|
|
loadBox = nil
|
|
g_game.cancelLogin()
|
|
CharacterList.show()
|
|
end
|
|
})
|
|
|
|
-- save last used character
|
|
g_settings.set('last-used-character', charInfo.characterName)
|
|
g_settings.set('last-used-world', charInfo.worldName)
|
|
removeAutoReconnectEvent()
|
|
end
|
|
|
|
local function updateWait(timeStart, timeEnd)
|
|
if waitingWindow then
|
|
local time = g_clock.seconds()
|
|
if time <= timeEnd then
|
|
local percent = ((time - timeStart) / (timeEnd - timeStart)) * 100
|
|
local timeStr = string.format('%.0f', timeEnd - time)
|
|
|
|
local progressBar = waitingWindow:getChildById('progressBar')
|
|
progressBar:setPercent(percent)
|
|
|
|
local label = waitingWindow:getChildById('timeLabel')
|
|
label:setText(tr('Trying to reconnect in %s seconds.', timeStr))
|
|
|
|
updateWaitEvent = scheduleEvent(function()
|
|
updateWait(timeStart, timeEnd)
|
|
end, 1000 * progressBar:getPercentPixels() / 100 * (timeEnd - timeStart))
|
|
return true
|
|
end
|
|
end
|
|
|
|
if updateWaitEvent then
|
|
updateWaitEvent:cancel()
|
|
updateWaitEvent = nil
|
|
end
|
|
end
|
|
|
|
local function resendWait()
|
|
if waitingWindow then
|
|
waitingWindow:destroy()
|
|
waitingWindow = nil
|
|
|
|
if updateWaitEvent then
|
|
updateWaitEvent:cancel()
|
|
updateWaitEvent = nil
|
|
end
|
|
|
|
if charactersWindow then
|
|
local selected = characterList:getFocusedChild()
|
|
if selected then
|
|
local charInfo = {
|
|
worldHost = selected.worldHost,
|
|
worldPort = selected.worldPort,
|
|
worldName = selected.worldName,
|
|
characterName = selected.characterName,
|
|
characterLevel = selected.characterLevel,
|
|
main = selected.main,
|
|
dailyreward = selected.dailyreward,
|
|
hidden = selected.hidden,
|
|
outfitid = selected.outfitid,
|
|
headcolor = selected.headcolor,
|
|
torsocolor = selected.torsocolor,
|
|
legscolor = selected.legscolor,
|
|
detailcolor = selected.detailcolor,
|
|
addonsflags = selected.addonsflags,
|
|
characterVocation = selected.characterVocation
|
|
}
|
|
tryLogin(charInfo)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onLoginWait(message, time)
|
|
CharacterList.destroyLoadBox()
|
|
|
|
waitingWindow = g_ui.displayUI('waitinglist')
|
|
|
|
local label = waitingWindow:getChildById('infoLabel')
|
|
label:setText(message)
|
|
|
|
updateWaitEvent = scheduleEvent(function()
|
|
updateWait(g_clock.seconds(), g_clock.seconds() + time)
|
|
end, 0)
|
|
resendWaitEvent = scheduleEvent(resendWait, time * 1000)
|
|
end
|
|
|
|
function onGameLoginError(message)
|
|
CharacterList.destroyLoadBox()
|
|
errorBox = displayErrorBox(tr('Login Error'), message)
|
|
errorBox.onOk = function()
|
|
errorBox = nil
|
|
CharacterList.showAgain()
|
|
end
|
|
end
|
|
|
|
function onGameSessionEnd(reason)
|
|
CharacterList.destroyLoadBox()
|
|
CharacterList.showAgain()
|
|
end
|
|
|
|
function onGameConnectionError(message, code)
|
|
CharacterList.destroyLoadBox()
|
|
local text = translateNetworkError(code, g_game.getProtocolGame() and g_game.getProtocolGame():isConnecting(),
|
|
message)
|
|
errorBox = displayErrorBox(tr('Connection Error'), text)
|
|
errorBox.onOk = function()
|
|
errorBox = nil
|
|
CharacterList.showAgain()
|
|
end
|
|
end
|
|
|
|
function onGameUpdateNeeded(signature)
|
|
CharacterList.destroyLoadBox()
|
|
errorBox = displayErrorBox(tr('Update needed'), tr('Enter with your account again to update your client.'))
|
|
errorBox.onOk = function()
|
|
errorBox = nil
|
|
CharacterList.showAgain()
|
|
end
|
|
end
|
|
|
|
-- public functions
|
|
function CharacterList.init()
|
|
connect(g_game, {
|
|
onLoginError = onGameLoginError
|
|
})
|
|
connect(g_game, {
|
|
onSessionEnd = onGameSessionEnd
|
|
})
|
|
connect(g_game, {
|
|
onUpdateNeeded = onGameUpdateNeeded
|
|
})
|
|
connect(g_game, {
|
|
onConnectionError = onGameConnectionError
|
|
})
|
|
connect(g_game, {
|
|
onGameStart = CharacterList.destroyLoadBox
|
|
})
|
|
connect(g_game, {
|
|
onLoginWait = onLoginWait
|
|
})
|
|
connect(g_game, {
|
|
onGameEnd = CharacterList.showAgain
|
|
})
|
|
connect(g_game, {
|
|
onLogout = onLogout
|
|
})
|
|
|
|
if G.characters then
|
|
CharacterList.create(G.characters, G.characterAccount)
|
|
end
|
|
end
|
|
|
|
function CharacterList.terminate()
|
|
disconnect(g_game, {
|
|
onLoginError = onGameLoginError
|
|
})
|
|
disconnect(g_game, {
|
|
onSessionEnd = onGameSessionEnd
|
|
})
|
|
disconnect(g_game, {
|
|
onUpdateNeeded = onGameUpdateNeeded
|
|
})
|
|
disconnect(g_game, {
|
|
onConnectionError = onGameConnectionError
|
|
})
|
|
disconnect(g_game, {
|
|
onGameStart = CharacterList.destroyLoadBox
|
|
})
|
|
disconnect(g_game, {
|
|
onLoginWait = onLoginWait
|
|
})
|
|
disconnect(g_game, {
|
|
onGameEnd = CharacterList.showAgain
|
|
})
|
|
disconnect(g_game, {
|
|
onLogout = onLogout
|
|
})
|
|
|
|
if charactersWindow then
|
|
characterList = nil
|
|
charactersWindow:destroy()
|
|
charactersWindow = nil
|
|
end
|
|
|
|
if loadBox then
|
|
g_game.cancelLogin()
|
|
loadBox:destroy()
|
|
loadBox = nil
|
|
end
|
|
|
|
if waitingWindow then
|
|
waitingWindow:destroy()
|
|
waitingWindow = nil
|
|
end
|
|
|
|
if updateWaitEvent then
|
|
removeEvent(updateWaitEvent)
|
|
updateWaitEvent = nil
|
|
end
|
|
|
|
if resendWaitEvent then
|
|
removeEvent(resendWaitEvent)
|
|
resendWaitEvent = nil
|
|
end
|
|
|
|
if loginEvent then
|
|
removeEvent(loginEvent)
|
|
loginEvent = nil
|
|
end
|
|
|
|
removeAutoReconnectEvent()
|
|
destroyCreateAccount()
|
|
|
|
CharacterList = nil
|
|
end
|
|
|
|
function CharacterList.create(characters, account, otui)
|
|
if not otui then
|
|
otui = 'characterlist'
|
|
end
|
|
|
|
if charactersWindow then
|
|
charactersWindow:destroy()
|
|
end
|
|
|
|
charactersWindow = g_ui.displayUI(otui)
|
|
characterList = charactersWindow:getChildById('characters')
|
|
autoReconnectButton = charactersWindow:getChildById('autoReconnect')
|
|
|
|
-- characters
|
|
G.characters = characters
|
|
G.characterAccount = account
|
|
|
|
characterList:destroyChildren()
|
|
local accountStatusLabel = charactersWindow:getChildById('accountStatusLabel')
|
|
local accountStatusIcon = nil
|
|
if g_game.getFeature(GameEnterGameShowAppearance) then
|
|
accountStatusIcon = charactersWindow:getChildById('accountStatusIcon')
|
|
end
|
|
|
|
local focusLabel
|
|
for i, characterInfo in ipairs(characters) do
|
|
local widget = g_ui.createWidget('CharacterWidget', characterList)
|
|
for key, value in pairs(characterInfo) do
|
|
local subWidget = widget:getChildById(key)
|
|
if subWidget then
|
|
if key == 'outfit' then -- it's an exception
|
|
subWidget:setOutfit(value)
|
|
else
|
|
local text = value
|
|
if subWidget.baseText and subWidget.baseTranslate then
|
|
text = tr(subWidget.baseText, text)
|
|
elseif subWidget.baseText then
|
|
text = string.format(subWidget.baseText, text)
|
|
end
|
|
subWidget:setText(text)
|
|
end
|
|
end
|
|
end
|
|
|
|
if g_game.getFeature(GameEnterGameShowAppearance) then
|
|
local creatureDisplay = widget:getChildById('outfitCreatureBox', characterList)
|
|
creatureDisplay:setSize("64 64")
|
|
local creature = Creature.create()
|
|
local outfit = {type = characterInfo.outfitid, head = characterInfo.headcolor, body = characterInfo.torsocolor, legs = characterInfo.legscolor, feet = characterInfo.detailcolor, addons = characterInfo.addonsflags}
|
|
creature:setOutfit(outfit)
|
|
creature:setDirection(2)
|
|
creatureDisplay:setCreature(creature)
|
|
|
|
local mainCharacter = widget:getChildById('mainCharacter', characterList)
|
|
if characterInfo.main then
|
|
mainCharacter:setImageSource('/images/game/entergame/maincharacter')
|
|
else
|
|
mainCharacter:setImageSource('')
|
|
end
|
|
|
|
local statusDailyReward = widget:getChildById('statusDailyReward', characterList)
|
|
if characterInfo.dailyreward == 0 then
|
|
statusDailyReward:setImageSource('/images/game/entergame/dailyreward_collected')
|
|
else
|
|
statusDailyReward:setImageSource('/images/game/entergame/dailyreward_notcollected')
|
|
end
|
|
|
|
local statusHidden = widget:getChildById('statusHidden', characterList)
|
|
if characterInfo.hidden then
|
|
statusHidden:setImageSource('/images/game/entergame/hidden')
|
|
else
|
|
statusHidden:setImageSource('')
|
|
end
|
|
end
|
|
|
|
-- these are used by login
|
|
widget.characterName = characterInfo.name
|
|
widget.worldName = characterInfo.worldName
|
|
widget.worldHost = characterInfo.worldIp
|
|
widget.worldPort = characterInfo.worldPort
|
|
|
|
connect(widget, {
|
|
onDoubleClick = function()
|
|
CharacterList.doLogin()
|
|
return true
|
|
end
|
|
})
|
|
|
|
if i == 1 or
|
|
(g_settings.get('last-used-character') == widget.characterName and g_settings.get('last-used-world') ==
|
|
widget.worldName) then
|
|
focusLabel = widget
|
|
end
|
|
end
|
|
|
|
if focusLabel then
|
|
characterList:focusChild(focusLabel, KeyboardFocusReason)
|
|
addEvent(function()
|
|
characterList:ensureChildVisible(focusLabel)
|
|
end)
|
|
end
|
|
characterList.onChildFocusChange = function()
|
|
removeAutoReconnectEvent()
|
|
end
|
|
|
|
-- account
|
|
local status = ''
|
|
if account.status == AccountStatus.Frozen then
|
|
status = tr(' (Frozen)')
|
|
elseif account.status == AccountStatus.Suspended then
|
|
status = tr(' (Suspended)')
|
|
end
|
|
|
|
if account.subStatus == SubscriptionStatus.Free then
|
|
accountStatusLabel:setText(('%s%s'):format(tr('Free Account'), status))
|
|
if accountStatusIcon ~= nil then
|
|
accountStatusIcon:setImageSource('/images/game/entergame/nopremium')
|
|
end
|
|
elseif account.subStatus == SubscriptionStatus.Premium then
|
|
if account.premDays == 0 or account.premDays == 65535 then
|
|
accountStatusLabel:setText(('%s%s'):format(tr('Gratis Premium Account'), status))
|
|
else
|
|
accountStatusLabel:setText(('%s%s'):format(tr('Premium Account (%s) days left', account.premDays), status))
|
|
end
|
|
if accountStatusIcon ~= nil then
|
|
accountStatusIcon:setImageSource('/images/game/entergame/premium')
|
|
end
|
|
end
|
|
|
|
if account.premDays > 0 and account.premDays <= 7 then
|
|
accountStatusLabel:setOn(true)
|
|
else
|
|
accountStatusLabel:setOn(false)
|
|
end
|
|
|
|
autoReconnectButton.onClick = function(widget)
|
|
local autoReconnect = not g_settings.getBoolean('autoReconnect', false)
|
|
autoReconnectButton:setOn(autoReconnect)
|
|
g_settings.set('autoReconnect', autoReconnect)
|
|
local statusText = autoReconnect and 'Auto reconnect: On' or 'Auto reconnect: off'
|
|
if not g_game.getFeature(GameEnterGameShowAppearance) then
|
|
statusText = autoReconnect and 'Auto reconnect:\n On' or 'Auto reconnect:\n off'
|
|
end
|
|
|
|
autoReconnectButton:setText(statusText)
|
|
end
|
|
end
|
|
|
|
function CharacterList.destroy()
|
|
CharacterList.hide(true)
|
|
|
|
if charactersWindow then
|
|
characterList = nil
|
|
charactersWindow:destroy()
|
|
charactersWindow = nil
|
|
end
|
|
end
|
|
|
|
function CharacterList.show()
|
|
if loadBox or errorBox or not charactersWindow then
|
|
return
|
|
end
|
|
charactersWindow:show()
|
|
charactersWindow:raise()
|
|
charactersWindow:focus()
|
|
|
|
local autoReconnect = g_settings.getBoolean('autoReconnect', false)
|
|
autoReconnectButton:setOn(autoReconnect)
|
|
local reconnectStatus = autoReconnect and "On" or "Off"
|
|
if not g_game.getFeature(GameEnterGameShowAppearance) then
|
|
autoReconnectButton:setText('Auto reconnect:\n ' .. reconnectStatus)
|
|
else
|
|
autoReconnectButton:setText('Auto reconnect: ' .. reconnectStatus)
|
|
end
|
|
end
|
|
|
|
function CharacterList.hide(showLogin)
|
|
removeAutoReconnectEvent()
|
|
showLogin = showLogin or false
|
|
charactersWindow:hide()
|
|
|
|
if showLogin and EnterGame and not g_game.isOnline() then
|
|
EnterGame.show()
|
|
end
|
|
end
|
|
|
|
function CharacterList.showAgain()
|
|
if characterList and characterList:hasChildren() then
|
|
CharacterList.show()
|
|
scheduleAutoReconnect()
|
|
end
|
|
end
|
|
|
|
function CharacterList.isVisible()
|
|
if charactersWindow and charactersWindow:isVisible() then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
function CharacterList.doLogin()
|
|
removeAutoReconnectEvent()
|
|
local selected = characterList:getFocusedChild()
|
|
if selected then
|
|
local charInfo = {
|
|
worldHost = selected.worldHost,
|
|
worldPort = selected.worldPort,
|
|
worldName = selected.worldName,
|
|
characterName = selected.characterName
|
|
}
|
|
charactersWindow:hide()
|
|
if loginEvent then
|
|
removeEvent(loginEvent)
|
|
loginEvent = nil
|
|
end
|
|
tryLogin(charInfo)
|
|
else
|
|
displayErrorBox(tr('Error'), tr('You must select a character to login!'))
|
|
end
|
|
end
|
|
|
|
function CharacterList.destroyLoadBox()
|
|
if loadBox then
|
|
loadBox:destroy()
|
|
loadBox = nil
|
|
end
|
|
destroyCreateAccount()
|
|
end
|
|
|
|
function CharacterList.cancelWait()
|
|
if waitingWindow then
|
|
waitingWindow:destroy()
|
|
waitingWindow = nil
|
|
end
|
|
|
|
if updateWaitEvent then
|
|
removeEvent(updateWaitEvent)
|
|
updateWaitEvent = nil
|
|
end
|
|
|
|
if resendWaitEvent then
|
|
removeEvent(resendWaitEvent)
|
|
resendWaitEvent = nil
|
|
end
|
|
|
|
CharacterList.destroyLoadBox()
|
|
CharacterList.showAgain()
|
|
end
|
|
|
|
function CharacterList.updateCharactersAppearances(showOutfits)
|
|
if showOutfitsCheckbox and showOutfits ~= showOutfitsCheckbox:isChecked() then
|
|
showOutfitsCheckbox:setChecked(showOutfits)
|
|
end
|
|
|
|
if not(characterList) or #(characterList:getChildren()) == 0 then
|
|
return
|
|
end
|
|
|
|
for _, widget in ipairs(characterList:getChildren()) do
|
|
if not widget.characterInfo then
|
|
break
|
|
end
|
|
|
|
if not(widget.creature) or not(widget.creatureBorder) then
|
|
widget.creature = widget:recursiveGetChildById('creature')
|
|
widget.creatureBorder = widget.creature:getParent()
|
|
end
|
|
|
|
CharacterList.updateCharactersAppearance(widget, widget.characterInfo, showOutfits)
|
|
end
|
|
end
|
|
|
|
function onLogout()
|
|
lastLogout = g_clock.millis()
|
|
end
|
|
|
|
function scheduleAutoReconnect()
|
|
if not g_settings.getBoolean('autoReconnect') or lastLogout + 2000 > g_clock.millis() then
|
|
return
|
|
end
|
|
|
|
removeAutoReconnectEvent()
|
|
autoReconnectEvent = scheduleEvent(executeAutoReconnect, 2500)
|
|
end
|
|
|
|
function executeAutoReconnect()
|
|
if not g_settings.getBoolean('autoReconnect') then
|
|
return
|
|
end
|
|
|
|
if errorBox then
|
|
errorBox:destroy()
|
|
errorBox = nil
|
|
end
|
|
CharacterList.doLogin()
|
|
end
|