Fase 1: estructura base i les 5 habitacions

This commit is contained in:
2026-05-18 13:35:03 +02:00
parent 4a2abf46d3
commit 7fe5bab9a0
5 changed files with 997 additions and 1 deletions
+8 -1
View File
@@ -31,7 +31,7 @@ $RECYCLE.BIN/
.LSOverride
# Icon must end with two \r
Icon
Icon
# Thumbnails
._*
@@ -67,3 +67,10 @@ Temporary Items
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
# ---> Guante Blanco
# Binari de l'intèrpret ascii (es compila a part)
/ascii
# Fitxer de rècord generat pel joc
/records
+275
View File
@@ -0,0 +1,275 @@
# ASCII — Referencia del intérprete Lua
Documento extraído del código fuente en `c:/mingw/gitea/ascii/` (principalmente `ascii.cpp`, `lua.cpp`, `play.cpp`, `ascii.h`). Sirve como guía para portar Pepe Runner desde Turbo Pascal a Lua.
> Versión analizada del intérprete: v0.6.1 aprox (según el mensaje del boot ROM en `lua.cpp`).
---
## 1. Modelo de ejecución
Cada juego/programa es **un solo fichero `.lua`** que define dos funciones globales:
```lua
function init()
-- se llama una sola vez al arrancar
end
function update()
-- se llama cada frame (~60 FPS, vsync)
end
```
- El intérprete se invoca como `ascii.exe nombre_juego.lua`. Si no se pasa argumento, intenta cargar `game.lua`.
- También se puede **arrastrar y soltar** un `.lua` sobre la ventana para cargarlo.
- **F5** reinicia el juego (re-llama a `init()` y vuelve a empezar el bucle).
- **ESC** pausa la ejecución y abre una consola de depuración (`> ` prompt). Los comandos `run` y `cont` la cierran. Comandos prefijados con `?` evalúan e imprimen (ej.: `?1+1`).
- Se usa la versión estándar de Lua que está vendorizada en `ascii/lua/` (con `luaL_openlibs`), así que están disponibles `string`, `math`, `table`, etc.
---
## 2. Modos de pantalla — `mode(n)`
| Modo | Resolución carácter | Resolución pixel | Notas |
|------|--------------------:|-----------------:|-------|
| 0 | 80 × 30 | 640 × 240 | Color único global (no por-carácter) — usado para depuración / texto. `current_color` aplica a toda la pantalla. |
| 1 | 40 × 30 | 320 × 240 | **Modo por defecto.** Color por carácter. |
| 2 | 20 × 15 | 160 × 120 | Mitad de resolución. Cómodo para tiles grandes (ej.: sokoban). |
| 3 | 32 × 24 | 256 × 192 | Estilo "ZX Spectrum" (con bordes anchos). |
Cada carácter es **8×8 píxeles**. Los gráficos son texto coloreado, no píxeles libres — el "lienzo" es una matriz de celdas (carácter + atributo de color).
---
## 3. Paleta de colores (16, CGA/EGA)
Constantes Lua predefinidas (`lua.cpp` líneas 610-625):
| Código | Constante | Aprox. |
|-------:|------------------------|---------------|
| 0 | `COLOR_BLACK` | #000000 |
| 1 | `COLOR_BLUE` | #0000AA |
| 2 | `COLOR_GREEN` | #00AA00 |
| 3 | `COLOR_CYAN` | #00AAAA |
| 4 | `COLOR_RED` | #AA0000 |
| 5 | `COLOR_MAGENTA` | #AA00AA |
| 6 | `COLOR_BROWN` | #AA5500 |
| 7 | `COLOR_LIGHT_GRAY` | #AAAAAA |
| 8 | `COLOR_DARK_GRAY` | #555555 |
| 9 | `COLOR_LIGHT_BLUE` | #5555FF |
| 10 | `COLOR_LIGHT_GREEN` | #55FF55 |
| 11 | `COLOR_LIGHT_CYAN` | #55FFFF |
| 12 | `COLOR_LIGHT_RED` | #FF5555 |
| 13 | `COLOR_LIGHT_MAGENTA` | #FF55FF |
| 14 | `COLOR_YELLOW` | #FFFF55 |
| 15 | `COLOR_WHITE` | #FFFFFF |
El atributo de color de una celda es 1 byte: nibble bajo = INK (tinta), nibble alto = PAPER (fondo).
---
## 4. API — Funciones expuestas a Lua
### Pantalla y color
| Función | Descripción |
|---------|-------------|
| `mode(n)` | Cambia modo de pantalla (0-3) y hace cls. |
| `cls([chr=32])` | Limpia con el carácter dado (32 = espacio). En modo ≠0 además rellena el color attr. |
| `ink(c)` | Color de tinta (0-15). |
| `paper(c)` | Color de fondo (0-15). |
| `border(c)` | Color del borde de la ventana. |
| `color(ink, paper, [border])` | Combina los tres. |
| `locate(x, y)` | Posiciona cursor en celda (x, y). |
| `print(str, [x, y])` | Imprime `str` (sin salto de línea). Si se dan x,y, primero hace `locate`. |
| `crlf()` | CR + LF (mueve cursor a inicio de siguiente línea). |
### Entrada
| Función | Descripción |
|---------|-------------|
| `btn(k)` | `true` si la tecla `k` está pulsada *en este frame* (estado SDL_GetKeyboardState). |
| `btnp(k)` | `true` solo en el frame en que la tecla se pulsa (edge). |
| `mousex()` / `mousey()` | Posición del ratón en **coordenadas de carácter** (ya escalado al modo). |
| `mousewheel()` | Delta de la rueda en este frame. |
| `mousebutton(i)` | `true` si el botón `i` está pulsado (1=izq, 2=medio, 3=der; usa `SDL_BUTTON(i)`). |
> **Nota**: `whichbtn()` está declarado en `ascii.h` y existe en C++, pero **no está expuesto a Lua** (no aparece en los `lua_setglobal` de `lua.cpp`). Para detectar qué tecla se ha pulsado en un frame hay que iterar con `btnp()` sobre las constantes `KEY_*`.
> **Bug de `tostr` con negativos**: la implementación de `tostr()` en `lua.cpp` (función `intToStr`) hace `(x % 10) + '0'` que con `x = -1` produce `-1 + 48 = 47`, o sea `'/'`. Por tanto `tostr(-1)` devuelve `"/"`, `tostr(-2)` devuelve `"."`, etc. Si vas a imprimir un número que puede ser negativo, usa `string.format("%d", n)` (que sí maneja signo) o clampa con `max(0, n)` antes de pasar a `tostr`.
Códigos de tecla — todos definidos como globales `KEY_*` en Lua. Lista completa (de `lua.cpp` 502-608):
```
KEY_A..KEY_Z = 4..29
KEY_1..KEY_0 = 30..39 (1=30, 2=31, ..., 9=38, 0=39)
KEY_RETURN=40 KEY_ESCAPE=41 KEY_BACKSPACE=42 KEY_TAB=43 KEY_SPACE=44
KEY_MINUS=45 KEY_EQUALS=46 KEY_LEFTBRACKET=47 KEY_RIGHTBRACKET=48
KEY_BACKSLASH=49 KEY_NONUSHASH=50 KEY_SEMICOLON=51 KEY_APOSTROPHE=52
KEY_GRAVE=53 KEY_COMMA=54 KEY_PERIOD=55 KEY_SLASH=56 KEY_CAPSLOCK=57
KEY_F1..KEY_F12 = 58..69
KEY_PRINTSCREEN=70 KEY_SCROLLLOCK=71 KEY_PAUSE=72
KEY_INSERT=73 KEY_HOME=74 KEY_PAGEUP=75 KEY_DELETE=76 KEY_END=77 KEY_PAGEDOWN=78
KEY_RIGHT=79 KEY_LEFT=80 KEY_DOWN=81 KEY_UP=82
KEY_NUMLOCKCLEAR=83 KEY_KP_DIVIDE=84 KEY_KP_MULTIPLY=85 KEY_KP_MINUS=86 KEY_KP_PLUS=87 KEY_KP_ENTER=88
KEY_KP_1..KEY_KP_0 = 89..98 KEY_KP_PERIOD=99
KEY_NONUSBACKSLASH=100 KEY_APPLICATION=101
KEY_LCTRL=224 KEY_LSHIFT=225 KEY_LALT=226 KEY_LGUI=227
KEY_RCTRL=228 KEY_RSHIFT=229 KEY_RALT=230 KEY_RGUI=231
```
(Son los SDL2 scancodes.)
### Matemáticas
`abs(x)`, `ceil(x)`, `flr(x)`, `sgn(x)`, `sin(x)`, `cos(x)`, `atan2(dx, dy)`, `sqrt(x)`, `max(a,b)`, `min(a,b)`, `mid(a,b,c)` (devuelve el del medio, equivalente a `clamp`).
`rnd(n)` devuelve un entero en `[0, n-1]` (`rand()%n`). `srand(seed)` siembra el RNG.
### Strings
| Función | Descripción |
|---------|-------------|
| `tostr(v)` | Convierte valor a string. Soporta nil, function, table (formato `{k=v,...}`), number, boolean, string. |
| `strlen(s)` | Longitud en bytes. |
| `ascii(s, i)` | Código del byte en índice `i` (0-based). |
| `chr(n)` | String de un solo carácter cuyo código es `n`. |
| `substr(s, start, length)` | Subcadena. |
> Nota: Lua estándar también está disponible, así que `string.format`, `string.sub`, etc., funcionan. Pero los demos usan estas helpers.
### Memoria
| Función | Descripción |
|---------|-------------|
| `peek(addr)` | Lee 1 byte de la VRAM/memoria (0..0x1FFF). |
| `poke(addr, val)` | Escribe 1 byte. |
| `memcpy(dst, src, size)` | Copia bytes en la memoria del fantasy console. |
| `setchar(idx, b0..b7)` | Define los 8 bytes del carácter `idx` en el char-ROM (sobrescribe la fuente). |
**Mapa de memoria** (8 KB total, `mem[8192]`):
- `0x0000` (0): char_screen (matriz de códigos de carácter por celda)
- Tras char_screen viene color_screen (offset = `screen_width * screen_height`)
- `0x0A00` (2560 = `MEM_CHAR_OFFSET`): char-ROM (definición de glifos, 8 bytes por carácter, 256 chars = 2048 bytes)
- `0x1200` (4608 = `MEM_BOOT_OFFSET`): zona de boot/recursos de ROM
Para los modos 1 y 2 los color_screen offsets son 1200 y 300 respectivamente; en modo 3 es 768; en modo 0 no hay color_screen por celda (color global).
### Audio
**Sonido simple:**
- `sound(freq, len)` — onda cuadrada a `freq` Hz durante `len` (en algo similar a centésimas de segundo; `audio_len = len*44.1`).
- `nosound()` — silencio inmediato.
**Mini-lenguaje MML — `play(str)`:**
Sintaxis tipo BASIC `PLAY` / MML. Tokens (case-sensitive, minúsculas):
| Token | Significado |
|-------|-------------|
| `c d e f g a b` | Nota. Acepta sufijo `#` o `+` (sostenido) o `-` (bemol). Luego dígito 0-9 para duración. |
| `r` | Silencio. Acepta dígito de duración. |
| `o<0-7>` | Octava absoluta. |
| `>` `<` | Sube / baja octava. |
| `l<0-9>` | Longitud por defecto para notas sin duración. |
| `v<0-9>` | Volumen (se traduce a `(d-0)<<4`). |
| `t<0-9>` | Tempo. |
Duraciones: índice 0-9 → tabla `{313,625,938,1250,1875,2500,3750,5000,7500,10000}` (de "redonda" a "trentaidosava", aproximadamente).
Ejemplo (de `breakout.lua`):
```lua
play("l0o3bagfedc") -- escala descendente como sonido de game-over
play("o5l0c") -- pitido agudo (rebote)
```
### Ficheros y portapapeles
| Función | Descripción |
|---------|-------------|
| `load([filename])` | Reinicia y carga otro `.lua` (o el mismo si filename=nil). |
| `fileout(name, addr, size)` | Vuelca `size` bytes de memoria a un binario. |
| `filein(name, addr, size)` | Carga un binario a memoria. |
| `toclipboard(str)` | Copia al portapapeles del SO. |
| `fromclipboard()` | Lee del portapapeles (máx 1023 chars). |
### Utilidades de tiempo / frame
| Función | Descripción |
|---------|-------------|
| `time()` | Milisegundos desde inicio (`SDL_GetTicks()`). |
| `cnt()` | Contador de frames desde el último `rst()`. |
| `rst()` | Resetea el contador de frames a 0. |
| `log(str)` | Imprime en la consola de debug (no en pantalla). |
---
## 5. Caracteres especiales útiles
Los demos usan códigos > 127 que corresponden a glifos definidos en `rom.c` (el char-ROM por defecto). Algunos vistos:
- `\003` (3) — un bloque relleno (usado en pong para los compases)
- `\016` (16) — cubo de caja (en sokoban)
- `\127` (127) — pared en sokoban (redefinido con `setchar(127, ...)`)
- `\143`, `\154`, `\150`, `\156`, `\149` — esquinas y trazos de marcos
- `\248`, `\250`, `\251` — sprite animado de "OK" en sokoban
- `\233` — pelota en breakout
- `\131` — pala en breakout
- `\001` — cubo de tetromino (redefinido con `setchar(1, 0xff,0x81,...)`)
Para usarlos siempre se puede hacer `setchar(idx, b0..b7)` con la bitmap deseada y luego imprimirlo con `print(chr(idx), x, y)`.
---
## 6. Patrón típico de un juego
```lua
function init()
mode(1)
cls()
-- estado inicial
player = {x=10, y=15}
score = 0
end
function update()
-- input
if btnp(KEY_LEFT) then player.x = player.x - 1 end
if btnp(KEY_RIGHT) then player.x = player.x + 1 end
-- lógica
-- ...
-- render (no hay vsync explícito; el bucle ya hace flip al final)
cls()
color(COLOR_WHITE, COLOR_BLACK)
print("\248", player.x, player.y)
print("SCORE: "..tostr(score), 0, 0)
end
```
**Cosas a recordar:**
- No se "pintan" píxeles; se imprime un código de carácter en una celda y se le asocia un atributo de color (ink + paper). Para gráficos personalizados, redefinir glifos con `setchar`.
- El bucle de render lo hace el motor C++ después de `update()` — no hay que llamar a ningún `flip`.
- El `state machine` típico se hace asignando `update = otra_funcion` (ver `demos/sokoban.lua`).
- Las coordenadas de pantalla son **enteras y por celda**, no por píxel. (0,0) es esquina superior izquierda.
- Para depurar: `log("mensaje")` o pulsar ESC y usar la consola con `?variable`.
---
## 7. Cómo compilar el intérprete
Desde `c:/mingw/gitea/ascii/`:
```sh
make windows
```
Requiere MinGW (g++) y SDL2 para Windows. Produce `ascii.exe`. Para correr Pepe Runner:
```sh
ascii.exe pepe_runner.lua
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

+318
View File
@@ -0,0 +1,318 @@
10 'GUANTE BLANCO, por David Radisic
20 'copyright (c) AMSOFT 1985
30 '
40 MODE 0:INK 0,0:BORDER 0:INK 1,26:INK 2,15:INK 3,25
50 INK 4,14:INK 5,24,12:INK 6,0:INK 7,0:INK 8,0:PAPER #1, 7
60 retardo=200
70 DIM objx(5,20),objy(5,20),joyax(5,20),joyay(5,20)
80 GOSUB 380
90 GOSUB 720
100 pausa=200:GOSUB 340
110 IF joyas=0 THEN GOSUB 980
120 PEN 4
130 FOR i=10 TO 12
140 LOCATE 15,i:PRINT"BOTIN";
150 NEXT
160 PAPER 0:CLS#2:PAPER 8
170 GOSUB 1180
180 GOSUB 1240
190 GOSUB 1380
200 GOSUB 1520
210 IF rm=0 THEN GOSUB 1910
220 IF muerto=0 THEN 160
230 pausa=100:GOSUB 340
240 PAPER 0:CLS:PEN 1
250 LOCATE 4,3:PRINT"Quiere jugar";
260 LOCATE 5,5:PRINT"otra vez?";
270 PEN 5:LOCATE 7,7:PRINT"S/N";
280 i$=UPPER$(INKEY$):IF i$<>"S" AND i$<>"N" THEN 280
290 IF i$="N" THEN MODE 2:PEN 1:STOP
300 RUN
310 IF perro=1 THEN RETURN
320 perro=1:perrox=minx(rm):perroy=miny(rm)
330 RETURN
340 FOR bucle=1 TO pausa
350 FRAME
360 NEXT
370 RETURN
380 rm=1:xp=6:yp=4:hombre$=CHR$(224):perro=0:robado=0
390 SYMBOL 240,8,8,8,8,8,8,8,8
400 SYMBOL 241,0,0,0,0,255,0,1,0
410 SYMBOL 242,0,0,0,0,15,8,8,8
420 SYMBOL 243,0,0,0,0,248,8,8,8
430 SYMBOL 244,8,8,8,8,248,0,0,0
440 SYMBOL 245,8,8,8,8,15,0,0,0
450 SYMBOL 246,8,12,13,14,12,12,8,8
460 SYMBOL 247,8,12,12,14,13,12,9,8
470 SYMBOL 248,8,24,88,56,24,24,8,8
480 SYMBOL 249,8,24,24,56,88,24,8,8
490 SYMBOL 250,0,0,255,129,129,129,255,0
500 SYMBOL 251,28,20,20,20,20,20,20,28
510 SYMBOL 252,0,0,255,255,255,255,255,0
520 SYMBOL 253,28,28,28,28,28,28,28,28
530 SYMBOL 255,195,165,60,126,90,60,36,24
540 ENT 1,12,-4,1
550 ENT -2,=1000,60,=3000,40
560 ENV 1,10,1,5,2,-4,1,2,-1,20
570 vent$(1)=STRING$(2,250):vent$(2)=CHR$(251)+CHR$(8)+CHR$(10)+CHR$(251)+CHR$(8)+CHR$(10)+CHR$(251)
580 puerta$(1)=STRING$(2,252): puerta$(2)=CHR$(253)+CHR$(8)+CHR$(10)+CHR$(253)+CHR$(8)+CHR$(10)+CHR$(253)
590 conm$(1,0)=CHR$(246):conm$(1,1)=CHR$(247)
600 conm$(2,0)=CHR$(248):conm$(2,1)=CHR$(249)
610 joya$=CHR$(144):obj$=CHR$(233):perro$=CHR$(255)
620 gol$=CHR$(246)+CHR$(248)+CHR$(247)+CHR$(249)+CHR$(252)+CHR$(253)+CHR$(250)+CHR$(251)+joya$+obj$+perro$
630 RESTORE 3020
640 FOR i=1 TO 5
650 READ minx(i),miny(i),maxx(i),maxy(i)
660 READ dir(i,1),dir(i,2),dir(i,3),dir(i,4)
670 NEXT
680 WINDOW #1,minx(rm)-1,maxx(rm)+1,miny(rm)-1,maxy(rm)+1
690 WINDOW #2,1,14,1,25
700 CLS#1:PAPER #0,8
710 RETURN
720 ORIGIN 50,50
730 INK 6,24,12
740 RESTORE 3070
750 GOSUB 1290
760 LOCATE 1,20
770 PEN 5:PRINT">";
780 PEN 1:PRINT"Puertas y ventanas";
790 PEN 5:PRINT"<";:PEN 1
800 LOCATE 5,21:PRINT"para escapar";
810 LOCATE 9,1:PRINT"PASE":LOCATE 10,2:PRINT CHR$(213)CHR$(212);
820 pausa=300:GOSUB 340
830 CLS:LOCATE 1,3:INK 6,0
840 PEN 1:PRINT hombre$;" Usted, el ladron":PRINT
850 PEN 2:PRINT LEFT$(puerta$(1),1);LEFT$(puerta$(2),1);"Puertas":PRINT
860 PEN 3:PRINT conm$(1,0);conm$(2,0);" Luces apagadas":PRINT
870 PEN 3:PRINT conm$(1,1);conm$(2,1);" Luces encendidas" :PRINT
880 PEN 4:PRINT LEFT$(vent$(1),1);LEFT$(vent$(2),1);" Ventanas":PRINT
890 PEN 5:PRINT joya$;" Piedras preciosas":PRINT
900 PAPER 1:PEN 0:PRINT obj$;" Obstaculos":PEN 1:PAPER 0:PRINT
910 PEN 1:PRINT perro$;" El perro"
920 PEN 5:PRINT:PRINT:PRINT
930 PRINT"Utilice joystick":PRINT"o teclas de cursor"
940 aux=REMAIN(1)
950 AFTER retardo*4,1 GOSUB 340
960 RETURN
970 '
980 'Generar joyas y obstaculos
990 '
1000 FOR hab=1 TO 5
1010 joyar=INT(RND*8)+2:objr=INT(RND*10)+5
1020 minx=minx(hab):miny=miny(hab):maxx=maxx(hab):maxy=maxy(hab)
1030 FOR i=1 TO joyar
1040 x=INT(RND*(maxx-minx+1))+minx
1050 y=INT(RND*(maxy-miny+1))+miny
1060 joyax(hab,i)=x:joyay(hab,i)=y
1070 joyas=joyas+1
1080 NEXT i
1090 FOR i=1 TO objr
1100 x=INT(RND*(maxx-minx+1))+minx
1110 y=INT(RND*(maxy-miny+1))+miny
1120 objx(hab,i)=x:objy(hab,i)=y
1130 NEXT i
1140 joyas(hab)=joyar:obj(hab)=objr
1150 NEXT hab
1160 CLS
1170 RETURN
1180 ON rm GOTO 1190,1200,1210,1220,1230
1190 RESTORE 2680:RETURN
1200 RESTORE 2750:RETURN
1210 RESTORE 2820:RETURN
1220 RESTORE 2890:RETURN
1230 RESTORE 2970:RETURN
1240 PAPER 0:READ rm$:PAPER 8
1250 WINDOW #1,minx(rm)-1,maxx(rm)+1,miny(rm)-1,maxy(rm)+1:CLS#1
1260 PEN 1:LOCATE 1,1:PRINT SPACE$(19);
1270 LOCATE 1,1:PRINT"Habitacion: ";rm$;
1280 IF luces(rm) THEN INK 7,10:INK 8,10 ELSE INK 7,0:INK 8,0
1290 READ a$:IF a$="FIN" THEN RETURN
1300 IF a$="D" THEN 2190
1310 IF a$="W" THEN 2270
1320 IF a$="L" THEN GRAPHICS PEN 1:GOTO 2350
1330 IF a$="S" THEN 2430
1340 IF a$="F" THEN GRAPHICS PEN 6:GOTO 2350
1350 PRINT"***ERROR ***";
1360 STOP
1370 '
1380 'Dibujar joyas/objetos
1390 '
1400 PEN 6
1410 FOR i=1 TO obj(rm)
1420 LOCATE objx(rm,i),objy(rm,i)
1430 PRINT obj$;
1440 NEXT
1450 PEN 5
1460 FOR i=1 TO joyas(rm)
1470 LOCATE joyax(rm,i),joyay(rm,i)
1480 PRINT joya$;
1490 NEXT
1500 PEN 1:LOCATE xp,yp:PRINT hombre$;
1510 RETURN
1520 xf=0:yf=0:PEN 1
1530 IF INKEY(0)<>-1 OR INKEY(72)<>-1 THEN yf=-1
1540 IF INKEY(2)<>-1 OR INKEY(73)<>-1 THEN yf=1
1550 IF INKEY(8)<>-1 OR INKEY(74)<>-1 THEN xf=-1
1560 IF INKEY(1)<>-1 OR INKEY(75)<>-1 THEN xf=1
1570 IF xf=0 AND yf=0 THEN 1640
1580 LOCATE xp+xf,yp+yf:ht$=COPYCHR$(#0)
1590 IF ASC(ht$)>239 AND ASC(ht$)<246 THEN 1520
1600 IF ht$<>" " THEN 1670
1610 LOCATE xp,yp:PRINT" ";
1620 PAPER 0:LOCATE 4,24:PRINT" ";:PAPER 8
1630 xp=xp+xf:yp=yp+yf
1640 LOCATE xp,yp:PRINT hombre$;
1650 IF perro>0 THEN perro=perro MOD 2+1:IF perro=2 THEN 2560
1660 GOTO 1520
1670 gol=INSTR(gol$,ht$):car=ASC(MID$(gol$,gol,1))
1680 ON gol GOTO 1700,1700,1700,1700,1760,1760,1860,1910,1980,2100,2660
1690 GOTO 1610
1700 IF gol>2 AND gol<5 THEN car=car-1
1710 IF gol<3 THEN car=car+1
1720 PEN 3:LOCATE xp+xf,yp+yf:PRINT CHR$(car);
1730 luces(rm)=luces(rm) XOR 1
1740 IF luces(rm) THEN INK 7,10:INK 8,10 ELSE INK 7,0:INK 8,0
1750 GOTO 1520
1760 IF xf<>0 AND yf<>0 THEN 1640
1770 IF xf<0 THEN dir=4 ELSE IF xf>0 THEN dir=3
1780 IF yf<0 THEN dir=1 ELSE IF yf>0 THEN dir=2
1790 IF dir(rm,dir)=-1 THEN 1640 ELSE rm=dir(rm,dir)
1800 IF perro>0 THEN GOSUB 310
1810 IF dir=1 THEN xp=6:yp=maxy(rm)
1820 IF dir=2 THEN xp=6:yp=miny(rm)
1830 IF dir=3 THEN xp=minx(rm):yp=13
1840 IF dir=4 THEN xp=maxx(rm):yp=13
1850 RETURN
1860 IF xp>5 AND xp<8 THEN 1890
1870 IF xp<6 THEN dir=4 ELSE dir=3
1880 GOTO 1790
1890 IF yp>13 THEN dir=2 ELSE dir=1
1900 GOTO 1790
1910 PAPER 0:CLS:PEN 1
1920 LOCATE 3,3:PRINT"Usted ha escapado";
1930 LOCATE 8,5:PRINT"con";
1940 IF joyas=robado THEN LOCATE 8,7:PRINT"todas las";ELSE LOCATE 8,7
1950 PRINT USING" ##";robado;
1960 PEN 5:LOCATE 8,9:PRINT"joyas";
1970 muerto=1:RETURN
1980 LOCATE xp,yp:PRINT" ";:xp=xp+xf:yp=yp+yf
1990 i=0
2000 i=i+1
2010 IF i>joyas(rm) THEN 1520
2020 IF joyax(rm,i)<>xp OR joyay(rm,i)<>yp THEN 2000
2030 IF i=joyas(rm) THEN 2060
2040 joyax(rm,i)=joyax(rm,joyas(rm))
2050 joyay(rm,i)=joyay(rm,joyas(rm))
2060 joyas(rm)=joyas(rm)-1:robado=robado+1
2070 MOVE 400,150+(robado*2),1,1:DRAW 555,150+(robado*2),1,1
2080 SOUND 129,248,10,12,0,1
2090 GOTO 1990
2100 ruido=INT(RND*15)
2110 SOUND 1,3000,10,ruido,0,0,10
2120 PAPER 0:LOCATE 4,24:PRINT"Choque ";:PAPER 8
2130 IF ruido<10 OR retardo=50 THEN 1640
2140 retardo=retardo-50
2150 aux=REMAIN(1)
2160 AFTER retardo*4,1 GOSUB 310
2170 GOTO 1640
2180 '
2190 'Dibujar puertas
2200 '
2210 READ no,pu$
2220 IF pu$="V" THEN pu=2 ELSE pu=1
2230 PEN 2
2240 pic$=puerta$(pu):GOSUB 2510
2250 GOTO 1290
2260 '
2270 'Dibujar ventanas
2280 '
2290 READ no,ve$
2300 IF ve$="V" THEN ve=2 ELSE ve=1
2310 PEN 4
2320 pic$=vent$(ve):GOSUB 2510
2330 GOTO 1290
2340 '
2350 'Dibujar rectas
2360 '
2370 READ x1,y1,x2,y2
2380 MOVE x1,y1,0
2390 DRAW x1,y2,0:DRAW x2,y2,0
2400 DRAW x2,y1,0:DRAW x1,y1,0
2410 GOTO 1290
2420 '
2430 'Dibujar interruptores
2440 '
2450 READ no,co$
2460 IF co$="L" THEN co=1 ELSE co=2
2470 PEN 3
2480 pic$=conm$(co,0):GOSUB 2510
2490 GOTO 1290
2500 '
2510 'Escribir car
2520 '
2530 READ x,y:LOCATE x,y:PRINT pic$;
2540 no=no-1:IF no>0 THEN 2530
2550 RETURN
2560 PEN 1:LOCATE perrox,perroy:PRINT" ";
2570 hombre$=CHR$(225)
2580 IF (perrox=xp AND perroy=yp) OR (perrox=xp+xf AND perroy=yp+yf) THEN 2660
2590 IF perrox<xp THEN perrox=perrox+1
2600 IF perrox>xp THEN perrox=perrox-1
2610 IF perroy<yp THEN perroy=perroy+1
2620 IF perroy>yp THEN perroy=perroy-1
2630 LOCATE perrox,perroy:PRINT perro$;
2640 SOUND 1,0,RND*40,10,1,2,31
2650 GOTO 1520
2660 PRINT"MORDIDO";
2670 muerto=1:RETURN
2680 DATA Pasillo
2690 DATA L,64,308,226,4
2700 DATA D,2,H,6,3,6,22
2710 DATA D,2,V,4,12,9,11
2720 DATA S,1,L,4,11
2730 DATA S,1,R,9,14
2740 DATA FIN
2750 DATA Sala
2760 DATA L,2,308,258,4
2770 DATA D,1,V,10,12
2780 DATA W,1,H,6,3
2790 DATA W,1,V,2,12
2800 DATA S,2,R,10,11,10,15
2810 DATA FIN
2820 DATA Comedor
2830 DATA L,2,308,258,4
2840 DATA W,1,V,10,12
2850 DATA W,1,H,6,3
2860 DATA D,1,V,2,12
2870 DATA S,2,L,2,11,2,15
2880 DATA FIN
2890 DATA Cocina
2900 DATA L,2,276,384,4
2910 DATA D,2,H,6,5,6,22
2920 DATA W,1,H,10,22
2930 DATA W,1,V,14,13
2940 DATA D,1,V,2,13
2950 DATA S,1,L,2,16
2960 DATA FIN
2970 DATA Despensa
2980 DATA L,2,276,256,4
2990 DATA D,1,V,10,12
3000 DATA S,1,R,10,11
3010 DATA FIN
3020 DATA 5,4,8,21,0,4,3,2
3030 DATA 3,4,9,21,-1,-1,1,-1
3040 DATA 3,4,9,21,-1,-1,-1,1
3050 DATA 3,6,13,21,1,0,-1,5
3060 DATA 3,6,9,21,-1,-1,4,-1
3070 DATA L,64,308,480,100
3080 DATA F,250,98,294,102
3090 DATA F,250,306,294,310
3100 DATA F,390,94,430,106
3110 DATA F,390,302,430,314
3120 DATA F,474,240,488,270
3130 DATA F,474,124,488,154
3140 DATA F,58,240,72,270
3150 DATA L,226,308,322,180
3160 DATA L,160,180,480,100
3170 DATA L,64,180,160,100
3180 DATA FIN
+396
View File
@@ -0,0 +1,396 @@
-- ============================================================
-- GUANTE BLANCO — port a la fantasy console "ascii" del original
-- de David Radisic para Amstrad CPC (AMSOFT, 1985).
-- Sergi Valor, 2026.
--
-- FASE 1: estructura base. Las 5 habitaciones se dibujan con
-- sus paredes, puertas, ventanas y conmutadores. Tecla 1..5
-- conmuta la habitación visible para verificar el render.
-- ============================================================
-- ============================================================
-- CONFIGURACION
-- ============================================================
MODO = 3 -- mode(3) = 32x24
ANCHO = 32
ALTO = 24
-- Layout (HUD/banners alrededor del área de habitación)
FILA_BANNER = 0 -- "Habitacion: X"
FILA_MSG = 23 -- mensajes "Choque", "MORDIDO"
AREA_Y0 = 1 -- primera fila donde se pinta la habitación
AREA_Y1 = 22 -- última fila
-- ============================================================
-- PALETA (mapeo CPC firmware → CGA disponible)
-- Original: INK 0,0 (negro) / 1,26 (pastel) / 2,15 (blanco) /
-- 3,25 (pastel verde) / 4,14 (amarillo) / 5,24,12 (parpadeo) /
-- 6,0 (negro al inicio, luego 24,12) / 7,8 = paper de la habit.
-- ============================================================
COL_FONDO = COLOR_BLACK
COL_LADRON = COLOR_WHITE -- INK 1 pastel
COL_PUERTA = COLOR_LIGHT_GRAY -- INK 2 blanco
COL_VENTANA = COLOR_YELLOW -- INK 4
COL_CONMUT = COLOR_LIGHT_GREEN -- INK 3
COL_JOYA = COLOR_LIGHT_RED -- INK 5 (parpadeo en original)
COL_OBSTACULO = COLOR_BROWN -- INK 6
COL_PERRO = COLOR_WHITE -- INK 1
COL_PARED = COLOR_LIGHT_BLUE -- el rectángulo L original era PEN 1 azul
COL_PAPER_HAB_ON = COLOR_DARK_GRAY -- paper de la habitación con luz (INK 7,10)
COL_PAPER_HAB_OFF = COLOR_BLACK -- paper de la habitación sin luz (INK 7,0)
COL_TEXTO = COLOR_LIGHT_GRAY
COL_MSG = COLOR_LIGHT_RED
-- ============================================================
-- GLIFOS (códigos que vamos a redefinir con setchar)
-- ============================================================
GL_LADRON = 224 -- hombre$ = chr(224)
GL_LADRON_M = 225 -- ladrón "mordido" (línea 2570 del original)
GL_VENT_H = 250 -- ventana horizontal (2 chars)
GL_VENT_V = 251 -- ventana vertical (3 chars verticales)
GL_PUERTA_H = 252 -- puerta horizontal
GL_PUERTA_V = 253 -- puerta vertical
GL_PERRO = 255 -- el perro
GL_JOYA = 144 -- joya$ = chr(144)
GL_OBSTACULO = 233 -- obj$ = chr(233)
GL_CONM_L_OFF = 246 -- conm$(1,0)
GL_CONM_L_ON = 247 -- conm$(1,1)
GL_CONM_R_OFF = 248 -- conm$(2,0)
GL_CONM_R_ON = 249 -- conm$(2,1)
GL_PARED = 143 -- nuestro char de pared (el original usaba DRAW de líneas)
-- ============================================================
-- TIPOS DE CELDA DEL MAPA
-- ============================================================
T_VACIO = 0
T_PARED = 1
T_PUERTA_H = 2
T_PUERTA_V = 3
T_VENT_H = 4
T_VENT_V = 5
T_CONM_L = 6
T_CONM_R = 7
T_JOYA = 8
T_OBSTACULO = 9
T_PERRO = 10
-- ============================================================
-- DEFINIR GLIFOS (bitmaps idénticos a los SYMBOL del original
-- cuando los hay, diseñados a mano cuando vienen del char-ROM
-- CPC y no aparecen en el .bas).
-- ============================================================
function definir_glifos()
-- SYMBOL 240..245 del original (partes del sprite del ladrón
-- y/o decoración — el código los ignora en colisiones con
-- "IF ASC(ht$)>239 AND ASC(ht$)<246 THEN 1520")
setchar(240, 8, 8, 8, 8, 8, 8, 8, 8)
setchar(241, 0, 0, 0, 0, 255, 0, 1, 0)
setchar(242, 0, 0, 0, 0, 15, 8, 8, 8)
setchar(243, 0, 0, 0, 0, 248, 8, 8, 8)
setchar(244, 8, 8, 8, 8, 248, 0, 0, 0)
setchar(245, 8, 8, 8, 8, 15, 0, 0, 0)
-- SYMBOL 246..249 — conmutadores L/R, on/off (bitmaps literales)
setchar(GL_CONM_L_OFF, 8, 12, 13, 14, 12, 12, 8, 8)
setchar(GL_CONM_L_ON, 8, 12, 12, 14, 13, 12, 9, 8)
setchar(GL_CONM_R_OFF, 8, 24, 88, 56, 24, 24, 8, 8)
setchar(GL_CONM_R_ON, 8, 24, 24, 56, 88, 24, 8, 8)
-- SYMBOL 250..253 — ventanas y puertas (bitmaps literales)
setchar(GL_VENT_H, 0, 0, 255, 129, 129, 129, 255, 0)
setchar(GL_VENT_V, 28, 20, 20, 20, 20, 20, 20, 28)
setchar(GL_PUERTA_H, 0, 0, 255, 255, 255, 255, 255, 0)
setchar(GL_PUERTA_V, 28, 28, 28, 28, 28, 28, 28, 28)
-- SYMBOL 255 — el perro (bitmap literal del original)
setchar(GL_PERRO, 195, 165, 60, 126, 90, 60, 36, 24)
-- chr 224 (ladrón normal) y 225 (ladrón mordido): el original usaba
-- las caritas del char-ROM CPC tal cual. En ascii el charset trae
-- esas mismas caritas en los mismos códigos, así que no se redefinen.
-- Pared: bloque sólido para el rectángulo L del original
setchar(GL_PARED, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)
end
-- ============================================================
-- DEFINICIÓN DE LAS 5 HABITACIONES
-- Coordenadas tomadas LITERALMENTE de las DATAs del bombardero.bas:
-- - minx, miny, maxx, maxy: interior (líneas 3020-3060)
-- - puertas D y ventanas W: posición en pared (líneas 2680-3010)
-- - conmutadores S: posición en pared, orient L/R
-- - dir: N, S, E, O (-1 = bloqueado, 0 = escape, n = id habitación)
-- ============================================================
HABITACIONES = {
[1] = {
nombre = "Pasillo",
minx = 5, miny = 4, maxx = 8, maxy = 21,
elementos = {
{ tipo="puerta_h", x=6, y=3 }, -- N: a dir[1]=0 (escape)
{ tipo="puerta_h", x=6, y=22 }, -- S: a dir[2]=4 (Cocina)
{ tipo="puerta_v", x=4, y=12 }, -- O: a dir[4]=2 (Sala)
{ tipo="puerta_v", x=9, y=11 }, -- E: a dir[3]=3 (Comedor)
{ tipo="conm", x=4, y=11, orient="L", on=false },
{ tipo="conm", x=9, y=14, orient="R", on=false },
},
dir = { 0, 4, 3, 2 }, -- N, S, E, O
luz = false,
},
[2] = {
nombre = "Sala",
minx = 3, miny = 4, maxx = 9, maxy = 21,
elementos = {
{ tipo="puerta_v", x=10, y=12 }, -- E: a dir[3]=1 (Pasillo)
{ tipo="vent_h", x=6, y=3 }, -- N (bloqueado, dir[1]=-1)
{ tipo="vent_v", x=2, y=12 }, -- O (bloqueado, dir[4]=-1)
{ tipo="conm", x=10, y=11, orient="R", on=false },
{ tipo="conm", x=10, y=15, orient="R", on=false },
},
dir = { -1, -1, 1, -1 },
luz = false,
},
[3] = {
nombre = "Comedor",
minx = 3, miny = 4, maxx = 9, maxy = 21,
elementos = {
{ tipo="vent_v", x=10, y=12 }, -- E (bloqueado, dir[3]=-1)
{ tipo="vent_h", x=6, y=3 }, -- N (bloqueado, dir[1]=-1)
{ tipo="puerta_v", x=2, y=12 }, -- O: a dir[4]=1 (Pasillo)
{ tipo="conm", x=2, y=11, orient="L", on=false },
{ tipo="conm", x=2, y=15, orient="L", on=false },
},
dir = { -1, -1, -1, 1 },
luz = false,
},
[4] = {
nombre = "Cocina",
minx = 3, miny = 6, maxx = 13, maxy = 21,
elementos = {
{ tipo="puerta_h", x=6, y=5 }, -- N: a dir[1]=1 (Pasillo)
{ tipo="puerta_h", x=6, y=22 }, -- S: a dir[2]=0 (escape)
{ tipo="vent_h", x=10, y=22 }, -- otra ventana en la pared S
{ tipo="vent_v", x=14, y=13 }, -- E (bloqueado, dir[3]=-1)
{ tipo="puerta_v", x=2, y=13 }, -- O: a dir[4]=5 (Despensa)
{ tipo="conm", x=2, y=16, orient="L", on=false },
},
dir = { 1, 0, -1, 5 },
luz = false,
},
[5] = {
nombre = "Despensa",
minx = 3, miny = 6, maxx = 9, maxy = 21,
elementos = {
{ tipo="puerta_v", x=10, y=12 }, -- E: a dir[3]=4 (Cocina)
{ tipo="conm", x=10, y=11, orient="R", on=false },
},
dir = { -1, -1, 4, -1 },
luz = false,
},
}
-- ============================================================
-- ESTADO GLOBAL
-- ============================================================
rm = 1 -- habitación actual (variable rm del original)
xp, yp = 6, 4 -- posición del ladrón
hombre_glifo = GL_LADRON
mapa = {} -- mapa[hab][x][y] = tipo de celda
-- ============================================================
-- CONSTRUCCIÓN DEL MAPA LÓGICO
-- Por habitación se construye una matriz [x][y] con el tipo de
-- celda. Las paredes son el contorno (minx-1, maxx+1, miny-1,
-- maxy+1). Las puertas, ventanas y conmutadores sobrescriben la
-- pared. El interior queda como T_VACIO (se rellenará con joyas
-- y obstáculos en la Fase 3).
-- ============================================================
function init_mapa()
for id, hab in pairs(HABITACIONES) do
mapa[id] = {}
for x = 0, ANCHO-1 do
mapa[id][x] = {}
for y = 0, ALTO-1 do
mapa[id][x][y] = T_VACIO
end
end
-- Paredes: contorno del rectángulo interior
local x0, y0 = hab.minx - 1, hab.miny - 1
local x1, y1 = hab.maxx + 1, hab.maxy + 1
for x = x0, x1 do
mapa[id][x][y0] = T_PARED
mapa[id][x][y1] = T_PARED
end
for y = y0, y1 do
mapa[id][x0][y] = T_PARED
mapa[id][x1][y] = T_PARED
end
-- Elementos: sobrescriben la pared
for _, e in ipairs(hab.elementos) do
if e.tipo == "puerta_h" then
mapa[id][e.x ][e.y] = T_PUERTA_H
mapa[id][e.x+1][e.y] = T_PUERTA_H
elseif e.tipo == "vent_h" then
mapa[id][e.x ][e.y] = T_VENT_H
mapa[id][e.x+1][e.y] = T_VENT_H
elseif e.tipo == "puerta_v" then
mapa[id][e.x][e.y ] = T_PUERTA_V
mapa[id][e.x][e.y+1] = T_PUERTA_V
mapa[id][e.x][e.y+2] = T_PUERTA_V
elseif e.tipo == "vent_v" then
mapa[id][e.x][e.y ] = T_VENT_V
mapa[id][e.x][e.y+1] = T_VENT_V
mapa[id][e.x][e.y+2] = T_VENT_V
elseif e.tipo == "conm" then
mapa[id][e.x][e.y] = (e.orient == "L") and T_CONM_L or T_CONM_R
end
end
end
end
-- ============================================================
-- OFFSET DE CENTRADO
-- En cada render, calcula el offset (off_x, off_y) para centrar
-- el rectángulo de la habitación (paredes incluidas) dentro del
-- área de juego.
-- ============================================================
function offset_centrado()
local hab = HABITACIONES[rm]
local w = (hab.maxx + 1) - (hab.minx - 1) + 1
local h = (hab.maxy + 1) - (hab.miny - 1) + 1
local off_x = flr((ANCHO - w) / 2) - (hab.minx - 1)
local off_y = flr((AREA_Y0 + AREA_Y1 - h) / 2) - (hab.miny - 1) + 1
return off_x, off_y
end
-- ============================================================
-- RENDER
-- ============================================================
function pintar_fondo()
color(COL_TEXTO, COL_FONDO)
cls()
end
function pintar_banner()
color(COL_TEXTO, COL_FONDO)
print("Habitacion: "..HABITACIONES[rm].nombre, 1, FILA_BANNER)
end
function glifo_de(tipo, on)
if tipo == T_PARED then return GL_PARED end
if tipo == T_PUERTA_H then return GL_PUERTA_H end
if tipo == T_PUERTA_V then return GL_PUERTA_V end
if tipo == T_VENT_H then return GL_VENT_H end
if tipo == T_VENT_V then return GL_VENT_V end
if tipo == T_CONM_L then return on and GL_CONM_L_ON or GL_CONM_L_OFF end
if tipo == T_CONM_R then return on and GL_CONM_R_ON or GL_CONM_R_OFF end
if tipo == T_JOYA then return GL_JOYA end
if tipo == T_OBSTACULO then return GL_OBSTACULO end
if tipo == T_PERRO then return GL_PERRO end
return nil
end
function color_de(tipo)
if tipo == T_PARED then return COL_PARED end
if tipo == T_PUERTA_H then return COL_PUERTA end
if tipo == T_PUERTA_V then return COL_PUERTA end
if tipo == T_VENT_H then return COL_VENTANA end
if tipo == T_VENT_V then return COL_VENTANA end
if tipo == T_CONM_L then return COL_CONMUT end
if tipo == T_CONM_R then return COL_CONMUT end
if tipo == T_JOYA then return COL_JOYA end
if tipo == T_OBSTACULO then return COL_OBSTACULO end
if tipo == T_PERRO then return COL_PERRO end
return COL_TEXTO
end
-- Devuelve el "estado on/off" de un conmutador concreto buscando en
-- los elementos de la habitación.
function conm_estado(hab, x, y)
for _, e in ipairs(hab.elementos) do
if e.tipo == "conm" and e.x == x and e.y == y then
return e.on
end
end
return false
end
function pintar_habitacion()
local hab = HABITACIONES[rm]
local off_x, off_y = offset_centrado()
local paper = hab.luz and COL_PAPER_HAB_ON or COL_PAPER_HAB_OFF
-- Paper del interior
color(COL_TEXTO, paper)
local blank = " "
for y = hab.miny, hab.maxy do
for x = hab.minx, hab.maxx do
print(blank, x + off_x, y + off_y)
end
end
-- Paredes y elementos
for y = hab.miny - 1, hab.maxy + 1 do
for x = hab.minx - 1, hab.maxx + 1 do
local t = mapa[rm][x][y]
if t ~= T_VACIO then
local on = false
if t == T_CONM_L or t == T_CONM_R then
on = conm_estado(hab, x, y)
end
local g = glifo_de(t, on)
if g then
color(color_de(t), COL_FONDO)
print(chr(g), x + off_x, y + off_y)
end
end
end
end
-- El ladrón
color(COL_LADRON, paper)
print(chr(hombre_glifo), xp + off_x, yp + off_y)
end
-- ============================================================
-- BUCLE PRINCIPAL — FASE 1
-- Solo render estático. Teclas 1..5 cambian habitación visible.
-- ============================================================
function init()
mode(MODO)
border(COL_FONDO)
color(COL_TEXTO, COL_FONDO)
definir_glifos()
init_mapa()
rm = 1
-- Posición inicial del ladrón en cada habitación (centrada-ish)
xp = flr((HABITACIONES[rm].minx + HABITACIONES[rm].maxx) / 2)
yp = HABITACIONES[rm].miny + 2
cls()
end
function update()
-- FASE 1: navegación manual entre habitaciones para verificar
if btnp(KEY_1) then rm = 1 end
if btnp(KEY_2) then rm = 2 end
if btnp(KEY_3) then rm = 3 end
if btnp(KEY_4) then rm = 4 end
if btnp(KEY_5) then rm = 5 end
-- Tecla L para alternar la luz de la habitación actual (debug)
if btnp(KEY_L) then HABITACIONES[rm].luz = not HABITACIONES[rm].luz end
-- Reposicionar al ladrón al cambiar de habitación
local hab = HABITACIONES[rm]
if xp < hab.minx or xp > hab.maxx or yp < hab.miny or yp > hab.maxy then
xp = flr((hab.minx + hab.maxx) / 2)
yp = hab.miny + 2
end
pintar_fondo()
pintar_banner()
pintar_habitacion()
color(COL_TEXTO, COL_FONDO)
print("1..5 hab. L luz", 1, FILA_MSG)
end