• 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!

Problem with updater and 'window' a nil value: window:setVisible(false)

elnelson

Lunaria World Dev
Joined
Jun 20, 2009
Messages
586
Solutions
2
Reaction score
61
Location
México
Good afternoon otlanders, everything was running but but when i zipped the files to upload to website for updater and tested i got this error, any idea what could be? i've tried changing names of variables but had no luck...
1715480520221.png

error.log:
ERROR: failed to load UI from 'Quests.otui': unable to open file '/modules/game_quests/Quests.otui': not found
FATAL ERROR: Unable to load module 'game_quests': /modules/game_quests/quests.lua:171: attempt to index global 'questWindow' (a nil value)
stack traceback:
[C]: in function '__index'
/modules/game_quests/quests.lua:171: in function 'init'
/modules/game_quests/quests.otmod:7:[@onLoad]:1: in main chunk
[C]: in function 'ensureModuleLoaded'
/init.lua:71: in function 'tmpLoadFunc'
/modules/updater/updater.lua:60: in function 'loadModules'
/modules/updater/updater.lua:219: in function 'abort'
/modules/updater/updater.lua:262: in function </modules/updater/updater.lua:261>
[C]: in function 'pcall'
/modules/corelib/util.lua:311: in function 'signalcall'
/modules/corelib/ui/uimessagebox.lua:87: in function 'ok'
/modules/corelib/ui/uimessagebox.lua:62: in function </modules/corelib/ui/uimessagebox.lua:62>



this is the quest.lua (its a total mess because im still learning otclient modules :p):
Lua:
questWindow = nil
selectedQuestName = nil
defaultQuests = {
    [1] = {
        item = "Scout the Lost Dog to Wolve\'s Den.",
        itemID = 32,
        aid = 8001,
        amount = 1,
        reagent = "The Lost Dog is looking for his friend \'Ruffles\', the dog looks wounded by wolves, lets look for the Wolve\'s Den at the North-East Coasts.\n\nBe careful, dont let the Lost Dog die!!",
        price = 100,
        type = 250,
        reagentName = "wooden hammer",
        reqItems = {
            [1] = { ingredientId = 3105, reqItemTier = 0.9, reqItemCount = 5 }, -- dirty fur
            [2] = { ingredientId = 3107, reqItemTier = 0.9, reqItemCount = 2 }, -- raw wood
            [3] = { ingredientId = 3106, reqItemTier = 0.9, reqItemCount = 3 }, -- dirty fur
            [4] = { ingredientId = 3577, reqItemTier = 0.9, reqItemCount = 3 }, -- raw wood
            [5] = { ingredientId = 8194, reqItemTier = 0.9, reqItemCount = 4 }, -- dirty fur
            [6] = { ingredientId = 3585, reqItemTier = 0.9, reqItemCount = 20 }, -- raw wood
        },
    },
 ...
                    
}
local QUEST_OPCODE = 155
function onProtocolGame(opcode, buffer)
    if opcode == QUEST_OPCODE then
        handleQuestsMenuOpen(nil, opcode, buffer)
    end
end
function init()
    -- print("Initializing...")
    connect(g_game, {
        onGameStart = onGameStart,
        onGameEnd = destroy
    })
    questWindow = g_ui.displayUI('Quests')
    questWindow:setVisible(false)
    -- g_keyboard.bindKeyDown('Ctrl+A', toggleWindow)
    g_keyboard.bindKeyDown('Escape', hideQuestWindow)
    -- questButton = modules.client_topmenu.addLeftGameButton('questButton', tr('Quest'), '/modules/game_quests/images/questIcon', toggleWindow)
    ProtocolGame.registerExtendedOpcode(QUEST_OPCODE, handleQuestsMenuOpen)
end

function handleQuestsMenuOpen(protocol, opcode, buffer)
    local craftData = {}
    local craftsData = string.split(buffer, '@')
    for _, data in ipairs(craftsData) do
        local craft = deserializeCraft(data)
        table.insert(craftData, craft)
    end
    updateQuests(craftData)
    openQuestsWindow()
end
function tableToString(tbl)
    local str = "{"
    for k, v in pairs(tbl) do
        str = str .. "[" .. tostring(k) .. "]="
        if type(v) == "table" then
            str = str .. tableToString(v)
        else
            str = str .. tostring(v)
        end
        str = str .. ","
    end
    str = str .. "}"
    return str
