Registrarse

[pokeemerald] DNS con filtros dinámicos

Samu

Usuario de Platino
Bueno, lo prometido es deuda. Que mejor forma de volver que con lo mismo que empecé, pero adaptado para pokeemerald (?).

Actualmente estoy porteando todo el trabajo que tenía hecho en el rom de Emerald a pokeemerald y ayer me dí cuenta de que no existía DNS, así que aquí está.

https://github.com/Xhyzi/pokeemerald/tree/dns

Me gustaría pensar que todos los que trabajáis por aquí con pokeemerald sois capaces de ver el branch de mi fork de pokeemerald y coger lo que necesitéis, pero no voy a ser un vago de mierda y voy a dejar explicado por aquí como utilizar el dns en vuestros proyectos.

Esta basado en una modificación que hice del DNS de Prime Dialga en ASM utilizando modificaciones de Andrea. El DNS divide el día en varios timelapses o franjas y posee una serie de filtros que va aplicando a lo largo de estas.

Los filtros son colores de 15bit RGB, cuyos canales R, G, y B se restan a los canales RGB de los colores 'reales'.

Los filtros son aplicados a las paletas cuando se realiza el DMA transfer desde el buffer de paletas a la ram de paletas (de esta forma evitamos aplicar el filtro de forma repetida sobre el mismo color, convirtiéndolo en negro).
El primer paso será ampliar la funcionalidad de nuestro RTC para poder obtener directamente el valor de los minutos y las horas actuales desde el reloj de nuestro emulador (la opción RTC debe de estar activada).

Créditos @blax501- por este tutorial.

En 'src/rtc.c' añadimos estas dos funciones:
Código:
+u8 Rtc_GetCurrentHour(void){ // Gets current hour from RTC data
    RtcGetInfo(&sRtc);   
    if(sRtc.hour>25){
        return sRtc.hour-12;
    }
    else if(sRtc.hour>9){
        return sRtc.hour-6;
    }
   
    return sRtc.hour;
}

u8 Rtc_GetCurrentMinute(void){ // Gets current minutes from RTC data
    RtcGetInfo(&sRtc);   
    if(sRtc.minute>73){
        return sRtc.minute-30;
    }
    else if(sRtc.minute>57){
        return sRtc.minute-24;
    }
    else if(sRtc.minute>41){
        return sRtc.minute-18;
    }
    else if(sRtc.minute>25){
        return sRtc.minute-12;
    }
    else if(sRtc.minute>9){
        return sRtc.minute-6;
    }
   
    return sRtc.minute;
}
En 'include/rtc.h' añadimos:
Código:
u8 Rtc_GetCurrentHour(void);
u8 Rtc_GetCurrentMinute(void);
En 'include/palette.h' añadimos:
Código:
#define CHECK_MENU_OR_OVERWORLD 0x2021685;
#define CHECK_COMBAT_OR_OVERWORLD 0x2021686;
#define MENU_FLAG 1
#define COMBAT_FLAG 1 << 4 

#define RED_CHANNEL_OPERATOR 0x1F;
#define GREEN_CHANNEL_OPERATOR 0x3E0;
#define BLUE_CHANNEL_OPERATOR 0x7C00;

#define DNS_EXCEPTION 0
#define PAL_ACTIVE 1

//Timelapses for DNS
enum
{
    TIME_MIDNIGHT,
    TIME_DAWN,
    TIME_DAY,
    TIME_SUNSET,
    TIME_NIGHTFALL,
    TIME_NIGHT
};

//End hours for timelapses
#define MIDNIGHT_END_HOUR   7       //00 - 07
#define DAWN_END_HOUR       8       //07 - 08
#define DAY_END_HOUR        19      //08 - 19
#define SUNSET_END_HOUR     20      //19 - 20
#define NIGHTFALL_END_HOUR  21      //20 - 21
#define NIGHT_END_HOUR      0       //21 - 00

struct DnsPalExceptions
{
    u8 pal[32];
};

También añadimos lo siguiente en palette.h
Código:
//Dns System
void DNS_System(void *src, void *dest);
void DnsDmaTransfer16(void *src, void *dest);
u16 ApplyDNSFilterToColor(u16 color, u16 filter);
bool8 IsMapDNSException(void);
u16 GetDNSFilter(void);
u8 GetTimeLapse(u8 hour);
void BlacknWhiteDmaTransfer(void);

