QB1AGLE; 20160116-19

QBAGL EGA

Para que esto sea posible, tengo que conseguir escribir un conversor PNG->PUT en formato EGA. Para ello me he generado una imagen con un patrón conocido que ahora investigaré empleando el visor hexadecimal.

Es una imagen de 16×16, o sea, 256 pixels, que ocupará 128 bytes (64 enteros). El archivo usa 2 enteros como cabecera W/H y antes de eso 7 bytes de cabecera QBasic (FD, SEG, OFF, SIZE)

Como siempre, todos los enteros de 16 bits son LSB MSB.

HEADER		$FD
SEGMENT		$00 $00
OFFSET		$00 $00
SIZE		$84 $00 -> $0084 = 132 = W * H + 4
WIDTH		$10 $00 -> 16
HEIGHT		$10 $00 -> 16
imagen      128 bytes.

Lo chungo es ver como se organiza la imagen. Originalmente este es un modo gráfico organizado en 4 bitplanes, así que he hecho una imagen con un patrón sencillo usando colores que solo activen un bit de cada bitplane, a ver si veo algo.

7F FE 00 01 80 00 00 00 
80 01 00 02 40 00 00 00 
80 01 00 04 20 00 00 00 
80 01 00 08 10 00 00 00 
80 01 00 10 08 00 00 00 
80 01 00 20 04 00 00 00 
80 01 03 C0 03 C0 03 C0 
80 01 03 C0 03 C0 03 C0 
80 01 03 C0 03 C0 03 C0 
80 01 03 C0 03 C0 03 C0 
80 01 04 00 00 20 00 00 
80 01 08 00 00 10 00 00 
80 01 10 00 00 08 00 00 
80 01 20 00 00 04 00 00 
80 01 40 00 00 02 00 00 
7F FE 80 00 00 01 00 00

Es fácil de ver: cada dos columnas es un bitmap correspondiente a un plano, y los planos están intercalados:

Plano B, plano G, plano R, plano I

Ahora a hacer pruebas. Para máxima simplicidad voy a generar únicamente recortes cuyo ancho sea múltiplo de 8 y así no tengo que andar con cosas raras. Voy a hacer un conversor sencillo en cuanto tenga un rato y vemos.

El conversor podrá funcionar en modo sólido o en modo con máscara. La máscara se generará automáticamente a partir de un color especial (o cualquier color que no pertenezca a la paleta EGA). La idea es reservar un array de 64K donde cargar la imagen.

El tema super chulo sería poder meter máscara e imagen en el mismo array, pero no tengo muy claro la forma que tendría QBasic de intercalar un array de dos dimensiones. Voy a hacer una prueba con un array sencillo y luego intento extrapolar.

Voy a hacer un array de dos dimensiones (del tipo apto para imagen). La dimensión “grande” donde va la lista conocida o la imagen es la primera, la dimensión “pequeña” de dos elementos es la segunda:

DIM array%(15,1) ' 2 listas de 16 elementos, GET/PUT friendly.

Esto es porque QBasic ordenaba los arrays al revés o algo por el estilo. Veamos qué sale. En el primer array voy a meter 1-16, en el segundo solo -1 (que serán $FF $FF, para verlos bien).

Jodó, qué bien se ven estas cosas con el editor hexadecimal:

FD F6 6D 00 00 40 00 

01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 
09 00 0A 00 0B 00 0C 00 0D 00 0E 00 0F 00 10 00 

FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF

Cabecera, primera dimensión, segunda dimensión.

Entonces puedo generar perfectamente el array completo. Voy a hacer una prueba más. Voy a ver si puedo grabar un array monodimensional y usarlo para cargar dos imagenes…

O sea, voy a ver si puedo crear un array lo suficientemente grande para contener la imagen y mascara más grandes posible (32004 enteros), y si puedo usarlo para cargar con BLOAD cualquier cosa y, calculando offsets, imprimir los dos elementos… Yo me entiendo.