end
function deserializeCraft(data)
    -- print("Deserializing craft data:", data)
    local craft = {}
    craft.reqItems = {} -- Cambio "ingredients" a "reqItems"
    local parts = string.split(data, ',')
    for _, part in ipairs(parts) do
        if part ~= '' then
            local pair = string.split(part, '=')
            if #pair == 2 then
                local key = pair[1]
                local value = pair[2]
                if key == 'reqItems' then -- Cambio "ingredients" a "reqItems"
                    -- print("Deserializing reqItems:")
                    local reqItemsData = string.sub(value, 2, -2) -- Cambio de caracteres de delimitación
                    local reqItems = string.split(reqItemsData, '},') -- Cambio del separador
                    for _, reqItemData in ipairs(reqItems) do
                        local item = {}
                        local itemParts = string.split(reqItemData, ', ')
                        -- print("ReqItem parts:", table.concat(itemParts, ", "))
                        for _, itemPart in ipairs(itemParts) do
                            local itemPair = string.split(itemPart, '=')
                            local itemKey = itemPair[1]
                            local itemValue = itemPair[2]
                            if itemKey == 'ingredientId' then -- Cambio "itemid" a "ingredientId"
                                itemKey = 'ingredientId'
                            elseif itemKey == 'count' then -- Agregar soporte para "count"
                                itemKey = 'count'
                            elseif itemKey == 'tier' then -- Agregar soporte para "tier"
                                itemKey = 'tier'
                            end
                            -- print("Deserializing " .. itemKey .. ": " .. tostring(itemValue))
                            item[itemKey] = tonumber(itemValue) or itemValue
                        end
                        table.insert(craft.reqItems, item) -- Cambio "ingredients" a "reqItems"
                    end
                    -- print("ReqItems after deserialization:", tableToString(craft.reqItems))
                else
                    -- Eliminar las impresiones innecesarias
                    -- print("Deserializing " .. key .. ": " .. tostring(value))
                    craft[key] = value
                end
            else
                -- Eliminar las impresiones innecesarias
                -- print("Error: Invalid part format:", part)
            end
        end
    end
    -- print("Craft after deserialization:", (craft))
    return craft
end


function deserializeIngredients(data)
    local items = {}
    local parts = string.split(data, '|')
    for _, part in ipairs(parts) do
        local item = {}
        local subParts = string.split(part, ',')
        for _, subPart in ipairs(subParts) do
            local subPair = string.split(subPart, '=')
            local subKey = subPair[1]
            local subValue = subPair[2]
            if subKey == 'itemid' then
                subKey = 'ingredientID'
            end
            item[subKey] = tonumber(subValue) or subValue
        end
        table.insert(items, item)
    end
    return items
end
function openQuestsWindow()
    toggleQuestWindow()
end
function terminate()
    -- print("Terminating...")
    disconnect(g_game, {
        onGameEnd = destroy
    })
    -- questButton:destroy()
    destroy()
end
function onGameStart()
    -- print("Game started.")
    if (questWindow) then
        questWindow:destroy()
        questWindow = nil
    end   
    
    if not questWindow then
        questWindow = g_ui.displayUI('Challenges')
        questWindow:setVisible(false)
    end
end
function destroy()
    -- print("Destroying window...")
    if (questWindow) then
        questWindow:destroy()
        questWindow = nil
    end
end
function toggleQuestWindow()
    if (not g_game.isOnline()) then
        return
    end
    if (questWindow:isVisible()) then
        questWindow:setVisible(false)
    else
        questWindow:setVisible(true)
    end
end
function hideQuestWindow()
    if (not g_game.isOnline()) then
        return
    end
    if (questWindow:isVisible()) then
        questWindow:setVisible(false)
    end
end
function getSelectedQuestName()
    -- print("Getting selected craft name...")
    return selectedQuestName
end
local aidSelected = nil
function onItemSelect(list, focusedChild, unfocusedChild, reason)
    -- print("Selecting item...")
    if focusedChild then
        selectedQuestName = focusedChild.name:getText()
        -- print('Selected Craft: ' .. selectedQuestName)
        
        -- Encuentra la receta seleccionada en la tabla defaultQuests
        for _, craft in pairs(defaultQuests) do
            if string.lower(craft.item) == string.lower(selectedQuestName) then
                aidSelected = craft.aid  -- Obtiene el aid de la receta
                break
            end
        end
        if aid then
            -- print('AID: ' .. aidSelected)  -- Muestra el AID de la receta seleccionada
        else
            -- print('Recipe not found in defaultQuests')
        end
    else
        -- print('Focused child is nil')
    end