Ahora nos dirigiremos a 'src/palette.c', y tras los includes añadiremos el siguiente código:
Código:
#include "constants/map_types.h"
#include "rtc.h"

//Configure palettes to be affected or not by DNS filtering
const struct DnsPalExceptions gOWPalExceptions =
{
    .pal =
    {
        PAL_ACTIVE,     //1
        PAL_ACTIVE,     //2
        PAL_ACTIVE,     //3
        PAL_ACTIVE,     //4
        PAL_ACTIVE,     //5
        PAL_ACTIVE,     //6
        PAL_ACTIVE,     //7
        PAL_ACTIVE,     //8
        PAL_ACTIVE,     //9
        PAL_ACTIVE,     //10
        PAL_ACTIVE,     //11
        PAL_ACTIVE,     //12
        PAL_ACTIVE,     //13
        DNS_EXCEPTION,  //14
        DNS_EXCEPTION,  //15
        DNS_EXCEPTION,  //16
        PAL_ACTIVE,     //17
        PAL_ACTIVE,     //18
        PAL_ACTIVE,     //19
        PAL_ACTIVE,     //20
        PAL_ACTIVE,     //21
        PAL_ACTIVE,     //22
        PAL_ACTIVE,     //23
        PAL_ACTIVE,     //24
        PAL_ACTIVE,     //25
        PAL_ACTIVE,     //26
        PAL_ACTIVE,     //27
        PAL_ACTIVE,     //28
        PAL_ACTIVE,     //29
        PAL_ACTIVE,     //30
        PAL_ACTIVE,     //31
        PAL_ACTIVE,     //32
    },
};

//Configure palettes to be affected or not by DNS while in combat
const struct DnsPalExceptions gCombatPalExceptions =
{
    .pal =
    {
        DNS_EXCEPTION,  //1
        DNS_EXCEPTION,  //2
        PAL_ACTIVE,     //3
        PAL_ACTIVE,     //4
        PAL_ACTIVE,     //5
        DNS_EXCEPTION,  //6
        PAL_ACTIVE,     //7
        PAL_ACTIVE,     //8
        PAL_ACTIVE,     //9
        PAL_ACTIVE,     //10
        PAL_ACTIVE,     //11
        PAL_ACTIVE,     //12
        PAL_ACTIVE,     //13
        PAL_ACTIVE,     //14
        PAL_ACTIVE,     //15
        PAL_ACTIVE,     //16
        PAL_ACTIVE,     //17
        PAL_ACTIVE,     //18
        PAL_ACTIVE,     //19
        PAL_ACTIVE,     //20
        DNS_EXCEPTION,  //21
        DNS_EXCEPTION,  //22
        DNS_EXCEPTION,  //23
        PAL_ACTIVE,     //24
        PAL_ACTIVE,     //25
        PAL_ACTIVE,     //26
        PAL_ACTIVE,     //27
        PAL_ACTIVE,     //28
        PAL_ACTIVE,     //29
        PAL_ACTIVE,     //30
        PAL_ACTIVE,     //31
        PAL_ACTIVE,     //32
    },
};

//MapTypes not affected by DNS
const u8 gDnsMapExceptions[] =
{
    MAP_TYPE_UNUSED_1,
    MAP_TYPE_INDOOR,
    MAP_TYPE_UNDERGROUND,
    MAP_TYPE_SECRET_BASE,
};

/* ***********************************************************
* DNS filters are actual 15bit RGB colours.
* This colours R - G - B channels are substracted from
* the original colour in the palette buffer during the
* transfer from the buffer to the palette RAM.
*
*  [BUFFER] -> (Value - Filter) -> [PAL_RAM]
*
* This means that you shouln't use too high values for RGB
* channels in the filters.
* I Suggest you to not use channels with a value above 16.
*
* Feel free to experiment with your own filters.
* **********************************************************/

//Filters used by DNS at Midnight
const u16 gMidnightFilters[] =
{
    RGB2(14, 14, 6),    //CE19
    RGB2(14, 14, 7),    //CE1D
    RGB2(14, 14, 8),    //CE21
    RGB2(15, 15, 8),    //EF21
    RGB2(15, 15, 9),    //EF25
    RGB2(15, 15, 9),    //EF25
    RGB2(16, 16, 9),    //1026
    RGB2(16, 16, 10),   //102A
};

