Estructura DX i escena logo JAILGAMES animada

This commit is contained in:
2026-05-16 10:54:07 +02:00
parent d8883372b0
commit 8a5f97bad4
12 changed files with 786 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
-- Escena 'joc' — gameplay.
--
-- PLACEHOLDER. La intencio es mantindre la mecanica fidel (pintar terra,
-- enemics que te perseguixen pel terra pintat, recarrega al pot) pero
-- amb tota la implementacio nova: moviment per delta time, FSM interna
-- per a fases / mort / fade-ins, etc.
--
-- Per ara nomes mostra que has entrat al joc i et deixa tornar al titol
-- amb ESC, per a poder provar la cadena d'escenes sense haver-ho fet
-- tot encara.
local M = {}
local manager, input
local temps = 0
function M.entra(servicis, _args)
manager = servicis.manager
input = servicis.input
temps = 0
end
function M.update(dt)
temps = temps + dt
if input.acaba_premuda("cancel") then
manager.canvia("titol")
end
end
function M.draw()
color(COLOR_WHITE, COLOR_BLACK)
cls()
color(COLOR_LIGHT_GREEN, COLOR_BLACK)
print("[ ESCENA JOC ]", 13, 4)
color(COLOR_LIGHT_GRAY, COLOR_BLACK)
print("Aci anira el gameplay DX.", 7, 10)
print("Pendent: motor de pintura,", 7, 12)
print("enemics, fases, FSM interna.", 7, 13)
color(COLOR_DARK_GRAY, COLOR_BLACK)
print("ESC = tornar al titol", 9, 22)
color(COLOR_DARK_GRAY, COLOR_BLACK)
print(string.format("temps: %.1fs", temps), 1, 28)
end
return M
+253
View File
@@ -0,0 +1,253 @@
-- Escena 'logo' — splash JAILGAMES amb animacio.
--
-- Tres fases:
-- ENTRADA -> les 9 lletres entren des de direccions diferents
-- (esquerra, dreta, dalt, baix), esglaonades, amb easings
-- variats. Una vegada al lloc, queden quietes.
-- ESTABLE -> 4 s totalment quiet, logo blanc ple.
-- FADE_OUT -> rampa combinada char+paleta de 8 nivells:
-- blanc -> groc -> rosa -> vermell -> magenta -> blau
-- -> gris fosc -> negre, mentre el char passa de BLOCK
-- -> SHADE_75 -> SHADE_50 -> SHADE_25 -> espai.
--
-- Premer tecla durant ENTRADA o ESTABLE salta al fade. Quan acaba el
-- fade, salta a 'titol'.
--
-- Notes sobre el fade CGA: no recorrem els 16 indexs en ordre lineal.
-- Triem una rampa tematica (warm -> cool -> dark) que es el patro
-- classic d'apagada de monitor CRT, combinada amb difuminat del char
-- per a mes pasos perceptuals (l'ull veu el conjunt com una rampa
-- d'intensitat continua).
local M = {}
local logo = dofile("pepe_pintor_dx/data/logo.lua")
local easing = dofile("pepe_pintor_dx/lib/easing.lua")
-- Timings
local T_PREVI = 1.0 -- pantalla negra abans que comencin a entrar
local T_FALL = 0.65 -- durada de l'entrada d'una lletra
local T_DELAY = 0.09 -- delay entre lletres consecutives
local T_ESTABLE = 2.0
local T_FADE = 0.4
local T_POST = 1.0 -- pantalla negra despres del fade, abans de saltar
local FASE_ENTRADA = 1
local FASE_ESTABLE = 2
local FASE_FADE_OUT = 3
local FASE_POST = 4
-- Lletres del logo com a rangs de columnes del bitmap 35-wide (1-based).
local SEGMENTS = {
{ c0 = 1, c1 = 3, nom = "J" },
{ c0 = 5, c1 = 7, nom = "A" },
{ c0 = 9, c1 = 9, nom = "I" },
{ c0 = 11, c1 = 13, nom = "L" },
{ c0 = 15, c1 = 17, nom = "G" },
{ c0 = 19, c1 = 21, nom = "A" },
{ c0 = 23, c1 = 27, nom = "M" },
{ c0 = 29, c1 = 31, nom = "E" },
{ c0 = 33, c1 = 35, nom = "S" },
}
-- Origen i easing per lletra. (dx, dy) son l'offset inicial respecte
-- a la posicio final. fn es l'easing a aplicar al progres [0,1].
-- Esquerra del logo (1-3) ve de l'esquerra; centre (4-6) ve vertical;
-- dreta (7-9) ve de la dreta. Simetric pero no monoton.
local ORIGENS = {}
local function inicialitza_origens()
ORIGENS = {
{ dx = -32, dy = 0, fn = easing.outCubic }, -- J <-
{ dx = -28, dy = 0, fn = easing.outCubic }, -- A <-
{ dx = -24, dy = 0, fn = easing.outCubic }, -- I <-
{ dx = 0, dy = -18, fn = easing.outBounce }, -- L v
{ dx = 0, dy = 20, fn = easing.outBack }, -- G ^
{ dx = 0, dy = -18, fn = easing.outBounce }, -- A v
{ dx = 24, dy = 0, fn = easing.outCubic }, -- M ->
{ dx = 28, dy = 0, fn = easing.outCubic }, -- E ->
{ dx = 32, dy = 0, fn = easing.outCubic }, -- S ->
}
end
-- Rampa de fade: 8 nivells. Construida a entra() perque depen de
-- glyphs.* (no disponibles al carregar el modul).
local RAMPA
local NIVELL_MAX = 7
local function inicialitza_rampa(glyphs)
RAMPA = {
[7] = { codi = glyphs.BLOCK, ink = COLOR_WHITE },
[6] = { codi = glyphs.BLOCK, ink = COLOR_YELLOW },
[5] = { codi = glyphs.SHADE_75, ink = COLOR_LIGHT_RED },
[4] = { codi = glyphs.SHADE_75, ink = COLOR_RED },
[3] = { codi = glyphs.SHADE_50, ink = COLOR_MAGENTA },
[2] = { codi = glyphs.SHADE_50, ink = COLOR_BLUE },
[1] = { codi = glyphs.SHADE_25, ink = COLOR_BLUE },
[0] = { codi = 32, ink = COLOR_BLACK },
}
end
local manager, input, glyphs
local lletres = {}
local fase = FASE_ENTRADA
local temps = 0
local nivell_actual = NIVELL_MAX
local total_entrada = 0
local function construeix_lletres()
lletres = {}
for i, seg in ipairs(SEGMENTS) do
local px = {}
for r = 1, logo.height do
local fila = logo.files[r]
for c = seg.c0, seg.c1 do
if string.sub(fila, c, c) == "#" then
px[#px + 1] = { rx = c - seg.c0, ry = r - 1 }
end
end
end
local origen = ORIGENS[i]
lletres[i] = {
base_x = logo.origen_x + (seg.c0 - 1),
base_y = logo.origen_y,
pixels = px,
ox = origen.dx,
oy = origen.dy,
dx = origen.dx,
dy = origen.dy,
fn = origen.fn,
delay = T_PREVI + (i - 1) * T_DELAY,
arribada = false, -- una vegada true, ja no toquem dx/dy
}
end
total_entrada = T_PREVI + (#SEGMENTS - 1) * T_DELAY + T_FALL
end
function M.entra(servicis, _args)
manager = servicis.manager
input = servicis.input
glyphs = servicis.glyphs
fase = FASE_ENTRADA
temps = 0
nivell_actual = NIVELL_MAX
inicialitza_origens()
inicialitza_rampa(glyphs)
construeix_lletres()
color(COLOR_WHITE, COLOR_BLACK)
cls()
end
local function salta_a_fade()
fase = FASE_FADE_OUT
temps = 0
-- forcem que totes les lletres estiguin a casa, no es mouen mes
for _, l in ipairs(lletres) do
l.dx = 0
l.dy = 0
l.arribada = true
end
end
local function update_entrada()
local totes_acabades = true
for _, l in ipairs(lletres) do
if not l.arribada then
local tl = temps - l.delay
if tl < 0 then
l.dx, l.dy = l.ox, l.oy
totes_acabades = false
elseif tl < T_FALL then
local p = tl / T_FALL
local e = l.fn(p)
l.dx = l.ox * (1 - e)
l.dy = l.oy * (1 - e)
totes_acabades = false
else
l.dx, l.dy = 0, 0
l.arribada = true
end
end
end
if totes_acabades then
fase = FASE_ESTABLE
temps = 0
end
end
local function update_estable()
-- intencionadament buit — lletres quietes
if temps >= T_ESTABLE then
salta_a_fade()
end
end
local function update_fade()
local p = temps / T_FADE
if p >= 1 then
nivell_actual = 0
fase = FASE_POST
temps = 0
return
end
-- Mapa el progres [0,1] al nivell [NIVELL_MAX..0] de la rampa.
nivell_actual = math.floor(NIVELL_MAX * (1 - p) + 0.5)
if nivell_actual < 0 then nivell_actual = 0 end
end
local function update_post()
if temps >= T_POST then
manager.canvia("titol")
end
end
function M.update(dt)
temps = temps + dt
if (fase == FASE_ENTRADA or fase == FASE_ESTABLE) and input.qualsevol_tecla() then
salta_a_fade()
return
end
if fase == FASE_ENTRADA then
update_entrada()
elseif fase == FASE_ESTABLE then
update_estable()
elseif fase == FASE_FADE_OUT then
update_fade()
else
update_post()
end
end
function M.draw()
color(COLOR_WHITE, COLOR_BLACK)
cls()
local nivell
if fase == FASE_FADE_OUT or fase == FASE_POST then
nivell = nivell_actual
else
nivell = NIVELL_MAX
end
if nivell <= 0 then return end
local conf = RAMPA[nivell]
color(conf.ink, COLOR_BLACK)
for _, l in ipairs(lletres) do
local ox = math.floor(l.dx + 0.5)
local oy = math.floor(l.dy + 0.5)
for _, p in ipairs(l.pixels) do
local x = l.base_x + p.rx + ox
local y = l.base_y + p.ry + oy
if x >= 0 and x < 40 and y >= 0 and y < 30 then
print(chr(conf.codi), x, y)
end
end
end
end
return M
+85
View File
@@ -0,0 +1,85 @@
-- Escena 'titol' — menu principal.
--
-- Tot esta basat en delta time: parpelleig de cursor, animacio del
-- subtitol, etc. La llista d'opcions es extensible: nomes cal afegir
-- una entrada amb un text i una funcio.
local M = {}
local TITOL = "PEPE EL PINTOR DX"
local SUBTITOL = "port lliure de PINTOR3 (Sergi Valor, 1999)"
local PERIODE_CURSOR = 0.35
local manager, input
local temps = 0
local cursor = 1
local opcions = {}
local function fes_opcions()
opcions = {
{
text = "COMENCAR PARTIDA",
accio = function() manager.canvia("joc") end,
},
-- A futur: CONTROLS, CREDITS, OPCIONS, EIXIR...
}
end
function M.entra(servicis, _args)
manager = servicis.manager
input = servicis.input
temps = 0
cursor = 1
fes_opcions()
end
function M.update(dt)
temps = temps + dt
if input.acaba_premuda("amunt") then
cursor = cursor - 1
if cursor < 1 then cursor = #opcions end
end
if input.acaba_premuda("avall") then
cursor = cursor + 1
if cursor > #opcions then cursor = 1 end
end
if input.acaba_premuda("accept") then
local op = opcions[cursor]
if op and op.accio then op.accio() end
end
end
local function centra(text, y, ink, paper)
local x = math.floor((40 - #text) / 2)
color(ink, paper)
print(text, x, y)
end
function M.draw()
color(COLOR_WHITE, COLOR_BLACK)
cls()
-- Marc decoratiu — barres horitzontals de pintura.
color(COLOR_LIGHT_RED, COLOR_BLACK)
print(string.rep(chr(220), 40), 0, 1)
print(string.rep(chr(223), 40), 0, 27)
centra(TITOL, 6, COLOR_YELLOW, COLOR_BLACK)
centra(SUBTITOL, 8, COLOR_LIGHT_GRAY, COLOR_BLACK)
-- Menu d'opcions
local y0 = 14
for i, op in ipairs(opcions) do
local actiu = (i == cursor)
local visible_cursor = math.floor(temps / PERIODE_CURSOR) % 2 == 0
local marca = (actiu and visible_cursor) and chr(16) or " "
local text = marca .. " " .. op.text
local ink = actiu and COLOR_LIGHT_GREEN or COLOR_LIGHT_GRAY
centra(text, y0 + (i - 1) * 2, ink, COLOR_BLACK)
end
centra("amunt/avall + enter", 24, COLOR_DARK_GRAY, COLOR_BLACK)
end
return M