• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[OTCV8] Characterlist vocation display

xaii

Member
Joined
Nov 17, 2018
Messages
43
Reaction score
5
How can I display a different image in characterlist when I have a different vocation? For example, if I create a character then i have vocation NONE, but when i join to clan i have vocation HUNTER, I want to display a different image in characterlist based on my vocation.
It is not very clear to me. Maybe someone could show or explain what I need to do?



Distribution TFS 1.2 Pokedash by Pota
OTCV8


Characterlist.lua
LUA:
CharacterList = { }

-- private variables
local charactersWindow
local loadBox
local characterList
local characterListPanel
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 = CharacterList.showAgain })
 -- 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 = CharacterList.showAgain })
 --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
      updateWaitEvent:cancel()
    --removeEvent(updateWaitEvent)
   
    updateWaitEvent = nil
  end

  if resendWaitEvent then
    --removeEvent(resendWaitEvent)
    resendWaitEvent:cancel()
    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 = 'newcharacterlist' 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)
   
    widget:setImageSource('/images/trainerCards/' .. getVocationNameById(characterInfo.vocation))
    widget:getChildById("ClaICON"):setImageSource('/images/trainerCards/clans/' .. getVocationNameById(characterInfo.vocation))
   
    --g_ui.createWidget('Character', widget):getOutfit(characterInfo.outfit)
   
   
    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
  if account.subStatus == SubscriptionStatus.Free then
    accountStatusLabel:setText(('%s%s'):format(tr('Free Account'), status))
  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
  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

util.lua
LUA:
function postostring(pos)
  return pos.x .. " " .. pos.y .. " " .. pos.z
end

function dirtostring(dir)
  for k,v in pairs(Directions) do
    if v == dir then
      return k
    end
  end
end


function getVocationNameById(vocation)
  if (vocation == 0) then
    return "hunter"
  elseif (vocation == 2) then
    return "catcher"
  elseif (vocation == 3) then
    return "healer"
  elseif (vocation == 4) then
    return "blocker"
  elseif (vocation == 5) then
    return "explorer"
   end
       return "none"
 end

newcharacterlist.otui
Code:
Character < Creature
  fixed-creature-size: true
  anchors.top: parent.top
  anchors.left: parent.left
  margin-top: 45
  margin-left: 140
  image-source: ~

CharacterWidget < UIWidget
  image-source: /images/trainerCards/none
  margin: 4
  size: 290 80
  &updateOnStates: |
    function(self)
      local children = self:getChildren()
      for i=1,#children do
        children[i]:setOn(self:isFocused())
      end
    end
  @onFocusChange: self:updateOnStates()
  @onSetup: self:updateOnStates()
  opacity: 0.7

  $focus:
    opacity: 1.0

  Label
    id: name
    color: #aaaaaa
    anchors.top: parent.top
    anchors.left: parent.left
    anchors.right: parent.right
    font: verdana-11px-monochrome
    text-auto-resize: true
    margin-top: 12
    margin-left: 15

    $on:
      color: #ffffff

  Label
    visible: true
    id: worldName
    color: #ffffff
    anchors.top: ClaICON.top
    anchors.left: ClaICON.left
    margin-left: 310
    margin-top: 15
    font: verdana-11px-monochrome
    text-auto-resize: true
    text-align: center
    &baseText: '%s'

    $on:
      color: #ffffff

  Label
    id: level
    color: #ffffff
    anchors.top: ClaICON.top
    anchors.left: ClaICON.left
    margin-left: 43
    margin-top: 13
    font: verdana-11px-monochrome
    text-auto-resize: true
    text-align: center

    $on:
      color: #ffffff

  UIWidget
    //ICONE DO CLA
    id: ClaICON
    image-source: /images/trainerCards/clans/none
    size: 40 40
    anchors.top: parent.top
    anchors.left: parent.left
    margin-top: 110
    margin-left: 10

  UIWidget
    id: pokemon1
    color: #5C5C5C
    anchors.bottom: parent.bottom
    anchors.left: parent.left
    margin-bottom: 20
    margin-left: 19
    image-source: ~
    size: 50 54

  UIWidget
    id: pokemon2
    color: #5C5C5C
    anchors.bottom: parent.bottom
    anchors.left: parent.left
    margin-bottom: 20
    margin-left: 80
    image-source: ~
    size: 50 54

  UIWidget
    id: pokemon3
    color: #5C5C5C
    anchors.bottom: parent.bottom
    anchors.left: parent.left
    margin-bottom: 20
    margin-left: 145
    image-source: ~
    size: 50 54

  UIWidget
    id: pokemon4
    color: #5C5C5C
    anchors.bottom: parent.bottom
    anchors.left: parent.left
    margin-bottom: 20
    margin-left: 208
    image-source: ~
    size: 50 54

  UIWidget
    id: pokemon5
    color: #5C5C5C
    anchors.bottom: parent.bottom
    anchors.left: parent.left
    margin-bottom: 20
    margin-left: 271
    image-source: ~
    size: 50 54

  UIWidget
    id: pokemon6
    color: #5C5C5C
    anchors.bottom: parent.bottom
    anchors.left: parent.left
    margin-bottom: 20
    margin-left: 335
    image-source: ~
    size: 50 54

