El hilo de las modificaciones a la Churrera

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
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

El hilo de las modificaciones a la Churrera

Mensajepor na_th_an » Mié, 18 Sep 2013, 11:11

Iré subiendo en este hilo modificaciones fáciles de hacer al motor de la Churrera que puedan servir de algo a gente que esté haciendo sus propios güegos. La Churrera es muy configurable, pero hay cosas que no se pueden hacer de entrada, pero que son fáciles de añadir y/o modificar.
Como diría Rorshach: "Urm..."
Avatar de Usuario
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

Re: El hilo de las modificaciones a la Churrera

Mensajepor na_th_an » Mié, 18 Sep 2013, 11:17

fjpoyato pregunta por el siguiente cambio:
fjpoyato escribió:Hola, quería preguntar cómo se hace para aumentar las vidas o energía cuando te toca un enemigo (al revés, de 1 a 15) y para disminuir las vidas en lugar de recargarlas, es que tengo pensado un juego para esto, no es broma. Felicidades por este estupendo tutorial, un saludo :-)


En un principio había pensado en modificar toda la gestión de vidas de la Churrera (inicialización, decremento e incremento, condición de Game Over) cuando me he dado cuenta de que es mucho más fácil timar al jugador. En lugar de mostrarle el número de vidas, se le mostrará PLAYER_LIFE - numero de vidas, con lo que se empezará en 0 y se terminará en el máximo. Cuando se pierda una vida parecerá que se gana y cuando se coja una recarga parecerá que se pierde.

Para realizar esta modificación, no hay más que tocar mainloop.h, a partir de la linea 193 (puede que cambie la linea si actualizamos la versión, busca algo parecido a esto):

Código: Seleccionar todo

if (player.life != life_old) {
    print_number2 (LIFE_X, LIFE_Y, player.life);
    life_old = player.life;
}


Debería quedar así:

Código: Seleccionar todo

if (player.life != life_old) {
    print_number2 (LIFE_X, LIFE_Y, PLAYER_LIFE - player.life);
    life_old = player.life;
}


Con esto, se consigue el efecto que se quería.
Como diría Rorshach: "Urm..."
Avatar de Usuario
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

Re: El hilo de las modificaciones a la Churrera

Mensajepor na_th_an » Mié, 02 Oct 2013, 07:59

D_Skywalk ha propuesto el siguiente cambio:

D_Skywalk escribió:Respecto a las recargas, [...] en la documentación dice algo como que las recargas salen aleatoriamente si el jugador ha cogido el objeto, en el lugar donde se puso el hotspot. Mi pregunta era que sabiendo que sólo puedo poner un hotspot por pantalla hay alguna forma sencilla donde se pueda configurar (usando un tipo nuevo?) que un hotspot sea recarga o sea objeto (trozo de espada).


Vamos a invalidar el comportamiento normal y a establecer la recarga como tipo de hotspot número 3. Usaremos el colocador para poner las recargas de vida como los objetos o las llaves, pero con tipo = 3.

En el código del motor hay que cambiar dos cosas: primero tendremos que modificar el código que dibuja el hotspot al entrar en una nueva pantalla. Este código está en la función draw_scr, a partir de la linea 1159 del archivo engine.h. Este trozo distingue si hemos cogido o no el objeto/llave/balas de un hotspot activo para mostrar el item correspondiente o, al azar, una recarga:

Código: Seleccionar todo

    if ((hotspots [n_pant].act == 1 && hotspots [n_pant].tipo) ||
        (hotspots [n_pant].act == 0 && (rand () & 7) == 2)) {
        hotspot_x = gpx << 4;
        hotspot_y = gpy << 4;
        orig_tile = map_buff [15 * gpy + gpx];
        draw_coloured_tile (VIEWPORT_X + gpx + gpx, VIEWPORT_Y + gpy + gpy, 16 + (hotspots [n_pant].act ? hotspots [n_pant].tipo : 0));
    }


Tendremos que modificarlo para que quede así:

Código: Seleccionar todo

    if (hotspots [n_pant].act == 1 && hotspots [n_pant].tipo) {
        hotspot_x = gpx << 4;
        hotspot_y = gpy << 4;
        orig_tile = map_buff [15 * gpy + gpx];
        draw_coloured_tile (VIEWPORT_X + gpx + gpx, VIEWPORT_Y + gpy + gpy, 16 + (hotspots [n_pant].tipo != 3 ? hotspots [n_pant].tipo : 0));
    }


