WENOPALDe usuarios para usuarios

L00 · Qué es programar y qué es Lua

Para quien nunca programó. Qué es un lenguaje, de dónde nace Lua, por qué Project Zomboid está hecho en Java y cómo se hablan los dos. Sin instalar nada.

Lua para Project Zomboid · Project Zomboid Build 42 · Actualizado el

Objetivo: que llegues a L01 sabiendo qué es programar, qué es Lua, de dónde viene, qué es Java y cómo conversan los dos dentro de Project Zomboid. Este módulo no pide instalar nada ni saber nada. Solo pide que hayas jugado.

1. Una escena que ya viviste

Son las tres de la mañana en Muldraugh. Rompes la ventana de una cocina, el vidrio cae y suena. Dos casas más allá, un zombie gira la cabeza y empieza a caminar hacia ti. Entras, abres la alacena y encuentras una lata de frijoles. Tu personaje tiene hambre y está un poco asustado.

Todo eso que pasó en cinco segundos son decisiones que alguien escribió antes:

  • Si rompes una ventana, entonces hace ruido en un radio de tantos metros.
  • Para cada zombie dentro de ese radio, si oyó el ruido, camina hacia él.
  • Cuando abres un contenedor por primera vez, el juego decide qué hay adentro según el tipo de cuarto y el tipo de mueble.
  • Cada cierto tiempo, el hambre sube un poco.

Nadie mueve a ese zombie a mano. Hay un texto, escrito por una persona, que dice qué hacer en cada caso, y el computador lo cumple al pie de la letra millones de veces por segundo. Ese texto es un programa. Escribirlo es programar. Y un mod es exactamente eso: tu propio texto, que se suma al del juego y le cambia o le agrega reglas.

2. Programar es dar instrucciones a alguien muy literal

Imagina que tienes un sobreviviente que hace todo lo que le dices, pero exactamente lo que le dices. No adivina, no completa, no interpreta. Si le dices "hierve agua", no sabe qué es hervir, ni dónde está la olla, ni que hay que prender la cocina.

Entonces le escribes así:

  1. Toma la olla.
  2. Si la olla está vacía, llénala en el lavaplatos.
  3. Pon la olla en la cocina.
  4. Prende la cocina.
  5. Espera hasta que el agua esté hirviendo.
  6. Apaga la cocina.

Eso ya es un programa, escrito en español. Fíjate que aparecen tres ideas que vas a ver en todo el curso:

  • Pasos en orden: uno después del otro, de arriba hacia abajo.
  • Decisiones: "si la olla está vacía". A veces se hace algo y a veces no.
  • Esperar o repetir: "espera hasta que hierva". Algo se repite hasta que se cumple una condición.

El problema del español es que es ambiguo. "Llénala" ¿hasta dónde? "Espera" ¿cuánto? Las personas rellenamos esos huecos sin darnos cuenta. Un computador no puede. Por eso existen los lenguajes de programación: idiomas pequeños, con pocas palabras y reglas estrictas, donde cada frase significa una sola cosa.

3. Qué es un lenguaje de programación

Por dentro, un computador solo entiende números: instrucciones muy chicas como "suma estos dos números" o "copia este dato de aquí a allá", codificadas en ceros y unos. Escribir así es posible, pero lento y fácil de equivocarse.

Un lenguaje de programación es un punto medio: lo escribe una persona, se lee casi como inglés, y un programa traductor lo convierte en algo que la máquina puede ejecutar. La receta del agua, en Lua, se vería más o menos así (no es código real del juego, es para que veas la forma):

lua
local olla = tomarOlla()

if ollaVacia(olla) then
    llenar(olla)
end

ponerEnCocina(olla)
prenderCocina()

while not hirviendo(olla) do
    esperar()
end

apagarCocina()