MainWindow
  id: charactersWindow
  image-source: images/background_characterlist
  size: 881 459
  padding: 0
  focusable: false
  draggable: false
  visible: false
  @onEnter: CharacterList.doLogin()
  @onEscape: CharacterList.hide(true)
  @onSetup: |
    g_keyboard.bindKeyPress('Up', function() self:getChildById('characters'):focusPreviousChild(KeyboardFocusReason) end, self)
    g_keyboard.bindKeyPress('Down', function() self:getChildById('characters'):focusNextChild(KeyboardFocusReason) end, self)

  UIWidget
    //ICONE DO CLA
    id: ClaICON
    image-source: images/time
    size: 30 30
    anchors.bottom: parent.bottom
    anchors.left: parent.left
    margin-bottom: 75
    margin-left: 20

  ScrollableFlatPanel
    id: characters
    anchors.top: parent.top
    anchors.left: parent.left
    anchors.right: parent.right
    anchors.bottom: parent.bottom
    margin-top: 90
    margin-bottom: 132
    margin-left: 18
    margin-right: 18
    image-source: ~
    padding: 0
    layout:
      type: horizontalBox
      cell-size: 404 224
      flow: true
      cell-spacing: 1
    horizontal-scrollbar: characterListScrollBar
    auto-focus: first
    focusable: false

  HorizontalScrollBar
    id: characterListScrollBar
    anchors.top: parent.top
    anchors.bottom: characters.bottom
    anchors.top: characters.top
    anchors.right: parent.right
    anchors.left: parent.left
    margin-bottom: -17
    margin-top: 240
    margin-left: 25
    margin-right: 25
    step: 15
    pixels-scroll: true

  Label
    id: accountStatusCaption
    !text: tr('Account Status') .. ':'
    anchors.left: parent.left
    anchors.bottom: separator.top
    margin-bottom: 20
    margin-left: 50

  Label
    id: accountStatusLabel
    !text: tr('Free Account')
    anchors.left: accountStatusCaption.right
    anchors.right: parent.right
    anchors.bottom: separator.top
    margin-left: 5
    margin-bottom: 19
    text-auto-resize: true

    $on:
      color: #FF0000

  HorizontalSeparator
    id: separator
    anchors.left: parent.left
    anchors.right: parent.right
    anchors.bottom: parent.bottom
    margin-bottom: 60
    opacity: 0

  //CheckBox
  //  id: charAutoLoginBox
  //  !text: tr('Auto login')
  //  !tooltip: tr('Auto login selected character on next charlist load')
  //  anchors.left: parent.left
  //  anchors.right: parent.right
  //  anchors.bottom: next.top
  //  margin-bottom: 6
  //  margin-left: 18
  //  margin-right: 18

  //UIWidget
  //  size: 27 30
  //  image-source: images/new_character
  //  !tooltip: tr('Novo Personagem')
  //  anchors.top: parent.top
  //  anchors.right: parent.right
  //  margin-top: 30
  //  margin-right: 15
  //  @onClick: modules.poke_create.showChar()
  //  opacity: 0.8
  //  $hover:
  //    opacity: 1
  //  $pressed:
  //    opacity: 0.9

  UIButton
    id: buttonOk
    image-source: images/selecionar
    size: 130 37
    anchors.right: parent.right
    anchors.bottom: parent.bottom
    margin-right: 305
    margin-bottom: 11
    opacity: 1
    @onClick: CharacterList.doLogin()
    $hover:
      size: 135 42
      opacity: 1
    $pressed:
      size: 135 42
      opacity: 0.9

  UIButton
    id: buttonCancel
    image-source: images/voltar
    size: 130 37
    anchors.right: buttonOk.right
    anchors.bottom: parent.bottom
    margin-right: 140
    margin-bottom: 11
    @onClick: CharacterList.hide(true)
    opacity: 1
    $hover:
      size: 135 42
      opacity: 1
    $pressed:
      size: 135 42
      opacity: 0.9