¡Funciona de la hostia! Joder, ¿por qué no se me había ocurrido esto antes? Podría haber “inventado” hace quince años la forma definitiva para grabar y cargar imagenes en modo EGA, mejor que el coñazo de los cuatro archivos, y haber triunfado en los ABC packets.

En fin, todo llega, aunque tarde.

¡Voy a escribir un conversor!

~~

Ideas: sistema de inventario. Se puede cargar un set de dibujos de 32×32 en una página de VRAM y luego mostrar el que sea. O se pueden ir definiendo con ADDOBJECT “DESC”, FILE.PUT. El sistema los va añadiendo a un array y cargando FILE.PUT en esa página de RAM.

A la hora de manejarlo, podemos tener los comandos GRAB “DESC” y THROW “DESC”, que lo añaden o quitan del inventario.

En la interfaz, puede mostrarse un icono de inventario en una esquina de la pantalla. Al pulsarlo se muestra el inventario, hasta 10 slots. Se hace click en el objeto, y se esconde el inventario. La frase de accion cambia a “USAR DESC EN ” y se muestra la zona activa.

El auto-label es PREFIX_USAR_DESC_EN_ZONA

Esto “pa luegow”.

~~~

Probado el conversor ¡funciona genial! Y el código que pone las imagenes en pantalla (autodetectando si tiene que hacerlo en modo transparente o sólido) también.

Tengo que diseñar el tema del inventario:

TYPE TypeItem
	gX AS INTEGER
	gY AS INTEGER
	desc AS STRING * 16
	fn AS STRING * 20		' Not really used in EGA
END TYPE

Cuando en el código hay un ADDOBJECT “DESC”, FILE.PUT:

Cargamos el objeto en la página 6, en iX%,iY%, incrementamos coordenadas, y almacenamos un nuevo objeto TypeItem en un array items.

Esto será así:

VRAM0 ← página visual
VRAM1 ← Donde se cargan las imagenes

BLIT hace un PCOPY 1,0

VRAM6 ← Donde se cargan los gráficos de 32×32 para los objetos. La posición donde se cargue el siguiente objeto se modifica dinámicamente. Caben 60 objetos, que son suficientes. Siempre puede haber un CLEARALLOBJECTS para cargar un nuevo set.

VRAM7 ← Temporal para SVE/REC

Sólo tengo que modificar QB1AGLE quitando BLAST y adaptando la carga de los gráficos al formato EGA y añadir esta mierda. Es un rato. Y además el resultado es compilable directamente por QB45.

Será lo próximo que haga.

~~

Más: todo hecho. ¡Y vaya como funciona! QB1AGLE >>> QB1AGL0, así que tendré que desportar. O reportar. O portar para atrás. O yo qué sé, eso que hago siempre. No paro de usar el puto winMergeU para estas mierdas XD

Por ejemplo, tengo que ampliar msc3.exe con las cosas nuevas que he puesto en msc3nes.exe. Añadidos y mejoras de ida y vuelta, los llaman. Porque NES es Spectrum con más negritos, Spectrum es NES con más salero.

Básicamente, la cacota del inventario funciona de la muerte. He hecho una mini-aventura de 1 localización para probar, lo que a su vez me ha llevado a pulir un poco más el motor. Ahora sólo queda meter GOSUBs que, con la última adición que hecho, son triviales. Los GOSUBs podría molar de la puta leche.

Total, que tal. Vaya mierda de diario este, no he apuntado ni una fecha.

Este es el código de la aventura de prueba. Ahí voy, a floodear mi propio blog:

# Ejemplo con modificación de pantalla, items, y cosas
# Copyleft 2015 The Mojon Twins

# Pequeña intro

# Pintamos la pantalla de fondo y luego ponemos a Maykk
    SCREEN GFX\SCR00BG.PUT
    
# Maykk fue creado desde un PNG con un color fuera de la paleta EGA, que
# es detectdo por el conversor como "color transparente". De este modo, el
# gráfico se pinta respetando lo que hay detrás...
    PUT 10, 0, GFX\SCR00CH.PUT
    