Aunque nunca hayas programado, probablemente entiendes casi todo. if es "si", then es "entonces", while es "mientras", not es "no", end marca dónde termina cada bloque. Eso es justamente lo que hace a Lua un buen primer lenguaje: tiene muy pocas palabras y se lee casi como una receta.

Hay cientos de lenguajes: Python, JavaScript, C, Java, Lua... Cada uno nació para algo. Los vas a ver nombrados en internet y no necesitas saberlos. Para hacer mods de Project Zomboid necesitas Lua, y entender un poco qué es Java para saber con quién estás hablando.

4. De dónde nace Lua

Lua no viene de Silicon Valley. Nació en Brasil, en 1993, en un laboratorio de la Pontificia Universidad Católica de Río de Janeiro (PUC-Rio) llamado Tecgraf. Sus creadores son tres: Roberto Ierusalimschy, Luiz Henrique de Figueiredo y Waldemar Celes.

Tecgraf hacía programas para Petrobras, la petrolera brasileña. Esos programas necesitaban que los ingenieros pudieran configurarlos y extenderlos sin tocar el programa principal. En esa época Brasil tenía restricciones fuertes para importar software, así que en vez de comprar una solución extranjera hicieron la suya.

Antes de Lua habían creado un lenguaje llamado SOL ("sol" en portugués). Al nuevo, más chico y más flexible, le pusieron Lua, que en portugués significa luna. Por eso el logo de Lua es una luna orbitando un planeta. Y por eso se escribe "Lua", no "LUA": es un nombre propio, no una sigla.

Desde el principio Lua se diseñó con una idea que hoy explica por qué está en tantos juegos:

  • Es chico. El intérprete completo pesa muy poco y cabe dentro de otro programa.
  • Se incrusta. No está pensado para hacer un programa entero solo, sino para vivir dentro de un programa más grande y darle a la gente una forma fácil de extenderlo.
  • Es simple. Tiene un solo tipo de estructura de datos (la tabla, la ves en L05) y muy pocas reglas.

Esa idea, "un programa grande que deja que otros le agreguen cosas con un lenguaje chico", es exactamente lo que es un juego con mods. Por eso Lua aparece en los complementos de World of Warcraft, en los mods de Factorio y Garry's Mod, y Roblox usa un derivado llamado Luau. Lo que aprendes aquí te sirve fuera de Project Zomboid.

5. Qué es Java y por qué el juego está hecho en Java

Project Zomboid lo hace The Indie Stone, un estudio británico, y el motor del juego (todo lo pesado: el mapa, la física, los zombies, el dibujo en pantalla, la red en multijugador) está escrito en Java.

Java es otro lenguaje, más grande y más estricto que Lua, muy usado en programas grandes. Un programa en Java se empaqueta en archivos .jar. El corazón de Project Zomboid es justamente uno de esos archivos, projectzomboid.jar, con miles de piezas adentro. Cada pieza es una clase: un molde que describe un tipo de cosa del juego. Hay una clase para el jugador (IsoPlayer), una para los zombies (IsoZombie), una para los ítems (InventoryItem), una para cada cuadrado del mapa (IsoGridSquare).

No vas a escribir Java para hacer mods. Pero vas a estar todo el tiempo hablándole a Java, así que te conviene saber que está ahí.

6. Kahlua: el traductor que vive dentro del juego

Aquí está el truco. Lua normal está escrito en el lenguaje C y no sabe nada de Java. Para que Project Zomboid pudiera usar Lua, el juego trae Kahlua: una versión de Lua escrita en Java, que corre dentro del mismo programa que el resto del juego. Kahlua habla Lua 5.1 (una versión de Lua de 2006, muy estable) con algunas diferencias que vas a ir viendo, sobre todo en 06 · Lua en Project Zomboid (Kahlua).

Piénsalo como una ciudad:

En la ciudadEn el juego
La ciudad entera, con sus calles, edificios y serviciosEl motor en Java (projectzomboid.jar)
Un traductor que vive en la municipalidadKahlua
Las cartas que le escribes al traductorTus archivos .lua
Las ventanillas donde el traductor puede pedir cosasLas funciones y objetos que Java deja usar desde Lua
El aviso por altoparlante cuando pasa algoLos eventos (OnZombieDead, OnGameStart...)

Tú no puedes entrar a la sala de máquinas de la ciudad. Pero puedes escribirle al traductor, y él puede pedir cosas en las ventanillas que la ciudad dejó abiertas. Hay miles de ventanillas, y gran parte del curso consiste en aprender cuáles hay y cómo pedir en cada una.

Una parte grande del juego, además, ya está escrita en Lua: los menús, la mayoría de las ventanas, las acciones con barra de progreso, las tablas que dicen qué loot sale en cada mueble. Viene en la carpeta media/lua del juego y la puedes leer. Eso es una ventaja enorme: para aprender cómo se hace algo, muchas veces basta con abrir cómo lo hizo el juego.

7. Cómo se hablan Lua y Java, en tres gestos

Toda la conversación entre tu mod y el juego se reduce a tres gestos. Los vas a usar en cada mod que hagas.

Pedir algo. Java deja funciones globales que puedes llamar por su nombre. La más usada:

lua
local jugador = getPlayer()

getPlayer() le pide al juego "dame el jugador de esta pantalla". Lo que vuelve no es un dato de Lua: es el objeto de Java que representa a tu personaje, el mismo que el motor usa para moverlo y dibujarlo.

Decirle algo a un objeto. Con un objeto de Java en la mano, le pides cosas con dos puntos :

lua
jugador:Say("Tengo hambre")

Esa línea hace que tu personaje diga la frase sobre su cabeza. Say es un método: algo que ese tipo de objeto sabe hacer. El jugador sabe decir cosas, caminar, comer, tener stats. Un ítem sabe decir su nombre y su peso. Cada clase de Java trae su lista de métodos.

Que el juego te avise. No puedes estar preguntando cada segundo "¿ya murió un zombie?". En vez de eso, le dejas un recado al juego: "cuando muera un zombie, ejecuta esto".

lua
Events.OnZombieDead.Add(function(zombie)
    getPlayer():Say("Uno menos")
end)

Eso es un evento. El juego (Java) dispara el aviso OnZombieDead cada vez que muere un zombie, y Kahlua ejecuta todos los recados que los mods dejaron para ese aviso. Casi todo mod empieza así: engancharse a un evento y reaccionar.

Pedir, decirle algo a un objeto y esperar avisos. Con esos tres gestos se construye desde un mod que agrega una lata de comida hasta uno que cambia cómo se comportan los zombies.

8. Las piezas, antes de ver el código

En L01 a L10 vas a aprender cada pieza a fondo. Aquí va el mapa, con una imagen del juego para cada una, para que cuando llegues ya te suenen.

Variable: una caja con etiqueta

Una variable es un nombre que guarda un valor, como una caja con una etiqueta escrita encima. Puedes mirar qué tiene adentro y cambiar el contenido cuando quieras.

lua
local balas = 12
balas = balas - 1      -- disparaste: ahora hay 11

local significa "esta caja es mía, de este archivo". La razón por la que importa tanto está en L01.

Tipo: qué clase de cosa hay en la caja

No es lo mismo guardar un número que un texto. En Lua cada valor tiene un tipo:

TipoQué esEjemplo en el juego
numberUn númeroLas balas que te quedan, la hora, el peso de un ítem
stringUn texto, siempre entre comillasEl nombre del jugador, "Base.Axe"
booleanVerdadero o falso (true / false)¿Está de noche? ¿El auto tiene gasolina?
nil"Aquí no hay nada"Buscaste un ítem en una mochila vacía
tableUn contenedor con muchas cosasTu lista de zombies matados por día
functionUna receta guardadaLo que pasa cuando muere un zombie
userdataUn objeto que vive en JavaEl jugador, un zombie, un ítem