vocations.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<vocations>
    <vocation id="0" clientid="0" name="None" description="none" gaincap="0" gainhp="5" gainmana="5" gainhpticks="6" gainhpamount="1" gainmanaticks="6" gainmanaamount="1" manamultiplier="4.0" attackspeed="2000" basespeed="220" soulmax="100" gainsoulticks="120" fromvoc="0">
        <formula meleeDamage="1.0" distDamage="1.0" defense="1.0" armor="1.0" />
        <skill id="0" multiplier="1.5" />
        <skill id="1" multiplier="2.0" />
        <skill id="2" multiplier="2.0" />
        <skill id="3" multiplier="2.0" />
        <skill id="4" multiplier="2.0" />
        <skill id="5" multiplier="1.5" />
        <skill id="6" multiplier="1.1" />
    </vocation>
    <vocation id="1" clientid="3" name="Hunter" description="a hunter" gaincap="0" gainhp="5" gainmana="5" gainhpticks="6" gainhpamount="1" gainmanaticks="6" gainmanaamount="1" manamultiplier="4.0" attackspeed="2000" basespeed="220" soulmax="100" gainsoulticks="120" fromvoc="1">
        <formula meleeDamage="1.0" distDamage="1.0" defense="1.0" armor="1.0" />
        <skill id="0" multiplier="1.5" />
        <skill id="1" multiplier="2.0" />
        <skill id="2" multiplier="2.0" />
        <skill id="3" multiplier="2.0" />
        <skill id="4" multiplier="2.0" />
        <skill id="5" multiplier="1.5" />
        <skill id="6" multiplier="1.1" />
    </vocation>
    <vocation id="2" clientid="4" name="Catcher" description="a catcher" gaincap="0" gainhp="5" gainmana="5" gainhpticks="6" gainhpamount="1" gainmanaticks="6" gainmanaamount="1" manamultiplier="4.0" attackspeed="2000" basespeed="220" soulmax="100" gainsoulticks="120" fromvoc="2">
        <formula meleeDamage="1.0" distDamage="1.0" defense="1.0" armor="1.0" />
        <skill id="0" multiplier="1.5" />
        <skill id="1" multiplier="2.0" />
        <skill id="2" multiplier="2.0" />
        <skill id="3" multiplier="2.0" />
        <skill id="4" multiplier="2.0" />
        <skill id="5" multiplier="1.5" />
        <skill id="6" multiplier="1.1" />
    </vocation>
    <vocation id="3" clientid="2" name="Healer" description="a healer" gaincap="0" gainhp="5" gainmana="5" gainhpticks="6" gainhpamount="1" gainmanaticks="6" gainmanaamount="1" manamultiplier="4.0" attackspeed="2000" basespeed="220" soulmax="100" gainsoulticks="120" fromvoc="3">
        <formula meleeDamage="1.0" distDamage="1.0" defense="1.0" armor="1.0" />
        <skill id="0" multiplier="1.5" />
        <skill id="1" multiplier="2.0" />
        <skill id="2" multiplier="2.0" />
        <skill id="3" multiplier="2.0" />
        <skill id="4" multiplier="2.0" />
        <skill id="5" multiplier="1.5" />
        <skill id="6" multiplier="1.1" />
    </vocation>
    <vocation id="4" clientid="1" name="Blocker" description="a blocker" gaincap="0" gainhp="5" gainmana="5" gainhpticks="6" gainhpamount="1" gainmanaticks="6" gainmanaamount="1" manamultiplier="4.0" attackspeed="2000" basespeed="220" soulmax="100" gainsoulticks="120" fromvoc="4">
        <formula meleeDamage="1.0" distDamage="1.0" defense="1.0" armor="1.0" />
        <skill id="0" multiplier="1.5" />
        <skill id="1" multiplier="2.0" />
        <skill id="2" multiplier="2.0" />
        <skill id="3" multiplier="2.0" />
        <skill id="4" multiplier="2.0" />
        <skill id="5" multiplier="1.5" />
        <skill id="6" multiplier="1.1" />
    </vocation>
    <vocation id="5" clientid="3" name="Explorer" description="an explorer" gaincap="0" gainhp="5" gainmana="5" gainhpticks="6" gainhpamount="1" gainmanaticks="6" gainmanaamount="1" manamultiplier="4.0" attackspeed="2000" basespeed="220" soulmax="100" gainsoulticks="120" fromvoc="5">
        <formula meleeDamage="1.0" distDamage="1.0" defense="1.0" armor="1.0" />
        <skill id="0" multiplier="1.5" />
        <skill id="1" multiplier="2.0" />
        <skill id="2" multiplier="2.0" />
        <skill id="3" multiplier="2.0" />
        <skill id="4" multiplier="2.0" />
        <skill id="5" multiplier="1.5" />
        <skill id="6" multiplier="1.1" />
    </vocation>