end
-- function details(list, focusedChild, unfocusedChild, reason)
--     g_game.talk('!recipe '..selectedQuestName..'')
-- end
function buyRecipe()
    -- print('Selected details: ' .. selectedQuestName)
    if selectedQuestName ~= nil then
        g_game.talk('!challenge ' .. aidSelected)
        g_game.talk('Accept')
        questWindow:setVisible(false)
    else
        return displayErrorBox('Error', 'Select a valid quest.')
    end
end
function onIngredientSelect(list, focusedChild, unfocusedChild, reason, selectedIngredientName)
    -- print("Selecting Ingredient...")
    if focusedChild then
        -- print('Selected Ingredient: ' .. selectedIngredientName)
        g_game.talk('!ingredient ' .. selectedIngredientName)  -- Enviar un mensaje al juego con el nombre del ingrediente
    else
        -- print('Focused child is nil')
    end
end
function onFilterSearch()
    -- print("Filtering search...")
    addEvent(function()
        local searchText = questWindow.listSearch.search:getText():lower():trim()
        local children = questWindow.selectionList:getChildren()
        if (searchText:len() >= 1) then
            for _, child in ipairs(children) do
                local text = child.name:getText():lower()
                if (text:find(searchText)) then
                    child:show()
                else
                    child:hide()
                end
            end
        else
            for _, child in ipairs(children) do
                child:show()
            end
        end
    end)
end
local function formatNumber(num)
    return tostring(math.floor(num)):reverse():gsub("(%d%d%d)", "%1,"):reverse():gsub("^,", "")
end
-- Función para convertir una tabla a una cadena de caracteres
function tableToString(tbl)
    local str = '{'
    for k, v in pairs(tbl) do
        str = str .. tostring(k) .. '=' .. tostring(v) .. ', '
    end
    str = str:sub(1, -3) .. '}'  -- Eliminar la coma y el espacio adicionales al final y agregar el corchete de cierre
    return str
end
-- Ahora puedes llamar a la función tableToString para mostrar la representación de la tabla en cualquier parte del script donde lo desees.