Función: una receta con nombre

Una función es un grupo de pasos con un nombre, para usarlo las veces que quieras. Como un crafteo: lo defines una vez, lo haces mil veces.

lua
local function curarse(jugador)
    jugador:Say("Me pongo una venda")
end

Decisión: si pasa esto, haz aquello

lua
if balas == 0 then
    jugador:Say("Sin balas")
end

Repetición: para cada uno

lua
for dia = 1, 7 do
    print("Sobreviviste el día " .. dia)
end

Eso imprime siete líneas, una por cada día de la primera semana. Los .. pegan textos.

Tabla: la mochila

Una tabla guarda muchas cosas juntas, cada una con su lugar. Es la única estructura de datos de Lua y la vas a usar en todas partes.

lua
local mochila = { "Martillo", "Clavos", "Agua" }
print(mochila[1])      -- Martillo: en Lua se cuenta desde 1

Comentario: notas para personas

Todo lo que va después de -- el juego lo ignora. Sirve para explicarle a quien lea el código (incluido tú en tres meses) por qué hiciste algo.

9. Lee tu primer mod de verdad

Este es un mod completo y real. No lo tienes que escribir todavía: solo leerlo, con la explicación al lado. Cuenta los zombies que matas y cada diez te hace decir cuántos llevas.

lua
-- ContadorZombies: cuenta cuántos zombies matas en esta sesión.

local muertes = 0

local function alMorirUnZombie(zombie)
    muertes = muertes + 1
    if muertes % 10 == 0 then
        getPlayer():Say("Llevo " .. muertes .. " zombies")
    end
end

Events.OnZombieDead.Add(alMorirUnZombie)

Línea por línea:

  • La primera línea es un comentario: el juego la ignora, es para ti.
  • local muertes = 0 crea una caja llamada muertes y le pone un cero adentro.
  • local function alMorirUnZombie(zombie) define una receta. zombie es lo que el juego le va a entregar cada vez: el zombie que acaba de morir.
  • muertes = muertes + 1 saca el número de la caja, le suma uno y lo vuelve a guardar.
  • if muertes % 10 == 0 then pregunta "¿el número es múltiplo de diez?". % es el resto de una división: 20 dividido en 10 da resto 0, 23 dividido en 10 da resto 3. Los dos signos == preguntan "¿es igual?", a diferencia de uno solo =, que guarda.
  • getPlayer():Say(...) es el gesto de pedir el jugador y decirle algo, con el texto armado pegando partes con ...
  • Events.OnZombieDead.Add(alMorirUnZombie) deja el recado: "cada vez que muera un zombie, ejecuta esta receta".

Fíjate en lo que no hace este mod: no recorre el mapa buscando zombies muertos, no revisa nada cada segundo. Solo espera el aviso. Así son los buenos mods: reaccionan en vez de preguntar todo el tiempo, y por eso no le quitan rendimiento al juego.

Si este archivo te parece legible, ya entendiste lo más difícil: la forma de pensar. Lo que sigue es aprender las palabras.

10. Palabras que vas a ver en todas partes

PalabraQué significa
CódigoEl texto de un programa
ScriptUn archivo de código chico. En PZ también se llaman "scripts" los .txt que definen ítems y recetas, que no son Lua (los ves en el módulo 02)
SintaxisLas reglas de escritura de un lenguaje: dónde van los end, las comillas, los paréntesis
Error o bugAlgo que no funciona como esperabas. Un error de sintaxis impide que el archivo cargue; un bug hace que cargue pero haga otra cosa
ConsolaUna ventana donde escribes una línea de Lua y el juego la ejecuta al instante (la abres en L01)
LogEl archivo donde el juego anota lo que pasa, incluidos tus mensajes y los errores: console.txt
DebugBuscar y arreglar errores. El juego tiene un "modo debug" con herramientas para eso
APILa lista de ventanillas: qué funciones y métodos deja usar el juego
ModUna carpeta con tu código y tus archivos, que el juego carga junto con el suyo