</vocations>

 
You will need serverside opcode to send the outfit and clientside to receive the outfit
Serverside in source:

Code:
void SendCurrentOutfit(Player* player)
{
    Outfit_t outfit = player->getOutfit();
    Packet packet;
    packet << (uint16_t)7554; // opcode
    packet << outfit.lookType;
    packet << outfit.lookAddons;
    packet << outfit.lookHead;
    packet << outfit.lookBody;
    packet << outfit.lookLegs;
    packet << outfit.lookFeet;
    packet << outfit.lookTypeEx;

    player->sendPacket(packet);
}
Clientside:
Code:
void HandlePacket(uint16_t opcode, Packet& packet)
{
    if (opcode == 7554) {
        uint16_t lookType;
        uint8_t lookAddons;
        uint8_t lookHead;
        uint8_t lookBody;
        uint8_t lookLegs;
        uint8_t lookFeet;
        uint16_t lookTypeEx;

        packet >> lookType;
        packet >> lookAddons;
        packet >> lookHead;
        packet >> lookBody;
        packet >> lookLegs;
        packet >> lookFeet;
        packet >> lookTypeEx;

        Player* player = g_game.getPlayerLocal();
        if (player) {
            Outfit_t outfit;
            outfit.lookType = lookType;
            outfit.lookAddons = lookAddons;
            outfit.lookHead = lookHead;
            outfit.lookBody = lookBody;
            outfit.lookLegs = lookLegs;
            outfit.lookFeet = lookFeet;
            outfit.lookTypeEx = lookTypeEx;

            player->setOutfit(outfit);
        }
    }
}

void OnReceive(const boost::system::error_code& error, size_t bytes_transferred)
{
    if (!error) {
        Packet packet(data_, bytes_transferred);

        uint16_t opcode;
        packet >> opcode;

        HandlePacket(opcode, packet);

        socket_.async_receive(boost::asio::buffer(data_, max_length),
                              boost::bind(&OnReceive, this,
                                          boost::asio::placeholders::error,
                                          boost::asio::placeholders::bytes_transferred));
    } else {
        std::cout << "Error receiving data: " << error.message() << std::endl;
    }
}
 
Last edited:
You will need serverside opcode to send the outfit and clientside to receive the outfit
Serverside in source:

Code:
void SendCurrentOutfit(Player* player)
{
    Outfit_t outfit = player->getOutfit();
    Packet packet;
    packet << (uint16_t)7554; // opcode
    packet << outfit.lookType;
    packet << outfit.lookAddons;
    packet << outfit.lookHead;
    packet << outfit.lookBody;
    packet << outfit.lookLegs;
    packet << outfit.lookFeet;
    packet << outfit.lookTypeEx;

    player->sendPacket(packet);
}
Clientside:
Code:
void HandlePacket(uint16_t opcode, Packet& packet)
{
    if (opcode == 7554) {
        uint16_t lookType;
        uint8_t lookAddons;
        uint8_t lookHead;
        uint8_t lookBody;
        uint8_t lookLegs;
        uint8_t lookFeet;
        uint16_t lookTypeEx;

        packet >> lookType;
        packet >> lookAddons;
        packet >> lookHead;
        packet >> lookBody;
        packet >> lookLegs;
        packet >> lookFeet;
        packet >> lookTypeEx;

        Player* player = g_game.getPlayerLocal();
        if (player) {
            Outfit_t outfit;
            outfit.lookType = lookType;
            outfit.lookAddons = lookAddons;
            outfit.lookHead = lookHead;
            outfit.lookBody = lookBody;
            outfit.lookLegs = lookLegs;
            outfit.lookFeet = lookFeet;
            outfit.lookTypeEx = lookTypeEx;

            player->setOutfit(outfit);
        }
    }
}

void OnReceive(const boost::system::error_code& error, size_t bytes_transferred)
{
    if (!error) {
        Packet packet(data_, bytes_transferred);

        uint16_t opcode;
        packet >> opcode;

        HandlePacket(opcode, packet);

        socket_.async_receive(boost::asio::buffer(data_, max_length),
                              boost::bind(&OnReceive, this,
                                          boost::asio::placeholders::error,
                                          boost::asio::placeholders::bytes_transferred));
    } else {
        std::cout << "Error receiving data: " << error.message() << std::endl;
    }
}
Where i should put this?
 
characterlist.lua
LUA:
function loadCharacterList(characters)
  g_characterList = {}
  for _, characterInfo in ipairs(characters) do
    local character = {
      name = characterInfo.name,
      worldName = characterInfo.worldName,
      worldIp = characterInfo.worldIp,
      worldPort = characterInfo.worldPort,
      previewState = characterInfo.previewState,
      outfit = characterInfo.outfit
    }
    table.insert(g_characterList, character)
  end
end

Update onCharacterListUpdated() in characterlist.lua
LUA:
function onCharacterListUpdated(characters)
  loadCharacterList(characters)

  local characterList = g_ui.getRootWidget():recursiveGetChildById('characterList')
  characterList:destroyChildren()

  for i, character in ipairs(g_characterList) do
    local nameLabel = g_ui.createWidget('Label', characterList)
    nameLabel:setText(character.name)

    local outfitWidget = createOutfitWidget(character.outfit)
    outfitWidget:addAnchor(AnchorLeft, nameLabel, AnchorRight)
    outfitWidget:addAnchor(AnchorTop, nameLabel, AnchorTop)
    outfitWidget:addAnchor(AnchorBottom, nameLabel, AnchorBottom)

    local playButton = g_ui.createWidget('PushButton', characterList)
    playButton:setText(tr('Play'))
    playButton.onClick = function() g_game.loginWorld(character.worldName, character.worldIp, character.worldPort, character.previewState) end
  end
end

util.lua
LUA:
function createOutfitWidget(outfit)
  local widget = g_ui.createWidget('OutfitWidget', rootWidget)
  widget:setOutfit(outfit)
  widget:setImageClip(false)
  widget:setKeepAspectRatio(true)
  widget:setHeight(32)
  widget:setWidth(32)
  widget:setMarginRight(4)
  return widget
end
 
Last edited:
characterlist.lua
LUA:
function loadCharacterList(characters)
  g_characterList = {}
  for _, characterInfo in ipairs(characters) do
    local character = {
      name = characterInfo.name,
      worldName = characterInfo.worldName,
      worldIp = characterInfo.worldIp,
      worldPort = characterInfo.worldPort,
      previewState = characterInfo.previewState,
      outfit = characterInfo.outfit
    }
    table.insert(g_characterList, character)
  end
end

