0 Implementing the “textcolor” script command from FRLG and give object events their own text colour
chowder908 edited this page 2021-09-04 17:10:15 -05:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

In FireRed and LeafGreen, there is a scripting command that allows text to be a different colour. When it was used, it also stored the value of the last text colour in gSpecialVar_PrevTextColor so the colour could be restored later on in the script. The script for the player obtaining an item used this so it could get the correct colour of the text before the obtain item script was executed. This method was also integrated into object event interaction in that the text colour would be blue if the player was interacting with any “male” pic like a Youngster, a Bug Catcher or Brock and red if the player was interacting with any “female” pic like a Lass, a Picnicker or Misty. Anything else was the default dark grey. This feature only allowed 3 possible text colours, which are blue, red and dark grey and used a look up table for getting the correct text colour for the object event.

This tutorial will walk you through how to implement this into Emerald whilst also removing the restrictions it had. NOTE: this tutorial expects you to have dynamic overworld palettes as this uses bytes in the object event graphics info struct that got made redundant with the dynamic palette system.

Open field_specials.c and add the following function:

u8 ContextNpcGetTextColor(void)
{
    u8 gfxId;
    const struct ObjectEventGraphicsInfo *graphicsInfo;

    if (gSpecialVar_TextColor != 0xFF)
        return gSpecialVar_TextColor;
    else if (gSelectedObjectEvent == 0)
        gSpecialVar_TextColor = TEXT_COLOR_DARK_GRAY;
    else
    {
        gfxId = gObjectEvents[gSelectedObjectEvent].graphicsId;
        if (gfxId >= OBJ_EVENT_GFX_VAR_0)
            gfxId = VarGetObjectEventGraphicsId(gfxId - OBJ_EVENT_GFX_VAR_0);
        graphicsInfo = GetObjectEventGraphicsInfo(gfxId);
        gSpecialVar_TextColor = graphicsInfo->textColor;
    }
}

This function is what handles the context of the text. The if (gSpecialVar_TextColor; != 0xFF)… if Im honest, Im not sure what it does as of writing this. I think it looks for values that arent FF, if the value is FF, that particular snippet wont do anything. The else if check looks for the selected object event. This is 0 when interacting with signs or any other text that hasnt been triggered by the player interacting with an object event. What we need to be paying attention to is the gSpecialVar_TextColor = graphicsInfo->textColor;. This is how the function gets the object event text colour.

Extern it somewhere in include/field_specials.h like so:

u8 ContextNpcGetTextColor(void);

Next, we want to open menu.c and add the following function:

void AddTextPrinterForMessageWithTextColor(bool8 allowSkippingDelayWithButtonPress)
{
    u8 color;
    gTextFlags.canABSpeedUpPrint = allowSkippingDelayWithButtonPress;

    color = ContextNpcGetTextColor();
    AddTextPrinterParameterized2(0, 1, gStringVar4, GetPlayerTextSpeedDelay(), NULL, gSpecialVar_TextColor, 1, 3);      
}

I put this above AddTextPrinterForMessage which is the function base Emerald uses. In this function, we have the local variable color. This variable is what were using to call the ContextNpcGetTextColor function we added earlier. Because of ContextNpcGetTextColor we are able to use gSpecialVar_TextColor as the colour value in AddTextPrinterParameterized2 and because of the logic from the aforementioned functions, the colour value in AddTextPrinterParameterized2, will be the value of graphicsInfo->textColor.

Dont forget to extern the function somewhere in menu.h like this:

void AddTextPrinterForMessageWithTextColor(bool8 allowSkippingDelayWithButtonPress);

At the top of menu.c, add the following:

#include "event_data.h"
+    #include "field_specials.h"

This is so we can use the aformentioned ContextNpcGetTextColor function.

We need to open field_message_box.c and find the function StartDrawFieldMessage and make the following change:

static void StartDrawFieldMessage(void)
{

-    AddTextPrinterForMessage(TRUE);
+    AddTextPrinterForMessageWithTextColor(TRUE);
    CreateTask_DrawFieldMessage();
}

StartDrawFieldMessage is the function that handles message boxes in the overworld.

Next, we want to open field_control_avatar.c go to the function ProcessPlayerFieldInput and make the following change:

int ProcessPlayerFieldInput(struct FieldInput *input)
{
    struct MapPosition position;
    u8 playerDirection;
    u16 metatileBehavior;

    gSpecialVar_LastTalked = 0;
    gSelectedObjectEvent = 0;

+   gSpecialVar_TextColor = 0xFF;

This is so any text colour logic is reset when you gain control of the player in the overworld.

This is the object event side of things done.

You need to declare textColor in global.fieldmap.h at struct ObjectEventGraphicsInfo like so:

struct ObjectEventGraphicsInfo
{
    /*0x00*/ u16 tileTag;
    /*0x02*/ u16 paletteTag;
-   /*0x04*/ u16 reflectionPaletteTag;
+   /*0x04*/ u16 textColor;
    /*0x06*/ u16 size;
    /*0x08*/ s16 width;
    /*0x0A*/ s16 height;
    /*0x0C*/ u8 paletteSlot:4;
             u8 shadowSize:2;
             u8 inanimate:1;
             u8 disableReflectionPaletteLoad:1;
    /*0x0D*/ u8 tracks;
    /*0x10*/ const struct OamData *oam;
    /*0x14*/ const struct SubspriteTable *subspriteTables;
    /*0x18*/ const union AnimCmd *const *anims;
    /*0x1C*/ const struct SpriteFrameImage *images;
    /*0x20*/ const union AffineAnimCmd *const *affineAnims;
};

Were not done though.

Were gonna implement the textcolor scripting command so you can make particular scripts be the colour you want.

Go to scrcmd.c and add the following function:

bool8 ScrCmd_textcolor(struct ScriptContext * ctx)
 {
     gSpecialVar_PrevTextColor = gSpecialVar_TextColor;
     gSpecialVar_TextColor = ScriptReadByte(ctx);
     return FALSE;
 }

This is the script command function for the textcolor script command. ctx is the byte the command reads. If you notice, its using gSpecialVar_TextColor again. This means, we are essentially inputting a value for gSpecialVar_TextColor. gSpecialVar_PrevTextColor stores the last value of gSpecialVar_TextColor before the new value is used so it can possibly be restored later on.

We want to go to data/script_cmd_table.inc and go to line 202 and change the following:

-    .4byte ScrCmd_nop1                     @ 0xc7
+    .4byte ScrCmd_textcolor                @ 0xc7

This is so the scripting system knows to use ScrCmd_textcolor we added earlier.

We want to declare these new special vars were using! Go to event_data.c on line 15, make the follow change:

EWRAM_DATA u16 gSpecialVar_0x8000 = 0;
EWRAM_DATA u16 gSpecialVar_0x8001 = 0;
EWRAM_DATA u16 gSpecialVar_0x8002 = 0;
EWRAM_DATA u16 gSpecialVar_0x8003 = 0;
EWRAM_DATA u16 gSpecialVar_0x8004 = 0;
EWRAM_DATA u16 gSpecialVar_0x8005 = 0;
EWRAM_DATA u16 gSpecialVar_0x8006 = 0;
EWRAM_DATA u16 gSpecialVar_0x8007 = 0;
EWRAM_DATA u16 gSpecialVar_0x8008 = 0;
EWRAM_DATA u16 gSpecialVar_0x8009 = 0;
EWRAM_DATA u16 gSpecialVar_0x800A = 0;
EWRAM_DATA u16 gSpecialVar_0x800B = 0;
EWRAM_DATA u16 gSpecialVar_Result = 0;
EWRAM_DATA u16 gSpecialVar_LastTalked = 0;
EWRAM_DATA u16 gSpecialVar_Facing = 0;
EWRAM_DATA u16 gSpecialVar_MonBoxId = 0;
EWRAM_DATA u16 gSpecialVar_MonBoxPos = 0;
-    EWRAM_DATA u16 gSpecialVar_Unused_0x8014 = 0;
+    EWRAM_DATA u16 gSpecialVar_TextColor = 0;
+    EWRAM_DATA u16 gSpecialVar_PrevTextColor = 0;
EWRAM_DATA static u8 gSpecialFlags[SPECIAL_FLAGS_SIZE] = {0};

And on line 30 in include/event_data.h, make the following change:

extern u16 gSpecialVar_0x8000;
extern u16 gSpecialVar_0x8001;
extern u16 gSpecialVar_0x8002;
extern u16 gSpecialVar_0x8003;
extern u16 gSpecialVar_0x8004;
extern u16 gSpecialVar_0x8005;
extern u16 gSpecialVar_0x8006;
extern u16 gSpecialVar_0x8007;
extern u16 gSpecialVar_0x8008;
extern u16 gSpecialVar_0x8009;
extern u16 gSpecialVar_0x800A;
extern u16 gSpecialVar_0x800B;
extern u16 gSpecialVar_Result;
extern u16 gSpecialVar_LastTalked;
extern u16 gSpecialVar_Facing;
extern u16 gSpecialVar_MonBoxId;
extern u16 gSpecialVar_MonBoxPos;
-    extern u16 gSpecialVar_Unused_0x8014;
+    extern u16 gSpecialVar_TextColor;
+    extern u16 gSpecialVar_PrevTextColor;

We need to define them to constants because PrevTextColor and TextColor can be used in scripts. So we need to go to include/constants/vars and find #define VAR_UNUSED_0x08014 with #define VAR_TEXT_COLOR and add a new constant. The changes should look something like this:

-   #define VAR_UNUSED_08014           0x0814
+   #define VAR_TEXT_COLOR             0x8014
#define VAR_TRAINER_BATTLE_OPPONENT_A 0x8015 // Alias of gTrainerBattleOpponent_A
+   #define VAR_PREV_TEXT_COLOR        0x8016

-   #define SPECIAL_VARS_END           0x8015
+   #define SPECIAL_VARS_END           0x8016

Lastly you will want to edit data\event_scripts

-   .4byte gSpecialVar_Unused_0x8014
+   .4byte gSpecialVar_TextColor

And with that, you should have successfully reimplemented textcolor and gave object events their own text colour.