# Toda la carga de gráficos se hace en una página oculta. Para hacerlo todo
# visible...
    BLIT
    
# Un pequeño texto de introducción
    OPENBOX RIGHT
    PRINT "Esto es una peque\na demostraci\on de c\omo podr\ian ser los g\vegos dise\nados para este motowr."
    PRINT "Realmente se trata de algo muy sencillo: pintar imagenes sobre la pantalla (completa o trocitos), detectar la acci\on del usuario, toquetear con flags, y un inventario sencillo."
    WT
    CLOSEBOX
    
    OPENBOX RIGHT
    PRINT "Seguramente ya me conoces y sabes que me gusta el bocadillo de ch\opped."
    PRINT "Ay\udame a hacer un par de mierdas sencillas, que me tengo que ir a trabajar..."
    WT
    CLOSEBOX    
    
# Muchas veces no es necesario, pero me gusta empezar una localización con 
# una etiqueta que luego usaré de prefijo para todas las demás etiquetas.

:OFFICE

# Definimos los items
    CLEARITEMS
    DEFEMPTY GFX\ITEMPTY.CUT
    DEFITEM "LLAVE", GFX\ITEMKEY.CUT
    DEFITEM "ID", GFX\ITEMID.CUT
    DEFITEM "BRAGAS", GFX\ITEMPANT.CUT
    
# Iniciamos el inventario
    INVENTORY ON
    CLEARINVENTORY

# Inicializamos algunas flags
    # FLAG 2 is closet, 0 = closed, 1 = open
    $2 = 0

# Pintamos la escena.
:OFFICE_SCRSETUP

# Primero se dibuja el fondo
    SCREEN GFX\SCR00BG.PUT
    
# Ahora las cosas que cambian. 
# Si no tenemos el item "ID" hay que pintarlo sobre la mesa:
    HASITEM "ID", :OFFICE_SCRSETUP_NOID
    PUT 266, 126, GFX\SCR00C2.PUT
:OFFICE_SCRSETUP_NOID   

# El flag 2 dice si hemos abierto el armario.
# Si vale 1, es que está abierto, así que lo pintamos.
    EQ $2, 0, :OFFICE_SCRSETUP_NODOOR
    PUT 9, 40, GFX\SCR00C1.PUT
:OFFICE_SCRSETUP_NODOOR

# Ya está todo el pescao vendío. Ahora mostramos los cambios
    BLIT

SOUND SFX\AH.VOC FG
    
# Música
    MUSIC PLAY MUSIC\CHEM1.S3M
        
# Definimos las zonas. Esta pantalla tiene zonas diferentes
# dependiendo si está el armario abierto o no. Primero definimos
# las que son comunes a los dos estados...
:OFFICE_ZONESETUP
    RESETZONES
    ZONE "CAJAS", 156, 141, 196, 181
    ZONE "ESCRITORIO", 188, 122, 304, 144
    ZONE "LLAVERO", 179, 86, 212, 101
    ZONE "FUERA", 0, 193, 319, 199, EXIT

# Ahora las dependientes. Si el armario está cerrado...
# Fijáos que si hay una zona que incluya a otra más pequeña,
# definimos la pequeña antes, ya que el motor detecta la 
# primera de la lista que coincide.
    EQ $2, 1, :OFFICE_ZONESETUP_CLOSET_OPEN
    ZONE "LETRERO", 15, 62, 24, 89
    ZONE "ARMARIO", 3, 29, 42, 192
    GOTO :OFFICE_ZONESETUP_END
    
# Y si está abierto...
:OFFICE_ZONESETUP_CLOSET_OPEN
    ZONE "LETRERO", 36, 61, 54, 93
    ZONE "PUERTA", 31, 53, 73, 178
    ZONE "ARCON", 13, 164, 27, 185
    ZONE "ARMARIO", 9, 40, 30, 191
    ZONE "CAJON", 56, 107, 82, 121