Update onCharacterListUpdated() in characterlist.lua
LUA:
function onCharacterListUpdated(characters)
  loadCharacterList(characters)

  local characterList = g_ui.getRootWidget():recursiveGetChildById('characterList')
  characterList:destroyChildren()

  for i, character in ipairs(g_characterList) do
    local nameLabel = g_ui.createWidget('Label', characterList)
    nameLabel:setText(character.name)

    local outfitWidget = createOutfitWidget(character.outfit)
    outfitWidget:addAnchor(AnchorLeft, nameLabel, AnchorRight)
    outfitWidget:addAnchor(AnchorTop, nameLabel, AnchorTop)
    outfitWidget:addAnchor(AnchorBottom, nameLabel, AnchorBottom)

    local playButton = g_ui.createWidget('PushButton', characterList)
    playButton:setText(tr('Play'))
    playButton.onClick = function() g_game.loginWorld(character.worldName, character.worldIp, character.worldPort, character.previewState) end
  end
end

newcharacterlist.otui
Code:
<ListView id="characterList" style="listView" width="100%" height="100%">
  <ListColumn>
    <PushButton id="playButton" style="playButton" height="30px" width="60px" text="Play" onClick="playCharacter"/>
  </ListColumn>
  <ListColumn>
    <Label text="Name" />
  </ListColumn>
  <ListColumn>
    <Label text="Outfit" />
  </ListColumn>
</ListView>

util.lua
LUA:
function createOutfitWidget(outfit)
  local widget = g_ui.createWidget('OutfitWidget', rootWidget)
  widget:setOutfit(outfit)
  widget:setImageClip(false)
  widget:setKeepAspectRatio(true)
  widget:setHeight(32)
  widget:setWidth(32)
  widget:setMarginRight(4)
  return widget
end
You probably don't understand me, i don't want create outfit window.
 
I solved this.

TFS 1.2

iologindata.cpp

Find
C++:
void IOLoginData::removePremiumDays(uint32_t accountId, int32_t removeDays)
{
    std::ostringstream query;
    query << "UPDATE `accounts` SET `premdays` = `premdays` - " << removeDays << " WHERE `id` = " << accountId;
    Database::getInstance()->executeQuery(query.str());
}

add below
C++:
bool IOLoginData::getVocationForCharacter(const std::string& characterName, uint32_t& vocationId)
{
    Database* db = Database::getInstance();

    std::ostringstream query;
    query << "SELECT `vocation` FROM `players` WHERE `name` = " << db->escapeString(characterName);

    DBResult_ptr result = db->storeQuery(query.str());
    if (!result) {
        return false;
    }

    vocationId = result->getNumber<uint32_t>("vocation");
    return true;
}

protocollogin.cpp

Find this and replace
C++:
    uint8_t size = std::min<size_t>(std::numeric_limits<uint8_t>::max(), account.characters.size());
    output->addByte(size);
    for (uint8_t i = 0; i < size; i++) {
        output->addByte(0);
        output->addString(account.characters[i]);
    }

To This:
C++:
    uint8_t size = std::min<size_t>(std::numeric_limits<uint8_t>::max(), account.characters.size());
    output->addByte(size);
    for (uint8_t i = 0; i < size; i++) {
        uint32_t vocationId;
        IOLoginData::getVocationForCharacter(account.characters[i], vocationId);
        output->addByte(0);
        output->addString(account.characters[i]);
        output->add<uint32_t>(vocationId);
    }

iologindata.h
Find:
C++:
static void addPremiumDays(uint32_t accountId, int32_t addDays);

Above Add:
C++:
static bool getVocationForCharacter(const std::string& characterName, uint32_t& vocationId);

OTCV8
modules\gamelib\protocollogin.lua
Find:
LUA:
function ProtocolLogin:parseCharacterList(msg)

In this:
LUA:
    local charactersCount = msg:getU8()
    for i=1, charactersCount do
      local character = {}
      local worldId = msg:getU8()
      character.name = msg:getString()
      character.worldName = worlds[worldId].worldName
      character.worldIp = worlds[worldId].worldIp
      character.worldPort = worlds[worldId].worldPort
      character.previewState = worlds[worldId].previewState
      characters[i] = character
    end

Replace or Add:
LUA:
    local charactersCount = msg:getU8()
    for i=1, charactersCount do
      local character = {}
      local worldId = msg:getU8()
      character.name = msg:getString()

      character.vocationId = msg:getU32()

      character.worldName = worlds[worldId].worldName
      character.worldIp = worlds[worldId].worldIp
      character.worldPort = worlds[worldId].worldPort
      character.previewState = worlds[worldId].previewState
      characters[i] = character
    end

 
Back
Top