//Filters used by DNS at Dawn
const u16 gDawnFilters[] =
{
    RGB2(15, 15, 10),
    RGB2(15, 15, 10),   //1
    RGB2(14, 14, 10),   //2
    RGB2(13, 13, 10),   //3
    RGB2(12, 12, 10),   //4
    RGB2(11, 11, 10),   //5
    RGB2(10, 10, 10),   //6
    RGB2(9, 9, 10),     //7
    RGB2(8, 8, 10),     //8
    RGB2(8, 8, 11),     //9
    RGB2(7, 7, 11),     //10
    RGB2(6, 6, 11),     //11
    RGB2(5, 5, 11),     //12
    RGB2(4, 4, 11),     //13
    RGB2(3, 3, 11),     //14
    RGB2(2, 2, 11),     //15
    RGB2(1, 1, 11),     //16
    RGB2(0, 0, 11),     //17
    RGB2(0, 0, 10),     //18
    RGB2(0, 0, 9),      //19
    RGB2(0, 0, 8),      //20
    RGB2(0, 0, 7),      //21
    RGB2(0, 0, 6),      //22
    RGB2(0, 0, 5),      //23
    RGB2(0, 0, 4),      //24
    RGB2(0, 0, 3),      //0003
    RGB2(0, 0, 2),      //0002
    RGB2(0, 0, 1),      //0001
    RGB2(0, 0, 0),      //0000
    RGB2(0, 0, 0),      //0000
};

//DNS Day filter (no filter actually lul)
const u16 gDayFilter = RGB2(0, 0, 0);   //0000

//DNS Sunset filters
const u16 gSunsetFilters[] =
{
    RGB2(0, 0, 1),      //0004
    RGB2(0, 1, 1),      //2004
    RGB2(0, 1, 2),      //2008
    RGB2(0, 1, 3),      //200C
    RGB2(0, 2, 3),      //400C
    RGB2(0, 2, 4),      //4010
    RGB2(0, 2, 5),      //4014
    RGB2(0, 3, 5),      //6014
    RGB2(0, 3, 6),      //6018
    RGB2(0, 3, 7),      //601C
    RGB2(0, 4, 7),      //801C
    RGB2(0, 4, 8),      //8020
    RGB2(0, 4, 9),      //8024
    RGB2(0, 5, 9),      //A024
    RGB2(0, 5, 10),     //A028
    RGB2(0, 5, 11),     //A02C
    RGB2(0, 6, 11),     //C02C
    RGB2(0, 6, 12),     //C030
    RGB2(0, 6, 13),     //C034
    RGB2(0, 7, 13),     //E034
    RGB2(0, 7, 14),     //E038
    RGB2(0, 7, 14),     //E038
    RGB2(0, 8, 14),     //0039
    RGB2(0, 9, 14),     //2039
    RGB2(0, 10, 14),    //4039
    RGB2(0, 11, 14),    //6039
    RGB2(0, 12, 14),    //8039
    RGB2(0, 13, 14),    //A039
    RGB2(0, 14, 14),    //C039
    RGB2(0, 14, 14),    //C039
};

//DNS NightFall Filters
const u16 gNightfallFilters[] =
{
    RGB2(0, 14, 14),    //39C0
    RGB2(0, 14, 14),    //39C0
    RGB2(0, 14, 13),    //35C0
    RGB2(0, 14, 12),    //31C0
    RGB2(0, 14, 11),    //2DC0
    RGB2(0, 14, 10),    //29C0
    RGB2(1, 14, 10),    //29C1
    RGB2(1, 14, 9),     //25C1
    RGB2(0, 14, 8),     //21C0
    RGB2(1, 14, 7),     //1DC1
    RGB2(1, 14, 6),     //19C1
    RGB2(2, 14, 6),     //19C2
    RGB2(2, 14, 5),     //15C2
    RGB2(2, 14, 4),     //11C2
    RGB2(2, 14, 3),     //0DC2
    RGB2(2, 14, 2),     //09C2
    RGB2(2, 14, 2),     //09C2
    RGB2(3, 14, 3),     //0DC3
    RGB2(4, 14, 4),     //11C4
    RGB2(5, 14, 5),     //15C5
    RGB2(6, 14, 6),     //19C6
    RGB2(7, 14, 6),     //19C7
    RGB2(8, 14, 6),     //19C8
    RGB2(9, 14, 6),     //19C9
    RGB2(10, 14, 6),    //19CA
    RGB2(11, 14, 6),    //19CB
    RGB2(12, 14, 6),    //19CC
    RGB2(13, 14, 6),    //19CD
    RGB2(14, 14, 6),    //19CE
    RGB2(14, 14, 6),    //19CE
};

//DNS Night filter
const u16 gNightFilter = RGB2(14, 14, 6);   //19CE

Ahora añadiremos las funciones del DNS como tal (mi recomendación es dejarlas al final de 'src/palette.c'
Código:
/* ****************************************************
* **************** D&N for pokeemerald ***************
* ****************************************************
* Based on Prime Dialga DNS for Pokemon GBA Games.
* Additional credits to Andrea, Eing & BLAx501!
* Author: Xhyz/Samu
******************************************************/
void DNS_System(void *src, void *dest)
{
    u8* menu_status = (u8*) CHECK_MENU_OR_OVERWORLD;

    //If Player is in a menu regular DMA transfer will be used
    if (*menu_status & MENU_FLAG || IsMapDNSException())
    {
        DmaCopy16(3, src, dest, PLTT_SIZE);
    }
    else    //If Player is in OW or Combat DNS Transfer will be used instead
    {
        DnsDmaTransfer16(src, dest);
    }
}

//Applies filter to colors while doing manual dma transfer from buffer to palettes ram
void DnsDmaTransfer16(void *src, void *dest)
{
    u8 pal_num; u8 col_num; u8 i;
    u16 color; u16 filter;
    u8 palExceptionFlags[32];

    u16* pal_color = (u16*) PLTT;   //pointer to palette ram
    u8* combat_status = (u8*) CHECK_COMBAT_OR_OVERWORLD;   

    //Obtains DNS Filter
    filter = GetDNSFilter();

    //Inits palette exception flags
    for (i = 0; i < 32; i++)
        palExceptionFlags[i] = (*combat_status & COMBAT_FLAG) ? gCombatPalExceptions.pal[i] : gOWPalExceptions.pal[i];


    //Loops through all palettes checking whether they are dns active or not
    for (pal_num = 0; pal_num < 32; pal_num++)
    {  
        if (palExceptionFlags[pal_num] == PAL_ACTIVE)   //Filters de palette
        {
            for (col_num = 0; col_num < 16; col_num++)
            {
                *pal_color = ApplyDNSFilterToColor(gPlttBufferFaded[pal_num * 16 + col_num], filter);
                pal_color++;
            }
        }
        else
        {
            for (col_num = 0; col_num < 16; col_num++)  //Transfers palette without filtering
            {
                *pal_color = gPlttBufferFaded[pal_num * 16 + col_num];
                pal_color++;
            }
        }
    }
}

//Applies filter to a colour. Filters RGB channels are substracted from colour RGB channels.
u16 ApplyDNSFilterToColor(u16 color, u16 filter)
{
    u16 red; u16 green; u16 blue;
    u16 red_filter; u16 green_filter; u16 blue_filter;

    red = color & RED_CHANNEL_OPERATOR;
    green = color & GREEN_CHANNEL_OPERATOR;
    blue = color & BLUE_CHANNEL_OPERATOR;

    red_filter = filter & RED_CHANNEL_OPERATOR;
    green_filter = filter & GREEN_CHANNEL_OPERATOR;
    blue_filter = filter & BLUE_CHANNEL_OPERATOR;

    red = red - red_filter;
    green = (green >> 5) - (green_filter >> 5);
    blue = (blue >> 10) - (blue_filter >> 10);

    return RGB2(red <= 31 ? red : 0, green <= 31 ? green : 0, blue <= 31 ? blue : 0);
}

//returns true if the current mapType is affected by DNS.
bool8 IsMapDNSException()
{
    u8 i;
    for (i=0; i < sizeof(gDnsMapExceptions)/sizeof(gDnsMapExceptions[0]); i++)
        if (gMapHeader.mapType == gDnsMapExceptions[i])
            return TRUE;
    return FALSE;
}

//returns the filter to use depending on RTC time.
u16 GetDNSFilter()
{
    u8 hour = Rtc_GetCurrentHour();
    u8 minutes = Rtc_GetCurrentMinute();

    switch(GetTimeLapse(hour))
    {
        case TIME_MIDNIGHT:
            if (hour < 1)
                return gMidnightFilters[minutes >> 3];           
            else
                return gMidnightFilters[7];

        case TIME_DAWN:
            return gDawnFilters[minutes >> 1];

        case TIME_DAY:
            return gDayFilter;

        case TIME_SUNSET:
            return gSunsetFilters[minutes >> 1];

        case TIME_NIGHTFALL:
            return gNightfallFilters[minutes >> 1];

        case TIME_NIGHT:
            return gNightFilter;
    }

    return 0;
}

//Returns the timeLapse
u8 GetTimeLapse(u8 hour)
{
    if (hour < MIDNIGHT_END_HOUR)
        return TIME_MIDNIGHT;
    else if (hour < DAWN_END_HOUR)
        return TIME_DAWN;
    else if (hour < DAY_END_HOUR)
        return TIME_DAY;
    else if (hour < SUNSET_END_HOUR)
        return TIME_SUNSET;
    else if (hour < NIGHTFALL_END_HOUR)
        return TIME_NIGHTFALL;
    else
        return TIME_NIGHT;
}

//Does Dma palette transfer with a black & white effect
void BlacknWhiteDmaTransfer()
{
    u16* pal_color = (u16*) PLTT;   //pointer to palette ram
    u16 color, red, green, blue, i;

    for (i = 0; i < 512; i++)
    {
        color = gPlttBufferFaded[i];

        red = color & RED_CHANNEL_OPERATOR;
        green = color & GREEN_CHANNEL_OPERATOR;
        blue = color & BLUE_CHANNEL_OPERATOR;

        *pal_color = RGB2(red, green, blue);
        pal_color++;
    }
}

Por último, para activar nuestro DNS tendremos que dirigirnos a la función 'TransferPlttBuffer' dentro de 'palette.c'. Esta función es la encargada de transferir los colores del buffer a la RAM de paletas. Aquí tendrémos que sustituir la línea 'DmaCopy16(3, src, dest, PLTT_SIZE);' por una llamada a 'DNS_System(src, dest);'
Código:
void TransferPlttBuffer(void)
{
    if (!gPaletteFade.bufferTransferDisabled)
    {
        void *src = gPlttBufferFaded;
        void *dest = (void *)PLTT;
        [B]DNS_System(src, dest);  //Does 16b Dma Transfer[/B]   //Nueva línea
        [S][B]DmaCopy16(3, src, dest, PLTT_SIZE);[/B] [/S]            //Antigua línea
        sPlttBufferTransferPending = 0;
        if (gPaletteFade.mode == HARDWARE_FADE && gPaletteFade.active)
            UpdateBlendRegisters();
    }
}
Ya hice una guía sobre esto, pero lo volveré a decir aquí.

Para evitar el parpadeo del DNS vamos a la función 'BeginNormalPaletteFade' y sustituimos la línea 'CpuCopy32(gPlttBufferFaded, (void *)PLTT, PLTT_SIZE);' por 'TransferPlttBuffer();'.

De esta forma evitamos que se hagan Dma transfer del buffer a la paleta sin pasar por nuestros filtros DNS (que es lo que provoca el parpadeo).
Modificar el DNS debería ser bastante sencillo.

Para modificar las franjas horarias del DNS y cuando se producen simplemente hay que editar los siguientes defines en 'palette.h'
Código:
//Timelapses for DNS
enum
{
    TIME_MIDNIGHT,
    TIME_DAWN,
    TIME_DAY,
    TIME_SUNSET,
    TIME_NIGHTFALL,
    TIME_NIGHT
};

//End hours for timelapses
#define MIDNIGHT_END_HOUR   7       //00 - 07
#define DAWN_END_HOUR       8       //07 - 08
#define DAY_END_HOUR        19      //08 - 19
#define SUNSET_END_HOUR     20      //19 - 20
#define NIGHTFALL_END_HOUR  21      //20 - 21
#define NIGHT_END_HOUR      0       //21 - 00

Para personalizar las paletas que se ven afectadas o no por el DNS solo hay que modificar las instancias del struct DnsPalExceptions en 'pallette.c'. Las paletas marcadas como PAL_ACTIVE se verán afectadas por el DNS, aquellas marcadas como DNS_EXCEPTION no se verán afectadas.

gOWPalExceptions contiene las excepciones para el Overworld. gCombatPalExceptions contiene las excepciones para el combate.

Código:
//Configure palettes to be affected or not by DNS filtering
const struct DnsPalExceptions gOWPalExceptions =
{
    .pal =
    {
        PAL_ACTIVE,     //1
        PAL_ACTIVE,     //2
        PAL_ACTIVE,     //3
        PAL_ACTIVE,     //4
        PAL_ACTIVE,     //5
        PAL_ACTIVE,     //6
        PAL_ACTIVE,     //7
        PAL_ACTIVE,     //8
        PAL_ACTIVE,     //9
        PAL_ACTIVE,     //10
        PAL_ACTIVE,     //11
        PAL_ACTIVE,     //12
        PAL_ACTIVE,     //13
        DNS_EXCEPTION,  //14
        DNS_EXCEPTION,  //15
        DNS_EXCEPTION,  //16
        PAL_ACTIVE,     //17
        PAL_ACTIVE,     //18
        PAL_ACTIVE,     //19
        PAL_ACTIVE,     //20
        PAL_ACTIVE,     //21
        PAL_ACTIVE,     //22
        PAL_ACTIVE,     //23
        PAL_ACTIVE,     //24
        PAL_ACTIVE,     //25
        PAL_ACTIVE,     //26
        PAL_ACTIVE,     //27
        PAL_ACTIVE,     //28
        PAL_ACTIVE,     //29
        PAL_ACTIVE,     //30
        PAL_ACTIVE,     //31
        PAL_ACTIVE,     //32
    },
};

Los tipos de mapas que no son afectados por el DNS deben introducirse en gDnsMapExceptions.
Código:
//MapTypes not affected by DNS
const u8 gDnsMapExceptions[] =
{
    MAP_TYPE_UNUSED_1,
    MAP_TYPE_INDOOR,
    MAP_TYPE_UNDERGROUND,
    MAP_TYPE_SECRET_BASE,
};

Los filtros de cada timelapse son arrays de colores RGB2. Simplemente cambia el valor de los canales de cada color para modificar los filtros.
Código:
//Filters used by DNS at Midnight
const u16 gMidnightFilters[] =
{
    RGB2(14, 14, 6),    //CE19
    RGB2(14, 14, 7),    //CE1D
    RGB2(14, 14, 8),    //CE21
    RGB2(15, 15, 8),    //EF21
    RGB2(15, 15, 9),    //EF25
    RGB2(15, 15, 9),    //EF25
    RGB2(16, 16, 9),    //1026
    RGB2(16, 16, 10),   //102A
};

En fin, espero que lo disfrutéis y que le sirva de algo a alguien. Actualmente no tiene el sistema de iluminación, pero creo que es bastante obvio como hay que hacerlo. Solo hay escribir directamente en la RAM de paletas el color 'iluminado' en la dirección del color.
 
Última edición:

InmortalKaktus

Ludiloco
Respuesta: [pokeemerald] DNS con filtros dinámicos

Sí señor, pedazo de aporte.

Nunca he sido muy fan del sistema de DNS, pero creo que en esta ocasión le echaré un ojo. Me encanta ver gente currando tanto y tan bien para esta plataforma. ¡Muchos ánimos y a seguir dándole duro!
 

GaboExtreme

El Ayudante Infinito
Respuesta: [pokeemerald] DNS con filtros dinámicos

Excelente Nunca creí que la DNS se aplicara tan rapido, Felicitaciones.
 

Samu

Usuario de Platino
He actualizado el sistema, ahora debería ser bastante más estable que antes y algo más sencillo de personalizar.

Se ha añadido un sistema de iluminación y la capacidad de filtrar las paletas por tags en lugar de hacerlo únicamente por slots.
 

Jaizu

Decomp user
Me alegro mucho de haberte insistido tanto hasta que explotases y lo hicieses.
Como sugerencia podrías hacer una tabla de encuentros basándote en la hora del día o explicarnos cómo comprobar la hora o el estado en scripts, pero se agradece un montón igualmente :)
 
Arriba