JUEGO ACABADO (a falta de un par de detallitos).

For all things Churrera. ¿Estás haciendo un juego? ¿quieres proponer un cambio? ¿tienes alguna duda? ¡Cuéntanoslo!

Moderador: na_th_an

Avatar de Usuario
fjpoyato
Mensajes: 23
Registrado: Sab, 09 Ago 2014, 14:17
Ubicación: Huelva
Contactar:

JUEGO ACABADO (a falta de un par de detallitos).

Mensajepor fjpoyato » Mar, 21 Oct 2014, 16:16

Hola a todos, pese al escaso tiempo del que dispongo normalmente, he conseguido acabar por fin mi juego que ya está colgado en mi web, con carátula e instrucciones, música propia y resolví con éxito las dudas que os planteé hace tiempo. El resultado final ha sido para mí muy satisfactorio, así que muchísimas gracias por todo.

Sin embargo, y antes de ofreceros mi obra, creo que aún le faltan un par de guindas al pastel para que quede perfecto del todo y espero podáis ayudarme, veréis: el caso es que lo diseñé de tal forma que hay partes del juego donde es posible bloquearse en la partida (son pocas, tranquilos, pero lo hice intencionadamente muahahahhh) bien por olvidar alguna llave o bien por caer en algún sitio y no poder salir, lo cual obliga a forzar el game over o resetear (en el phantomas 2 creo recordar que en algún sitio caías y a la porra todas tus vidas), quisiera poder abortar la partida y volver al menú pulsando una tecla (la "a" mismo que no se utiliza en el juego u otra) y un mensaje de confirmación tipo "¿Estás seguro? (S/N)", no sé hacer esto y mira que he mirado bien, no es plan de cargarme nada a estas alturas.

Y otra cuestión: hay pantallas que tienen 2 enemigos o alguna incluso 1, lo cual hace que en dicha pantalla el juego vaya más acelerado y dificulta un pelin más eso de precisar el salto y controlar al personaje, ¿existe alguna forma de ralentizar un poco esa pantalla o hacer como si tuviera 3 enemigos aunque ponga menos? no sé si me explico bien.

A ver si es posible todo esto y si no, pues lo dejo tal y como está, ya veréis de que va la cosa y os pasaré enlace para que vayáis probando, el juego creo que es muy original y dificilillo (si no a ver donde está la gracia) y me lo acabo en unos 17 minutos. Estoy ya muy contento con todo lo conseguido, y más tratándose del primero, espero que hayan muchos más.

Saludos a todos, y la próxima vez no olvidaré traerme mi corchoneta de sartar, jeje :-p
Gocho
Mensajes: 123
Registrado: Mar, 19 Nov 2013, 10:32

Re: JUEGO ACABADO (a falta de un par de detallitos).

Mensajepor Gocho » Sab, 25 Oct 2014, 22:54

Para lo de abortar puedes entrar en config.h y descomentar:

#define PAUSE_ABORT // Add h=PAUSE, y=ABORT

Lo que hace es que la tecla "h" pausa el juego y la tecla "y" permite abortar. Que imagino que es lo que quieres.

Lo de acelerarse a mi no se me ha dado en ningún de mis juegos y tengo pantallas con 0,1,2 y 3 enemigos.
¿No será del emulador?

PD;
Eso de poder quedarte atrancado y tener que resetear me parece que es tener muy mala leche :bronca: :D
Avatar de Usuario
na_th_an
Mensajes: 26413
Registrado: Vie, 09 Ene 2009, 12:18

Re: JUEGO ACABADO (a falta de un par de detallitos).

Mensajepor na_th_an » Sab, 25 Oct 2014, 23:53

Un poco sí se acelera. Explico por qué: el motor no está sincronizado con nada. Cada cuadro de juego dura lo que tarda en "pasar" todo: lógica, render... Originalmente, las pantallas con menos de tres enemigos tenían cuadros más "cortos", ya que había menos que procesar y menos que dibujar (que es lo que más tiempo dura). Intenté atenuar un poco el efecto haciendo que cuando hay menos de tres enemigos, los que faltan se siguen "procesando" (aunque no se haga nada) y "dibujando" (aunque lo que se dibuja es un sprite totalmente "vacío"). El problema es que, tal y como funciona la biblioteca gráfica, lo que se hace es redibujar en cada cuadro las celdas de carácter que han cambiado, y sólo estas. Como los sprites de enemigos que no existen, aunque se dibujen, no se mueven, pues no se actualizan. Además, dependiendo de la configuración del motor hay más o menos cosas pasando a la vez. Si usas disparos, cuando hay muchos disparos en pantalla notarás más aún el efecto. Lamentablemente, poco hay que pueda hacer (cuando digo esto, me refiero a que poco hay que sea factible y no añada centenares de bytes, que hay poco espacio).

Sobre los otros temas, ahora mismo no puedo atenderte, pero intentaré hacerlo a partir del lunes. Seguro que un par de cosas podemos apañar fácilmente :)
Como diría Rorshach: "Urm..."
Avatar de Usuario
fjpoyato
Mensajes: 23
Registrado: Sab, 09 Ago 2014, 14:17
Ubicación: Huelva
Contactar:

Re: JUEGO ACABADO (a falta de un par de detallitos).

Mensajepor fjpoyato » Dom, 26 Oct 2014, 00:53

Muchas gracias, estaré pendiente pues. Estoy deseando enseñaros todo mi trabajo ;-)
Avatar de Usuario
fjpoyato
Mensajes: 23
Registrado: Sab, 09 Ago 2014, 14:17
Ubicación: Huelva
Contactar:

Re: JUEGO ACABADO (a falta de un par de detallitos).

Mensajepor fjpoyato » Mié, 29 Oct 2014, 14:46

Bueno, pues ya he metido la posibilidad de pausar y abortar el juego sin mensaje de confirmación, así queda ya muy bien y doy por finalizado el juego. Le voy a dar un retoquillo al manual y en breve os pasaré el enlace. Muchísimas gracias a todos ;-)
Avatar de Usuario
iforeve
Mensajes: 731
Registrado: Vie, 09 Ene 2009, 19:49

Re: JUEGO ACABADO (a falta de un par de detallitos).

Mensajepor iforeve » Mié, 29 Oct 2014, 14:57

:ymca:
Avatar de Usuario
na_th_an
Mensajes: 26413
Registrado: Vie, 09 Ene 2009, 12:18

Re: JUEGO ACABADO (a falta de un par de detallitos).

Mensajepor na_th_an » Jue, 30 Oct 2014, 07:56

Vaya, he estado muy liado y al final se me terminó pasando. Si todavía estás interesado, podemos hacerle el apaño.

Personalmente, opino que un juego debe ser difícil pero también debe ser justo. Un jugador, a mi vez, no se sentirá frustrado si el juego responde a su habilidad y no hay situaciones donde la suerte o una trampa acaban contigo. Por eso evito este tipo de cosas, como enemigos pegados al borde de la pantalla que no puedes evitar porque no ves antes de saltar desde la pantalla contigua, o situaciones de no vuelta atrás. A lo más, hemos hecho que haya situaciones en las que volver sea jodidamente difícil, pero siempre posible. De todos modos, es mi opinión :)
Como diría Rorshach: "Urm..."
Avatar de Usuario
fjpoyato
Mensajes: 23
Registrado: Sab, 09 Ago 2014, 14:17
Ubicación: Huelva
Contactar:

Re: JUEGO ACABADO (a falta de un par de detallitos).

Mensajepor fjpoyato » Jue, 30 Oct 2014, 20:16

No te preocupes, cambié de opinión, no voy a recargarlo más y queda bastante bien ya sin ese mensaje de confirmación a la hora de abortar la partida. El juego no creo que sea más difícil que el Abu Simbel por poner un ejemplo, me lo termino en unos 18 minutos. Muy pronto os pasaré una partida grabada en formato rzx o enlace a youtube, ya veré (eso por si alguien se impacienta un poco jeje).

Ya le he pillado el truco a esto y espero hacer juegos aún mejores cuando disponga de más tiempo. Me encantaría portarlo a Amstrad CPC así que estaré pendiente del tutorial ese que estáis elaborando. Muchas gracias por todo ;-)
Avatar de Usuario
na_th_an
Mensajes: 26413
Registrado: Vie, 09 Ene 2009, 12:18

Re: JUEGO ACABADO (a falta de un par de detallitos).

Mensajepor na_th_an » Vie, 31 Oct 2014, 08:51

Puf, el tutorial va a tardar porque lo estaba escribiendo yo, tengo muy poco tiempo libre, y el que tengo lo tengo que aprovechar y dosificar muy bien... Y eso está muy abajo en la lista.

PERO.

