244 lines
8.7 KiB
Lua
244 lines
8.7 KiB
Lua
-- Pepe el Pintor — port a ascii/Lua del joc original en Turbo Pascal
|
|
-- (Sergi Valor Martínez, 1999). Base: PINTOR3.PAS.
|
|
--
|
|
-- ITER 1: motor mínim — fase única (la 3 = rectangle gran), Pepe es mou amb
|
|
-- O P Q A, pinta el fons al pas, gasta el pot de pintura i el recarrega al
|
|
-- tornar a la posició inicial. Sense enemics, vides ni transicions.
|
|
|
|
-- ====================================================================
|
|
-- CONSTANTS
|
|
-- ====================================================================
|
|
MAP_W, MAP_H = 40, 25
|
|
HUD_Y0 = 25 -- HUD ocupa files 25..29
|
|
|
|
-- Glifs (codis CP437 originals). Els redefinim amb setchar per a fidelitat.
|
|
PEPE_PLE = 2 -- ☻ Pepe amb pintura (cara plena)
|
|
PEPE_BUIT = 1 -- ☺ Pepe sense pintura (cara buida)
|
|
PARED = 0xB1 -- ▒ paret
|
|
FONS = 0xDB -- █ fons (sense pintar i pintat, distingit per atribut)
|
|
POT_GLIF = 232 -- ◘ marca visual del pot
|
|
|
|
-- Constants del joc
|
|
POT_MAX = 90
|
|
PEPE_INI_X = 37 -- cambra del pot, 1 cel·la sortint del marc a la dreta (fidel al PINTOR3)
|
|
PEPE_INI_Y = 11
|
|
TICS_MOVIMENT = 5 -- frames entre intents de moviment (60/5 = 12 Hz)
|
|
TICS_OMPLIR = 3 -- frames entre +1 de pot al recarregar
|
|
|
|
-- ====================================================================
|
|
-- ESTAT
|
|
-- ====================================================================
|
|
mapa = {} -- mapa[x][y] = { tipo=, pintat=bool }
|
|
pepe = { x=PEPE_INI_X, y=PEPE_INI_Y, pinta=true }
|
|
pot = POT_MAX
|
|
total_blocs = 0 -- es calcula al carregar mapa
|
|
omplint = false -- en mig d'una recàrrega?
|
|
omplir_i = 0 -- comptador del bucle de recàrrega (fidel al Pascal)
|
|
omplir_max = 0 -- valor de (90-pot) en el moment d'entrar al pot
|
|
|
|
-- Buffer d'edges d'input direccional.
|
|
-- update() corre a 60 fps però la lògica del Pepe avança cada TICS_MOVIMENT
|
|
-- frames. Si l'usuari fa un tap curt entre dos tics, btn() al moment del tic
|
|
-- ja no detecta la pulsació. Bufferitzem només l'edge (btnp), així un tap
|
|
-- ràpid sempre genera UN moviment. El moviment "mantingut" segueix funcionant
|
|
-- per btn() directe al tic. Buffer binari: si poses 3 taps en un tic, només
|
|
-- compta com 1 — no se sobren moviments al pròxim tic.
|
|
input_buf = { up=false, down=false, left=false, right=false }
|
|
|
|
function sample_input()
|
|
if btnp(KEY_Q) then input_buf.up = true end
|
|
if btnp(KEY_A) then input_buf.down = true end
|
|
if btnp(KEY_O) then input_buf.left = true end
|
|
if btnp(KEY_P) then input_buf.right = true end
|
|
end
|
|
|
|
function reset_input()
|
|
input_buf.up, input_buf.down, input_buf.left, input_buf.right = false, false, false, false
|
|
end
|
|
|
|
-- ====================================================================
|
|
-- GLIFS CUSTOM (bitmaps CP437)
|
|
-- ====================================================================
|
|
function definir_glifs()
|
|
-- Pepe ple (CP437 chr 2 — smiley relleno)
|
|
setchar(PEPE_PLE, 0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E)
|
|
-- Pepe buit (CP437 chr 1 — smiley contorn)
|
|
setchar(PEPE_BUIT, 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E)
|
|
-- Paret ▒ (CP437 chr 177 — half-tone gris)
|
|
setchar(PARED, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11)
|
|
-- Fons █ ple
|
|
setchar(FONS, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)
|
|
-- Pot de pintura: un trapezoide amb pintura dins (sprite propi)
|
|
setchar(POT_GLIF, 0x00, 0x7E, 0x7E, 0x42, 0x66, 0x7E, 0x3C, 0x00)
|
|
end
|
|
|
|
-- ====================================================================
|
|
-- MAPA
|
|
-- ====================================================================
|
|
function tipo_a(x, y)
|
|
if x < 0 or x >= MAP_W or y < 0 or y >= MAP_H then return PARED end
|
|
return mapa[x][y].tipo
|
|
end
|
|
|
|
function carregar_mapa(num)
|
|
filein("maps/"..tostr(num)..".map", 0, MAP_W*MAP_H)
|
|
total_blocs = 0
|
|
for x = 0, MAP_W-1 do
|
|
mapa[x] = {}
|
|
for y = 0, MAP_H-1 do
|
|
local b = peek(x*MAP_H + y)
|
|
mapa[x][y] = { tipo=b, pintat=false }
|
|
if b == FONS then total_blocs = total_blocs + 1 end
|
|
end
|
|
end
|
|
-- La cel·la del pot no es pinta: la marquem com "ja pintada" i la traiem
|
|
-- del compte, així `total_blocs == 0` és assolible com a condició de victòria.
|
|
mapa[PEPE_INI_X][PEPE_INI_Y].pintat = true
|
|
total_blocs = total_blocs - 1
|
|
end
|
|
|
|
function pintar_mapa()
|
|
for x = 0, MAP_W-1 do
|
|
for y = 0, MAP_H-1 do
|
|
local c = mapa[x][y]
|
|
if c.tipo == PARED then
|
|
color(COLOR_RED, COLOR_BLACK)
|
|
print(chr(PARED), x, y)
|
|
elseif c.tipo == FONS and c.pintat then
|
|
color(COLOR_BROWN, COLOR_BLACK)
|
|
print(chr(FONS), x, y)
|
|
end
|
|
-- FONS no pintat: deixem la cel·la negra (cls); no es dibuixa res
|
|
end
|
|
end
|
|
end
|
|
|
|
function pintar_pot()
|
|
-- Dibuixem la marca visual del pot només si Pepe NO hi és damunt
|
|
if not (pepe.x == PEPE_INI_X and pepe.y == PEPE_INI_Y) then
|
|
color(COLOR_YELLOW, COLOR_BROWN)
|
|
print(chr(POT_GLIF), PEPE_INI_X, PEPE_INI_Y)
|
|
end
|
|
end
|
|
|
|
function pintar_pepe()
|
|
local glif = pepe.pinta and PEPE_PLE or PEPE_BUIT
|
|
local fons_color
|
|
if mapa[pepe.x][pepe.y].pintat then
|
|
fons_color = COLOR_BROWN
|
|
else
|
|
fons_color = COLOR_BLACK
|
|
end
|
|
color(COLOR_YELLOW, fons_color)
|
|
print(chr(glif), pepe.x, pepe.y)
|
|
end
|
|
|
|
function pintar_hud()
|
|
color(COLOR_LIGHT_GRAY, COLOR_BLUE)
|
|
local blank = " "
|
|
for i = HUD_Y0, 29 do print(blank, 0, i) end
|
|
|
|
color(COLOR_WHITE, COLOR_BLUE)
|
|
print("PINTURA "..string.format("%02d", pot).."/"..tostr(POT_MAX), 1, 26)
|
|
color(COLOR_YELLOW, COLOR_BLUE)
|
|
print("BLOCS "..string.format("%04d", total_blocs), 22, 26)
|
|
|
|
color(COLOR_LIGHT_CYAN, COLOR_BLUE)
|
|
print("O P Q A: moure", 1, 28)
|
|
end
|
|
|
|
-- ====================================================================
|
|
-- LÒGICA DEL JOC
|
|
-- ====================================================================
|
|
|
|
-- Intent de moviment en (dx, dy). Si la cel·la destí és paret, no es mou.
|
|
-- Si és fons no pintat i hi ha pintura, la pinta i descompta del pot.
|
|
function intent_moviment(dx, dy)
|
|
local nx, ny = pepe.x + dx, pepe.y + dy
|
|
local t = tipo_a(nx, ny)
|
|
if t == PARED then return end
|
|
if t ~= FONS then return end
|
|
|
|
pepe.x, pepe.y = nx, ny
|
|
local c = mapa[nx][ny]
|
|
|
|
-- La cel·la del pot no es pinta mai (es queda neta per a la recàrrega)
|
|
if nx == PEPE_INI_X and ny == PEPE_INI_Y then
|
|
sound(2000, 4)
|
|
return
|
|
end
|
|
|
|
if not c.pintat then
|
|
if pot > 0 then
|
|
c.pintat = true
|
|
pot = pot - 1
|
|
total_blocs = total_blocs - 1
|
|
sound(5000, 3)
|
|
pepe.pinta = (pot > 0)
|
|
else
|
|
sound(800, 3)
|
|
end
|
|
end
|
|
end
|
|
|
|
function tic_pepe()
|
|
if omplint then return end -- bloquejat durant l'animació de recàrrega
|
|
-- Cada direcció: edge bufferitzat (tap curt) OR tecla mantinguda al moment del tic.
|
|
if input_buf.up or btn(KEY_Q) then intent_moviment( 0, -1)
|
|
elseif input_buf.down or btn(KEY_A) then intent_moviment( 0, 1)
|
|
elseif input_buf.left or btn(KEY_O) then intent_moviment(-1, 0)
|
|
elseif input_buf.right or btn(KEY_P) then intent_moviment( 1, 0)
|
|
end
|
|
|
|
-- Arribar al pot dispara la recàrrega
|
|
if pepe.x == PEPE_INI_X and pepe.y == PEPE_INI_Y and pot < POT_MAX then
|
|
omplint = true
|
|
omplir_i = 0
|
|
omplir_max = POT_MAX - pot -- (90 - pot) en el moment d'entrar
|
|
end
|
|
end
|
|
|
|
-- Fidel al Pascal original: `for i:=0 to 90-pot do sound(i*10); pot:=pot+1;`
|
|
-- El so depèn del comptador del bucle (sempre arranca a 0 Hz), no del nivell del pot.
|
|
function tic_omplir()
|
|
if not omplint then return end
|
|
sound(omplir_i * 10, 2)
|
|
pot = pot + 1
|
|
omplir_i = omplir_i + 1
|
|
if omplir_i > omplir_max then
|
|
if pot > POT_MAX then pot = POT_MAX end
|
|
omplint = false
|
|
nosound()
|
|
end
|
|
pepe.pinta = true
|
|
end
|
|
|
|
-- ====================================================================
|
|
-- BUCLE PRINCIPAL
|
|
-- ====================================================================
|
|
function init()
|
|
mode(1)
|
|
border(COLOR_BLUE)
|
|
definir_glifs()
|
|
carregar_mapa(3)
|
|
cls()
|
|
end
|
|
|
|
function update()
|
|
-- Sample d'edges d'input cada frame (mentre el joc està actiu i no en recàrrega)
|
|
if not omplint then sample_input() end
|
|
|
|
if omplint and (cnt() % TICS_OMPLIR) == 0 then tic_omplir() end
|
|
|
|
if not omplint and (cnt() % TICS_MOVIMENT) == 0 then
|
|
tic_pepe()
|
|
reset_input() -- descartem edges acumulats després de processar el tic
|
|
end
|
|
|
|
cls()
|
|
pintar_mapa()
|
|
pintar_pot()
|
|
pintar_pepe()
|
|
pintar_hud()
|
|
end
|