Así, si el hotspot es de tipo 3 se dibujará el tile 16. Si vale 1, el 17 (objeto), si vale 2, el 18 (llave), y si vale 4, el 20 (munición). Ahora tenemos que cambiar la parte del código que se encarga de detectar cuando el jugador toca el hotspot. Este código se encuentra en el archivo mainloop.h, a partir de la linea 316. Se trata de mover el código que aparece en la primera rama del IF a un caso de la instrucción selectiva de más abajo. El código original es:

Código: Seleccionar todo

                // Was it an object, key or life boost?
                if (hotspots [n_pant].act == 0) {
                    player.life += PLAYER_REFILL;
                    if (player.life > PLAYER_LIFE)
                        player.life = PLAYER_LIFE;
                    hotspots [n_pant].act = 2;
                    peta_el_beeper (8);
                } else {                   
                    switch (hotspots [n_pant].tipo) {
#ifndef DEACTIVATE_OBJECTS                     
                        case 1:
#ifdef ONLY_ONE_OBJECT
                            if (player.objs == 0) {
                                player.objs ++;
                                peta_el_beeper (9);
                            } else {
                                peta_el_beeper (4);
                                draw_coloured_tile (VIEWPORT_X + (hotspot_x >> 3), VIEWPORT_Y + (hotspot_y >> 3), 17);
                                gpit = 1;
                            }
#else
                            player.objs ++;
                            peta_el_beeper (9);
                            break;
#endif
#endif
#ifndef DEACTIVATE_KEYS
                        case 2:
                            player.keys ++;
                            peta_el_beeper (7);
                            break;
#endif
#ifdef MAX_AMMO
                        case 4:
                            if (MAX_AMMO - player.ammo > AMMO_REFILL)
                                player.ammo += AMMO_REFILL;
                            else
                                player.ammo = MAX_AMMO;
                            peta_el_beeper (9);
                            break;
#endif
                    }
                    hotspots [n_pant].act = gpit;
                }
                hotspot_x = hotspot_y = 240;
            }   


Y debe quedar así:

Código: Seleccionar todo

                // Was it an object, key or life boost?
                if (hotspots [n_pant].act) {
                    switch (hotspots [n_pant].tipo) {
#ifndef DEACTIVATE_OBJECTS                     
                        case 1:
#ifdef ONLY_ONE_OBJECT
                            if (player.objs == 0) {
                                player.objs ++;
                                peta_el_beeper (9);
                            } else {
                                peta_el_beeper (4);
                                draw_coloured_tile (VIEWPORT_X + (hotspot_x >> 3), VIEWPORT_Y + (hotspot_y >> 3), 17);
                                gpit = 1;
                            }
#else
                            player.objs ++;
                            peta_el_beeper (9);
#endif
                            break;
#endif
#ifndef DEACTIVATE_KEYS
                        case 2:
                            player.keys ++;
                            peta_el_beeper (7);
                            break;
#endif
                        case 3:
                            player.life += PLAYER_REFILL;
                            if (player.life > PLAYER_LIFE)
                                player.life = PLAYER_LIFE;
                            peta_el_beeper (8);
                            break;
#ifdef MAX_AMMO
                        case 4:
                            if (MAX_AMMO - player.ammo > AMMO_REFILL)
                                player.ammo += AMMO_REFILL;
                            else
                                player.ammo = MAX_AMMO;
                            peta_el_beeper (9);
                            break;
#endif
                    }
                    hotspots [n_pant].act = gpit;
                }
                hotspot_x = hotspot_y = 240;
            }


(las explicaciones se refieren a al versión actual 3.99.1, es posible que las lineas bailen un poco en otras versiones)
Como diría Rorshach: "Urm..."
antoniovillena
Mensajes: 494
Registrado: Jue, 24 Oct 2013, 15:52

Re: El hilo de las modificaciones a la Churrera

Mensajepor antoniovillena » Lun, 27 Ene 2014, 11:20

Propongo los siguientes cambios para añadir soporte de mapas comprimidos. Son 4 cambios en total.