La última vez que porté de Spectrum a CPC hice un diario de lo que iba haciendo. Es en lo que me iba a basar para hacer el tutorial... A lo mejor te sirve. No es para un juego "churrera" (es para un juego que se nos ha agostado pero que SALDRÁ algún día), pero es casi lo mismo... Aquí va:

$this->bbcode_second_pass_code('', 'Conversión a CPC:

1.- Lo primero que hago son los gráficos y tal. Las conversiones
son las siguientes:

a) Para el spriteset, pongo todos los sprites de 8x16 seguidos en
un archivo, y luego escribo la paleta que uso en la configuración
Img2CPC.exe.config. También pongo el color de máscara.

Los convierto con:

$ Img2CPC.exe /w=8 /h=16 /bn=spr_ /m=0 /o=workspr.h spriteset.png

No voy a usar máscaras porque me da igual desperdiciar un color en
modo 0. Para modo 1, el archivo de sprites debería tener las máscaras
intercaladas y luego habría que llamar a arrangespr.exe para obtener
los sprites reordenados en tempspr.h

Con esto obtengo tempsr.h que copio a /dev/spriteset.h

Lo que sí tengo que hacer es cambiar los punteros externos generados
de spriteset.h para que apunten a la cabecera y no a los datos de los
sprites:

extern unsigned char spr_0 [];
extern unsigned char spr_1 [];
extern unsigned char spr_2 [];
extern unsigned char spr_3 [];
extern unsigned char spr_4 [];
extern unsigned char spr_5 [];
extern unsigned char spr_6 [];
extern unsigned char spr_7 [];
extern unsigned char spr_8 [];
extern unsigned char spr_9 [];
extern unsigned char spr_10 [];
extern unsigned char spr_11 [];
extern unsigned char spr_12 [];
extern unsigned char spr_13 [];
extern unsigned char spr_14 [];
extern unsigned char spr_15 [];
extern unsigned char spr_16 [];
extern unsigned char spr_17 [];
extern unsigned char spr_18 [];
extern unsigned char spr_19 [];
extern unsigned char spr_20 [];

También borro unsigned char *tiles [] que no sirve pa ná.

b) Para el tileset, los pongo reordenados como siempre (por caracter
de 4x8) en tileset.png y lo convierto con:

$ Img2CPC.exe /w=4 /h=8 /bn=tile_ /m=0 /o=tilesetwork.h tileset.png

Luego los recorto y los meto en tilemapconf.asm, al final, y no
me olvido de sustituir la linea que define el tamaño de cada tile por
"NADA" para eliminarlos.

c) Dibujo título/marco, lo salvo en BMP. Abro ConvImgCPC y edito
la paleta. De camino apunto los números:

0, 1, 2, 11, 23, 26, 25, 15, 7, 6, 4, 3, 13, 9, 21, 16

Luego los tendré que convertir a colores firmware para meter en el güego.

Abro el marco.bmp, marco todos los colores de la paltea, lo convierto y
lo guardo como marco.scr.

Este archivo tiene una cabecera AMSDOS que tengo que quitar. Ejecuto
striphd.exe (cutre como él solo) y escribo "marco" para abrirlo.

Comprimo el marco con exomizer:

$ exo marco.bin ..\dev\marcoc.exo

d) Cojo los niveles de Sir Ababol (mapa+enems), level0.bin y level1.bin,
y los comprimo con exomizer:

$ exo level0.bin ..\dev\level0c.exo
$ exo level1.bin ..\dev\level1c.exo

e) Revisando tilemapconf.asm (pillado del Cheril CPC) me doy cuenta de que
los buffers empiezan en $100 y ocupan 9600 bytes, por eso compilaba en la
dirección 10000. Voy a dejar 2220 bytes para descomprimir los niveles, por
lo que los niveles se descomprimen en 10000 y mi binario deberá empezar en
12220.

En 9700 dejo 300 bytes ahí sueltos para usarlos si los necesito (por ejemplo,
podría mover los buffers de pantalla ahí si es necesario).

f) Ahora empiezo a convertir el código. Primero toda la inicialización.
Abro ababol2.c para empezar. Me guío con chbcpc.c, la conversión que hicimos
en 2010 de Cheril of the Bosque, porque va en el mismo modo y usa la misma
configuración de pantalla.

#include <cpcrslib.h> // Biblioteca de juego por Artaburu.
#include <cpcwyzlib.h> // Replayer AY por WYZ, adaptado por Artaburu.
//#include "sound.h" // Configuración del replayer AY.