function updateQuests(crafts)
    questWindow.selectionList:destroyChildren()
    -- Ordenar la tabla crafts en función de la experiencia total
    for _, craft in ipairs(crafts) do
        local button = g_ui.createWidget("SelectionButtonQuest", questWindow.selectionList)
        button:setId(craft.itemID)
        button.item:setOutfit({ type = craft.itemID, head = 19, body = 19, legs = 19, feet = 19})
        -- button.item:setItemCount(craft.amount)
        button.name:setText(craft.item)
        for i = 1, 6 do
            local ingredientWidget = button['ingredientsReq' .. i]
            if ingredientWidget then
                ingredientWidget:setItemId(nil)
                ingredientWidget:setItemCount(nil)
                ingredientWidget:setVisible(false)
            end
        end
        local foundCraft = nil
        for _, defaultCraft in pairs(defaultQuests) do
            if string.lower(defaultCraft.item) == string.lower(craft.item) then
                foundCraft = defaultCraft
                break
            end
        end
        local totalExp = foundCraft.type
        local getPrice = 0
        if foundCraft then
            getPrice = foundCraft.price
                -- print(totalExp)
            local numIngredients = math.min(#foundCraft.reqItems, 6)
            for i = 1, numIngredients do
                local ingredient = foundCraft.reqItems[i]
                local ingredientWidget = button['ingredientsReq' .. i]
                if ingredient and ingredientWidget then
                    ingredientWidget:setItemId(ingredient.ingredientId)
                    ingredientWidget:setItemCount(ingredient.reqItemCount or 1)
                    ingredientWidget:setVisible(true)
                     -- Calculamos la experiencia total para este ingrediente
                    --  local expForIngredient = (ingredient.type)
                    --  totalExp = totalExp + expForIngredient                     
                end
            end
        else
            -- print("Error: Recipe not found in defaultCrafts")
            -- print("Item: " .. craft.item)
        end
        if foundCraft and foundCraft.reagent then
            
            button.reqReagent:setText(foundCraft.reagent)
        else
            button.reqReagent:setVisible(false)
        end
                -- Configurar la experiencia como recompensa
        if foundCraft then
            button.rewardCraftPoints:setText('' .. formatNumber(totalExp) .. ' Exp')
            button.priceCraft:setText('' .. formatNumber(getPrice) .. '$')
        else
            button.rewardCraftPoints:setText('Exp: 0')
        end
        button.progress:setWidth(0)
        button.onClick = function(widget) onItemSelect(nil, widget, nil, nil) end
    end
end





function toggleQuestWindow()
    if (not g_game.isOnline()) then
        return
    end
    if (questWindow:isVisible()) then
        questWindow:setVisible(false)
    else
        questWindow:setVisible(true)
    end
end
function hideQuestWindow()
    if (not g_game.isOnline()) then
        return
    end
    -- if (window:isVisible()) then
    --     window:setVisible(false)
    -- end
end

function setCraftConsoleText(text, color)
    if (not color) then
        color = 'white'
    end
    questWindow.info:setText(text)
    questWindow.info:setColor(color)
    if consoleEvent then
        removeEvent(consoleEvent)
        consoleEvent = nil
    end
    consoleEvent = scheduleEvent(function()
        questWindow.info:setText('')
    end, 5000)
    return true
end


and this is my quest.otui
CSS:
MiniPanel44 < Panel
  text-offset: 0 3
  text-align: top
  image-source: /images/ui/minipanel
  image-border: 2
  image-border-top: 19
  padding-left: 7
  padding-bottom: 7
  padding-top: 24
  padding-right: 7


SelectionButtonQuest < Panel
  image-source: /images/ui/button
  image-color: #dfdfdf
  image-clip: 0 0 22 23
  image-border: 3
  border: 1 alpha
  focusable: true
  phantom: false


  $hover:
    border: 1 white


  $focus:
    border: 1 white
    image-color: #ffffff


  Panel
    id: progress
    image-source: /images/ui/panel_flat
    image-color: #00ff00
    margin-left: 1
    margin-top: 1
    anchors.top: parent.top
    anchors.left: parent.left
    size: 159 158


  UICreature
    id: item
    size: 90 90
    margin-top: 26
    margin-left: 8
    anchors.top: parent.top
    phantom: true
    anchors.left: prev.left
    background-color: #00000077
    border: 1 black


  Label
    id: name
    anchors.bottom: prev.top
    anchors.left: parent.left
    anchors.right: parent.right
    margin-left: 3
    margin-bottom: 8
    text-align: left
    text-wrap: true
    background-color: #00000077
    text-border: 50 black


  Label
    id: title
    anchors.top: parent.top
    anchors.left: item.right
    text: Rewards:
    margin-top: 30
    margin-left: 65
    text-align: left
    text-wrap: true


  Label
    id: kills
    anchors.top: item.bottom
    anchors.left: parent.left
    anchors.right: parent.right
    text-align: center
    margin-top: -5
    text-wrap: true
 
  UIItem
    id: ingredientsReq1
    anchors.top: title.bottom
    anchors.left: item.right
    text-align: right
    margin-left: 15
    margin-right: 5
    margin-top: 5
    text-wrap: true
    height: 32
    width: 32
    focusable: true
    phantom: false
    onMousePress: modules.game_quests.onIngredientClick(1)
    background-color: #00000077
    border: 1 alpha


    $hover:
      border: 1 white


    $focus:
      border: 1 white
      image-color: #ffffff


  UIItem
    id: ingredientsReq2
    anchors.top: title.bottom
    anchors.left: ingredientsReq1.right
    text-align: right
    margin-right: 5
    margin-left: 5
    margin-top: 5
    text-wrap: true
    height: 32
    width: 32
    focusable: true
    onMousePress: modules.game_quests.onIngredientClick(2)
    background-color: #00000077
    border: 1 alpha


    $hover:
      border: 1 white


    $focus:
      border: 1 white
      image-color: #ffffff


  UIItem
    id: ingredientsReq3
    anchors.top: title.bottom
    anchors.left: ingredientsReq2.right
    text-align: right
    margin-left: 5
    margin-right: 5
    margin-top: 5
    text-wrap: true
    height: 32
    width: 32
    focusable: true
    onMousePress: modules.game_quests.onIngredientClick(3)
    background-color: #00000077
    border: 1 alpha


    $hover:
      border: 1 white


    $focus:
      border: 1 white
      image-color: #ffffff


  UIItem
    id: ingredientsReq4
    anchors.top: title.bottom
    anchors.left: ingredientsReq3.right
    text-align: right
    margin-right: 5
    margin-left: 5
    margin-top: 5
    text-wrap: true
    height: 32
    width: 32
    focusable: true
    onMousePress: modules.game_quests.onIngredientClick(4)
    background-color: #00000077
    border: 1 alpha


    $hover:
      border: 1 white


    $focus:
      border: 1 white
      image-color: #ffffff


  UIItem
    id: ingredientsReq5
    anchors.top: title.bottom
    anchors.left: ingredientsReq4.right
    text-align: right
    margin-left: 5
    margin-right: 5
    margin-top: 5
    text-wrap: true
    height: 32
    width: 32
    focusable: true
    onMousePress: modules.game_quests.onIngredientClick(5)
    background-color: #00000077
    border: 1 alpha


    $hover:
      border: 1 white


    $focus:
      border: 1 white
      image-color: #ffffff


  UIItem
    id: ingredientsReq6
    anchors.top: title.bottom
    anchors.left: ingredientsReq5.right
    text-align: right
    margin-right: 5
    margin-left: 5
    margin-top: 5
    text-wrap: true
    height: 32
    width: 32
    focusable: true
    onMousePress: modules.game_quests.onIngredientClick(6)
    background-color: #00000077
    border: 1 alpha


    $hover:
      border: 1 white


    $focus:
      border: 1 white
      image-color: #ffffff


  Label
    id: reqReagent
    anchors.top: item.bottom
    anchors.left: parent.left
    anchors.right: parent.right
    anchors.bottom: parent.bottom
    text-align: left
    margin-bottom: 10
    margin-left: 3
    text-wrap: true


  Label
    id: priceCraft
    anchors.top: parent.top
    anchors.right: parent.right
    anchors.left: title.right
    text-align: right
    margin-top: 5
    margin-right: 15
    text-wrap: true


  Label
    id: rewardCraftPoints
    anchors.top: parent.top
    anchors.left: title.right
    text-align: left
    margin-top: 30
    margin-left: 3
    text-wrap: true


MainQuestWindow
  size: 415 370
  id: questWindow
  !text: tr('Challenges')


  @onEnter: modules.game_quests.toggleQuestWindow()
  @onEscape: modules.game_quests.toggleQuestWindow()


  MiniPanel4
    id: listSearch
    anchors.top: parent.top
    anchors.left: parent.left
    anchors.right: parent.right
    margin-left: 5
    height: 50
    text: Filter


    TextEdit
      id: search
      anchors.fill: parent
      placeholder: Search by name


  ScrollablePanel
    id: selectionList
    anchors.top: listSearch.bottom
    anchors.left: listSearch.left
    anchors.right: parent.right
    anchors.bottom: separator.top
    margin-top: 5
    margin-bottom: 5
    image-source: /images/ui/panel_flat
    image-border: 1
    padding: 4
    padding-right: 16
    vertical-scrollbar: selectionScroll
    layout:
      type: grid
      cell-size: 350 225
      cell-spacing: 8
      flow: true


  VerticalScrollBar
    id: selectionScroll
    anchors.top: prev.top
    anchors.right: prev.right
    anchors.bottom: prev.bottom
    step: 80
    pixel-scroll: true


  HorizontalSeparator
    id: separator
    anchors.bottom: parent.bottom
    anchors.left: parent.left
    anchors.right: parent.right
    margin-bottom: 30


  Label
    id: info
    anchors.top: separator.top
    anchors.left: parent.left
    width: 140
    height: 45
    text-align: center
    text-wrap: true


  Button
    id: toggleButton
    anchors.bottom: parent.bottom
    anchors.right: parent.right
    text: Close
    width: 65
    @onClick: modules.game_quests.toggleQuestWindow()


  Button
    id: finishButton
    anchors.bottom: parent.bottom
    anchors.right: toggleButton.left
    text: Finish
    width: 55
    margin-right: 5
    @onClick: modules.game_quests.finish()


  Button
    id: startButton
    anchors.bottom: parent.bottom
    anchors.right: toggleButton.left
    text: Start
    width: 55
    margin-right: 5
    @onClick: modules.game_quests.start()


  Button
    id: buyRecipeButton
    anchors.bottom: parent.bottom
    anchors.right: toggleButton.left
    text: Accept
    width: 80
    margin-right: 5
    @onClick: modules.game_quests.buyRecipe()
 
Back
Top