Files

908 lines
31 KiB
Lua

-- ============================================================
-- GUANTE BLANCO — port a la fantasy console "ascii" del original
-- de David Radisic para Amstrad CPC (AMSOFT, 1985).
-- Sergi Valor, 2026.
--
-- FASE 1: estructura base. Las 5 habitaciones se dibujan con
-- sus paredes, puertas, ventanas y conmutadores. Tecla 1..5
-- conmuta la habitación visible para verificar el render.
-- ============================================================
-- ============================================================
-- CONFIGURACION
-- ============================================================
MODO = 3 -- mode(3) = 32x24
ANCHO = 32
ALTO = 24
-- Layout (HUD/banners alrededor del área de habitación)
FILA_BANNER = 0 -- "Habitacion: X"
FILA_MSG = 23 -- mensajes "Choque", "MORDIDO"
AREA_Y0 = 1 -- primera fila donde se pinta la habitación
AREA_Y1 = 22 -- última fila
-- Ritmo del movimiento del jugador (frames entre pasos)
TICS_JUGADOR = 4
-- Generación procedural (líneas 1000-1170 del original):
-- joyar = INT(RND*8) + 2 → 2..9 joyas por habitación
-- objr = INT(RND*10) + 5 → 5..14 obstáculos por habitación
JOYAS_MIN = 2
JOYAS_RND = 8
OBJ_MIN = 5
OBJ_RND = 10
-- Retardo del perro (línea 60): empieza en 200, cada choque ruidoso resta 50,
-- mínimo 50. La activación del perro la maneja la Fase 4.
RETARDO_INI = 200
RETARDO_MIN = 50
RETARDO_STEP = 50
RUIDO_RND = 15
RUIDO_UMBRAL = 10
-- AFTER de BASIC del CPC trabaja en 1/50 s (jiffies de 20 ms). El original
-- programa "AFTER retardo*4, 1 GOSUB 310" → retardo*4 jiffies = retardo*80 ms.
MS_POR_JIFFY = 20
-- Cada cuántos movimientos del jugador avanza el perro (línea 1650: perro
-- alterna 1,2 y solo se mueve cuando == 2 → 1 paso de perro cada 2 del jugador).
PERRO_RATIO = 2
-- Duraciones de transición (ms)
MS_ESCAPE = 2500
MS_MORDIDO = 1500
MS_PREGUNTA_MIN = 600 -- antes de aceptar S/N
-- Estados de la máquina global
ESTADO_PASE = "pase"
ESTADO_JUEGO = "juego"
ESTADO_ESCAPE = "escape"
ESTADO_MORDIDO = "mordido"
ESTADO_PREGUNTA = "pregunta"
ESTADO_FIN = "fin"
-- ============================================================
-- PALETA (mapeo CPC firmware → CGA disponible)
-- Original: INK 0,0 (negro) / 1,26 (pastel) / 2,15 (blanco) /
-- 3,25 (pastel verde) / 4,14 (amarillo) / 5,24,12 (parpadeo) /
-- 6,0 (negro al inicio, luego 24,12) / 7,8 = paper de la habit.
-- ============================================================
COL_FONDO = COLOR_BLACK
COL_LADRON = COLOR_WHITE -- INK 1 pastel
COL_PUERTA = COLOR_LIGHT_GRAY -- INK 2 blanco
COL_VENTANA = COLOR_YELLOW -- INK 4
COL_CONMUT = COLOR_LIGHT_GREEN -- INK 3
COL_JOYA = COLOR_LIGHT_RED -- INK 5 (parpadeo en original)
COL_OBSTACULO = COLOR_BROWN -- INK 6
COL_PERRO = COLOR_WHITE -- INK 1
COL_PARED = COLOR_LIGHT_BLUE -- el rectángulo L original era PEN 1 azul
COL_PAPER_HAB_ON = COLOR_DARK_GRAY -- paper de la habitación con luz (INK 7,10)
COL_PAPER_HAB_OFF = COLOR_BLACK -- paper de la habitación sin luz (INK 7,0)
COL_TEXTO = COLOR_LIGHT_GRAY
COL_MSG = COLOR_LIGHT_RED
-- ============================================================
-- GLIFOS (códigos que vamos a redefinir con setchar)
-- ============================================================
GL_LADRON = 224 -- hombre$ = chr(224)
GL_LADRON_M = 225 -- ladrón "mordido" (línea 2570 del original)
GL_VENT_H = 250 -- ventana horizontal (2 chars)
GL_VENT_V = 251 -- ventana vertical (3 chars verticales)
GL_PUERTA_H = 252 -- puerta horizontal
GL_PUERTA_V = 253 -- puerta vertical
GL_PERRO = 255 -- el perro
GL_JOYA = 144 -- joya$ = chr(144)
GL_OBSTACULO = 233 -- obj$ = chr(233)
GL_CONM_L_OFF = 246 -- conm$(1,0)
GL_CONM_L_ON = 247 -- conm$(1,1)
GL_CONM_R_OFF = 248 -- conm$(2,0)
GL_CONM_R_ON = 249 -- conm$(2,1)
GL_PARED = 143 -- nuestro char de pared (el original usaba DRAW de líneas)
-- ============================================================
-- TIPOS DE CELDA DEL MAPA
-- ============================================================
T_VACIO = 0
T_PARED = 1
T_PUERTA_H = 2
T_PUERTA_V = 3
T_VENT_H = 4
T_VENT_V = 5
T_CONM_L = 6
T_CONM_R = 7
T_JOYA = 8
T_OBSTACULO = 9
T_PERRO = 10
-- ============================================================
-- DEFINIR GLIFOS (bitmaps idénticos a los SYMBOL del original
-- cuando los hay, diseñados a mano cuando vienen del char-ROM
-- CPC y no aparecen en el .bas).
-- ============================================================
function definir_glifos()
-- SYMBOL 240..245 del original (partes del sprite del ladrón
-- y/o decoración — el código los ignora en colisiones con
-- "IF ASC(ht$)>239 AND ASC(ht$)<246 THEN 1520")
setchar(240, 8, 8, 8, 8, 8, 8, 8, 8)
setchar(241, 0, 0, 0, 0, 255, 0, 1, 0)
setchar(242, 0, 0, 0, 0, 15, 8, 8, 8)
setchar(243, 0, 0, 0, 0, 248, 8, 8, 8)
setchar(244, 8, 8, 8, 8, 248, 0, 0, 0)
setchar(245, 8, 8, 8, 8, 15, 0, 0, 0)
-- SYMBOL 246..249 — conmutadores L/R, on/off (bitmaps literales)
setchar(GL_CONM_L_OFF, 8, 12, 13, 14, 12, 12, 8, 8)
setchar(GL_CONM_L_ON, 8, 12, 12, 14, 13, 12, 9, 8)
setchar(GL_CONM_R_OFF, 8, 24, 88, 56, 24, 24, 8, 8)
setchar(GL_CONM_R_ON, 8, 24, 24, 56, 88, 24, 8, 8)
-- SYMBOL 250..253 — ventanas y puertas (bitmaps literales)
setchar(GL_VENT_H, 0, 0, 255, 129, 129, 129, 255, 0)
setchar(GL_VENT_V, 28, 20, 20, 20, 20, 20, 20, 28)
setchar(GL_PUERTA_H, 0, 0, 255, 255, 255, 255, 255, 0)
setchar(GL_PUERTA_V, 28, 28, 28, 28, 28, 28, 28, 28)
-- SYMBOL 255 — el perro (bitmap literal del original)
setchar(GL_PERRO, 195, 165, 60, 126, 90, 60, 36, 24)
-- chr 224 (ladrón normal) y 225 (ladrón mordido): el original usaba
-- las caritas del char-ROM CPC tal cual. En ascii el charset trae
-- esas mismas caritas en los mismos códigos, así que no se redefinen.
-- Pared: bloque sólido para el rectángulo L del original
setchar(GL_PARED, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)
end
-- ============================================================
-- DEFINICIÓN DE LAS 5 HABITACIONES
-- Coordenadas tomadas LITERALMENTE de las DATAs del bombardero.bas:
-- - minx, miny, maxx, maxy: interior (líneas 3020-3060)
-- - puertas D y ventanas W: posición en pared (líneas 2680-3010)
-- - conmutadores S: posición en pared, orient L/R
-- - dir: N, S, E, O (-1 = bloqueado, 0 = escape, n = id habitación)
-- ============================================================
HABITACIONES = {
[1] = {
nombre = "Pasillo",
minx = 5, miny = 4, maxx = 8, maxy = 21,
elementos = {
{ tipo="puerta_h", x=6, y=3 }, -- N: a dir[1]=0 (escape)
{ tipo="puerta_h", x=6, y=22 }, -- S: a dir[2]=4 (Cocina)
{ tipo="puerta_v", x=4, y=12 }, -- O: a dir[4]=2 (Sala)
{ tipo="puerta_v", x=9, y=11 }, -- E: a dir[3]=3 (Comedor)
{ tipo="conm", x=4, y=11, orient="L", on=false },
{ tipo="conm", x=9, y=14, orient="R", on=false },
},
dir = { 0, 4, 3, 2 }, -- N, S, E, O
luz = false,
},
[2] = {
nombre = "Sala",
minx = 3, miny = 4, maxx = 9, maxy = 21,
elementos = {
{ tipo="puerta_v", x=10, y=12 }, -- E: a dir[3]=1 (Pasillo)
{ tipo="vent_h", x=6, y=3 }, -- N (bloqueado, dir[1]=-1)
{ tipo="vent_v", x=2, y=12 }, -- O (bloqueado, dir[4]=-1)
{ tipo="conm", x=10, y=11, orient="R", on=false },
{ tipo="conm", x=10, y=15, orient="R", on=false },
},
dir = { -1, -1, 1, -1 },
luz = false,
},
[3] = {
nombre = "Comedor",
minx = 3, miny = 4, maxx = 9, maxy = 21,
elementos = {
{ tipo="vent_v", x=10, y=12 }, -- E (bloqueado, dir[3]=-1)
{ tipo="vent_h", x=6, y=3 }, -- N (bloqueado, dir[1]=-1)
{ tipo="puerta_v", x=2, y=12 }, -- O: a dir[4]=1 (Pasillo)
{ tipo="conm", x=2, y=11, orient="L", on=false },
{ tipo="conm", x=2, y=15, orient="L", on=false },
},
dir = { -1, -1, -1, 1 },
luz = false,
},
[4] = {
nombre = "Cocina",
minx = 3, miny = 6, maxx = 13, maxy = 21,
elementos = {
{ tipo="puerta_h", x=6, y=5 }, -- N: a dir[1]=1 (Pasillo)
{ tipo="puerta_h", x=6, y=22 }, -- S: a dir[2]=0 (escape)
{ tipo="vent_h", x=10, y=22 }, -- otra ventana en la pared S
{ tipo="vent_v", x=14, y=13 }, -- E (bloqueado, dir[3]=-1)
{ tipo="puerta_v", x=2, y=13 }, -- O: a dir[4]=5 (Despensa)
{ tipo="conm", x=2, y=16, orient="L", on=false },
},
dir = { 1, 0, -1, 5 },
luz = false,
},
[5] = {
nombre = "Despensa",
minx = 3, miny = 6, maxx = 9, maxy = 21,
elementos = {
{ tipo="puerta_v", x=10, y=12 }, -- E: a dir[3]=4 (Cocina)
{ tipo="conm", x=10, y=11, orient="R", on=false },
},
dir = { -1, -1, 4, -1 },
luz = false,
},
}
-- ============================================================
-- ESTADO GLOBAL
-- ============================================================
rm = 1 -- habitación actual (variable rm del original)
xp, yp = 6, 4 -- posición del ladrón
hombre_glifo = GL_LADRON
mapa = {} -- mapa[hab][x][y] = tipo de celda
ultimo_tic = 0 -- contador de frames para temporizar el movimiento
escapado = false -- se activa al alcanzar dir=0 (Fase 5 muestra el final)
muerto = false -- se activa al ser mordido por el perro (Fase 4)
-- Joyas y obstáculos (listas paralelas al mapa por habitación)
joyas_lista = {} -- joyas_lista[rm] = { {x,y}, ... }
obj_lista = {} -- obj_lista[rm] = { {x,y}, ... }
joyas_total = 0 -- total inicial (suma de las 5 habitaciones)
robado = 0 -- joyas recogidas por el ladrón
mensaje_msg = "" -- mensaje de la fila inferior (Choque, etc.)
retardo = RETARDO_INI
-- Máquina de estados
estado = ESTADO_PASE
estado_t0_ms = 0
-- Perro guardián
perro = {
activo = false, -- perro==1 del original
rm = 0, -- habitación donde se activó
x = 0, y = 0, -- posición
paso = 0, -- alterna 0/1 para PERRO_RATIO (línea 1650)
pendiente = false, -- AFTER programado pero aún no disparado
pend_t = 0, -- time() en que se disparará el AFTER
}
-- ============================================================
-- CONSTRUCCIÓN DEL MAPA LÓGICO
-- Por habitación se construye una matriz [x][y] con el tipo de
-- celda. Las paredes son el contorno (minx-1, maxx+1, miny-1,
-- maxy+1). Las puertas, ventanas y conmutadores sobrescriben la
-- pared. El interior queda como T_VACIO (se rellenará con joyas
-- y obstáculos en la Fase 3).
-- ============================================================
function init_mapa()
for id, hab in pairs(HABITACIONES) do
mapa[id] = {}
for x = 0, ANCHO-1 do
mapa[id][x] = {}
for y = 0, ALTO-1 do
mapa[id][x][y] = T_VACIO
end
end
-- Paredes: contorno del rectángulo interior
local x0, y0 = hab.minx - 1, hab.miny - 1
local x1, y1 = hab.maxx + 1, hab.maxy + 1
for x = x0, x1 do
mapa[id][x][y0] = T_PARED
mapa[id][x][y1] = T_PARED
end
for y = y0, y1 do
mapa[id][x0][y] = T_PARED
mapa[id][x1][y] = T_PARED
end
-- Elementos: sobrescriben la pared
for _, e in ipairs(hab.elementos) do
if e.tipo == "puerta_h" then
mapa[id][e.x ][e.y] = T_PUERTA_H
mapa[id][e.x+1][e.y] = T_PUERTA_H
elseif e.tipo == "vent_h" then
mapa[id][e.x ][e.y] = T_VENT_H
mapa[id][e.x+1][e.y] = T_VENT_H
elseif e.tipo == "puerta_v" then
mapa[id][e.x][e.y ] = T_PUERTA_V
mapa[id][e.x][e.y+1] = T_PUERTA_V
mapa[id][e.x][e.y+2] = T_PUERTA_V
elseif e.tipo == "vent_v" then
mapa[id][e.x][e.y ] = T_VENT_V
mapa[id][e.x][e.y+1] = T_VENT_V
mapa[id][e.x][e.y+2] = T_VENT_V
elseif e.tipo == "conm" then
mapa[id][e.x][e.y] = (e.orient == "L") and T_CONM_L or T_CONM_R
end
end
end
end
-- ============================================================
-- OFFSET DE CENTRADO
-- En cada render, calcula el offset (off_x, off_y) para centrar
-- el rectángulo de la habitación (paredes incluidas) dentro del
-- área de juego.
-- ============================================================
function offset_centrado()
local hab = HABITACIONES[rm]
local w = (hab.maxx + 1) - (hab.minx - 1) + 1
local h = (hab.maxy + 1) - (hab.miny - 1) + 1
local off_x = flr((ANCHO - w) / 2) - (hab.minx - 1)
local off_y = flr((AREA_Y0 + AREA_Y1 - h) / 2) - (hab.miny - 1) + 1
return off_x, off_y
end
-- ============================================================
-- RENDER
-- ============================================================
function pintar_fondo()
color(COL_TEXTO, COL_FONDO)
cls()
end
function pintar_banner()
color(COL_TEXTO, COL_FONDO)
print("Habitacion: "..HABITACIONES[rm].nombre, 1, FILA_BANNER)
end
function glifo_de(tipo, on)
if tipo == T_PARED then return GL_PARED end
if tipo == T_PUERTA_H then return GL_PUERTA_H end
if tipo == T_PUERTA_V then return GL_PUERTA_V end
if tipo == T_VENT_H then return GL_VENT_H end
if tipo == T_VENT_V then return GL_VENT_V end
if tipo == T_CONM_L then return on and GL_CONM_L_ON or GL_CONM_L_OFF end
if tipo == T_CONM_R then return on and GL_CONM_R_ON or GL_CONM_R_OFF end
if tipo == T_JOYA then return GL_JOYA end
if tipo == T_OBSTACULO then return GL_OBSTACULO end
if tipo == T_PERRO then return GL_PERRO end
return nil
end
function color_de(tipo)
if tipo == T_PARED then return COL_PARED end
if tipo == T_PUERTA_H then return COL_PUERTA end
if tipo == T_PUERTA_V then return COL_PUERTA end
if tipo == T_VENT_H then return COL_VENTANA end
if tipo == T_VENT_V then return COL_VENTANA end
if tipo == T_CONM_L then return COL_CONMUT end
if tipo == T_CONM_R then return COL_CONMUT end
if tipo == T_JOYA then return COL_JOYA end
if tipo == T_OBSTACULO then return COL_OBSTACULO end
if tipo == T_PERRO then return COL_PERRO end
return COL_TEXTO
end
-- Devuelve el "estado on/off" de un conmutador concreto buscando en
-- los elementos de la habitación.
function conm_estado(hab, x, y)
for _, e in ipairs(hab.elementos) do
if e.tipo == "conm" and e.x == x and e.y == y then
return e.on
end
end
return false
end
function pintar_habitacion()
local hab = HABITACIONES[rm]
local off_x, off_y = offset_centrado()
local paper = hab.luz and COL_PAPER_HAB_ON or COL_PAPER_HAB_OFF
-- Paper del interior
color(COL_TEXTO, paper)
local blank = " "
for y = hab.miny, hab.maxy do
for x = hab.minx, hab.maxx do
print(blank, x + off_x, y + off_y)
end
end
-- Paredes y elementos. Las joyas y obstáculos solo se ven con la luz
-- encendida (línea 1280 del original: INK 7,10 ↔ INK 7,0).
for y = hab.miny - 1, hab.maxy + 1 do
for x = hab.minx - 1, hab.maxx + 1 do
local t = mapa[rm][x][y]
if t ~= T_VACIO then
local oculto = (not hab.luz) and (t == T_JOYA or t == T_OBSTACULO)
if not oculto then
local on = false
if t == T_CONM_L or t == T_CONM_R then
on = conm_estado(hab, x, y)
end
local g = glifo_de(t, on)
if g then
local bg = (t == T_JOYA or t == T_OBSTACULO) and paper or COL_FONDO
color(color_de(t), bg)
print(chr(g), x + off_x, y + off_y)
end
end
end
end
end
-- Perro (si está activo y comparte habitación con el ladrón)
if perro.activo and perro.rm == rm then
color(COL_PERRO, paper)
print(chr(GL_PERRO), perro.x + off_x, perro.y + off_y)
end
-- El ladrón
color(COL_LADRON, paper)
print(chr(hombre_glifo), xp + off_x, yp + off_y)
end
-- Marcador lateral de joyas robadas: una marca vertical por joya, replicando
-- el "MOVE 400,150+(robado*2):DRAW 555,150+(robado*2)" del original.
function pintar_marcador()
color(COL_JOYA, COL_FONDO)
print("Joyas", 26, 1)
print(string.format("%02d", robado), 27, 2)
for i = 1, robado do
local y = AREA_Y1 - (i - 1)
if y >= 4 then
print(chr(154), 28, y)
end
end
end
function pintar_msg()
if mensaje_msg ~= "" then
color(COL_MSG, COL_FONDO)
print(mensaje_msg, 4, FILA_MSG)
end
end
-- ============================================================
-- GENERACIÓN PROCEDURAL DE JOYAS Y OBSTÁCULOS
-- Líneas 980-1170 del original. Las posiciones se eligen al
-- azar dentro del interior, sin comprobar conflictos (fiel al
-- original — una joya/obstáculo puede caer encima de otra).
-- ============================================================
function generar_objetos()
joyas_total = 0
robado = 0
joyas_lista = {}
obj_lista = {}
for id, hab in pairs(HABITACIONES) do
joyas_lista[id] = {}
obj_lista[id] = {}
local nj = rnd(JOYAS_RND) + JOYAS_MIN
local no = rnd(OBJ_RND) + OBJ_MIN
for i = 1, nj do
local x = rnd(hab.maxx - hab.minx + 1) + hab.minx
local y = rnd(hab.maxy - hab.miny + 1) + hab.miny
joyas_lista[id][#joyas_lista[id] + 1] = { x=x, y=y }
mapa[id][x][y] = T_JOYA
joyas_total = joyas_total + 1
end
for i = 1, no do
local x = rnd(hab.maxx - hab.minx + 1) + hab.minx
local y = rnd(hab.maxy - hab.miny + 1) + hab.miny
obj_lista[id][#obj_lista[id] + 1] = { x=x, y=y }
if mapa[id][x][y] ~= T_JOYA then
mapa[id][x][y] = T_OBSTACULO
end
end
end
end
-- ============================================================
-- TOGGLE DE CONMUTADOR Y LUZ (líneas 1700-1740 del original)
-- ============================================================
function toggle_conmutador(x, y)
local hab = HABITACIONES[rm]
for _, e in ipairs(hab.elementos) do
if e.tipo == "conm" and e.x == x and e.y == y then
e.on = not e.on
break
end
end
hab.luz = not hab.luz -- línea 1730: luces(rm) XOR 1
sfx_conm()
end
-- ============================================================
-- RECOGIDA DE JOYAS (líneas 1980-2090 del original)
-- El bucle recoge TODAS las joyas que estén en (x,y) — fiel al
-- "GOTO 1990" del original.
-- ============================================================
function recoger_joyas_en(x, y)
local i = 1
while i <= #joyas_lista[rm] do
local j = joyas_lista[rm][i]
if j.x == x and j.y == y then
table.remove(joyas_lista[rm], i)
robado = robado + 1
sfx_joya()
else
i = i + 1
end
end
mapa[rm][x][y] = T_VACIO
end
-- ============================================================
-- CHOQUE CONTRA OBSTÁCULO (líneas 2100-2170 del original)
-- ============================================================
function chocar_obstaculo()
local ruido = rnd(RUIDO_RND)
sfx_obstaculo(ruido)
mensaje_msg = "Choque"
if ruido >= RUIDO_UMBRAL and retardo > RETARDO_MIN then
retardo = retardo - RETARDO_STEP
-- AFTER retardo*4, 1 GOSUB 310 (línea 2160 del original)
programar_perro(retardo * 4 * MS_POR_JIFFY)
end
end
-- ============================================================
-- PERRO GUARDIÁN (líneas 310-330, 1650, 2560-2670 del original)
-- ============================================================
function programar_perro(ms)
perro.pendiente = true
perro.pend_t = time() + ms
end
-- Rutina 310 del original: activa el perro si no lo estaba ya.
function activar_perro()
if perro.activo then return end
perro.activo = true
perro.rm = rm
perro.x = HABITACIONES[rm].minx
perro.y = HABITACIONES[rm].miny
perro.paso = 0
hombre_glifo = GL_LADRON_M -- línea 2570: hombre$ = chr(225)
end
function hay_perro_en(rm_, x, y)
return perro.activo and perro.rm == rm_ and perro.x == x and perro.y == y
end
-- Rutina 2560 del original: el perro se mueve 1 char hacia el jugador.
function mover_perro()
if not perro.activo then return end
if perro.rm ~= rm then return end -- solo activo en su habitación
-- Si ya está sobre el jugador, mordido (línea 2580)
if perro.x == xp and perro.y == yp then
muerto = true
return
end
if perro.x < xp then perro.x = perro.x + 1
elseif perro.x > xp then perro.x = perro.x - 1 end
if perro.y < yp then perro.y = perro.y + 1
elseif perro.y > yp then perro.y = perro.y - 1 end
if perro.x == xp and perro.y == yp then
muerto = true
return
end
sfx_ladrido()
end
-- Llamada después de cada movimiento exitoso del jugador (línea 1650).
function tick_perro_post_jugador()
if not perro.activo then return end
if perro.rm ~= rm then return end
perro.paso = (perro.paso + 1) % PERRO_RATIO
if perro.paso == 0 then
mover_perro()
end
end
-- Llamada cada frame: dispara la activación si ha vencido el AFTER.
function tick_perro_pendiente()
if perro.pendiente and time() >= perro.pend_t then
perro.pendiente = false
activar_perro()
end
end
-- ============================================================
-- SFX
-- ============================================================
function sfx_joya() sound(2000, 6) end
function sfx_obstaculo(r) sound(3000, 5 + r) end
function sfx_conm() sound(800, 3) end
function sfx_ladrido() sound(rnd(40) + 60, 6) end
-- ============================================================
-- MOVIMIENTO Y CAMBIO DE HABITACIÓN
-- Reproducción de la lógica de las líneas 1520-1900 del original.
-- ============================================================
-- Aplica la nueva posición del ladrón tras cambiar a otra habitación,
-- según la dirección por la que ha entrado (línea 1810-1840 original).
function aplicar_dir(dir)
local hab = HABITACIONES[rm]
if dir == 1 then xp = 6; yp = hab.maxy
elseif dir == 2 then xp = 6; yp = hab.miny
elseif dir == 3 then xp = hab.minx; yp = 13
elseif dir == 4 then xp = hab.maxx; yp = 13
end
end
-- Resuelve el cambio de habitación dada una dirección (1=N, 2=S, 3=E, 4=O).
-- dir(rm, dir) = -1 → bloqueado; = 0 → escapar; = n → ir a habitación n.
function cambiar_a_dir(dir)
local destino = HABITACIONES[rm].dir[dir]
if destino == -1 then return end
if destino == 0 then
escapado = true
return
end
rm = destino
aplicar_dir(dir)
end
-- Dirección estándar según el vector de movimiento (xf, yf).
-- Original: línea 1770-1780 — prioridad al eje vertical si yf != 0.
function dir_de_movimiento(xf, yf)
local dir
if xf < 0 then dir = 4
elseif xf > 0 then dir = 3 end
if yf < 0 then dir = 1
elseif yf > 0 then dir = 2 end
return dir
end
-- Parche del Pasillo para ventanas H (líneas 1860-1900 del original):
-- según xp (no xf/yf), decide la dirección de salida.
function dir_vent_h()
if xp > 5 and xp < 8 then
if yp > 13 then return 2 else return 1 end
else
if xp < 6 then return 4 else return 3 end
end
end
-- Intenta mover el ladrón en (xf, yf). Replica la cadena gol del
-- original (línea 1670 y siguientes).
function mover_jugador(xf, yf)
if xf == 0 and yf == 0 then return end
local nx, ny = xp + xf, yp + yf
-- Perro (gol=11): si la celda destino tiene el perro, mordido directo
if hay_perro_en(rm, nx, ny) then
muerto = true
return
end
local t = mapa[rm][nx][ny]
-- Conmutador (gol=1..4): toggle luz, no se mueve
if t == T_CONM_L or t == T_CONM_R then
toggle_conmutador(nx, ny)
return
end
-- Puerta H o V (gol=5,6): cambio de habitación con dirección estándar
if t == T_PUERTA_H or t == T_PUERTA_V then
local d = dir_de_movimiento(xf, yf)
if d then cambiar_a_dir(d) end
return
end
-- Ventana H (gol=7): parche del Pasillo
if t == T_VENT_H then
cambiar_a_dir(dir_vent_h())
return
end
-- Ventana V (gol=8): escape directo (línea 1910 del original)
if t == T_VENT_V then
escapado = true
return
end
-- Pared: bloqueada
if t == T_PARED then return end
-- Joya (gol=9): mover y recoger
if t == T_JOYA then
xp, yp = nx, ny
recoger_joyas_en(nx, ny)
mensaje_msg = ""
tick_perro_post_jugador()
return
end
-- Obstáculo (gol=10): no mueve, ruido
if t == T_OBSTACULO then
chocar_obstaculo()
return
end
-- Vacío: mover
xp, yp = nx, ny
mensaje_msg = ""
tick_perro_post_jugador()
end
-- ============================================================
-- MÁQUINA DE ESTADOS
-- ============================================================
function set_estado(s)
estado = s
estado_t0_ms = time()
end
function tiempo_estado_ms() return time() - estado_t0_ms end
-- Reset completo del estado de partida (equivale al RUN del original).
function reset_partida()
init_mapa()
generar_objetos()
retardo = RETARDO_INI
mensaje_msg = ""
rm = 1
xp = 6
yp = 4
escapado = false
muerto = false
hombre_glifo = GL_LADRON
perro.activo = false
perro.pendiente = false
perro.paso = 0
end
-- ----- PANTALLA PASE (líneas 720-960 del original) -----
function update_pase()
pintar_fondo()
color(COL_LADRON, COL_FONDO)
print("- P A S E -", 11, 1)
color(COL_TEXTO, COL_FONDO)
print("Puertas y ventanas para escapar", 1, 3)
-- Leyenda de simbolos (líneas 840-930 del original)
color(COL_LADRON, COL_FONDO)
print(chr(GL_LADRON).." Usted, el ladron", 2, 6)
color(COL_PUERTA, COL_FONDO)
print(chr(GL_PUERTA_H)..chr(GL_PUERTA_V).." Puertas", 2, 8)
color(COL_CONMUT, COL_FONDO)
print(chr(GL_CONM_L_OFF)..chr(GL_CONM_R_OFF).." Luces apagadas", 2, 10)
print(chr(GL_CONM_L_ON) ..chr(GL_CONM_R_ON) .." Luces encendidas", 2, 11)
color(COL_VENTANA, COL_FONDO)
print(chr(GL_VENT_H)..chr(GL_VENT_V).." Ventanas", 2, 13)
color(COL_JOYA, COL_FONDO)
print(chr(GL_JOYA).." Piedras preciosas", 2, 15)
color(COL_OBSTACULO, COL_FONDO)
print(chr(GL_OBSTACULO).." Obstaculos", 2, 17)
color(COL_PERRO, COL_FONDO)
print(chr(GL_PERRO).." El perro", 2, 19)
if (cnt() // 30) % 2 == 0 then
color(COL_LADRON, COL_FONDO)
print("Pulsa ESPACIO para empezar", 3, 22)
end
if btnp(KEY_SPACE) then
reset_partida()
set_estado(ESTADO_JUEGO)
end
end
-- ----- JUEGO -----
function update_juego()
tick_perro_pendiente()
if (cnt() - ultimo_tic) >= TICS_JUGADOR then
local xf, yf = 0, 0
if btn(KEY_UP) then yf = -1
elseif btn(KEY_DOWN) then yf = 1 end
if btn(KEY_LEFT) then xf = -1
elseif btn(KEY_RIGHT) then xf = 1 end
if xf ~= 0 or yf ~= 0 then
mover_jugador(xf, yf)
ultimo_tic = cnt()
end
end
pintar_fondo()
pintar_banner()
pintar_habitacion()
pintar_marcador()
pintar_msg()
if escapado then set_estado(ESTADO_ESCAPE) return end
if muerto then set_estado(ESTADO_MORDIDO) return end
end
-- ----- ESCAPE (líneas 1910-1970 del original) -----
function update_escape()
pintar_fondo()
color(COL_LADRON, COL_FONDO)
print("Usted ha escapado", 2, 3)
color(COL_TEXTO, COL_FONDO)
print("con", 7, 5)
if robado == joyas_total then
print("todas las", 7, 7)
end
color(COL_JOYA, COL_FONDO)
print(string.format("%2d", robado), 7, 7 + ((robado == joyas_total) and 2 or 0))
color(COL_LADRON, COL_FONDO)
print("joyas", 7, 9 + ((robado == joyas_total) and 2 or 0))
if tiempo_estado_ms() >= MS_ESCAPE then
set_estado(ESTADO_PREGUNTA)
end
end
-- ----- MORDIDO (línea 2660 del original) -----
function update_mordido()
-- Sigue mostrando el último frame del juego con "MORDIDO" encima
pintar_fondo()
pintar_banner()
pintar_habitacion()
pintar_marcador()
color(COL_MSG, COL_FONDO)
print(" MORDIDO ", 12, FILA_MSG)
if tiempo_estado_ms() >= MS_MORDIDO then
set_estado(ESTADO_PREGUNTA)
end
end
-- ----- PREGUNTA (líneas 240-300 del original) -----
function update_pregunta()
pintar_fondo()
color(COL_LADRON, COL_FONDO)
print("Quiere jugar", 4, 3)
print("otra vez?", 5, 5)
color(COL_JOYA, COL_FONDO)
print("S/N", 7, 7)
if tiempo_estado_ms() < MS_PREGUNTA_MIN then return end
if btnp(KEY_S) then
reset_partida()
set_estado(ESTADO_JUEGO)
elseif btnp(KEY_N) then
set_estado(ESTADO_FIN)
end
end
-- ----- FIN -----
function update_fin()
pintar_fondo()
color(COL_LADRON, COL_FONDO)
print("F I N", 13, 11)
end
-- ============================================================
-- BUCLE PRINCIPAL
-- ============================================================
function init()
wintitle("© 1985 Guante Blanco — David Radisic")
mode(MODO)
border(COL_FONDO)
color(COL_TEXTO, COL_FONDO)
definir_glifos()
init_mapa()
generar_objetos()
retardo = RETARDO_INI
mensaje_msg = ""
rm = 1
xp = 6
yp = 4
escapado = false
muerto = false
hombre_glifo = GL_LADRON
perro.activo = false
perro.pendiente = false
perro.paso = 0
set_estado(ESTADO_PASE)
cls()
end
function update()
if btnp(KEY_ESCAPE) then os.exit(0) end
if estado == ESTADO_PASE then update_pase()
elseif estado == ESTADO_JUEGO then update_juego()
elseif estado == ESTADO_ESCAPE then update_escape()
elseif estado == ESTADO_MORDIDO then update_mordido()
elseif estado == ESTADO_PREGUNTA then update_pregunta()
elseif estado == ESTADO_FIN then update_fin()
end
end