908 lines
31 KiB
Lua
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
|