From 8a5f97bad4d9aec6a43f40db873b118c213f17d3 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sat, 16 May 2026 10:54:07 +0200 Subject: [PATCH] Estructura DX i escena logo JAILGAMES animada --- art/jailgames.aseprite | Bin 0 -> 465 bytes art/jailgames.gif | Bin 0 -> 118 bytes pepe_pintor_dx/data/logo.lua | 21 +++ pepe_pintor_dx/glyphs.lua | 113 ++++++++++++++ pepe_pintor_dx/lib/easing.lua | 71 +++++++++ pepe_pintor_dx/lib/input.lua | 63 ++++++++ pepe_pintor_dx/lib/manager.lua | 49 +++++++ pepe_pintor_dx/lib/timing.lua | 33 +++++ pepe_pintor_dx/main.lua | 49 +++++++ pepe_pintor_dx/scenes/joc.lua | 49 +++++++ pepe_pintor_dx/scenes/logo.lua | 253 ++++++++++++++++++++++++++++++++ pepe_pintor_dx/scenes/titol.lua | 85 +++++++++++ 12 files changed, 786 insertions(+) create mode 100644 art/jailgames.aseprite create mode 100644 art/jailgames.gif create mode 100644 pepe_pintor_dx/data/logo.lua create mode 100644 pepe_pintor_dx/glyphs.lua create mode 100644 pepe_pintor_dx/lib/easing.lua create mode 100644 pepe_pintor_dx/lib/input.lua create mode 100644 pepe_pintor_dx/lib/manager.lua create mode 100644 pepe_pintor_dx/lib/timing.lua create mode 100644 pepe_pintor_dx/main.lua create mode 100644 pepe_pintor_dx/scenes/joc.lua create mode 100644 pepe_pintor_dx/scenes/logo.lua create mode 100644 pepe_pintor_dx/scenes/titol.lua diff --git a/art/jailgames.aseprite b/art/jailgames.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..1a866aed86be1fc4e2ea910a131604c92eceb100 GIT binary patch literal 465 zcmcb}$iVPmDIlU)z`zI+WDo#CT7W>H>Aya*fo)|2S|A3*>Z@aYv%0qetd9}sXch(xeHTu1IVf_t2LAuQyqnFj zZcbwOuyb40o73}u*UT5c8+=cG|I}ZbUVPsCdvoo#b8ml@J$m_PQQg(bl|MKCcYhvf Vn{{9N@0&9q|5dDIs@B^&5dcg7ZsPy| literal 0 HcmV?d00001 diff --git a/art/jailgames.gif b/art/jailgames.gif new file mode 100644 index 0000000000000000000000000000000000000000..2075a1f8b27cdaf26497a5c3a5ddc3e11302964e GIT binary patch literal 118 zcmZ?wbh9u|)L@Wfn8*MH|Ns97vJ`)^aB=}@9grwUo`K1|r+?+?xBQFeY`N9F`QD!2 z{B4g^Sq^0`I~KVq>(wqV?)^`>wws*0@b4q{tbk1|@7k?z#n~uq6svw$bZX+)uCrO& O@2vaI%~#LOU=08TGB1?? literal 0 HcmV?d00001 diff --git a/pepe_pintor_dx/data/logo.lua b/pepe_pintor_dx/data/logo.lua new file mode 100644 index 0000000..ec4b926 --- /dev/null +++ b/pepe_pintor_dx/data/logo.lua @@ -0,0 +1,21 @@ +-- Logo JAILGAMES — bitmap monocromatic 35x5 extret de art/jailgames.gif. +-- +-- '#' = pixel actiu (color del logo) +-- '.' = pixel inactiu (fons negre) +-- +-- L'escena 'logo' col.loca aquest bitmap a (origen_x, origen_y) i en +-- gestiona l'animacio (entrada per columnes + estable + fade-out). + +return { + origen_x = 2, + origen_y = 12, + width = 35, + height = 5, + files = { + "..#.###.#.#...###.###.#...#.###.###", + "..#.#.#.#.#...#...#.#.##.##.#...#..", + "..#.#.#.#.#...#.#.#.#.#.#.#.###.###", + "..#.###.#.#...#.#.###.#...#.#.....#", + "###.#.#.#.###.###.#.#.#...#.###.###", + }, +} diff --git a/pepe_pintor_dx/glyphs.lua b/pepe_pintor_dx/glyphs.lua new file mode 100644 index 0000000..a5ae03f --- /dev/null +++ b/pepe_pintor_dx/glyphs.lua @@ -0,0 +1,113 @@ +-- Glifs personalitzats compartits per totes les escenes. +-- +-- Cada glif es defineix com 8 bytes (files de 8 px, MSB esquerra). Ens +-- reservem el rang 1..15 (caracters de control que no usem) per als +-- glifs propis del joc. Aixi no pisem caracters del CP437 que despres +-- voldrem imprimir en text. + +local M = {} + +-- Codis simbolics per evitar magic numbers en la resta del codi. +-- IMPORTANT: la ROM d'ascii NO es CP437 estandar — chr(176..178) son +-- lletres gregues i chr(219) es un patro quadriculat, no els shades +-- esperats. Per aixo redefinim els shades nosaltres a un rang lliure. +M.PEPE = 1 -- contorn de pinguinet (Pepe) +M.MALO = 2 -- enemic +M.POT = 3 -- pot de pintura +M.GOTA = 4 -- gota caient +M.ARR_AMUNT = 5 +M.ARR_AVALL = 6 +M.ARR_ESQ = 7 +M.ARR_DRETA = 8 +M.SHADE_25 = 9 -- equivalent a CP437 chr(176) — trama clara +M.SHADE_50 = 10 -- equivalent a CP437 chr(177) — trama escacs +M.SHADE_75 = 11 -- equivalent a CP437 chr(178) — trama densa +M.BLOCK = 12 -- equivalent a CP437 chr(219) — bloc ple + +-- Bitmaps. Cada fila es un byte (8 px). 1 = pixel encés. +local BITMAPS = { + [M.PEPE] = { + 0x18, -- ..XX.... + 0x3C, -- .XXXX... + 0x3C, -- .XXXX... + 0x18, -- ..XX.... + 0x7E, -- .XXXXXX. + 0x18, -- ..XX.... + 0x24, -- ..X..X.. + 0x42, -- .X....X. + }, + [M.MALO] = { + 0x3C, + 0x66, + 0xDB, -- ulls + 0xFF, + 0xFF, + 0xDB, + 0x66, + 0x3C, + }, + [M.POT] = { + 0x00, + 0x7E, -- vora superior + 0x42, + 0x42, + 0x42, + 0x42, + 0x7E, + 0x00, + }, + [M.GOTA] = { + 0x00, + 0x18, + 0x18, + 0x3C, + 0x3C, + 0x18, + 0x00, + 0x00, + }, + [M.ARR_AMUNT] = { + 0x18, 0x3C, 0x7E, 0xFF, + 0x18, 0x18, 0x18, 0x00, + }, + [M.ARR_AVALL] = { + 0x00, 0x18, 0x18, 0x18, + 0xFF, 0x7E, 0x3C, 0x18, + }, + [M.ARR_ESQ] = { + 0x00, 0x10, 0x30, 0x7F, + 0x7F, 0x30, 0x10, 0x00, + }, + [M.ARR_DRETA] = { + 0x00, 0x08, 0x0C, 0xFE, + 0xFE, 0x0C, 0x08, 0x00, + }, + + -- Shades CP437 per a fades. Patrons alternats de bits. + [M.SHADE_25] = { + 0x88, 0x22, 0x88, 0x22, + 0x88, 0x22, 0x88, 0x22, + }, + [M.SHADE_50] = { + 0x55, 0xAA, 0x55, 0xAA, + 0x55, 0xAA, 0x55, 0xAA, + }, + [M.SHADE_75] = { + 0x77, 0xDD, 0x77, 0xDD, + 0x77, 0xDD, 0x77, 0xDD, + }, + [M.BLOCK] = { + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + }, +} + +function M.instala() + for codi, fila in pairs(BITMAPS) do + setchar(codi, + fila[1], fila[2], fila[3], fila[4], + fila[5], fila[6], fila[7], fila[8]) + end +end + +return M diff --git a/pepe_pintor_dx/lib/easing.lua b/pepe_pintor_dx/lib/easing.lua new file mode 100644 index 0000000..47faf8d --- /dev/null +++ b/pepe_pintor_dx/lib/easing.lua @@ -0,0 +1,71 @@ +-- Funcions de suavitzat — entrada t in [0, 1], sortida ~[0, 1]. +-- Algunes (back/bounce) poden sortir de [0,1] temporalment. +-- +-- Referencia: https://easings.net (versions clausurades algebraicament) + +local M = {} + +function M.linear(t) + return t +end + +function M.inQuad(t) + return t * t +end + +function M.outQuad(t) + local u = 1 - t + return 1 - u * u +end + +function M.inOutQuad(t) + if t < 0.5 then + return 2 * t * t + else + local u = 2 * (1 - t) + return 1 - 0.5 * u * u + end +end + +function M.inCubic(t) + return t * t * t +end + +function M.outCubic(t) + local u = 1 - t + return 1 - u * u * u +end + +-- Botet: cau, rebota, cau, rebota mes petit, ... fins quedar-se. +function M.outBounce(t) + local n = 7.5625 + local d = 2.75 + if t < 1/d then + return n * t * t + elseif t < 2/d then + local u = t - 1.5/d + return n * u * u + 0.75 + elseif t < 2.5/d then + local u = t - 2.25/d + return n * u * u + 0.9375 + else + local u = t - 2.625/d + return n * u * u + 0.984375 + end +end + +-- Overshoot (es passa del valor i torna). +function M.outBack(t) + local c1 = 1.70158 + local c3 = c1 + 1 + local u = t - 1 + return 1 + c3 * u * u * u + c1 * u * u +end + +function M.inBack(t) + local c1 = 1.70158 + local c3 = c1 + 1 + return c3 * t * t * t - c1 * t * t +end + +return M diff --git a/pepe_pintor_dx/lib/input.lua b/pepe_pintor_dx/lib/input.lua new file mode 100644 index 0000000..e9bec75 --- /dev/null +++ b/pepe_pintor_dx/lib/input.lua @@ -0,0 +1,63 @@ +-- Input modern — wrappers per btn/btnp amb un mapejat unic d'accions. +-- +-- Cada accio te una llista de tecles (perque l'usuari puga jugar amb +-- fletxes o amb O P Q A indistintament). En cada frame guardem l'estat +-- 'held' (mante premut) i 'pressed' (acaba de ser premut). Aixi les +-- escenes no han de saber res de KEY_*. + +local M = {} + +M.accions = { + amunt = { KEY_UP, KEY_Q }, + avall = { KEY_DOWN, KEY_A }, + esq = { KEY_LEFT, KEY_O }, + dreta = { KEY_RIGHT, KEY_P }, + accept = { KEY_RETURN, KEY_SPACE, KEY_KP_ENTER }, + cancel = { KEY_ESCAPE }, +} + +M.held = {} +M.pressed = {} + +local function qualsevol_btn(tecles) + for i = 1, #tecles do + if btn(tecles[i]) then return true end + end + return false +end + +local function qualsevol_btnp(tecles) + for i = 1, #tecles do + if btnp(tecles[i]) then return true end + end + return false +end + +function M.reset() + for nom, _ in pairs(M.accions) do + M.held[nom] = false + M.pressed[nom] = false + end +end + +function M.tick() + for nom, tecles in pairs(M.accions) do + M.held[nom] = qualsevol_btn(tecles) + M.pressed[nom] = qualsevol_btnp(tecles) + end +end + +-- Helpers per a les escenes — mes legibles que llegir taules directament. +function M.es_mante(accio) return M.held[accio] == true end +function M.acaba_premuda(accio) return M.pressed[accio] == true end + +-- Per a la pantalla titol/logo: «qualsevol tecla» per avançar. +local TECLES_AVANCAR = { KEY_RETURN, KEY_SPACE, KEY_KP_ENTER, + KEY_O, KEY_P, KEY_Q, KEY_A, + KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT } + +function M.qualsevol_tecla() + return qualsevol_btnp(TECLES_AVANCAR) +end + +return M diff --git a/pepe_pintor_dx/lib/manager.lua b/pepe_pintor_dx/lib/manager.lua new file mode 100644 index 0000000..47ab2fa --- /dev/null +++ b/pepe_pintor_dx/lib/manager.lua @@ -0,0 +1,49 @@ +-- Maquina d'escenes (FSM simple). +-- +-- Cada escena es una taula amb les funcions opcionals: +-- entra(servicis, args) -- en activar-se (rep els servicis globals) +-- ix() -- en eixir (per netejar timers, etc) +-- update(dt) -- per frame, amb delta en segons +-- draw() -- per frame, despres d'update() +-- +-- El motor d'ascii no separa update i draw; ho fem aci nosaltres per +-- mantindre el codi de cada escena ordenat. + +local M = {} + +M.servicis = {} -- injectat des de main.lua +M.escenes = {} -- nom -> escena +M.actual = nil -- escena activa + +function M.registra(nom, escena) + M.escenes[nom] = escena +end + +function M.canvia(nom, args) + local seguent = M.escenes[nom] + if not seguent then + log("[manager] escena desconeguda: "..tostr(nom)) + return + end + if M.actual and M.actual.ix then + M.actual.ix() + end + M.actual = seguent + if M.actual.entra then + M.actual.entra(M.servicis, args or {}) + end +end + +function M.update(dt) + if M.actual and M.actual.update then + M.actual.update(dt) + end +end + +function M.draw() + if M.actual and M.actual.draw then + M.actual.draw() + end +end + +return M diff --git a/pepe_pintor_dx/lib/timing.lua b/pepe_pintor_dx/lib/timing.lua new file mode 100644 index 0000000..6bd1883 --- /dev/null +++ b/pepe_pintor_dx/lib/timing.lua @@ -0,0 +1,33 @@ +-- Delta time helper. +-- +-- L'ascii no exposa delta directament. Calculem en cada frame el temps +-- transcorregut amb time() (ms des d'inici). El primer frame retornem 0 +-- per evitar salts grans, i clampem dt a 1/15 s per protegir-nos de +-- pauses (ESC -> consola) o stalls. + +local M = {} + +local DT_MAX = 1.0 / 15.0 -- 66 ms; un parell de frames a 30 fps + +local last_ms = 0 +local first = true + +function M.reset() + last_ms = time() + first = true +end + +function M.tick() + local now = time() + local dt = (now - last_ms) / 1000.0 + last_ms = now + if first then + first = false + return 0.0 + end + if dt > DT_MAX then dt = DT_MAX end + if dt < 0 then dt = 0 end + return dt +end + +return M diff --git a/pepe_pintor_dx/main.lua b/pepe_pintor_dx/main.lua new file mode 100644 index 0000000..0003661 --- /dev/null +++ b/pepe_pintor_dx/main.lua @@ -0,0 +1,49 @@ +-- Pepe Pintor DX — punt d'entrada +-- Carrega els moduls i delega a la maquina d'escenes. + +local BASE = "pepe_pintor_dx/" + +local manager = dofile(BASE.."lib/manager.lua") +local timing = dofile(BASE.."lib/timing.lua") +local input = dofile(BASE.."lib/input.lua") +local glyphs = dofile(BASE.."glyphs.lua") + +-- Les escenes es carreguen una sola volta i es registren al manager +-- per nom. Aixi qualsevol escena pot saltar a una altra sense passar +-- referencies a mig codi. +local escena_logo = dofile(BASE.."scenes/logo.lua") +local escena_titol = dofile(BASE.."scenes/titol.lua") +local escena_joc = dofile(BASE.."scenes/joc.lua") + +manager.registra("logo", escena_logo) +manager.registra("titol", escena_titol) +manager.registra("joc", escena_joc) + +-- Servicis injectats a totes les escenes — aixi no han de tornar +-- a fer dofile() del manager/input/timing. +manager.servicis = { + manager = manager, + input = input, + timing = timing, + glyphs = glyphs, +} + +function init() + mode(1) + border(COLOR_BLACK) + color(COLOR_WHITE, COLOR_BLACK) + cls() + + glyphs.instala() + timing.reset() + input.reset() + + manager.canvia("logo") +end + +function update() + local dt = timing.tick() + input.tick() + manager.update(dt) + manager.draw() +end diff --git a/pepe_pintor_dx/scenes/joc.lua b/pepe_pintor_dx/scenes/joc.lua new file mode 100644 index 0000000..3d69810 --- /dev/null +++ b/pepe_pintor_dx/scenes/joc.lua @@ -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 diff --git a/pepe_pintor_dx/scenes/logo.lua b/pepe_pintor_dx/scenes/logo.lua new file mode 100644 index 0000000..59f0d46 --- /dev/null +++ b/pepe_pintor_dx/scenes/logo.lua @@ -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 diff --git a/pepe_pintor_dx/scenes/titol.lua b/pepe_pintor_dx/scenes/titol.lua new file mode 100644 index 0000000..6cbc6fd --- /dev/null +++ b/pepe_pintor_dx/scenes/titol.lua @@ -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