Objetivo: usar tablas como listas, mapas y conjuntos, recorrerlas sin sorpresas en Kahlua y entender cuándo dos variables apuntan a la misma tabla. Al final vas a leer las listas de loot del juego como las lee el propio juego.
1. Una sola estructura para todo
Lua tiene un solo tipo para agrupar datos: la tabla. Con ella se hacen listas, diccionarios, conjuntos, objetos y módulos. Se crea con {}:
local vacia = {}
local zombi = {
nombre = "Corredor",
vida = 1.5,
rapido = true,
}
print(zombi.nombre) -- Corredor
print(zombi["vida"]) -- 1.5: t.x es lo mismo que t["x"]
print(zombi.color) -- nil: una clave que no existe vale nilAgregar, cambiar y borrar es lo mismo: asignar.
zombi.color = "verde" -- agrega
zombi.vida = 2 -- cambia
zombi.rapido = nil -- borra (la clave deja de existir)Las claves pueden ser strings, números, booleanos, otras tablas, funciones u objetos del juego. Lo único que no puede ser clave es nil. En un constructor, una clave que resulta nil se descarta sin error:
local x = nil
local t = { [x] = 1 } -- no da error, pero t queda vacíaCuidado con los tipos: el número 1 y el string "1" son claves distintas.
local t = {}
t[1] = "número"
t["1"] = "texto"
print(t[1], t["1"]) -- número textoEsto muerde cuando un dato llega como texto (por ejemplo, de un archivo o de un comando) y lo usas como índice. Conviértelo antes con tonumber (L02).
2. Listas: empiezan en 1
Una lista es una tabla con claves 1, 2, 3... sin saltos:
local frutas = { "Apple", "Banana", "Cherry" }
print(frutas[1]) -- Apple (no hay frutas[0])
print(#frutas) -- 3: el operador # da el largo
print(frutas[#frutas]) -- Cherry: el últimoEn Lua las listas empiezan en 1, no en 0. En L06 vas a ver que las listas de Java que te da el juego (ArrayList) empiezan en 0: son cosas distintas, cada una con su regla.
Para recorrer una lista con un for numérico:
for i = 1, #frutas do
print(i, frutas[i])
endPara agregar al final, dos formas equivalentes:
frutas[#frutas + 1] = "Grapes"
table.insert(frutas, "Lemon")3. # y los huecos
En Kahlua, #t no cuenta los elementos uno por uno: hace una búsqueda binaria sobre las claves enteras. Si la lista no tiene huecos, el resultado es correcto. Si tiene un nil en el medio, el resultado depende de cómo quedó la tabla por dentro:
print(#{ 1, 2, nil, 4 }) -- 2 en Kahlua
print(#{ 1, 2, 3, nil, 5, 6, 7, 8 }) -- 8 en Kahlua
local t = { "a", "b", "c", "d", "e" }
t[2] = nil
print(#t) -- 5 en Kahlua, aunque t[2] es nilNo memorices esos números: lo que importa es que con huecos # no es confiable, y todo lo que se apoya en # (table.insert, table.remove, table.concat, unpack) tampoco. Reglas para no tener problemas:
- No asignes
nila una posición del medio de una lista. Para sacar un elemento usatable.remove(t, i), que corre los siguientes un lugar. - Si necesitas guardar "nada" en una posición, usa un valor centinela como
false. - Si una lista puede tener huecos (por ejemplo, la armaste con
{...}de argumentos que pueden ser nil, como viste en L04), guarda el largo aparte:{ n = select('#', ...), ... }.
4. Recorrer: ipairs y pairs
ipairs(t) | pairs(t) | |
|---|---|---|
| Qué recorre | t[1], t[2], ... hasta el primer nil | Todas las claves, de cualquier tipo |
| Orden | 1, 2, 3... | En Kahlua, orden de inserción (ver abajo) |
| Úsalo para | Listas | Mapas, conjuntos, tablas mezcladas |
local lista = { "Apple", "Banana", nil, "Grapes" }
for i, v in ipairs(lista) do
print(i, v) -- 1 Apple, 2 Banana, y se detiene en el hueco
end
local precios = { Apple = 2, Banana = 3, Cherry = 1 }
for clave, valor in pairs(precios) do
print(clave, valor)
endSi no necesitas la clave o el valor, se usa _ como nombre (es una variable normal, solo es una convención para decir "no me importa"):
for _, v in ipairs(lista) do print(v) endEl orden de pairs en Kahlua
En Lua estándar el orden de pairs no está definido. En Kahlua cada tabla es por dentro un LinkedHashMap de Java, así que pairs recorre las claves en el orden en que se insertaron:
local t = {}
t.zeta = 1
t.alfa = 2
t.medio = 3
for k in pairs(t) do print(k) end -- zeta, alfa, medio
t.alfa = 20 -- cambiar el valor no la mueve
t.zeta = nil -- borrar la saca
t.zeta = 9 -- volver a crearla la pone al final
for k in pairs(t) do print(k) end -- alfa, medio, zetaEs útil para depurar (los volcados salen en un orden predecible), pero no dependas de él para nada que tenga que coincidir entre dos máquinas, como el servidor y un cliente: el orden depende de en qué orden se insertó en cada una, y eso puede cambiar por el orden de carga de los mods o por cómo llegaron los datos. Si necesitas un orden, pon las claves en una lista y ordénala con table.sort (sección 5).
Borrar mientras recorres
El iterador de pairs de Kahlua trabaja sobre una copia de las claves tomada al empezar. Por eso:
- Borrar la entrada actual (
t[k] = nil) dentro del bucle es seguro. - Las claves que agregues durante el bucle no aparecen en ese recorrido.
- Si borras otra clave que todavía no salió, igual va a salir, pero con valor
nil.
local pesos = { Apple = 2, Watermelon = 0.5, Pineapple = 1, Avocado = 1 }
for item, peso in pairs(pesos) do
if peso < 1 then
pesos[item] = nil -- seguro con pairs en Kahlua
end
endCon una lista es distinto: table.remove corre los elementos, y si recorres hacia adelante te saltas el que quedó en el lugar del borrado. Recorre hacia atrás:
local items = { "Apple", "Rotten", "Rotten", "Banana" }
for i = #items, 1, -1 do -- de atrás hacia adelante
if items[i] == "Rotten" then
table.remove(items, i)
end
end
-- items = { "Apple", "Banana" }5. La librería table
| Función | Qué hace |
|---|---|
table.insert(t, v) | Agrega v al final |
table.insert(t, i, v) | Inserta v en la posición i y corre los siguientes |
table.remove(t) | Saca el último y lo devuelve |
table.remove(t, i) | Saca el de la posición i, corre los siguientes y lo devuelve |
table.sort(t) / table.sort(t, menor) | Ordena la lista en el lugar |
table.concat(t, sep, i, j) | Une los elementos en un string |
table.wipe(t) | Extra de Kahlua: vacía la tabla (la misma tabla, no una nueva) |
table.isempty(t) | Extra de Kahlua: true si la tabla no tiene ninguna clave |
local cola = { "Apple", "Banana" }
table.insert(cola, 1, "Cherry") -- { "Cherry", "Apple", "Banana" }
local primero = table.remove(cola, 1) -- primero = "Cherry"
local ultimo = table.remove(cola) -- ultimo = "Banana", cola = { "Apple" }table.sort
Sin segundo argumento ordena de menor a mayor (números o strings, no los mezcles). Con una función de comparación decides tú: la función recibe dos elementos y devuelve true si el primero tiene que ir antes que el segundo.
local nums = { 5, 2, 8, 1 }
table.sort(nums) -- { 1, 2, 5, 8 }
table.sort(nums, function(a, b) return a > b end) -- { 8, 5, 2, 1 }
local loot = {
{ nombre = "Apple", peso = 2 },
{ nombre = "Watermelon", peso = 0.5 },
{ nombre = "Pineapple", peso = 1 },
}
table.sort(loot, function(a, b) return a.peso > b.peso end)
-- Apple, Pineapple, WatermelonLa función tiene que ser un "menor estricto": nunca devuelvas true para dos elementos iguales (no uses >= ni <=).
table.concat
Une una lista de strings o números. Es la forma correcta de armar un texto con muchas partes (concatenar con .. dentro de un bucle crea un string nuevo en cada vuelta; L10 vuelve sobre esto):
local partes = { "Apple", "Banana", "Cherry" }
print(table.concat(partes, ", ")) -- Apple, Banana, Cherry
print(table.concat(partes, ", ", 2)) -- Banana, CherryUna rareza de Kahlua: si un elemento no es string ni número, table.concat no da error, escribe null. table.concat({ "a", true, "b" }, ",") da "a,null,b". Si ves null en un texto tuyo, busca un table.concat con un booleano o una tabla adentro, y pásalos por tostring antes.
table.wipe y table.isempty
Son extras de Kahlua que no existen en Lua estándar, y el juego los usa mucho (table.wipe aparece 75 veces):
local seleccion = { "Apple", "Banana" }
table.wipe(seleccion)
print(#seleccion, table.isempty(seleccion)) -- 0 true
local mapa = { Apple = 2 }
print(#mapa) -- 0: # solo mira la parte de lista
print(table.isempty(mapa)) -- false: isempty mira todas las claves#t == 0 no sirve para saber si un mapa está vacío; table.isempty(t) sí.
6. Mapas y conjuntos
Un mapa (o diccionario) asocia una clave con un valor. Es la tabla que más vas a usar en un mod: datos por tipo de ítem, por jugador, por nombre de zona.
local calorias = {
["Base.Apple"] = 95,
["Base.Banana"] = 105,
}
calorias["Base.Cherry"] = 50
local tipo = "Base.Apple"
if calorias[tipo] then
print("[CursoLab] " .. tipo .. " tiene " .. calorias[tipo] .. " calorías")
endBuscar en un mapa por clave es instantáneo; buscar un valor recorriendo una lista es más lento cuanto más larga es la lista. Si vas a preguntar muchas veces "¿está X?", arma un mapa una vez y pregunta muchas.
Para contar cosas, el patrón es t[k] = (t[k] or 0) + 1:
local muertes = {}
local function anotar(tipoZombi)
muertes[tipoZombi] = (muertes[tipoZombi] or 0) + 1
endSi el valor de un mapa puede ser false, revisa con ~= nil en vez de if t[k] then, por la misma razón que en L04 con los booleanos por defecto.
Un conjunto es un mapa donde solo importa la clave: el valor es true.
local comestibles = { Apple = true, Banana = true, Cherry = true }
if comestibles["Apple"] then print("se come") end
comestibles["Cherry"] = nil -- sacar del conjuntolocal function removeDuplicates(list)
local result = {}
local seen = {}
for _,item in ipairs(list) do
if not seen[item] then
seen[item] = true
table.insert(result, item)
end
end
return result
endCompáralo con luautils.tableContains (en shared/luautils.lua), que recorre toda la tabla con pairs cada vez que preguntas. Para una pregunta suelta está bien; dentro de un bucle, el conjunto gana.
7. Tablas anidadas
Una tabla puede tener tablas adentro. Así están armados casi todos los datos del juego:
local partida = {
jugadores = {
{ nombre = "Kate", kills = 12 },
{ nombre = "Bob", kills = 3 },
},
config = { dificultad = "dura" },
}
print(partida.jugadores[1].nombre) -- Kate
print(partida.config.dificultad) -- duraSi una tabla del medio no existe, el acceso falla con un error del tipo attempted index ... of non-table. Para leer datos que pueden faltar, revisa cada nivel con and:
local dif = partida.config and partida.config.dificultad or "normal"Míralo en el juego: las listas de loot
Abre server/Items/ProceduralDistributions.lua. Es una sola tabla global gigante. La entrada de los refrigeradores comunes empieza así (línea 22287):
FridgeGeneric = {
ignoreZombieDensity = true,
rolls = 2,
items = {
-- Fruit
"Apple", 2,
"Banana", 2,
"Cherry", 2,
"Grapefruit", 2,
"Grapes", 2,
"Lemon", 2,
"Lime", 2,
"Orange", 2,
"Peach", 2,
"Pear", 2,
"Pineapple", 1,
"Strewberrie", 2,
"Watermelon", 0.5,Tres niveles: ProceduralDistributions.list es un mapa de contenedores; cada contenedor es un mapa con rolls, items y a veces junk (que tiene su propio rolls e items); e items es una lista plana que alterna nombre y peso: nombre, peso, nombre, peso.... El ítem está en las posiciones impares y su peso en la siguiente. Para leerla se recorre de a dos:
local function listarLoot(nombreContenedor)
local cont = ProceduralDistributions.list[nombreContenedor]
if not cont or not cont.items then
print("[CursoLab] No existe el contenedor " .. tostring(nombreContenedor))
return
end
local items = cont.items
for i = 1, #items, 2 do
local nombre, peso = items[i], items[i + 1]
print("[CursoLab] " .. nombre .. " peso " .. tostring(peso))
end
end
listarLoot("FridgeGeneric")Por eso, para agregar un ítem, el módulo 13 hace dos table.insert seguidos (primero el nombre, después el peso). Si alguien inserta solo uno, toda la lista queda corrida: los nombres pasan a las posiciones pares y el juego lee pesos donde había nombres.
Leer estas tablas se puede en cualquier momento de una partida de un jugador. Cambiarlas para que el loot cambie de verdad tiene su momento exacto (OnPostDistributionMerge): eso es el módulo 13.
Recorrer algo anidado: recursión
Para recorrer tablas que tienen tablas adentro, sin saber cuántos niveles hay, se usa una función recursiva (L04). Así junta el juego las distribuciones de los mods con las suyas, en server/Items/SuburbsDistributions.lua:
function MergeDistributionRecursive(_orig, _mod)
for k,v in pairs(_mod) do
if _orig[k]~=nil then
if type(_mod[k])=="table" then
if type(k)=="string" and k=="items" then
for _,v2 in ipairs(_mod[k]) do
table.insert(_orig[k], v2)
end
else
MergeDistributionRecursive(_orig[k], _mod[k])
end
end
else --if original doesnt have the table the mod has, add it.
_orig[k] = _mod[k]
end
end
endLéela con lo que ya sabes: si la clave no existe en el original, se copia la referencia (_orig[k] = _mod[k]); si existe y es la lista items, agrega los pares al final con ipairs y table.insert; si existe y es otra tabla, baja un nivel llamándose a sí misma.
8. Referencias, copias superficiales y copias profundas
Esta es la parte que más errores causa. Una variable no contiene la tabla: contiene una referencia a ella. Asignar una tabla a otra variable no la copia:
local a = { vida = 100 }
local b = a -- b apunta a LA MISMA tabla
b.vida = 50
print(a.vida) -- 50
print(a == b) -- true: misma tabla
print({} == {}) -- false: dos tablas distintas, aunque estén vacíasLo mismo pasa al pasar una tabla a una función: la función recibe la referencia y puede cambiar la tabla original.
local function vaciar(t)
table.wipe(t) -- vacía la tabla del que llamó
end
local function reemplazar(t)
t = {} -- solo cambia la variable local t; la original no se toca
endCopia superficial
Una tabla nueva con las mismas claves y valores. Si un valor es otra tabla, se copia la referencia a esa tabla interna, no la tabla:
local function copiaSuperficial(t)
local nueva = {}
for k, v in pairs(t) do
nueva[k] = v
end
return nueva
end
local original = { nombre = "Kate", stats = { fuerza = 5 } }
local copia = copiaSuperficial(original)
copia.nombre = "Bob" -- no afecta al original
copia.stats.fuerza = 10 -- SÍ afecta: stats es la misma tabla en las dos
print(original.nombre, original.stats.fuerza) -- Kate 10Copia profunda
Copia también las tablas de adentro, con recursión:
local function copiaProfunda(t)
if type(t) ~= "table" then return t end
local nueva = {}
for k, v in pairs(t) do
nueva[k] = copiaProfunda(v)
end
return nueva
endEl juego ya trae una: la función global copyTable(t), definida en Java (LuaManager.copyTable) y usada en 49 lugares del Lua del juego. Según su código, copia recursivamente las tablas de adentro, copia las claves tal cual y no copia la metatabla. Tampoco controla ciclos: no se la pases a una tabla que se contiene a sí misma (directa o indirectamente), porque se llamaría sin fin.
local copia = copyTable(original)
copia.stats.fuerza = 99
print(original.stats.fuerza) -- no cambiaUna versión que sí aguanta ciclos y además conserva la metatabla está en client/PZAPI/ui/atoms/Meta.lua. Usa una tabla seen para recordar lo que ya copió:
UI._copyValue = function(obj, seen)
if type(obj) ~= 'table' then return obj end
if seen and seen[obj] then return seen[obj] end
local s = seen or {}
local res = setmetatable({}, getmetatable(obj))
s[obj] = res
for k, v in pairs(obj) do
if k ~= "javaObj" then
res[UI._copyValue(k, s)] = UI._copyValue(v, s)
end
end
for i, v in ipairs(obj) do res[i] = UI._copyValue(v, s) end
return res
endFíjate en el if k ~= "javaObj": los objetos de Java no se pueden copiar así, solo se puede copiar la referencia. Lo ves en L06.
Cuándo copiar
| Situación | Qué hacer |
|---|---|
| Quieres que dos partes del código vean los mismos datos | Comparte la referencia |
| Vas a modificar una tabla que te pasaron y el dueño no debe enterarse | Copia (superficial si no tiene tablas adentro, profunda si tiene) |
| Quieres vaciar una tabla que otros tienen guardada | table.wipe(t) |
| Quieres empezar de cero sin tocar lo que otros tienen | t = {} |
Ejercicios
Prueba todo en tu archivo de CursoLab o en la consola de debug, dentro de una partida de un jugador (L01).
Ejercicio L5.1 · La lista de la mochila
Escribe CursoLab.mochila, una lista vacía, y tres funciones:
CursoLab.guardar(item): lo agrega al final.CursoLab.sacar(item): saca la primera aparición de ese ítem y devuelvetrue, ofalsesi no estaba.CursoLab.verMochila(): imprime los ítems ordenados alfabéticamente y separados por coma, sin cambiar el orden de la mochila.
Listo cuando: después de guardar "Banana", "Apple", "Banana" y sacar "Banana", verMochila() imprime [CursoLab] Apple, Banana y CursoLab.mochila[1] sigue siendo "Apple".
Solución
CursoLab = CursoLab or {}
CursoLab.mochila = CursoLab.mochila or {}
function CursoLab.guardar(item)
table.insert(CursoLab.mochila, item)
end
function CursoLab.sacar(item)
local m = CursoLab.mochila
for i = 1, #m do
if m[i] == item then
table.remove(m, i)
return true
end
end
return false
end
function CursoLab.verMochila()
local copia = {}
for i, v in ipairs(CursoLab.mochila) do copia[i] = v end -- copia para no desordenar
table.sort(copia)
print("[CursoLab] " .. table.concat(copia, ", "))
endComo sacar devuelve apenas encuentra, el table.remove dentro del for no causa saltos.
Ejercicio L5.2 · Los pesos de un contenedor
Escribe CursoLab.topLoot(contenedor, n) que lea ProceduralDistributions.list[contenedor].items, arme una lista de tablas { nombre = ..., peso = ... }, la ordene de mayor a menor peso e imprima los n primeros (por defecto 5).
Listo cuando: CursoLab.topLoot("KitchenCannedFood", 3) imprime TinOpener primero (peso 8) y CursoLab.topLoot("NoExiste") imprime un aviso en vez de un error.
Solución
function CursoLab.topLoot(contenedor, n)
n = n or 5
local cont = ProceduralDistributions.list[contenedor]
if not cont or not cont.items then
print("[CursoLab] No existe: " .. tostring(contenedor))
return
end
local lista = {}
local items = cont.items
for i = 1, #items, 2 do
table.insert(lista, { nombre = items[i], peso = items[i + 1] })
end
table.sort(lista, function(a, b) return a.peso > b.peso end)
for i = 1, math.min(n, #lista) do
print("[CursoLab] " .. i .. ". " .. lista[i].nombre .. " (" .. lista[i].peso .. ")")
end
endEjercicio L5.3 · Lo que tienen en común
Escribe CursoLab.enComun(contA, contB) que devuelva una lista con los ítems que aparecen en los dos contenedores, usando un conjunto para el primero (no dos bucles anidados). Ignora los pesos.
Listo cuando: CursoLab.enComun("FridgeGeneric", "FridgeOther") devuelve una lista con frutas como "Apple", y la imprimes con table.concat.
Solución
local function nombresDe(contenedor)
local cont = ProceduralDistributions.list[contenedor]
local nombres = {}
if not cont or not cont.items then return nombres end
for i = 1, #cont.items, 2 do
table.insert(nombres, cont.items[i])
end
return nombres
end
function CursoLab.enComun(contA, contB)
local enA = {}
for _, nombre in ipairs(nombresDe(contA)) do
enA[nombre] = true
end
local resultado, yaPuesto = {}, {}
for _, nombre in ipairs(nombresDe(contB)) do
if enA[nombre] and not yaPuesto[nombre] then
yaPuesto[nombre] = true
table.insert(resultado, nombre)
end
end
return resultado
end
print("[CursoLab] " .. table.concat(CursoLab.enComun("FridgeGeneric", "FridgeOther"), ", "))yaPuesto evita repetidos si un ítem aparece dos veces en la misma lista.
Ejercicio L5.4 · Referencia o copia
Sin ejecutarlo, escribe qué imprime cada print. Después ejecútalo y compara.
local base = { nombre = "Kate", stats = { fuerza = 5 }, tags = { "rapida" } }
local r = base
local s = {}
for k, v in pairs(base) do s[k] = v end
local p = copyTable(base)
r.nombre = "Bob"
s.stats.fuerza = 7
p.stats.fuerza = 9
table.insert(s.tags, "fuerte")
table.wipe(p.tags)
print(base.nombre, base.stats.fuerza, #base.tags)
print(s.nombre, #s.tags, #p.tags)Listo cuando: acertaste las dos líneas y puedes explicar cada valor diciendo "es la misma tabla" o "es otra tabla".
Solución
Bob 7 2
Kate 2 0res la misma tabla quebase, así quebase.nombrepasa a"Bob".ses una copia superficial: tiene su propionombre(sigue"Kate", se copió antes del cambio), peros.statsys.tagsson las mismas tablas que enbase. Por esobase.stats.fuerzaqueda en 7 ybase.tagstiene 2 elementos.pes una copia profunda: cambiarp.statsy vaciarp.tagsno toca abase.
Ejercicio L5.5 · Limpiar un mapa
Arma un mapa nombre → peso con los ítems de FridgeGeneric (si un ítem aparece dos veces, suma los pesos). Después, en un solo recorrido con pairs, borra todos los que tienen peso menor que 1 y cuenta cuántos borraste. Al final imprime cuántos quedan, contándolos con pairs (no con #).
Listo cuando: imprime algo como [CursoLab] Borrados: X, quedan: Y y explicas por qué #mapa daría 0.
Solución
local cont = ProceduralDistributions.list.FridgeGeneric
local mapa = {}
for i = 1, #cont.items, 2 do
local nombre, peso = cont.items[i], cont.items[i + 1]
mapa[nombre] = (mapa[nombre] or 0) + peso
end
local borrados = 0
for nombre, peso in pairs(mapa) do
if peso < 1 then
mapa[nombre] = nil -- seguro: pairs de Kahlua recorre una copia de las claves
borrados = borrados + 1
end
end
local quedan = 0
for _ in pairs(mapa) do quedan = quedan + 1 end
print("[CursoLab] Borrados: " .. borrados .. ", quedan: " .. quedan)#mapa da 0 porque # solo mira las claves enteras 1, 2, 3... y este mapa solo tiene claves de texto.
Desafío L5
Escribe CursoLab.dondeSale(item): recorre todos los contenedores de ProceduralDistributions.list y devuelve una lista de { contenedor = ..., peso = ..., junk = true/false } con cada lugar donde aparece ese ítem, mirando tanto items como junk.items. Ordénala por peso de mayor a menor y escribe CursoLab.imprimirDondeSale(item, n) que muestre los n primeros (por defecto 10) en una sola línea armada con table.concat.
Condiciones:
- No modifiques
ProceduralDistributions: solo léelo. - Tolera contenedores sin
itemso sinjunk. - Si el ítem no aparece en ningún lado, dilo.
Listo cuando: CursoLab.imprimirDondeSale("Apple") empieza por los contenedores donde más salen manzanas (peso 20, como GroceryStandFruits1 o ProduceStorageLooseFruit) y CursoLab.imprimirDondeSale("Sopaipilla") dice que no aparece.
Una solución
local function buscarEn(lista, item, contenedor, esJunk, resultado)
if not lista then return end
for i = 1, #lista, 2 do
if lista[i] == item then
table.insert(resultado, { contenedor = contenedor, peso = lista[i + 1], junk = esJunk })
end
end
end
function CursoLab.dondeSale(item)
local resultado = {}
for nombre, cont in pairs(ProceduralDistributions.list) do
if type(cont) == "table" then
buscarEn(cont.items, item, nombre, false, resultado)
if cont.junk then
buscarEn(cont.junk.items, item, nombre, true, resultado)
end
end
end
table.sort(resultado, function(a, b) return a.peso > b.peso end)
return resultado
end
function CursoLab.imprimirDondeSale(item, n)
n = n or 10
local lugares = CursoLab.dondeSale(item)
if #lugares == 0 then
print("[CursoLab] " .. tostring(item) .. " no aparece en ProceduralDistributions")
return
end
local partes = {}
for i = 1, math.min(n, #lugares) do
local l = lugares[i]
local texto = l.contenedor .. " " .. tostring(l.peso)
if l.junk then texto = texto .. " (junk)" end
table.insert(partes, texto)
end
print("[CursoLab] " .. item .. ": " .. table.concat(partes, ", "))
endFíjate en el tostring(l.peso): si algún día un mod rompe una lista y deja un nil como peso, .. daría error, pero tostring escribe nil y te muestra el problema.
Anterior: L04 · Funciones · Siguiente: L06 · Lua y objetos de Java