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á.
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.
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.
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á.
GitHub - Xhyzi/pokeemerald at day-and-night
Decompilation of Pokémon Emerald. Contribute to Xhyzi/pokeemerald development by creating an account on GitHub.
github.com
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).
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:
En 'include/rtc.h' añadimos:
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;
}
Código:
u8 Rtc_GetCurrentHour(void);
u8 Rtc_GetCurrentMinute(void);
En 'include/palette.h' añadimos:
También añadimos lo siguiente en palette.h
Ahora nos dirigiremos a 'src/palette.c', y tras los includes añadiremos el siguiente código:
Ahora añadiremos las funciones del DNS como tal (mi recomendación es dejarlas al final de 'src/palette.c'
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:
#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).
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'
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.
Los tipos de mapas que no son afectados por el DNS deben introducirse en gDnsMapExceptions.
Los filtros de cada timelapse son arrays de colores RGB2. Simplemente cambia el valor de los canales de cada color para modificar los filtros.
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: