elnelson
Lunaria World Dev
Hello, otlanders, i recently moved to a newer version of OTC (wings,shaders and auras enabled) it is working perfect but i have a problem. i have this game_craft module, it shows item recipes on stores, sent via server.

But when that module is enabled, my outfit screen is bugged (i cannot see the outfits, mounts, shaders, etc) and i get this error in console> ERROR: /modules/game_outfit/outfit.lua:628: attempt to index field 'outfit' (a nil value)

If i disable game_crafts i can see the outfits, mounts, etc

it would be great if you could help me to identify the problem, its driving me crazy...
this crafts.lua
and this is the crafts.otui
outfit.lua

But when that module is enabled, my outfit screen is bugged (i cannot see the outfits, mounts, shaders, etc) and i get this error in console> ERROR: /modules/game_outfit/outfit.lua:628: attempt to index field 'outfit' (a nil value)

If i disable game_crafts i can see the outfits, mounts, etc

it would be great if you could help me to identify the problem, its driving me crazy...
this crafts.lua
LUA:
craftWindow = nil
local selectedCraftName = nil
defaultCrafts = {
[1] = {
item = "Hand axe",
itemID = 3268,
aid = 8001,
amount = 1,
reagent = 3459,
price = 10,
type = "smith",
reagentName = "wooden hammer",
reqItems = {
[1] = { ingredientId = 3105, reqItemTier = 0.9, reqItemCount = 2 }, -- dirty fur
[2] = { ingredientId = 3107, reqItemTier = 0.9, reqItemCount = 2 }, -- raw wood
},
},
[2] = {
item = "Rapier",
itemID = 3272,
aid = 8002,
amount = 1,
reagent = 3459,
price = 10,
type = "smith",
reagentName = "wooden hammer",
reqItems = {
[1] = { ingredientId = 3105, reqItemTier = 0.9, reqItemCount = 2 }, -- dirty fur
[2] = { ingredientId = 3107, reqItemTier = 0.9, reqItemCount = 2 }, -- raw wood
},
},
[3] = {
item = "Studded club",
itemID = 3336,
aid = 8003,
amount = 1,
reagent = 3459,
price = 10,
type = "smith",
reagentName = "wooden hammer",
reqItems = {
[1] = { ingredientId = 3105, reqItemTier = 0.9, reqItemCount = 2 }, -- dirty fur
[2] = { ingredientId = 3107, reqItemTier = 0.9, reqItemCount = 2 }, -- raw wood
},
},
[42] = {item = "bag", -- item name (THIS MUST BE EXACT OR IT WILL NOT WORK!)
itemID = 2864, -- item to be made
aid = 8039,
amount = 1,
reagent = 0,
price = 20,
type = "woodwork",
reagentName = "none",
reqItems = { -- items and the amounts in order to craft.
[1] = {ingredientId = 3105, reqItemTier = 0.9, reqItemCount = 3},--dirty fur
},
},
[40] = {item = "Iron hammer", -- item name (THIS MUST BE EXACT OR IT WILL NOT WORK!)
itemID = 3310, -- item to be made
aid = 8040,
amount = 1,
reagent = 3459,
price = 20,
type = "smith",
reagentName = "wooden hammer",
reqItems = { -- items and the amounts in order to craft.
[1] = {ingredientId = 5880, reqItemTier = 2, reqItemCount = 1},--iron ore
[2] = {ingredientId = 5901, reqItemTier = 2, reqItemCount = 1},--wood planks
},
},
[41] = {item = "Bow", -- item name (THIS MUST BE EXACT OR IT WILL NOT WORK!)
itemID = 3350, -- item to be made
aid = 8041,
amount = 1,
reagent = 3459,
price = 500,
type = "woodwork",
reagentName = "wooden hammer",
reqItems = { -- items and the amounts in order to craft.
[1] = {ingredientId = 5901, reqItemTier = 2, reqItemCount = 2}, -- wood planks
[2] = {ingredientId = 3105, reqItemTier = 0.9, reqItemCount = 1}, -- dirty fur
},
},
}
local CRAFT_OPCODE = 156
function onProtocolGame(opcode, buffer)
if opcode == CRAFT_OPCODE then
handleCraftMenuOpen(nil, opcode, buffer)
end
end
function init()
-- print("Initializing...")
connect(g_game, {
onGameStart = onGameStart,
onGameEnd = destroy
})
craftWindow = g_ui.displayUI('crafts')
craftWindow:setVisible(false)
-- g_keyboard.bindKeyDown('Ctrl+A', toggleWindow)
-- g_keyboard.bindKeyDown('Escape', hideWindow)
-- craftButton = modules.client_topmenu.addLeftGameButton('craftButton', tr('Craft'), '/modules/game_craft/images/craftIcon', toggleWindow)
ProtocolGame.registerExtendedOpcode(CRAFT_OPCODE, handleCraftMenuOpen)
end
function handleCraftMenuOpen(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
updateCrafts(craftData)
openCraftWindow()
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 openCraftWindow()
toggleWindow()
end
function terminate()
-- print("Terminating...")
disconnect(g_game, {
onGameEnd = destroy
})
-- craftButton:destroy()
destroy()
end
function onGameStart()
-- print("Game started.")
if (craftWindow) then
craftWindow:destroy()
craftWindow = nil
end
craftWindow = g_ui.displayUI('crafts')
craftWindow:setVisible(false)
craftWindow.listSearch.search.onKeyPress = onFilterSearch
end
function destroy()
-- print("Destroying window...")
if (craftWindow) then
craftWindow:destroy()
craftWindow = nil
end
end
function toggleWindow()
if (not g_game.isOnline()) then
return
end
if (craftWindow:isVisible()) then
craftWindow:setVisible(false)
else
craftWindow:setVisible(true)
end
end
function hideWindow()
if (not g_game.isOnline()) then
return
end
craftWindow:setVisible(false)
end
function getSelectedCraftName()
-- print("Getting selected craft name...")
return selectedCraftName
end
function onItemSelect(list, focusedChild, unfocusedChild, reason)
-- print("Selecting item...")
if focusedChild then
selectedCraftName = focusedChild.name:getText()
-- print('Selected Craft: ' .. selectedCraftName)
else
-- print('Focused child is nil')
end
end
function details(list, focusedChild, unfocusedChild, reason)
-- print('Selected details: ' .. selectedCraftName)
g_game.talk('!recipe '..selectedCraftName..'')
end
function buyRecipe()
-- print('Selected details: ' .. selectedCraftName)
g_game.talk('!buyRecipe '..selectedCraftName..'')
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 = craftWindow.listSearch.search:getText():lower():trim()
local children = craftWindow.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 updateCrafts(crafts)
craftWindow.selectionList:destroyChildren()
-- Ordenar la tabla crafts en función de la experiencia total
for _, craft in ipairs(crafts) do
local button = g_ui.createWidget("SelectionButton", craftWindow.selectionList)
button:setId(craft.itemID)
button.item:setItemId(craft.itemID)
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(defaultCrafts) do
if string.lower(defaultCraft.item) == string.lower(craft.item) then
foundCraft = defaultCraft
break
end
end
local totalExp = 0
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.reqItemTier *135)-110)* ingredient.reqItemCount)
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:setItemId(foundCraft.reagent)
button.reqReagent:setItemCount(18)
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 hideWindow()
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
craftWindow.info:setText(text)
craftWindow.info:setColor(color)
if consoleEvent then
removeEvent(consoleEvent)
consoleEvent = nil
end
consoleEvent = scheduleEvent(function()
craftWindow.info:setText('')
end, 5000)
return true
end
and this is the crafts.otui
CSS:
MiniPanel < 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
SelectionButton < 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
UIItem
id: item
size: 64 64
margin-top: 22
margin-left: 2
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: 3
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: Materials:
margin-top: 20
margin-left: 3
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: 2
margin-right: 5
margin-top: 5
text-wrap: true
height: 26
width: 26
focusable: true
phantom: false
onMousePress: modules.game_craft.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: 26
width: 26
focusable: true
onMousePress: modules.game_craft.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: 26
width: 26
focusable: true
onMousePress: modules.game_craft.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: 26
width: 26
focusable: true
onMousePress: modules.game_craft.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: 26
width: 26
focusable: true
onMousePress: modules.game_craft.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: 26
width: 26
focusable: true
onMousePress: modules.game_craft.onIngredientClick(6)
background-color: #00000077
border: 1 alpha
$hover:
border: 1 white
$focus:
border: 1 white
image-color: #ffffff
UIItem
id: reqReagent
anchors.top: ingredientsReq1.bottom
anchors.left: tool.right
text-align: right
margin-right: 5
margin-left: 5
margin-top: 3
text-wrap: true
height: 20
width: 20
focusable: true
onMousePress: modules.game_craft.onIngredientClick(6)
background-color: #00000077
border: 1 alpha
$hover:
border: 1 white
$focus:
border: 1 white
image-color: #ffffff
Label
id: tool
anchors.top: ingredientsReq1.bottom
anchors.left: item.right
text: Tool:
text-align: right
margin-top: 5
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: 20
margin-left: 3
text-wrap: true
MainWindow
size: 820 450
id: craftsWindow
!text: tr('Crafts')
@onEnter: modules.game_craft.toggleWindow()
@onEscape: modules.game_craft.toggleWindow()
MiniPanel
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: 248 90
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_craft.toggleWindow()
Button
id: finishButton
anchors.bottom: parent.bottom
anchors.right: toggleButton.left
text: Finish
width: 55
margin-right: 5
@onClick: modules.game_craft.finish()
Button
id: startButton
anchors.bottom: parent.bottom
anchors.right: toggleButton.left
text: Start
width: 55
margin-right: 5
@onClick: modules.game_craft.start()
Button
id: buyRecipeButton
anchors.bottom: parent.bottom
anchors.right: toggleButton.left
text: Buy
width: 80
margin-right: 5
@onClick: modules.game_craft.buyRecipe()
Button
id: detailsButton
anchors.bottom: parent.bottom
anchors.right: buyRecipeButton.left
text: Details
width: 80
margin-right: 5
@onClick: modules.game_craft.details()
ResizeBorder
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
enabled: true
minimum: 190
outfit.lua
LUA:
function showOutfits()
outfitWindow.presetsList:hide()
outfitWindow.presetsScroll:hide()
outfitWindow.presetButtons:hide()
outfitWindow.selectionList.onChildFocusChange = nil
outfitWindow.selectionList:destroyChildren()
local focused = nil
for _, outfitData in ipairs(ServerData.outfits) do
local button = g_ui.createWidget("SelectionButton", outfitWindow.selectionList)
button:setId(outfitData[1])
local outfit = table.copy(previewCreature:getOutfit())
outfit.type = outfitData[1]
outfit.addons = outfitData[3]
outfit.mount = 0
outfit.aura = 0
outfit.wings = 0
outfit.shader = "outfit_default"
outfit.healthBar = 0
outfit.manaBar = 0
button.outfit:setOutfit(outfit) -- this is where console targets the bug...
button.outfit:setCenter(true)
button.name:setText(outfitData[2])
if tempOutfit.type == outfitData[1] then
focused = outfitData[1]
configureAddons(outfitData[3])
end
end
if focused then
local w = outfitWindow.selectionList[focused]
w:focus()
outfitWindow.selectionList:ensureChildVisible(w, {x = 0, y = 196})
end
outfitWindow.selectionList.onChildFocusChange = onOutfitSelect
outfitWindow.selectionList:show()
outfitWindow.selectionScroll:show()
outfitWindow.listSearch:show()
end