(sound.h lo descomentaré/haré luego, por ahora voy a dejar esto funcionando
aunque sea sin sonido).

f2) Cambiar importación de los gráficos:

// Graphics
#include "spriteset.h"

f3) Cambiar pantalla comprimida en pantalla.h

g) En definitions.h, quito las definiciones de splib2 y pongo las de CPCRSLIB:

// (CPC)

struct sprite { // estructura mínima para usar la librería de dibujar sprites capturando el fondo.
int sp0; // 2 bytes 01
int sp1; // 2 bytes 23
int coord0; // 2 bytes 45 posición en superbuffer
int coord1; // 2 bytes 67 posición anterior en superbuffer
unsigned char cx, cy; // 2 bytes 89 coordenadas nuevas
unsigned char ox, oy; // 2 bytes AB coordenadas actuales
unsigned char move; // 1 byte C si NO es 0, el sprite se mueve.
unsigned char move1; // 1 byte D si NO es 0, el sprite se mueve.
// Si es 8 en cuanto se mueva se pone a 0 y ya no se mueve más hasta nueva orden.
};

struct sprite sp_player;
struct sprite sp_enem1;
struct sprite sp_enem2;
struct sprite sp_enem3;

extern unsigned char tile_0 []; // Tiles en tilemapconf

// EOFCPC

También añado las teclas:

#define KEY_IZQ 0
#define KEY_DER 1
#define KEY_ARR 2
#define KEY_SAL 3
#define KEY_FIR 4
#define KEY_ESC 5

Y quitamos los malloc y demás cosas.

También hay que poner explícitamente "signed" todas las variables
globales que no lo tengan puesto.

h) En mysystem.h voy a poner las funciones y definiciones que necesito
específicas de CPC:

h1) Tintas:


/*
Tabla de conversión de paletas

firm hard
0 20
1 4
2 21
3 28
4 24
5 29
6 12
7 5
8 13
9 22
10 6
11 23
12 30
13 0
14 31
15 14
16 7
17 15
18 18
19 2
20 19
21 26
22 25
23 27
24 10
25 3
26 11
*/

unsigned char my_inks [] = {
20, 4, 21, 23, 27, 11, 3, 14,
5, 12, 24, 28, 0, 22, 26, 7
};
void set_inks () {
unsigned char i;
for (i = 0; i < 16; i ++)
cpc_SetColour (i, my_inks [i]);
}

h2) Interrupciones:

void halt_me (void) {
#asm
halt ;unas esperas para que se actualicen los colores
halt
halt
halt
halt
#endasm
}

void interrupciones(void) {
// Manejador de interrupciones por Artaburu
#asm
di
ld hl,($0039)
ld (_int_original),HL //guardo el salto original

ld HL,_interrupcion
ld ($0039),HL
ei
jp term
._int_original
defw 0
._interrupcion
ei
ret
.term
#endasm
}

h3) void init_cpcrslib_system () llama a estas cosas para inicializar.
También llamará a wyz player, pero eso aún no.

void init_cpcrslib_system () {
interrupciones ();
halt_me ();

set_inks ();
halt_me ();
cpc_SetMode (0);

// Inicializamos el player de wyz:

//cpc_WyzInitPlayer (wyz_sound_table, wyz_pattern_table, wyz_effects_table, wyz_song_table);

// Batería por el canal C

//cpc_WyzConfigurePlayer (0);

// Ponemos el buffer en un sitio adecuado:

//cpc_WyzSetBuffer (0xA500);

// Borde

cpc_SetColour (16, 20);

// Redefinimos las teclas

cpc_AssignKey (KEY_IZQ, 0x4820); // A
cpc_AssignKey (KEY_DER, 0x4720); // D
cpc_AssignKey (KEY_ARR, 0x4708); // W
cpc_AssignKey (KEY_SAL, 0x4440); // M
cpc_AssignKey (KEY_FIR, 0x4540); // N
cpc_AssignKey (KEY_ESC, 0x4804); // ESC
}

i1) En ababol2.c, cambiamos el bucle principal para usar CPCRSLIB. Esto
descomprime el título y wait_a_bit la pulsación de una tecla. Por ahora, dejamos
comentado las llamadas al player...

Si te fijas, antes se descomprime la canción en 0xB400. Esta dirección apunta a
una reserva de algo más de 3Kb al final de la RAM, justo antes de la pantalla
(que está en 0xC000). Nosotros tendremos las canciones comprimidas con exomizer
y las descomprimiremos en ese área. El player está configurado para que todas
las canciones las cargue justo de ahí. Así "lo timamos" y podemos usar canciones
comprimidas.