Cambio número 1. El archivo make.bat

En este caso habría que añadir la siguiente línea:

Código: Seleccionar todo

TmxCompress mapa.tmx mapa_comprimido.bin


Y en caso de trabajar con Mappy habría que hacer una previa conversión de .map a .tmx con algo así (adecuar los parámetros a tu juego):

Código: Seleccionar todo

Map2Tmx 5 4 15 10 99 mapa.tmx mapa.map


Las herramientas TmxCompress y Map2Tmx están aquí:
https://github.com/DSkywalk/fase/blob/m ... Compress.c
https://github.com/DSkywalk/fase/blob/m ... Compress.c

También hay que tener en cuenta que el mapa original debe ser ocultado mediante directivas de compilación y la constante COMPRESSED_MAPS, para ello sería conveniente modificar la herramienta MapCnv para que genere un mapa.h como este:

Código: Seleccionar todo

...
#ifdef COMPRESSED_MAPS
  unsigned char *mapa;
#else
  unsigned char mapa [] = {
...
};
#endif
...


Como yo trabajo con Tiled, utilizo la herramienta TmxCnv que ya viene correctamente preparada para ocultar el mapa original:
https://github.com/DSkywalk/fase/blob/m ... s/TmxCnv.c

También se puede modificar a mano mapa.h, pero es lo menos aconsejable.

Es muy importante tomar nota de los valores bitsym y bithlf que nos devuelve TmxCompress, puesto que tendremos que pasarlos a mano posteriormente a las directivas DMAP_BITSYMB y DMAP_BITHALF de config.h

Cambio número 2. El archivo config.h

Este archivo es el más sencillo de modificar, tan solo hay que añadir estas 4 líneas en cualquier parte del archivo:

Código: Seleccionar todo

#define COMPRESSED_MAPS
#define DMAP_BITSYMB  4
#define DMAP_BITHALF  0
#define DMAP_BUFFER   0x5b01


Evidentemente las constantes DMAP_BITSYMB y DMAP_BITHALF las debemos modificar con los valores que nos sugiera el compresor de mapas.

Cambio número 3. El archivo engine.h

Tenemos que cambiar este código:

Código: Seleccionar todo