11. Cómo seguir

El curso está pensado para hacerse en orden:

  • L01 · Tu primer Lua en el juego: abres la consola, escribes tu primera línea y armas el mod de práctica CursoLab.
  • L02 a L10: cada pieza de este módulo, a fondo y con ejercicios dentro del juego.
  • Desde el módulo 00: el modding completo, de los ítems y recetas al multijugador.

No te preocupes si algo de este módulo quedó a medias. Todo vuelve a aparecer con calma y con ejercicios. Lo único que necesitas llevarte es la imagen: el juego es una ciudad hecha en Java, Kahlua es el traductor, y tus archivos Lua son las cartas con las que le pides cosas y le dejas recados.

Ejercicios

Estos ejercicios no necesitan computador. Son para entrenar la forma de pensar, que es lo que más cuesta al principio.

Ejercicio L0.1 · Receta para alguien muy literal

Escribe en español, en pasos numerados, las instrucciones para que un sobreviviente que hace exactamente lo que le dices tape con tablas una ventana. Usa al menos un "si" y al menos un "repite" o "hasta que".

Listo cuando: alguien que lea tus pasos no tiene que adivinar nada (¿de dónde saca las tablas?, ¿qué pasa si no tiene martillo?).

Una solución posible
  1. Si no tienes martillo en el inventario, detente y di "Necesito un martillo".
  2. Si tienes menos de una tabla o menos de dos clavos, detente y di "Me faltan materiales".
  3. Camina hasta quedar frente a la ventana.
  4. Repite hasta que la ventana tenga 4 tablas o se acaben los materiales: clava una tabla con dos clavos.
  5. Di "Listo".

Lo importante no es esta lista exacta, sino que hayas notado cuántas cosas damos por obvias: tener las herramientas, qué hacer si faltan y cuándo parar. Programar es escribir lo obvio.

Ejercicio L0.2 · ¿Quién hace qué?

Para cada cosa, di si la hace el motor en Java, si la puede hacer tu mod en Lua, o las dos:

  1. Dibujar los zombies en pantalla.
  2. Hacer que tu personaje diga una frase cuando amanece.
  3. Agregar una opción nueva al menú del clic derecho.
  4. Calcular hacia dónde camina un zombie que oyó un ruido.
  5. Avisar que un zombie murió.
Solución
  1. Java. Es parte del motor; un mod no dibuja zombies a mano.
  2. Lua. Te enganchas a un evento de tiempo y usas Say.
  3. Lua. Los menús contextuales del juego están escritos en Lua y tu mod les agrega opciones (módulo 10).
  4. Java. La lógica de movimiento de los zombies vive en el motor. Desde Lua puedes influir en ella con lo que el juego expone, pero el cálculo lo hace Java.
  5. Las dos: Java detecta la muerte y dispara el evento OnZombieDead; tu Lua recibe el aviso.

Ejercicio L0.3 · Lee sin ejecutar

Mira este código y responde sin probarlo: ¿qué dice el personaje si tiene 3 balas? ¿Y si tiene 0?

lua
local balas = 3

if balas == 0 then
    getPlayer():Say("Sin balas, a correr")
else
    getPlayer():Say("Me quedan " .. balas)
end
Solución

Con 3 balas dice "Me quedan 3": la pregunta balas == 0 es falsa y se ejecuta lo que está después de else ("si no"). Con 0 balas dice "Sin balas, a correr". else es la parte del "si no" de una decisión, y la ves a fondo en L03.

Desafío L0

Elige un mod que uses y te guste. Sin abrir su código, escribe en español qué crees que hace por dentro, usando las palabras de este módulo: a qué eventos se engancha, qué le pide al juego, qué guarda en variables o tablas y qué decisiones toma. Cuando termines L08, vuelve a esta lista, abre los archivos .lua del mod (están en su carpeta media/lua) y compara.