Si al final ves que tu MUS más grande ocupa menos de 3Kb, puedes subir 0xB400
más arriba y dejar libre más RAM para tu güego.

cpc_UnExo ((unsigned int *) (s_title), (unsigned int *) (0xc000));

//cpc_UnExo ((unsigned int *) (song_0), (unsigned int *) (0xB400));
//cpc_WyzLoadSong (0);
//cpc_WyzSetPlayerOn ();

espera_activa (32767);

//cpc_WyzSetPlayerOff ();

i2) También dejamos comentado en el sitio correcto (bucle principal) la llamada a cargar y tocar la música ingame,
así como pararla después.

/ Poner música ingame
//cpc_UnExo ((unsigned int *) (song_1), (unsigned int *) (0xB400));
//cpc_WyzLoadSong (1);
//cpc_WyzSetPlayerOn ();

it = game ();

// Parar música ingame
//cpc_WyzSetPlayerOff ();

i3) En mysystem.h, añadimos la inicialización de sprites:

void init_sprites () {
// Initialize CPCRSLIB sprites

sp_player.sp0 = (int) (spr_0);
sp_player.sp1 = (int) (spr_0);
sp_player.move = 0;
sp_player.cx = sp_player.ox = (player.x >> 6) >> 2;
sp_player.cy = sp_player.oy = (player.y >> 6);

sp_enem1.sp0 = (int) (spr_8);
sp_enem1.sp1 = (int) (spr_8);
sp_enem1.move = 0;
sp_enem1.cx = sp_enem1.ox = 0;
sp_enem1.cy = sp_enem1.oy = 0;

sp_enem2.sp0 = (int) (spr_8);
sp_enem2.sp1 = (int) (spr_8);
sp_enem2.move = 0;
sp_enem2.cx = sp_enem2.ox = 0;
sp_enem2.cy = sp_enem2.oy = 0;

sp_enem3.sp0 = (int) (spr_8);
sp_enem3.sp1 = (int) (spr_8);
sp_enem3.move = 0;
sp_enem3.cx = sp_enem3.ox = 0;
sp_enem3.cy = sp_enem3.oy = 0;

sp_sword.sp0 = (int) (spr_8);
sp_sword.sp1 = (int) (spr_8);
sp_sword.move = 0;
sp_sword.cx = sp_sword.ox = 0;
sp_sword.cy = sp_sword.oy = 0;
}

Hay que mover la llamada a esta función al sitio correcto, justo
antes de empezar el bucle de juego, en engine.h (game)

load_level ();
init_game ();
init_sprites ();

j) Vamos a adaptar la descompresión de niveles y el dibujado de las
pantallas. Abrimos levelmanager.h

j1) Direcciones. El BUFFER en CPC está en 10000, lo ponemos ahí:

#define BUFFER 10000

j2) Niveles comprimidos, son level0c.exo y level1c.exo:

// Compressed levels
// Levels are map + enems, compressed.
extern unsigned char clevel0 [0];
extern unsigned char clevel1 [0];
#asm
._clevel0
BINARY "level0c.exo"
._clevel1
BINARY "level1c.exo"
#endasm
unsigned char *clevels [] = {
clevel0, clevel1
};

j3) Llamada a descomprimir:

void load_level () {
// Unpack level
cpc_UnExo ((unsigned int *) clevels [level], (unsigned int *) (BUFFER));
}

j4) Inicialización de sprites enemigos. Cada sprite en CPC ocupa 66 bytes,
y no usan máscara (modo 0). Con este valor puedo calcular cualquier frame...
Multiplico por 132 para saltarme un frame, of cors.

Los sprites son:
8, 9: enemigo tipo 1
10, 11: enemigo tipo 2
12, 13: enemigo tipo 3
14, 15: enemigo tipo 4
16: explosión
17: vacío
19, 20, 21: espada

j5) Añadimos el reseteado del tilemap al terminar de pintar la pantalla:

cpc_ResetTouchedTiles ();
cpc_ShowTileMap (0);

k) Modificamos rutinas de impresión de tiles de printer.h

k1) Cambiamos draw_map_tile para que use CPCRSLIB. Usamos sencillas
llamadsa a cpc_SetTile:

void draw_map_tile (unsigned char x, unsigned char y, unsigned char t) {
if (t == 0) {
if (y >= 5) {
tl_y = y - 5;
t = backdrop [x + (tl_y << 4) - tl_y];
}
}
if (t == 0 && (rand () & 15) == 1) t = 18;

st_index = 64 + (t << 2);
x = (x << 1);
y = (y << 1);

cpc_SetTile (x, y, st_index ++);
cpc_SetTile (x + 1, y, st_index ++);
cpc_SetTile (x, y + 1, st_index ++);
cpc_SetTile (x + 1, y + 1, st_index);
}

Hay que crear otra que imprima los tiles invalidados. La original se usa
para pintar la pantalla, y la de tiles invalidados en cualquier otro caso:

void draw_map_tile_inv (unsigned char x, unsigned char y, unsigned char t) {
if (t == 0) {
if (y >= 5) {
tl_y = y - 5;
t = backdrop [x + (tl_y << 4) - tl_y];
}
}
if (t == 0 && (rand () & 15) == 1) t = 18;

st_index = 64 + (t << 2);
x = (x << 1);
y = (y << 1);

cpc_SetTouchTileXY (x, y, st_index ++);
cpc_SetTouchTileXY (x + 1, y, st_index ++);
cpc_SetTouchTileXY (x, y + 1, st_index ++);
cpc_SetTouchTileXY (x + 1, y + 1, st_index);
}

k2) Cambiamos print_number2 y print_number3. Como estas rutinas imprimen,
por lo general, fuera del área tileada del juego, hay que usar llamadas a
cpc_PutTile2x8 y recordar que el primer tile está apuntado por tile_0.

Aquí, las coordenadas X van en bytes y las Y en pixels. Hay que ajustar
32 pixels (4 bytes) horizontalmente y 4 píxels verticalmente. Las coordenadas
en caracter hay que multiplicarlas por 2 horizontalmente (1 char = 2 bytes)
y por 8 verticalmente (1 char = 8 píxels).

Recordar, para los cálculos, que cada tile ocupa 16 bytes.

Por cosas de esta versión de CPCRSLIB, el puntero es unsigned int *, y las
coordenadas unsigned char *...

void print_number2 (unsigned char x, unsigned char y, unsigned char number) {
cpc_PutTile2x8 (
(unsigned int *) (tile_0 + ((16 + (number / 10)) << 4)),
(unsigned char *) (8 + (x << 1)),
(unsigned char *) (4 + (y << 3))
);

cpc_PutTile2x8 (
(unsigned int *) (tile_0 + ((16 + (number % 10)) << 4)),
(unsigned char *) (8 + (x << 1) + 2),
(unsigned char *) (4 + (y << 3))
);
}

void print_number3 (unsigned char x, unsigned char y, unsigned char number) {
sp_PrintAtInv (y, x, 5, 16 + (number / 100));
sp_PrintAtInv (y, x + 1, 5, 16 + ((number % 100) / 10));
sp_PrintAtInv (y, x + 2, 5, 16 + (number % 10));

cpc_PutTile2x8 (
(unsigned int *) (tile_0 + ((16 + (number / 100)) << 4)),
(unsigned char *) (8 + (x << 1)),
(unsigned char *) (4 + (y << 3))
);

cpc_PutTile2x8 (
(unsigned int *) (tile_0 + ((16 + ((number % 100) / 10)) << 4)),
(unsigned char *) (8 + (x << 1) + 2),
(unsigned char *) (4 + (y << 3))
);

cpc_PutTile2x8 (
(unsigned int *) (tile_0 + ((16 + (number % 10)) << 4)),
(unsigned char *) (8 + (x << 1) + 4),
(unsigned char *) (4 + (y << 3))
);
}

k3) De la misma forma, cambiamos la rutina que imprime textos, con lo
mismo en mente...

void print_str (unsigned char x, unsigned char y, unsigned char c, unsigned char *s) {
while (*s) {
cpc_PutTile2x8 (
(unsigned int *) (tile_0 + ((*s ++) << 4)),
(unsigned char *) (8 + (x << 1) + 4),
(unsigned char *) (4 + (y << 3))
);
x ++;
}
}

k4) La de blackout_area la hacemos así:
void blackout_area (void) {
gpx = gpy = 0;
for (it = 0; it < 150; it ++) {
draw_map_tile (gpx, gpy, 0);
gpx ++;
if (gpx == 15) {
gpy ++;
gpx = 0;
}
}
cpc_ResetTouchedTiles ();
cpc_ShowTileMap (0);
}