...
void __FASTCALL__ draw_scr_background (void) {
#ifdef UNPACKED_MAP
    map_pointer = mapa + (n_pant * 150);
#else
    map_pointer = mapa + (n_pant * 75);
#endif
    srand (n_pant);
    gpx = gpy = 0; 

    // Draw 150 tiles
   
    for (gpit = 0; gpit < 150; gpit ++) {   
#ifdef UNPACKED_MAP
        // Mapa tipo UNPACKED
        gpd = *map_pointer ++;
        map_attr [gpit] = comportamiento_tiles [gpd];
        map_buff [gpit] = gpd;
#else
        // Mapa tipo PACKED
        if (!(gpit & 1)) {
            gpc = *map_pointer ++;
            gpd = gpc >> 4;
        } else {
            gpd = gpc & 15;
        }
        map_attr [gpit] = comportamiento_tiles [gpd];
        if (gpd == 0 && (rand () & 15) == 1) gpd = 19;
        map_buff [gpit] = gpd;
#endif 
#ifdef BREAKABLE_WALLS
        brk_buff [gpit] = 0;
#endif     
        draw_coloured_tile (VIEWPORT_X + gpx, VIEWPORT_Y + gpy, gpd);   
...


Por este otro:

Código: Seleccionar todo

...
void __FASTCALL__ draw_scr_background (void) {
#ifdef COMPRESSED_MAPS
    descomprimir_map( n_pant );
    map_pointer = DMAP_BUFFER;
#else
  #ifdef UNPACKED_MAP
    map_pointer = mapa + (n_pant * 150);
  #else
    map_pointer = mapa + (n_pant * 75);
  #endif
#endif
    srand (n_pant);
    gpx = gpy = 0; 

    // Draw 150 tiles
   
    for (gpit = 0; gpit < 150; gpit ++) {   
#ifdef UNPACKED_MAP
        // Mapa tipo UNPACKED
        gpd = *map_pointer ++;
        map_attr [gpit] = comportamiento_tiles [gpd];
        map_buff [gpit] = gpd;
#else
        // Mapa tipo PACKED
  #ifdef COMPRESSED_MAPS
    gpd = *map_pointer ++;
  #else
        if (!(gpit & 1)) {
            gpc = *map_pointer ++;
            gpd = gpc >> 4;
        } else {
            gpd = gpc & 15;
        }
  #endif
        map_attr [gpit] = comportamiento_tiles [gpd];
        if (gpd == 0 && (rand () & 15) == 1) gpd = 19;
        map_buff [gpit] = gpd;
#endif 
#ifdef BREAKABLE_WALLS
        brk_buff [gpit] = 0;
#endif     
        draw_coloured_tile (VIEWPORT_X + gpx, VIEWPORT_Y + gpy, gpd);   
...



Cambio número 4. Añadir la función descomprimir_map, donde queramos.

En principio D_Skywalk sugirió meter dicha función en aplib.h, aunque yo más adelante creé un archivo nuevo llamado compress.h con su correspondiente include. La función a añadir sería la siguiente:

Código: Seleccionar todo

#ifndef SCR_W
#define SCR_W 15
#endif

#ifndef SCR_H
#define SCR_H 10
#endif

#ifdef COMPRESSED_MAPS
void __FASTCALL__ descomprimir_map ( unsigned char pantalla) {
  #asm
        ld      a, l
        and     a
        ld      b, h
        ld      c, h
        ld      de, map
        ld      hl, fin-1
desc1:  sbc     hl, bc
        ex      de, hl
        ld      c, (hl)
        ex      de, hl
        inc     de
        dec     a
        jp      p, desc1
        ld      de, DMAP_BUFFER+SCR_W*SCR_H-1
        ld      b, $80          ; marker bit
desc2:  ld      a, 256 / 2^DMAP_BITSYMB
desc3:  call    gbit3           ; load DMAP_BITSYMB bits (literal)
        jr      nc, desc3
#if   (DMAP_BITHALF==1)
  #if   (DMAP_BITSYMB==1)
        rrca
        jr      nc, desc4
        xor     a
        call    gbit3
        inc     a
  #else
        rrca                    ; half bit implementation (ie 48 tiles)
        call    c, gbit1
  #endif
#else
        and     a
#endif
desc4:  ld      (de), a         ; write literal
desc5:  dec     e               ; test end of file (map is always 150 bytes)
        ret     z
        call    gbit3           ; read one bit
        rra
        jr      nc, desc2       ; test if literal or sequence
        push    de              ; if sequence put de in stack
        ld      a, 1            ; determine number of bits used for length
desc6:  call    nc, gbit3       ; (Elias gamma coding)
        and     a
        call    gbit3
        rra
        jr      nc, desc6       ; check end marker
        inc     a               ; adjust length
        ld      c, a            ; save lenth to c
        xor     a
        ld      de, SCR_W
        call    gbit3           ; get two bits
        call    gbit3
        jr      z, desc9        ; 00 = 1
        dec     a
        call    gbit3
        jr      z, descb        ; 010 = 15
        bit     2, a
        jr      nz, desc7
#if   (SCR_W>15)
        call    gbit3           ; [011, 100, 101] xx = from 2 to 13
        dec     a
        call    gbit3
        jr      desca
desc7:  call    gbit3           ; [110, 111] xxxxxx = from 14-15, 17-142
        jr      nc, desc7
        cp      SCR_W-14
        sbc     a, -14
#else
  #if   (SCR_W==15)
        add     a, $7c          ; [011, 100, 101] xx = from 2 to 13
        dec     e
desc7:  dec     e               ; [110, 111] xxxxxx = 14 and from 16 to 142
desc8:  call    gbit3
        jr      nc, desc8
        jr      z, descb
        add     a, e
  #else
        call    gbit3           ; [011, 100, 101] xx = from 2 to 11 and from 13 to 14
        call    gbit3
        cp      SCR_W+2
        sbc     a, 2
        jr      desc9
desc7:  call    gbit3           ; [110, 111] xxxxxx = from 15 to 142
        jr      nc, desc7
        add     a, 14
  #endif
#endif
desc9:  inc     a
desca:  ld      e, a
descb:  ld      a, b            ; save b (byte reading) on a
        ld      b, d            ; b= 0 because lddr moves bc bytes
        ex      (sp), hl        ; store source, restore destination
        ex      de, hl          ; HL = destination + offset + 1
        add     hl, de          ; DE = destination
        lddr
        pop     hl              ; restore source address (compressed data)
        ld      b, a            ; restore b register
        inc     e               ; prepare test of end of file
        jr      desc5           ; jump to main loop
#if   (DMAP_BITHALF==1 && DMAP_BITSYMB>1)
gbit1:  sub     $80 - (2^(DMAP_BITSYMB-2))
        defb    $da             ; second part of half bit implementation
#endif
gbit2:  ld      b, (hl)         ; load another group of 8 bits
        dec     hl
gbit3:  rl      b               ; get next bit
        jr      z, gbit2        ; no more bits left?
        adc     a, a            ; put bit in a
        ret
.map    BINARY "mapa_comprimido.bin"
.fin
  #endasm
}
#endif


Edito: Al final he preferido usar las constantes, los primeros ifndef están para definirlas en caso de que no existan. Así el código es definitivo y no hay que hacer futuros cambios.
Última edición por antoniovillena el Mar, 28 Ene 2014, 00:40, editado 1 vez en total.
Avatar de Usuario
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

Re: El hilo de las modificaciones a la Churrera

Mensajepor na_th_an » Lun, 31 Ago 2015, 12:10

Desactivar el tile alternativo

Consiste en desactivar la funcionalidad que, al azar, sustituye el tile 0 por el tile 19 al dibujar la pantalla. Para ello, abrimos engine.h y buscamos este texto (alrededor de la linea 1368 en la 3.99.3d):

Código: Seleccionar todo

      if (gpd == 0 && (rand () & 15) == 1) gpd = 19;


Y lo comentamos:

Código: Seleccionar todo

      //if (gpd == 0 && (rand () & 15) == 1) gpd = 19;
Como diría Rorshach: "Urm..."
Avatar de Usuario
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

Re: El hilo de las modificaciones a la Churrera

Mensajepor na_th_an » Lun, 31 Ago 2015, 12:37

Desactivar plataformas en vista lateral

Para desactivar las plataformas en vista lateral, obteniendo más espacio libre y la posibilidad de usar el último par de sprites como un cuarto enemigo, hay que comentar algunos bloques de código en engine.h.

En primer lugar habrá que comentar todo el bloque de código que detecta las plataformas para realizar el arrastre. Este bloque empieza en la linea 1844 con

Código: Seleccionar todo

#ifndef PLAYER_MOGGY_STYLE
         // Platforms
         if (malotes [enoffsmasi].t == 4) {


Y termina en la linea 1946:

Código: Seleccionar todo

         } else if (!tocado && collide (gpx, gpy, gpen_cx, gpen_cy) && player.estado == EST_NORMAL) {
#else    <---- aquí termina


Tenemos que comentar todo ese código colocando /* antes de la linea 1844 y */ después de la 1946. También hay que comentar el "#endif" que hay justo después; que quede así:

Código: Seleccionar todo

/*
#ifndef PLAYER_MOGGY_STYLE
         // Platforms
         if (malotes [enoffsmasi].t == 4) {
         
         .
         .
         .
         Un montón de cosas
         .
         .
         .
         } else if (!tocado && collide (gpx, gpy, gpen_cx, gpen_cy) && player.estado == EST_NORMAL) {
#else
*/
         if (!tocado && collide (gpx, gpy, gpen_cx, gpen_cy) && player.estado == EST_NORMAL) {
//#endif   


Más tarde, alrededor de 2063 vemos esto, que evita que las plataformas móviles "pierdan vida":

Código: Seleccionar todo

#ifndef PLAYER_MOGGY_STYLE                     
                     if (malotes [enoffsmasi].t != 4) malotes [enoffsmasi].life --;
#else
                     malotes [enoffsmasi].life --;
#endif


Eliminamos todo el bloque y lo dejamos para que cualquier enemigo pierda vida. Sustituimos TODO lo de arriba por un sencillo:

Código: Seleccionar todo

                     malotes [enoffsmasi].life --;
Como diría Rorshach: "Urm..."

Volver a “La Churrera”

¿Quién está conectado?

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