Files

765 lines
26 KiB
Lua

-- ============================================================
-- BOMBARDERO — port a la fantasy console "ascii" del original
-- de Dave Town para Amstrad CPC (AMSOFT, 1984).
-- Sergi Valor, 2026.
-- ============================================================
-- ============================================================
-- CONFIGURACION (todo lo tuneable vive aqui)
-- ============================================================
-- Pantalla y layout
MODO = 1 -- mode(1) = 40x30, color por carácter
ANCHO = 40
ALTO = 30
AVION_Y_MIN = 1 -- fila más alta a la que puede llegar el avión
AVION_Y_MAX = 21 -- última fila antes de aterrizar (~ fila 21 del original)
EDIF_BASE_Y = 22 -- los edificios apoyan aquí (justo encima del suelo)
SUELO_Y = 23 -- línea visible del suelo
HUD_Y0 = 25 -- banda HUD (filas 25..27)
-- Avión (2 chars de ancho)
AVION_W = 2
AVION_X_MIN = 0
AVION_X_MAX = ANCHO - AVION_W -- 38: a partir de aquí el avión "envuelve"
AVION_X_INI = 0
AVION_Y_INI = AVION_Y_MIN + 1 -- empieza en fila 2 (como el original)
-- Edificios
EDIF_X_INI = 4
EDIF_X_FIN = 37
EDIF_X_STEP = 3 -- separación entre edificios (con avión de 2 chars deja 1 hueco)
EDIF_RANGO = 7 -- variabilidad aleatoria del tejado
-- Tejado más bajo (= edificio más alto) por nivel del jugador.
-- Nivel 0 (AS) → tejados desde fila 9 (edificios altos, casi tocando el avión).
-- Nivel 5 (principiante) → tejados desde fila 16 (edificios bajitos).
EDIF_TEJADO_BASE = 9
EDIF_TEJADO_NIVEL = 1 -- cada punto de nivel sube los tejados esta cantidad
-- Niveles y velocidad (escala "intuitiva": 0 = fácil/lento, máx = duro/rápido)
NIVEL_MIN = 0
NIVEL_MAX = 5
VEL_MIN = 0
VEL_MAX = 9
TICS_AVION_BASE = 11 -- frames entre paso de avión a vel = 0 (≈ 5 mov/s)
TICS_AVION_MIN = 2 -- a vel = 9 (≈ 30 mov/s)
TICS_BOMBA = 4 -- frames entre paso de bomba (cae más rápido que el avión a vel baja)
-- Profundidad de perforación de la bomba: en cada impacto destruye un número
-- aleatorio en [BOMBA_PERF_MIN, BOMBA_PERF_MAX] de bloques de la columna y se
-- consume (fiel al "FOR tr=1 TO INT(RND*3)+1" del original).
BOMBA_PERF_MIN = 1
BOMBA_PERF_MAX = 3
-- Subida de dificultad al aterrizar (el original bajaba `vel-20` y `nivel-1`)
SUBIDA_VEL_POR_ATERRIZAJE = 1
SUBIDA_NIVEL_POR_ATERRIZAJE = 1 -- nivel sube → edificios más altos (más difícil)
-- Puntuaciones
PUNTOS_BLOQUE = 5
PUNTOS_ATERRIZAJE = 100
PUNTOS_ATERRIZ_PEN = 10 -- restar por punto de nivel (más alto = menos puntos)
-- Vidas (el original no tenía, se acababa al primer choque)
VIDAS_INI = 1
-- Duraciones de transición (en ms, leídas con time())
MS_ATERRIZAJE = 1200 -- pausa al aterrizar antes de generar siguiente pantalla
MS_CHOQUE = 1500 -- animación de explosión del avión
MS_GAMEOVER_MIN = 800 -- antes de aceptar input en game over
-- ============================================================
-- PALETA (mapeo aproximado CPC firmware → CGA disponible)
-- ============================================================
COL_FONDO = COLOR_BLACK
COL_AVION = COLOR_LIGHT_CYAN
COL_BOMBA = COLOR_LIGHT_RED
COL_SUELO = COLOR_RED
COL_TEXTO = COLOR_LIGHT_GRAY
COL_TITULO = COLOR_YELLOW
COL_PROMPT = COLOR_LIGHT_GREEN
COL_HUD_FG = COLOR_LIGHT_GRAY
COL_HUD_BG = COLOR_BLUE
COL_RECORD = COLOR_YELLOW
COL_GAMEOVER = COLOR_LIGHT_RED
-- Colores cíclicos para edificios (el original iba con PEN base-2: cada columna
-- distinto). Se aplica módulo sobre el índice de edificio.
COL_EDIFICIOS = {
COLOR_BROWN,
COLOR_LIGHT_GREEN,
COLOR_LIGHT_BLUE,
COLOR_LIGHT_MAGENTA,
COLOR_CYAN,
COLOR_YELLOW,
COLOR_LIGHT_RED,
COLOR_GREEN,
COLOR_MAGENTA,
}
-- Ciclo de colores para la animación de explosión (mismo patrón "FOR t=1 TO 10:PEN t" del original)
COL_EXPLOSION = {
COLOR_YELLOW, COLOR_LIGHT_RED, COLOR_RED, COLOR_LIGHT_MAGENTA,
COLOR_WHITE, COLOR_YELLOW, COLOR_LIGHT_RED, COLOR_RED,
COLOR_LIGHT_MAGENTA, COLOR_WHITE,
}
-- ============================================================
-- GLIFOS (códigos que vamos a redefinir con setchar)
-- ============================================================
AVION_L = 241 -- mitad izquierda del avión (bitmap idéntico al SYMBOL del original)
AVION_R = 242 -- mitad derecha
BLOQUE = 143 -- ladrillo (el original usaba este código del char-ROM CPC)
BLOQUE2 = 244 -- variante (el original alternaba con chr(8) para superponer)
BOMBA = 252 -- bomba
EXPLO_A = 253 -- frame A de la explosión
EXPLO_B = 238 -- frame B de la explosión
-- ============================================================
-- ESTADOS (máquina de estados global)
-- ============================================================
ESTADO_TITULO = "titulo"
ESTADO_INSTRUC = "instruc"
ESTADO_NIVEL = "nivel" -- pidiendo elección de nivel (0-5)
ESTADO_VELOCIDAD = "velocidad" -- pidiendo elección de velocidad (0-9)
ESTADO_JUEGO = "juego"
ESTADO_ATERRIZA = "aterriza"
ESTADO_CHOQUE = "choque"
ESTADO_GAMEOVER = "gameover"
-- ============================================================
-- TIPOS DE CELDA DEL MAPA
-- ============================================================
T_VACIO = 0
T_EDIFICIO = 1
-- ============================================================
-- ESTADO GLOBAL DEL JUEGO
-- ============================================================
mapa = {} -- mapa[x][y] = { tipo=, color= }
avion = { x=0, y=0 }
bomba = { activa=false, fase="cayendo", x=0, y=0, restantes=0, ultimo_tic=0 }
nivel = 0 -- el que ha elegido el jugador (0..NIVEL_MAX)
nivel_act = 0 -- el que sube al aterrizar (puede crecer por encima)
vel = 0 -- velocidad (0..VEL_MAX)
puntos = 0
maxi = 0
nom_record = "AAA"
vidas = VIDAS_INI
ultimo_tic_avion = 0 -- contador de frames para temporizar al avión
estado = ESTADO_TITULO
estado_t0_ms = 0 -- time() cuando se entró al estado actual
estado_t0_fr = 0 -- cnt() cuando se entró al estado actual
-- Para la pantalla "elige nombre" tras batir récord
record_letra_idx = 1
hay_nuevo_record = false
-- ============================================================
-- UTILIDADES
-- ============================================================
function set_estado(nuevo)
estado = nuevo
estado_t0_ms = time()
estado_t0_fr = cnt()
end
function tiempo_en_estado_ms() return time() - estado_t0_ms end
function tiempo_en_estado_fr() return cnt() - estado_t0_fr end
function tics_por_paso_avion()
-- vel 0 → TICS_AVION_BASE, vel VEL_MAX → TICS_AVION_MIN
local span = TICS_AVION_BASE - TICS_AVION_MIN
local t = TICS_AVION_BASE - flr(vel * span / VEL_MAX)
if t < TICS_AVION_MIN then t = TICS_AVION_MIN end
return t
end
-- ============================================================
-- GLIFOS PERSONALIZADOS
-- ============================================================
function definir_glifos()
-- Avión: bitmaps idénticos a los SYMBOL del bombardero.bas original
setchar(AVION_L, 0x40, 0x60, 0x70, 0x7F, 0x7F, 0xEF, 0x07, 0x00)
setchar(AVION_R, 0x00, 0x32, 0x7A, 0xFE, 0xFA, 0xF2, 0xE0, 0x00)
-- Ladrillo macizo (con un patrón de mortero entre filas)
setchar(BLOQUE, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF)
-- Variante de ladrillo con junta desplazada (alterna con BLOQUE para textura)
setchar(BLOQUE2, 0xFF, 0xEF, 0x00, 0xFB, 0xFF, 0xEF, 0x00, 0xFB)
-- Bomba: óvalo con aleta
setchar(BOMBA, 0x18, 0x3C, 0x7E, 0x7E, 0x7E, 0x3C, 0x18, 0x42)
-- Explosión frame A: estrella radial
setchar(EXPLO_A, 0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99)
-- Explosión frame B: chispazo más denso
setchar(EXPLO_B, 0x42, 0xA5, 0xDB, 0x7E, 0x7E, 0xDB, 0xA5, 0x42)
end
-- ============================================================
-- MAPA Y EDIFICIOS
-- ============================================================
function init_mapa()
for x = 0, ANCHO-1 do
mapa[x] = {}
for y = 0, ALTO-1 do
mapa[x][y] = { tipo=T_VACIO, color=COL_FONDO, glifo=BLOQUE }
end
end
end
function tipo_en(x, y)
if x < 0 or x >= ANCHO or y < 0 or y >= ALTO then return T_VACIO end
return mapa[x][y].tipo
end
-- Genera la skyline en base al nivel actual de la partida (nivel_act).
-- A más nivel, edificios más bajos (= más fácil): mismo comportamiento que el
-- bucle FOR base=5 TO 15 / FOR altura=21 TO INT(RND*8+nivel) del original.
function generar_edificios()
init_mapa()
local idx_color = 0
local tejado_min = EDIF_TEJADO_BASE + nivel_act * EDIF_TEJADO_NIVEL
for x = EDIF_X_INI, EDIF_X_FIN, EDIF_X_STEP do
local tejado = rnd(EDIF_RANGO) + tejado_min
if tejado > EDIF_BASE_Y then
-- Si el "tejado calculado" cae por debajo de la base, no hay edificio
-- (mismo "bug-feature" del original que dejaba huecos en niveles fáciles).
else
local col = COL_EDIFICIOS[(idx_color % #COL_EDIFICIOS) + 1]
for y = tejado, EDIF_BASE_Y do
-- Alternar BLOQUE / BLOQUE2 para variedad de textura
local glifo = ((y - tejado) % 2 == 0) and BLOQUE or BLOQUE2
mapa[x][y] = { tipo=T_EDIFICIO, color=col, glifo=glifo }
end
end
idx_color = idx_color + 1
end
end
-- ============================================================
-- RENDER
-- ============================================================
function pintar_fondo()
color(COL_TEXTO, COL_FONDO)
cls()
end
function pintar_mapa()
for x = 0, ANCHO-1 do
for y = 0, ALTO-1 do
local c = mapa[x][y]
if c.tipo == T_EDIFICIO then
color(c.color, COL_FONDO)
print(chr(c.glifo), x, y)
end
end
end
end
function pintar_suelo()
color(COL_SUELO, COL_FONDO)
-- Una línea horizontal usando el char 154 del CPC (─ trazo medio).
-- Si no queda bien visualmente, cambiar por chr(140) o un BLOQUE bajo.
local linea = ""
for i = 1, ANCHO do linea = linea .. chr(154) end
print(linea, 0, SUELO_Y)
end
function pintar_avion()
color(COL_AVION, COL_FONDO)
print(chr(AVION_L), avion.x, avion.y)
print(chr(AVION_R), avion.x+1, avion.y)
end
function pintar_bomba()
if not bomba.activa then return end
color(COL_BOMBA, COL_FONDO)
print(chr(BOMBA), bomba.x, bomba.y)
end
function pintar_hud()
color(COL_HUD_FG, COL_HUD_BG)
local blank = ""
for i = 1, ANCHO do blank = blank .. " " end
print(blank, 0, HUD_Y0)
print(blank, 0, HUD_Y0 + 1)
print(blank, 0, HUD_Y0 + 2)
color(COL_HUD_FG, COL_HUD_BG)
print("PUNTOS "..string.format("%05d", puntos), 1, HUD_Y0)
print("MAX "..string.format("%05d", maxi).." "..nom_record, 22, HUD_Y0)
print("NIVEL "..tostr(nivel_act).." VEL "..tostr(vel), 1, HUD_Y0 + 1)
print("ESPACIO=bomba", 22, HUD_Y0 + 1)
end
-- ============================================================
-- LÓGICA DEL AVIÓN
-- ============================================================
function reset_avion()
avion.x = AVION_X_INI
avion.y = AVION_Y_INI
ultimo_tic_avion = cnt()
end
-- Devuelve true si el avión choca con un edificio en su posición actual
function avion_choca()
return tipo_en(avion.x, avion.y) == T_EDIFICIO
or tipo_en(avion.x+1, avion.y) == T_EDIFICIO
end
function avanza_avion()
avion.x = avion.x + 1
if avion.x > AVION_X_MAX then
-- Llegó al final de la pasada: si estaba en la última fila, aterriza
if avion.y >= AVION_Y_MAX then
iniciar_aterrizaje()
return
end
avion.x = AVION_X_MIN
avion.y = avion.y + 1
end
if avion_choca() then
iniciar_choque()
end
end
-- ============================================================
-- LÓGICA DE LA BOMBA
-- ============================================================
function lanzar_bomba()
if bomba.activa then return end
-- Bomba cae desde la cola del avión (mitad derecha), 1 fila por debajo
bomba.activa = true
bomba.fase = "cayendo"
bomba.x = avion.x + 1
bomba.y = avion.y + 1
bomba.restantes = 0
bomba.ultimo_tic = cnt()
sfx_lanzar()
end
function avanza_bomba()
if not bomba.activa then return end
if bomba.fase == "cayendo" then
if tipo_en(bomba.x, bomba.y + 1) == T_EDIFICIO then
-- Impacto: arrancar la fase de perforación (1..3 bloques)
bomba.fase = "explotando"
local span = BOMBA_PERF_MAX - BOMBA_PERF_MIN + 1
bomba.restantes = rnd(span) + BOMBA_PERF_MIN
else
bomba.y = bomba.y + 1
if bomba.y >= SUELO_Y then
bomba.activa = false
sfx_suelo()
end
end
return
end
-- fase == "explotando": destruye el bloque que tiene debajo, baja y resta
local ty = bomba.y + 1
if ty < SUELO_Y and tipo_en(bomba.x, ty) == T_EDIFICIO then
mapa[bomba.x][ty] = { tipo=T_VACIO, color=COL_FONDO, glifo=BLOQUE }
puntos = puntos + PUNTOS_BLOQUE
sfx_explo()
end
bomba.y = bomba.y + 1
bomba.restantes = bomba.restantes - 1
if bomba.restantes <= 0 or bomba.y >= SUELO_Y then
bomba.activa = false
end
end
-- ============================================================
-- TRANSICIONES
-- ============================================================
function iniciar_aterrizaje()
sfx_aterrizar()
puntos = puntos + max(0, PUNTOS_ATERRIZAJE - PUNTOS_ATERRIZ_PEN * nivel_act)
set_estado(ESTADO_ATERRIZA)
end
function iniciar_choque()
sfx_choque()
set_estado(ESTADO_CHOQUE)
end
function siguiente_pantalla()
nivel_act = nivel_act + SUBIDA_NIVEL_POR_ATERRIZAJE
vel = vel + SUBIDA_VEL_POR_ATERRIZAJE
if vel > VEL_MAX then vel = VEL_MAX end
bomba.activa = false
reset_avion()
generar_edificios()
end
function nueva_partida()
nivel_act = nivel
puntos = 0
vidas = VIDAS_INI
bomba.activa = false
reset_avion()
generar_edificios()
end
-- ============================================================
-- RECORDS (persistencia en fichero "records", igual que pepe_runner)
-- Formato: 5 bytes (centenas-millares..unidades) + 3 bytes nombre
-- ============================================================
function cargar_record()
local f = io.open("records", "rb")
if not f then return end
local data = f:read(8)
f:close()
if not data or #data < 8 then return end
local b = { string.byte(data, 1, 8) }
maxi = b[1]*10000 + b[2]*1000 + b[3]*100 + b[4]*10 + b[5]
if b[6] >= 32 and b[6] < 127
and b[7] >= 32 and b[7] < 127
and b[8] >= 32 and b[8] < 127 then
nom_record = string.char(b[6], b[7], b[8])
end
end
function guardar_record()
local f = io.open("records", "wb")
if not f then return end
local p = maxi
local d5 = flr(p / 10000); p = p - d5*10000
local d4 = flr(p / 1000); p = p - d4*1000
local d3 = flr(p / 100); p = p - d3*100
local d2 = flr(p / 10); p = p - d2*10
local d1 = p
f:write(string.char(d5, d4, d3, d2, d1,
string.byte(nom_record, 1) or 65,
string.byte(nom_record, 2) or 65,
string.byte(nom_record, 3) or 65))
f:close()
end
-- ============================================================
-- SFX
-- ============================================================
function sfx_lanzar() play("l0o4cdefg") end
function sfx_explo() sound(4000, 8) end
function sfx_suelo() sound(800, 10) end
function sfx_aterrizar() play("l1o4ceg>c") end
function sfx_choque() play("l1o3bal0gfedco2c") end
function sfx_select() sound(2000, 3) end
function sfx_gameover() play("l2o3bal1gfedco2c") end
-- ============================================================
-- ESTADOS — TÍTULO
-- ============================================================
function update_titulo()
pintar_fondo()
color(COL_TITULO, COL_FONDO)
print("B O M B A R D E R O", 10, 4)
color(COL_TEXTO, COL_FONDO)
print("(Dave Town / AMSOFT 1984)", 7, 6)
print("port a ascii — Sergi Valor, 2026", 3, 8)
-- Avión decorativo de muestra
color(COL_AVION, COL_FONDO)
print(chr(AVION_L), 18, 11)
print(chr(AVION_R), 19, 11)
color(COL_RECORD, COL_FONDO)
print("RECORD "..string.format("%05d", maxi).." "..nom_record, 11, 14)
if (cnt() // 30) % 2 == 0 then
color(COL_PROMPT, COL_FONDO)
print("Pulsa ESPACIO para jugar", 8, 18)
end
color(COL_TEXTO, COL_FONDO)
print("I = instrucciones", 11, 20)
if btnp(KEY_SPACE) then
sfx_select()
set_estado(ESTADO_NIVEL)
elseif btnp(KEY_I) then
sfx_select()
set_estado(ESTADO_INSTRUC)
end
end
-- ============================================================
-- ESTADOS — INSTRUCCIONES
-- ============================================================
function update_instruc()
pintar_fondo()
color(COL_TITULO, COL_FONDO)
print("- INSTRUCCIONES -", 11, 1)
color(COL_TEXTO, COL_FONDO)
print("Pilotas un avion sobre una ciudad", 3, 4)
print("desierta. Debes bombardear los", 3, 5)
print("edificios para poder aterrizar.", 3, 6)
print("El avion vuela de izquierda a", 3, 8)
print("derecha y, al llegar al borde,", 3, 9)
print("vuelve a salir por la izquierda", 3, 10)
print("una fila MAS BAJO.", 3, 11)
print("Si chocas con un edificio: muerte.", 3, 13)
print("Si aterrizas: subes de nivel y", 3, 14)
print("velocidad.", 3, 15)
print("Solo una bomba a la vez!", 3, 17)
color(COL_PROMPT, COL_FONDO)
print("ESPACIO = lanzar bomba", 3, 20)
if (cnt() // 30) % 2 == 0 then
color(COL_PROMPT, COL_FONDO)
print("Pulsa una tecla para volver", 6, 23)
end
if btnp(KEY_SPACE) or btnp(KEY_RETURN) or btnp(KEY_ESCAPE) then
sfx_select()
set_estado(ESTADO_TITULO)
end
end
-- ============================================================
-- ESTADOS — SELECCIÓN DE NIVEL
-- ============================================================
function update_nivel()
pintar_fondo()
color(COL_TITULO, COL_FONDO)
print("ELIGE NIVEL", 14, 8)
color(COL_TEXTO, COL_FONDO)
print("0 = AS (edificios altos, dificil)", 3, 11)
print("5 = PRINCIPIANTE (edif. bajos)", 3, 12)
if (cnt() // 30) % 2 == 0 then
color(COL_PROMPT, COL_FONDO)
print("Pulsa 0..5", 14, 16)
end
-- KEY_1..KEY_9 = 30..38 y KEY_0 = 39 (no contiguos como dígitos, así que va a mano)
if btnp(KEY_0) then nivel = 0; arrancar_partida_nivel(); return end
if btnp(KEY_1) then nivel = 1; arrancar_partida_nivel(); return end
if btnp(KEY_2) then nivel = 2; arrancar_partida_nivel(); return end
if btnp(KEY_3) then nivel = 3; arrancar_partida_nivel(); return end
if btnp(KEY_4) then nivel = 4; arrancar_partida_nivel(); return end
if btnp(KEY_5) then nivel = 5; arrancar_partida_nivel(); return end
end
function arrancar_partida_nivel()
sfx_select()
set_estado(ESTADO_VELOCIDAD)
end
-- ============================================================
-- ESTADOS — SELECCIÓN DE VELOCIDAD
-- ============================================================
function update_velocidad()
pintar_fondo()
color(COL_TITULO, COL_FONDO)
print("ELIGE VELOCIDAD", 12, 8)
color(COL_TEXTO, COL_FONDO)
print("0 = LENTA 9 = RAPIDA", 9, 11)
print("Nivel elegido: "..tostr(nivel), 12, 13)
if (cnt() // 30) % 2 == 0 then
color(COL_PROMPT, COL_FONDO)
print("Pulsa 0..9", 14, 16)
end
if btnp(KEY_0) then vel = 0; arrancar_juego(); return end
if btnp(KEY_1) then vel = 1; arrancar_juego(); return end
if btnp(KEY_2) then vel = 2; arrancar_juego(); return end
if btnp(KEY_3) then vel = 3; arrancar_juego(); return end
if btnp(KEY_4) then vel = 4; arrancar_juego(); return end
if btnp(KEY_5) then vel = 5; arrancar_juego(); return end
if btnp(KEY_6) then vel = 6; arrancar_juego(); return end
if btnp(KEY_7) then vel = 7; arrancar_juego(); return end
if btnp(KEY_8) then vel = 8; arrancar_juego(); return end
if btnp(KEY_9) then vel = 9; arrancar_juego(); return end
end
function arrancar_juego()
sfx_select()
nueva_partida()
set_estado(ESTADO_JUEGO)
end
-- ============================================================
-- ESTADOS — JUEGO
-- ============================================================
function update_juego()
-- Input: bomba
if btnp(KEY_SPACE) then lanzar_bomba() end
-- Tic del avión (según velocidad)
if cnt() - ultimo_tic_avion >= tics_por_paso_avion() then
ultimo_tic_avion = cnt()
avanza_avion()
if estado ~= ESTADO_JUEGO then return end -- aterrizó o chocó
end
-- Tic de la bomba (ritmo propio, independiente)
if bomba.activa and (cnt() - bomba.ultimo_tic) >= TICS_BOMBA then
bomba.ultimo_tic = cnt()
avanza_bomba()
end
-- Render
pintar_fondo()
pintar_mapa()
pintar_suelo()
pintar_bomba()
pintar_avion()
pintar_hud()
end
-- ============================================================
-- ESTADOS — ATERRIZAJE (transición con pausa de ms)
-- ============================================================
function update_aterriza()
pintar_fondo()
pintar_mapa()
pintar_suelo()
pintar_avion()
pintar_hud()
color(COL_PROMPT, COL_FONDO)
print(" ATERRIZAJE ! ", 13, 10)
if tiempo_en_estado_ms() >= MS_ATERRIZAJE then
siguiente_pantalla()
set_estado(ESTADO_JUEGO)
end
end
-- ============================================================
-- ESTADOS — CHOQUE (animación de explosión sobre el avión)
-- ============================================================
function update_choque()
pintar_fondo()
pintar_mapa()
pintar_suelo()
pintar_hud()
-- Pintar el avión con explosión parpadeante en su posición
local fr = tiempo_en_estado_fr()
local idx = (fr // 4) % #COL_EXPLOSION + 1
color(COL_EXPLOSION[idx], COL_FONDO)
local glifo = (fr // 2) % 2 == 0 and EXPLO_A or EXPLO_B
print(chr(glifo), avion.x, avion.y)
print(chr(glifo), avion.x+1, avion.y)
if tiempo_en_estado_ms() >= MS_CHOQUE then
vidas = vidas - 1
if vidas <= 0 then
-- Game over: ¿récord?
if puntos > maxi then
maxi = puntos
nom_record = "AAA"
record_letra_idx = 1
hay_nuevo_record = true
else
hay_nuevo_record = false
end
sfx_gameover()
set_estado(ESTADO_GAMEOVER)
else
reset_avion()
set_estado(ESTADO_JUEGO)
end
end
end
-- ============================================================
-- ESTADOS — GAME OVER
-- ============================================================
function update_gameover()
pintar_fondo()
pintar_mapa()
pintar_suelo()
pintar_hud()
color(COL_GAMEOVER, COL_FONDO)
print(" G A M E O V E R ", 10, 10)
color(COL_TEXTO, COL_FONDO)
print("Puntos: "..string.format("%05d", puntos), 14, 13)
if hay_nuevo_record then
color(COL_RECORD, COL_FONDO)
print("NUEVO RECORD!", 13, 15)
print("Nombre: "..nom_record, 14, 17)
if (cnt() // 30) % 2 == 0 then
color(COL_PROMPT, COL_FONDO)
print("Pulsa A..Z (3 letras)", 9, 19)
end
if tiempo_en_estado_ms() >= MS_GAMEOVER_MIN then
for sc = KEY_A, KEY_Z do
if btnp(sc) then
local lt = string.char(65 + sc - KEY_A)
nom_record = string.sub(nom_record, 1, record_letra_idx-1)
..lt..
string.sub(nom_record, record_letra_idx+1)
record_letra_idx = record_letra_idx + 1
sfx_select()
if record_letra_idx > 3 then
guardar_record()
hay_nuevo_record = false
set_estado(ESTADO_TITULO)
end
return
end
end
end
else
if (cnt() // 30) % 2 == 0 then
color(COL_PROMPT, COL_FONDO)
print("Pulsa ESPACIO para volver", 8, 18)
end
if tiempo_en_estado_ms() >= MS_GAMEOVER_MIN and btnp(KEY_SPACE) then
set_estado(ESTADO_TITULO)
end
end
end
-- ============================================================
-- BUCLE PRINCIPAL
-- ============================================================
function init()
wintitle("© 1984 Bombardero — Dave Town")
mode(MODO)
border(COL_FONDO)
color(COL_TEXTO, COL_FONDO)
definir_glifos()
init_mapa()
cargar_record()
set_estado(ESTADO_TITULO)
cls()
end
function update()
if btnp(KEY_ESCAPE) then os.exit(0) end
if estado == ESTADO_TITULO then update_titulo()
elseif estado == ESTADO_INSTRUC then update_instruc()
elseif estado == ESTADO_NIVEL then update_nivel()
elseif estado == ESTADO_VELOCIDAD then update_velocidad()
elseif estado == ESTADO_JUEGO then update_juego()
elseif estado == ESTADO_ATERRIZA then update_aterriza()
elseif estado == ESTADO_CHOQUE then update_choque()
elseif estado == ESTADO_GAMEOVER then update_gameover()
end
end