:OFFICE_ZONESETUP_END
# Pongo aquí estas porque se podrían superponer con el armario abierto
# Y así el armario, si está definido, tiene prioridad.
    ZONE "VENTANA", 42, 28, 176, 108
    ZONE "CAJAS VIEJAS", 49, 133, 117, 168
    
# Y lanzamos el bucle de juego
:OFFICE_MAINLOOP
    DOACTIONS :OFFICE
    GOTO :OFFICE_MAINLOOP
    
# Respuesta a todas las opciones.
# ¡Tranquis! el motor ignorará las que no existan. 

# VENTANA
:OFFICE_MIRAR_VENTANA
    TEXTWT BOTTOM, "Me encanta el ventanal de mi despacho. Es enorme.", "Ahora hace un poco de viento fuera. Parece que va a llover..."
# Return hace un GOTO al "prefix" definido en el último
# DOACTIONS seguido de _MAINLOOP. Bastante cómodo...
    RETURN

:OFFICE_ACCION_VENTANA
    TEXTWT BOTTOM, "Paso de abrir la ventana. Con el ventazo que hay se me iba a volar todo, y acabo de ordenar.", "Bueno, en realidad lo hizo Xinkss..."
    RETURN
    
# CAJON
:OFFICE_MIRAR_CAJON
    TEXTWT BOTTOM, "Es un caj\on al que me gusta llamar 'El sitio miscelaneo'.", "Es donde va a parar toda la mierda suelta que hay por ah\i cuando recogemos."
    RETURN
    
:OFFICE_ACCION_CAJON
    TEXTWT BOTTOM, "No necesito nada de lo que hay ah\i ahora mismo. Y paso de ponerme a rebuscar, porque siempre termino encontrando algo que mola."
    RETURN

# CAJAS VIEJAS
:OFFICE_MIRAR_CAJAS_VIEJAS
    TEXTWT BOTTOM, "Cajas viejas de ingredientes para hacer galletas. Ahora tienen papeles. Y cosas. No s\e, creo que los archivos y tal van ah\i. Xinkss se encarga."
    RETURN
    
:OFFICE_ACCION_CAJAS_VIEJAS
    TEXTWT BOTTOM, "Esas cajas son cosa de Xinkss."
    RETURN

# CAJAS
:OFFICE_MIRAR_CAJAS
    TEXTWT BOTTOM, "En estas cajas guardo mis \utiles de trabajo. O eso creo. Recuerda que esto de las galletas es temporal."
    RETURN
    
:OFFICE_ACCION_CAJAS
    TEXTWT BOTTOM, "Paso de sacar cosas de ah\i. Tengo cosas m\as importantes y urgentes que hacer ahora mismo."
    RETURN

# ESCRITORIO
:OFFICE_MIRAR_ESCRITORIO
    TEXTWT BOTTOM, "Mi escritorio. C\omo mola ser la jefa.", "S\i, aunque sea de mentirijillas."
    
# Vamos a poner un texto extra si aún no hemos cogido la tarjeta
    HASITEM "ID", :OFFICE_MIRAR_ESCRITORIO_ID   
    TEXTWT BOTTOM, "Ah\i hay una tarjeta de identificaci\on vieja. Creo que es de la anterior due\na o algo as\i."
    
:OFFICE_MIRAR_ESCRITORIO_ID
    RETURN
    
:OFFICE_ACCION_ESCRITORIO
# Hemos cogido ya la tarjeta?
    HASITEM "ID", :OFFICE_ACCION_ESCRITORIO_ID
    TEXTWT BOTTOM, "Me voy a llevar esta tarjeta. Vendr\a bien."
    
# Con este comando "cogemos" el item    
    GRAB "ID"

# Hay que modificar el escenario para quitar la tarjeta. Podríamos simplemente
# volver a dibujarlo todo, pero es más rápido tener el cachito guardado y 
# simplemente...
    PUT 266, 126, GFX\SCR00C3.PUT
    BLIT
    RETURN
    
:OFFICE_ACCION_ESCRITORIO_ID
    TEXTWT BOTTOM, "No quiero trabajar en mi escritorio ahora. Tengo mejores cosas que hacer.", "De hecho, creo que siempre tengo mejores cosas que hacer."
    RETURN

