diff --git a/pepe_runner.lua b/pepe_runner.lua index d3981cc..b648d60 100644 --- a/pepe_runner.lua +++ b/pepe_runner.lua @@ -1,5 +1,5 @@ -- Pepe Runner — port a ascii/Lua del joc original en Turbo Pascal (JailDesigner, 2000) --- Fase 1: render estatic d'un mapa +-- Fase 3: gravetat, escales, cordes, forats -- Codis CP437 dels sprites del joc original (de TIPOS.PAS) BUIT = 0 @@ -20,44 +20,36 @@ COL_ESCALA = COLOR_LIGHT_GRAY -- 7 COL_CORDA = COLOR_LIGHT_GRAY -- 7 COL_BUIT = COLOR_BLACK -- 0 --- Mida del mapa (del joc original) -MAP_W = 40 -MAP_H = 25 +-- Estats del Pepe (com en TIPOS.PAS) +NORMAL = 0 +CAENT = 4 --- Estat -mapa = {} -- mapa[x][y] = { tipo=, color=, temps= } +-- Constants del joc +MAP_W = 40 +MAP_H = 25 +BLOC_OUT = 100 -- temps que dura un forat obert (de TIPOS.PAS) +TICS = 6 -- frames per tick de joc (60fps / 6 = 10 Hz) + +-- Estat global +mapa = {} -- mapa[x][y] = { tipo=, color=, temps= } level = 1 -pepe = { x=19, y=23, dibuix=PEPE_C, color=COLOR_WHITE, vides=0, estat=0 } +pepe = { x=19, y=23, dibuix=PEPE_C, color=COLOR_WHITE, vides=0, estat=NORMAL } +score = 0 +diners_pantalla = 0 function definir_glifs() - -- Cel·la buida (sobreescrivim el glif 0 del ROM, que no es buit) setchar(BUIT, 0,0,0,0,0,0,0,0) - - -- Pedra (█) — bloc solid setchar(PEDRA, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF) - - -- Diners ($) — signe dolar classic setchar(DINERS, 0x18,0x3E,0x60,0x3C,0x06,0x7C,0x18,0x00) - - -- Escala (═) — doble linia horitzontal setchar(ESCALA, 0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00) - - -- Corda (─) — linia horitzontal simple setchar(CORDA, 0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00) - - -- Blocs degradats (per al fade dels forats) - setchar(BLOC1, 0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11) -- ░ light - setchar(BLOC2, 0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55) -- ▒ medium - setchar(BLOC3, 0xBB,0xEE,0xBB,0xEE,0xBB,0xEE,0xBB,0xEE) -- ▓ dark - - -- Pepe (cara feliç tipus CP437 char 2 ☻) + setchar(BLOC1, 0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11) + setchar(BLOC2, 0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55) + setchar(BLOC3, 0xBB,0xEE,0xBB,0xEE,0xBB,0xEE,0xBB,0xEE) setchar(PEPE_C, 0x7E,0x81,0xA5,0x81,0xBD,0x99,0x81,0x7E) - - -- Enemic — una 'X' marcada setchar(MALO_C, 0x00,0xC3,0x66,0x3C,0x18,0x3C,0x66,0xC3) end --- Retorna el color associat a un tipus de cel·la function color_de(tipo) if tipo == PEDRA then return COL_PEDRA end if tipo == DINERS then return COL_DINERS end @@ -66,16 +58,21 @@ function color_de(tipo) return COL_BUIT end --- Carrega un mapa des d'un fitxer .map (1000 bytes, column-major: byte[x*25+y]) --- Estrategia: filein() a l'adreca 0 (sobreescriu char_screen), llegim amb peek() --- a una taula Lua, i despres fem cls() per netejar la pantalla. +-- Helper segur per llegir el tipus d'una cel·la (fora de mapa = pedra virtual) +function tipo_a(x, y) + if x < 0 or x >= MAP_W or y < 0 or y >= MAP_H then return PEDRA end + return mapa[x][y].tipo +end + function carregar_mapa(num) filein("maps/"..tostr(num)..".map", 0, MAP_W*MAP_H) + diners_pantalla = 0 for x = 0, MAP_W-1 do mapa[x] = {} for y = 0, MAP_H-1 do local tipo = peek(x*MAP_H + y) mapa[x][y] = { tipo=tipo, color=color_de(tipo), temps=-1 } + if tipo == DINERS then diners_pantalla = diners_pantalla + 1 end end end end @@ -97,32 +94,140 @@ function pintar_pepe() print(chr(pepe.dibuix), pepe.x, pepe.y) end --- Pepe pot entrar a una cel·la si no es pedra (de moment). --- Mes endavant afegirem regles d'escala, cuerda, gravetat, etc. -function pot_entrar(x, y) - if x < 0 or x >= MAP_W or y < 0 or y >= MAP_H then return false end - return mapa[x][y].tipo ~= PEDRA +-- Marca una cel·la com a forat (sols si actualment es pedra) +function foradar(x, y) + if tipo_a(x, y) == PEDRA then + mapa[x][y].temps = BLOC_OUT + end end -function mou_pepe() - -- Tecles originals: Q=amunt, A=avall, O=esquerra, P=dreta - if btnp(KEY_Q) and pot_entrar(pepe.x, pepe.y-1) then pepe.y = pepe.y - 1 end - if btnp(KEY_A) and pot_entrar(pepe.x, pepe.y+1) then pepe.y = pepe.y + 1 end - if btnp(KEY_O) and pot_entrar(pepe.x-1, pepe.y) then pepe.x = pepe.x - 1 end - if btnp(KEY_P) and pot_entrar(pepe.x+1, pepe.y) then pepe.x = pepe.x + 1 end +-- Pot Pepe cavar a esquerra/dreta? Condicions del MouPepe original: +-- - la cel·la diagonal-baix ha de ser pedra (per a obrir-hi forat) +-- - la cel·la lateral no pot ser pedra (per a que Pepe s'hi puga assomar) +-- - Pepe ha d'estar en estat normal (no caent) +function pot_cavar(dx) + return pepe.estat == NORMAL + and tipo_a(pepe.x+dx, pepe.y+1) == PEDRA + and tipo_a(pepe.x+dx, pepe.y) ~= PEDRA +end + +-- Tic de joc del Pepe: input de moviment, gravetat, recollir diners, emparedat +function tic_pepe() + local actual = tipo_a(pepe.x, pepe.y) + local sotto = tipo_a(pepe.x, pepe.y+1) + + -- Moviment vertical: Q/A (com en RUNNER.PAS, son if/else) + if btn(KEY_Q) then + if actual == ESCALA then pepe.y = pepe.y - 1 end + elseif btn(KEY_A) then + if sotto == ESCALA or sotto == BUIT or sotto == DINERS then + pepe.y = pepe.y + 1 + end + end + + -- Moviment horitzontal: O/P (no es pot moure si esta caent) + if btn(KEY_O) then + if tipo_a(pepe.x-1, pepe.y) ~= PEDRA and pepe.estat ~= CAENT then + pepe.x = pepe.x - 1 + end + elseif btn(KEY_P) then + if tipo_a(pepe.x+1, pepe.y) ~= PEDRA and pepe.estat ~= CAENT then + pepe.x = pepe.x + 1 + end + end + + -- Si no passa res especial, estat = normal (gravetat pot canviar-ho mes avall) + pepe.estat = NORMAL + + -- Emparedat: si la cel·la actual s'ha tornat pedra, Pepe mor + if tipo_a(pepe.x, pepe.y) == PEDRA then + mort_pepe() + return + end + + -- Recollir diners + if tipo_a(pepe.x, pepe.y) == DINERS then + mapa[pepe.x][pepe.y].tipo = BUIT + score = score + 1 + diners_pantalla = diners_pantalla - 1 + end + + -- Bordes X + if pepe.x < 0 then pepe.x = 0 end + if pepe.x > MAP_W-1 then pepe.x = MAP_W-1 end + + -- Gravetat: si la cel·la actual es buit/diners i la de baix no es escala/pedra → cau + actual = tipo_a(pepe.x, pepe.y) + sotto = tipo_a(pepe.x, pepe.y+1) + if (sotto ~= ESCALA and sotto ~= PEDRA) + and (actual == BUIT or actual == DINERS) then + pepe.y = pepe.y + 1 + pepe.estat = CAENT + end + + -- Bordes Y + if pepe.y < 0 then pepe.y = 0 end + if pepe.y > MAP_H-1 then pepe.y = MAP_H-1 end +end + +-- De moment, mort = respawn (vides i game over van en la Fase 5) +function mort_pepe() + pepe.vides = pepe.vides - 1 + pepe.x = 19 + pepe.y = 23 + pepe.estat = NORMAL +end + +-- Anima els forats: decrementa temps i cambia el tipus segons la fase +-- (idem case statement de CheckMapa al RUNNER.PAS) +function check_mapa() + for x = 0, MAP_W-1 do + for y = 0, MAP_H-1 do + local c = mapa[x][y] + local t = c.temps + if t == 0 then + c.temps = -1 + c.tipo = PEDRA + c.color = COL_PEDRA + elseif t == 1 or t == BLOC_OUT-1 then + c.tipo = BLOC3; c.color = COL_PEDRA; c.temps = t - 1 + elseif t == 2 or t == BLOC_OUT-2 then + c.tipo = BLOC2; c.color = COL_PEDRA; c.temps = t - 1 + elseif t == 3 or t == BLOC_OUT-3 then + c.tipo = BLOC1; c.color = COL_PEDRA; c.temps = t - 1 + elseif t == 4 or t == BLOC_OUT-4 then + c.tipo = BUIT; c.color = COL_BUIT; c.temps = t - 1 + elseif t > 0 then + c.temps = t - 1 + end + -- t == -1 → idle, no fer res + end + end end function init() - mode(1) -- 40x30 chars, color per cel·la + mode(1) border(COLOR_BLUE) color(COLOR_LIGHT_GRAY, COLOR_BLACK) definir_glifs() carregar_mapa(level) - cls() -- neteja despres del filein (que ha escrit a char_screen) + cls() end function update() - mou_pepe() + -- Cavar es immediat (un sol forat per pulsacio) + if pepe.estat == NORMAL then + if btnp(KEY_SPACE) and pot_cavar(-1) then foradar(pepe.x-1, pepe.y+1) end + if btnp(KEY_M) and pot_cavar( 1) then foradar(pepe.x+1, pepe.y+1) end + end + + -- Logica del joc: cada TICS frames + if (cnt() % TICS) == 0 then + tic_pepe() + check_mapa() + end + + -- Render: cada frame cls() pintar_mapa() pintar_pepe()