l) Modificamos algunas rutinas auxiliares en basio.h

void __FASTCALL__ wnokey (unsigned int key) {
while (cpc_TestKey (key));
}

char wait_a_bit (int espera) {
// Waits until "espera" halts have passed
// or a key has been pressed.
sp_WaitForNoKey ();
for (it = 0; it < espera; it ++) {
jt = 250; do { gpc = 1; } while (--jt);
if (cpc_AnyKeyPressed ()) return 0;
}
return 1;
}

m) Ahora viene lo más complicado: modificar los movimientos de
sprites y el render del bucle principal, así como las pulsaciones
de teclas... Abrimos engine.h

m0) Comento todos los peta_el_beeper !!

m1) Sprites!

m1.1) La espada, en render_sword. La vamos a quitar de aquí y la
vamos a mover al bucle principal. Aquí sólo la movemos, quitamos la
llamada a splib y listo.

m1.2) El render del bucle principal. La actualización de los sprites
en el tilemap tiene su castaña, y hay que cambiar bastantes cosas.
Primero hay que actualizar los sprites, luego se actualiza el tilemap,
luego se vuelcan los sprites al tilemap, y luego se muestra por pantalla.

// Render
if (s_on) render_sword ();

if (p_state == EST_PARP) {
if (half_life)
p_next_frame = spr_17;
p_counter --;
if (p_counter == 0)
p_state = EST_NORMAL;
}

sp_player.sp0 = (int) (p_next_frame);
sp_player.cx = player.x >> 8;
sp_player.cy = player.y >> 6;

if (malotes [enoffs].t < 6) {
px = malotes [enoffs].x >> 2;
py = malotes [enoffs].y;
} else {
px = en_an_x [0] >> 8;
py = en_an_y [0] >> 6;
}
sp_enem1.sp0 = (int) (en_an_next_frame [0]);
sp_enem1.cx = px;
sp_enem1.cy = py;

if (malotes [enoffs + 1].t < 6) {
px = malotes [enoffs + 1].x >> 2;
py = malotes [enoffs + 1].y;
} else {
px = en_an_x [1] >> 8;
py = en_an_y [1] >> 6;
}
sp_enem2.sp0 = (int) (en_an_next_frame [1]);
sp_enem2.cx = px;
sp_enem2.cy = py;

if (malotes [enoffs + 2].t < 6) {
px = malotes [enoffs + 2].x >> 2;
py = malotes [enoffs + 2].y;
} else {
px = en_an_x [2] >> 8;
py = en_an_y [2] >> 6;
}
sp_enem3.sp0 = (int) (en_an_next_frame [2]);
sp_enem3.cx = px;
sp_enem3.cy = py;

if (s_on) {
sp_sword.sp0 = (int) (s_next_frame);
sp_sword.cx = s_x >> 2;
sp_sword.cy = s_yy;

cpc_PutSpTileMap ((int) (sp_sword));
}
cpc_PutSpTileMap ((int) (sp_player));
cpc_PutSpTileMap ((int) (sp_enem1));
cpc_PutSpTileMap ((int) (sp_enem2));
cpc_PutSpTileMap ((int) (sp_enem3));

cpc_UpdScr ();

// Mode 0
if (s_on) {
cpc_PutTrSp8x16TileMap2b ((int) (sp_sword));
}
cpc_PutTrSp8x16TileMap2b ((int) (sp_player));
cpc_PutTrSp8x16TileMap2b ((int) (sp_enem1));
cpc_PutTrSp8x16TileMap2b ((int) (sp_enem2));
cpc_PutTrSp8x16TileMap2b ((int) (sp_enem3));

// Update
cpc_ShowTouchedTiles ();
cpc_ResetTouchedTiles ();

m1.3) Modificamos los frames de Sir Ababol...

// 0 1 2 3 + facing: walk, 1 = stand. 8 + facing = jump/fall
unsigned char *player_frames [] = {
spr_0, spr_1, spr_2, spr_1,
spr_4, spr_5, spr_6, spr_5,
spr_3, spr_7
};

m1.4) Quitamos la parte al final del bucle principal que quita
los sprites de la pantalla, no es necesario aquí.

m2) Las teclas...
cpc_TestKey (KEY_ARR), etc... en "move"

m3) Ojo! Hay un problema que es fácil de resolver. Todo el display
de sprites está alineado a bytes. Cada byte equivale a 2 ladrillos, o
a 4 píxels virtuales (de la versión Spectrum). Para que lo que vemos y
la lógica sean consistentes, hay que ajustar el valor horizontal de
nuestro jugador a bytes de cuando en cuando: al saltar, y al no
pulsar izq/der...