# LLAVERO
:OFFICE_MIRAR_LLAVERO
# Tenemos la llave?
    HASITEM "LLAVE", :OFFICE_MIRAR_LLAVERO_LLAVE
    TEXTWT BOTTOM, "Es el armarito donde guardamos la llave de la furgoneta.", "\!Misteriosamente, la llave est\a dentro!"
    RETURN
    
:OFFICE_MIRAR_LLAVERO_LLAVE 
    TEXTWT BOTTOM, "Es el armarito donde guardamos la llave de la furgoneta.", "Ahora la llave la tengo yo."
    RETURN
    
:OFFICE_ACCION_LLAVERO
# Tenemos la llave?
    HASITEM "LLAVE", :OFFICE_ACCION_LLAVERO_LLAVE
    TEXTWT BOTTOM, "Aqui esta la llave de la furgoneta, y la voy a necesitar..."
    GRAB "LLAVE"
    RETURN
    
:OFFICE_ACCION_LLAVERO_LLAVE
    TEXTWT BOTTOM, "Ya no hay nada m\as que hacer con esto."
    RETURN

# LETRERO
# Las acciones relacionadas con el letrero serán diferentes
# si la puerta del armario está o no abierta, porque los
# letreros son diferentes. Para ello usamos GOTOF que va a
# PREFIX_

:OFFICE_MIRAR_LETRERO
    GOTOF :OFFICE_MIRAR_LETRERO, 2

:OFFICE_MIRAR_LETRERO_0
# $2 = 0, armario cerrado, es el letrero "de fuera"
    TEXTWT BOTTOM, "El letrero pone: 'El armario se atasca. A ver si puedes echarle un poco de aceite a la cerradura. Fdo. Xinkss'", "Vaya, sab\ia que se olvidaba algo..."
    RETURN

:OFFICE_MIRAR_LETRERO_1
# $2 = 1, armario abierto, es el letrero "de dentro"
    TEXTWT BOTTOM, "El letrero pone: 'Por favor, no olvides cerrar el armario, que entra el gato. Fdo. Xinkss", "\?Y qu\e le molesta el gato ah\i? Qu\e t\ia m\as mani\atica."
    RETURN

:OFFICE_ACCION_LETRERO
# Aquí vamos a decir lo mismo para algo, así que da igual qué letrero sea
    TEXTWT BOTTOM, "Deja tranquilo el letrero, no molesta."
    RETURN

# ARMARIO
# Lo mismo que el letrero
:OFFICE_MIRAR_ARMARIO
    GOTOF :OFFICE_MIRAR_ARMARIO, 2

:OFFICE_MIRAR_ARMARIO_0
# $2 = 0, armario cerrado
    TEXTWT BOTTOM, "En este armario guardamos ropa. Est\a cerrado firmemente."
    RETURN

:OFFICE_MIRAR_AMARIO_1
# $2 = 1, armario abierto
    TEXTWT BOTTOM, "En este armario guardamos ropa. Creo que deber\iamos invertir en unas perchas. Ahora todo esta en ese arc\on de ah\i abajo."
    RETURN

:OFFICE_ACCION_ARMARIO
    GOTOF :OFFICE_ACCION_ARMARIO, 2

:OFFICE_ACCION_ARMARIO_0
# $2 = 0, armario cerrado
    TEXTWT BOTTOM, "El armario no se abre. Creo que la cerradura est\a oxidada o rota o yo qu\e s\e."
    RETURN

:OFFICE_ACCION_ARMARIO_1
# $2 = 1, armario abierto
    TEXTWT BOTTOM, "Una vez me escond\i ah\i dentro y le d\i a Xinkss un susto incre\ible. Fue la risa.", "Lo malo es que tuve que esperar tres horas a que llegase."
    RETURN

# Puerta y arcón sólo aparecen con el armario abierto. Si no está
# abierto ni siquiera se crean las zonas, así que no tenemos que
# hacer más comprobaciones, llegados a este punto.

