423 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			423 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
CharacterList = { }
 | 
						|
 | 
						|
-- private variables
 | 
						|
local charactersWindow
 | 
						|
local loadBox
 | 
						|
local characterList
 | 
						|
local errorBox
 | 
						|
local waitingWindow
 | 
						|
local autoReconnectButton
 | 
						|
local updateWaitEvent
 | 
						|
local resendWaitEvent
 | 
						|
local loginEvent
 | 
						|
local autoReconnectEvent
 | 
						|
local lastLogout = 0
 | 
						|
 | 
						|
-- 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()
 | 
						|
    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)
 | 
						|
  g_logger.info("Login to " .. charInfo.worldHost .. ":" .. charInfo.worldPort)
 | 
						|
  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)
 | 
						|
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 }
 | 
						|
        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
 | 
						|
  scheduleAutoReconnect()
 | 
						|
end
 | 
						|
 | 
						|
function onGameLoginToken(unknown)
 | 
						|
  CharacterList.destroyLoadBox()
 | 
						|
  -- TODO: make it possible to enter a new token here / prompt token
 | 
						|
  errorBox = displayErrorBox(tr("Two-Factor Authentification"), 'A new authentification token is required.\nPlease login again.')
 | 
						|
  errorBox.onOk = function()
 | 
						|
    errorBox = nil
 | 
						|
    EnterGame.show()
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
function onGameConnectionError(message, code)
 | 
						|
  CharacterList.destroyLoadBox()
 | 
						|
  if (not g_game.isOnline() or code ~= 2) and not errorBox then -- code 2 is normal disconnect, end of file
 | 
						|
    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
 | 
						|
  scheduleAutoReconnect()
 | 
						|
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
 | 
						|
 | 
						|
function onGameEnd()
 | 
						|
  scheduleAutoReconnect()
 | 
						|
  CharacterList.showAgain()
 | 
						|
end
 | 
						|
 | 
						|
function onLogout()
 | 
						|
  lastLogout = g_clock.millis()
 | 
						|
end
 | 
						|
 | 
						|
function scheduleAutoReconnect()
 | 
						|
  if lastLogout + 2000 > g_clock.millis() then
 | 
						|
    return
 | 
						|
  end
 | 
						|
  if autoReconnectEvent then
 | 
						|
    removeEvent(autoReconnectEvent)    
 | 
						|
  end
 | 
						|
  autoReconnectEvent = scheduleEvent(executeAutoReconnect, 2500)
 | 
						|
end
 | 
						|
 | 
						|
function executeAutoReconnect()  
 | 
						|
  if not autoReconnectButton or not autoReconnectButton:isOn() or g_game.isOnline() then
 | 
						|
    return
 | 
						|
  end
 | 
						|
  if errorBox then
 | 
						|
    errorBox:destroy()
 | 
						|
    errorBox = nil
 | 
						|
  end
 | 
						|
  CharacterList.doLogin()
 | 
						|
end
 | 
						|
 | 
						|
-- public functions
 | 
						|
function CharacterList.init()
 | 
						|
  if USE_NEW_ENERGAME then return end
 | 
						|
  connect(g_game, { onLoginError = onGameLoginError })
 | 
						|
  connect(g_game, { onLoginToken = onGameLoginToken })
 | 
						|
  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 = onGameEnd })
 | 
						|
  connect(g_game, { onLogout = onLogout })
 | 
						|
 | 
						|
  if G.characters then
 | 
						|
    CharacterList.create(G.characters, G.characterAccount)
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
function CharacterList.terminate()
 | 
						|
 if USE_NEW_ENERGAME then return end
 | 
						|
  disconnect(g_game, { onLoginError = onGameLoginError })
 | 
						|
  disconnect(g_game, { onLoginToken = onGameLoginToken })
 | 
						|
  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 = onGameEnd })
 | 
						|
  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
 | 
						|
 | 
						|
  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 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
 | 
						|
 | 
						|
    -- 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()
 | 
						|
    removeEvent(autoReconnectEvent)
 | 
						|
    autoReconnectEvent = nil
 | 
						|
  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 and account.premDays < 1 then
 | 
						|
    accountStatusLabel:setText(('%s%s'):format(tr('Free Account'), status))
 | 
						|
  else
 | 
						|
    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
 | 
						|
  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', true)
 | 
						|
    autoReconnectButton:setOn(autoReconnect)
 | 
						|
    g_settings.set('autoReconnect', autoReconnect)
 | 
						|
  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', true)
 | 
						|
  autoReconnectButton:setOn(autoReconnect)
 | 
						|
end
 | 
						|
 | 
						|
function CharacterList.hide(showLogin)
 | 
						|
  removeEvent(autoReconnectEvent)
 | 
						|
  autoReconnectEvent = nil
 | 
						|
 | 
						|
  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()
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
function CharacterList.isVisible()
 | 
						|
  if charactersWindow and charactersWindow:isVisible() then
 | 
						|
    return true
 | 
						|
  end
 | 
						|
  return false
 | 
						|
end
 | 
						|
 | 
						|
function CharacterList.doLogin()
 | 
						|
  removeEvent(autoReconnectEvent)
 | 
						|
  autoReconnectEvent = nil
 | 
						|
 | 
						|
  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
 | 
						|
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
 |