m3.1) Salto:

if (cpc_TestKey (KEY_ARR) && p_saltando == 0 && possee) {
p_saltando = 1;
p_cont_salto = 0;
//peta_el_beeper (1);
p_x = (p_x >> 8) << 8;
}

m3.2) Al parar de movernos horizontalmente:
if ( ! (cpc_TestKey (KEY_IZQ) || cpc_TestKey (KEY_DER)))
if (p_vx > 0) {
p_vx -= prx;
if (p_vx < 0) {
p_x = (p_x >> 8) << 8;
p_vx = 0;
}
} else if (p_vx < 0) {
p_vx += prx;
if (p_vx > 0){
p_x = (p_x >> 8) << 8;
p_vx = 0;
}
}

m3.3) Cambiamos el cálculo de frame de los enemigos:

n) Cruzamos los dedos e intentamos compilar...

zcc +cpc TileMapConf-sa2.asm -create-app -O3 -unsigned -o ababol2a.bin ababol2a.c -lcpcrslib -lcpcwyzlib -zorg=12220

o) Para probar, hay que copiar el BIN en la carpeta de WinApe y cargarlo.
Supongo que habrá otra forma mejor, pero esta es la que yo me sé.

Se pulsa F3 y se escribe:

org 12220
nolist
incbin "ababol2a.bin"

Y se pulsa F9 (run).

Luego se hace un call 12220 desde BASIC para ejecutar

BIEN

p) Ahora es cuando vamos a meter el sonido. Pillamos las músicas y los
efectos y los preparamos:

p1) Músicas: vamos a pillar "ingame1" y "title" de la versión de 128K. Cogemos
los .mus y los comprimimos con exomizer. Lo copiamos todo a /dev

También pillamos un .mus.asm, y los efectos. Luego los usaremos en sound.h, en
el siguiente paso.

p2) Necesitamos crear un sound.h que cargue las canciones, defina los patrones
(instrumentos), efectos (batería) y efectos de sonido. Voy a coger la de Cheril
y la voy a adaptar a las músicas murcianas. Todo lo que necesito está en el
.mus.asm que me pillé y en efectos.asm...

q) Descomentamos la inicialización del player en mysystem.h

// Inicializamos el player de wyz:

cpc_WyzInitPlayer (wyz_sound_table, wyz_pattern_table, wyz_effects_table, wyz_song_table);

// Batería por el canal B

cpc_WyzConfigurePlayer (1);

r) Descomentamos las llamadas a cargar/tocar músicas de antes.

s) Llegados a este punto, probamos. El player siempre se me peliagudiza y mejor
ir pasito a pasito...

t) Vamos a colocar las llamadas para los efectos de sonido. Estas van todas en
engine.h, y hay que tener paciencia porque son un montón...

Buscamos los peta_el_beeper comentados, y ahí vamos colocando llamadas a la
función cpc_WyzStartEffect (WYZ_CANAL_EFECTOS, n);

Aprovechamos para definir WYZ_CANAL_EFECTOS en definitions.h

u) Anju va y se da cuenta de que la CPCRSLIB (al menos la versión vieja que
funciona con z88dk) no soporta clipping, así que tengo que modificar el render
de la espada para que nunca se salga de la pantalla.

// CPC: cuidao con salirse!!
if (s_x < 0) s_x = 0;
if (s_x > 224) s_x = 224;
if (s_y < 0) s_y = 0;

v) Sartar en la corshoneta.

w) Seguro que murcianow tiene algún cambio musical.

x, y, z) Habrá que pulir algunas aristas, pero ¡FIN!')
Como diría Rorshach: "Urm..."
Avatar de Usuario
fjpoyato
Mensajes: 23
Registrado: Sab, 09 Ago 2014, 14:17
Ubicación: Huelva
Contactar:

Re: JUEGO ACABADO (a falta de un par de detallitos).

Mensajepor fjpoyato » Vie, 31 Oct 2014, 22:25

¡Juer! vaya tochazo pero gracias, me das muchas pistas. Estoy ya acostumbradísimo a descifrar estos galimatías, intentaré hacer cositas cuando tenga más tiempo, pero te animo a que sigas con ese tutorial, estaremos pendientes. Saludos :-)

Volver a “La Churrera”

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 43 invitados