# PUERTA
:OFFICE_MIRAR_PUERTA
    TEXTWT BOTTOM, "Una puerta de madera maciza. Tu culo me hipnotiza."
    RETURN
    
:OFFICE_ACCION_PUERTA
# Cerramos el armario. Cambiamos el valor de $2 y luego volvemos adonde se dibuja la escena.
    $2 = 0
    GOTO :OFFICE_SCRSETUP

# ARCON
:OFFICE_MIRAR_ARCON
# ¿Tenemos las bragas?
    HASITEM "BRAGAS", :OFFICE_MIRAR_ARCON_BRAGAS
    TEXTWT BOTTOM, "Es el arc\on donde guardamos la ropa. Est\a casi vac\io, s\olo quedan unas bragas."
    RETURN
    
:OFFICE_MIRAR_ARCON_BRAGAS
    TEXTWT BOTTOM, "Es el arc\on donde guardamos la ropa. Est\a vac\io."
    RETURN
    
:OFFICE_ACCION_ARCON
# ¿Tenemos las bragas?
    HASITEM "BRAGAS", :OFFICE_ACCION_ARCON_BRAGAS
    TEXTWT BOTTOM, "Me llevo las bragas. Son de Xinkss, pero las que llevo est\an sucias y no me gusta ir a trabajar con las bragas sucias."
    GRAB "BRAGAS"
    RETURN
    
:OFFICE_ACCION_ARCON_BRAGAS
    TEXTWT BOTTOM, "No tengo nada m\as que hacer con el arc\on."
    RETURN
    
# Cosas con el inventario.
# Las acciones de inventario generan llamadas a estas etiquetas:

:OFFICE_USAR_ID_EN_ARMARIO
# ¿Está el armario abierto?
    GOTOF :OFFICE_USAR_ID_EN_ARMARIO, 2

:OFFICE_USAR_ID_EN_ARMARIO_0
    TEXTWT BOTTOM, "\!Buena idea! Con esta tarjeta puedo intentar abrir el cierre del armario.", "Veamos..."
# Sonido. BG = background, FG = foreground (esperar a que acabe)
    SOUND SFX\DOOR.VOC BG
# Pintamos el armario abierto
    PUT 9, 40, GFX\SCR00C1.PUT
    BLIT
    TEXTWT BOTTOM, "\!VOIE LA!"
    $2 = 1
# Nos vamos a la definición de zonas, porque cambian... 
    GOTO :OFFICE_ZONESETUP

:OFFICE_USAR_ID_EN_ARMARIO_1
    RETURN  
    
:OFFICE_USAR_LLAVE_EN_ARMARIO
    TEXTWT BOTTOM, "Esa llave no es de este armario. Es de la furgoneta", "Cre\ia que lo hab\ia dicho ya..."
    RETURN
    
:OFFICE_DEFAULT_ACTION
# DOACTIONS saltará a _DEFAULT_ACTION si hay una acción que hacer
# pero no hay etiqueta correspondiente. Así no tenemos que programar todas
# las combinaciones de usar objetos en sitios
    TEXTWT BOTTOM, "Mejor no."
    RETURN
    
# FUERA
# Aquí hacemos las comprobaciones de que lo hemos hecho todo, y terminamos.
:OFFICE_IR_FUERA

# ¿Tenemos las llaves?
    HASITEM "LLAVE", :OFFICE_IR_FUERA_LLAVE
    TEXTWT BOTTOM, "No puedo irme sin las llaves de la furgoneta. Paso de ir andando."
    RETURN
    
:OFFICE_IR_FUERA_LLAVE

# ¿Tenemos las bragas?
    HASITEM "BRAGAS", :OFFICE_IR_FUERA_BRAGAS
    TEXTWT BOTTOM, "Hmmm... Mejor que no. Tengo las bragas sucias. No me gusta ir a la calle con las bragas sucias.", "Tengo que buscar unas."
    RETURN
    
:OFFICE_IR_FUERA_BRAGAS
    MUSIC STOP

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s