25 Useful Scripting Specials
psf edited this page 2024-08-11 19:30:24 -07:00

Useful Scripting Specials/Macros

This page will add some new scripting commands that may be useful for developing more advanced scripts.

All specials need to be added to the table in data/specials.inc following other special definitions

Table of Contents:

removeallitem

credits to Pawwkie

Remove all of a specific item from the player's bag, regardless of how many the player has.

  1. First, our scripting macro:
@ Remove all of specified item from the player's bag
.macro removeallitem itemId:req
callnative RemoveAllItem
.2byte \itemId
.endm
  1. Next, our special. Best suited for src/scrcmd.c.
void RemoveAllItem(struct ScriptContext *ctx)
{
    u16 itemId = VarGet(ScriptReadHalfword(ctx));
    RemoveBagItem(itemId, CountTotalItemQuantityInBag(itemId));
}

getobjectposition

credits to ghoulslash

This command will get either the map template position, or the current position of any object.

  1. First, our scripting macro:
@ return current (posType = 0) or map (posType = 1) position of object to VAR_0x8007 (x), VAR_0x8008 (y)
.macro getobjectxy localId:req, posType:req
setvar VAR_0x8000, \localId
setvar VAR_0x8001, \posType
special GetObjectPosition
.endm
  1. Next, our special. You can add this to any .c file, I suggest src/event_object_movement or src/field_specials.c
// get position (0 for current, 1 for map) of object event, return to VAR_0x8007, VAR_0x8008
void GetObjectPosition(void)
{
    u16 localId      = gSpecialVar_0x8000;
    u16 useTemplate  = gSpecialVar_0x8001;

    u16 *x = &gSpecialVar_0x8007;
    u16 *y = &gSpecialVar_0x8008;

    if (!useTemplate)
    {
        /* current position */
        const u16 objId = GetObjectEventIdByLocalId(localId);
        const struct ObjectEvent *objEvent = &gObjectEvents[objId];
        *x = objEvent->currentCoords.x - 7; // subtract out camera size
        *y = objEvent->currentCoords.y - 7;
    }
    else
    {
        const struct ObjectEventTemplate *objTemplate =
            FindObjectEventTemplateByLocalId(localId,
                    gSaveBlock1Ptr->objectEventTemplates,
                    gMapHeader.events->objectEventCount);
        *x = objTemplate->x;
        *y = objTemplate->y;
    }
}

checkobjectat

credits to ghoulslash

Checks in any object is at a given position

  1. scripting macro
@ checks if there is any object at a given position
.macro checkobjectat x:req, y:req
setorcopyvar VAR_0x8005, \x
setorcopyvar VAR_0x8006, \y
specialvar VAR_RESULT, CheckObjectAtXY
.endm
  1. code
// special to check if there is any object at a given position
u16 CheckObjectAtXY(void)
{
    u16 x = gSpecialVar_0x8005 + 7;
    u16 y = gSpecialVar_0x8006 + 7;
    u32 i;
    
    for (i = 0; i < OBJECT_EVENTS_COUNT; i++)
    {        
        if (gObjectEvents[i].active && gObjectEvents[i].currentCoords.x == x && gObjectEvents[i].currentCoords.y == y)
            return TRUE;
    }
    return FALSE;
}

getseenmon and getcaughtmon

Credits to Lunos.

These commands will check the state of either the Seen or the Caught Pokédex flags of a species whose ID stored in VAR_TEMP_1.

  1. First, our scripting macros:
    @ Checks the state of the Pokédex Seen flag of the specified Pokemon
    @ The result is stored in VAR_RESULT
    .macro getseenmon species:req
    setvar VAR_TEMP_1, \species
    specialvar VAR_RESULT, GetSeenMon
    .endm

    @ Checks the state of the Pokédex Caught flag of the specified Pokemon
    @ The result is stored in VAR_RESULT
    .macro getcaughtmon species:req
    setvar VAR_TEMP_1, \species
    specialvar VAR_RESULT, GetCaughtMon
    .endm
  1. Next, our special functions. You can add them to any .c file, although since these are meant to be used from the overworld, I suggest src/field_specials.c specifically.
bool8 GetSeenMon(void)
{
    return GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_GET_SEEN);
}

bool8 GetCaughtMon(void)
{
    return GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_GET_CAUGHT);
}

Keep in mind that in order to access FLAG_GET_SEEN and FLAG_GET_CAUGHT, you must use the #include directive to read the contents of the include/pokedex.h.

In other words, you need to add a #include "pokedex.h" to the list of headers at the top of the file.

To use them, simply type either getseenmon or getcaughtmon in your script, followed by the species ID. For example, getcaughtmon SPECIES_MAGIKARP.

The result (either TRUE or FALSE) will be stored in VAR_RESULT.

setseenmon and setcaughtmon

Credits to Lunos.

These commands will set either the Seen or the Caught Pokédex flags of a species whose ID stored in VAR_TEMP_1.

  1. First, our scripting macros:
    @ Sets the Pokédex Seen flag of the specified Pokemon
    .macro setseenmon species:req
    setvar VAR_TEMP_1, \species
    special SetSeenMon
    .endm

    @ Sets the Pokédex Caught flag of the specified Pokemon
    .macro setcaughtmon species:req
    setvar VAR_TEMP_1, \species
    special SetCaughtMon
    .endm
  1. Next, our special functions. You can add them to any .c file, although since these are meant to be used from the overworld, I suggest src/field_specials.c specifically.
void SetSeenMon(void)
{
    GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_SET_SEEN);
}

void SetCaughtMon(void)
{
    GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_SET_SEEN);
    GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_SET_CAUGHT);
}

Keep in mind that in order to access FLAG_SET_SEEN and FLAG_SET_CAUGHT, you must use the #include directive to read the contents of the include/pokedex.h.

In other words, you need to add a #include "pokedex.h" to the list of headers at the top of the file.

To use them, simply type either setseenmon or setcaughtmon in your script, followed by the species ID. For example, setcaughtmon SPECIES_MAGIKARP.

setmonball

Credits to Lunos.

This command will let you modify the caught ball of a Pokémon that is chosen by the Player.

  1. First, our scripting macro:
    @ Changes the caught ball of a selected Pokémon
    .macro setmonball ballId:req
    special ChoosePartyMon
    waitstate
    setvar VAR_TEMP_1, \ballId
    special SetMonBall
    .endm
  1. Next, our special function. You can add it to any .c file, although since it's meant to be used from the overworld, I suggest src/field_specials.c specifically.
void SetMonBall(void)
{
    u16 ballId = VarGet(VAR_TEMP_1);
    SetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_POKEBALL, &ballId);
}

To use it, simply type setmonball in your script followed by the desired Poké Ball Item ID. For example, setmonball ITEM_PREMIER_BALL.

checkforspecies

Credits to Lunos.

This command will let you check if the Player has a specific species in their party, either silently or through the usage of the special ChoosePartyMon.

  1. First, our scripting macro:
    OPEN_PARTY_SCREEN = FALSE
    NO_PARTY_SCREEN = TRUE

    @ Check if the Player has \speciesId in their party.
    .macro checkforspecies speciesId:req, silent:req, script:req
    .if \silent == OPEN_PARTY_SCREEN
        special ChoosePartyMon
        waitstate
        specialvar VAR_RESULT, ScriptGetPartyMonSpecies
        goto_if_eq VAR_RESULT, \speciesId, \script
    .else
        setvar VAR_TEMP_1, \speciesId
        specialvar VAR_RESULT, CheckPartyForMon
        goto_if_eq VAR_RESULT, TRUE, \script
    .endif
    .endm
  1. Next, a special function that will allow us to silently check the entire party for our species, in case we want to do that.
bool8 CheckPartyForMon(void)
{
    int i;
    for (i = 0; i < CalculatePlayerPartyCount(); i++)
    {
        if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) == VarGet(VAR_TEMP_1))
            return TRUE;
    }
    return FALSE;
}

Using this macro is very easy, you just need to call it in a script and provide a species constant, a mode (silent or not) and a script to jump to if it turns out that the Player does have the specified species. If they don't, the script you used this macro in will keep running like normal.

Here's a quick example of its usage:

Route101_EventScript_RouteSign::
    checkforspecies SPECIES_TORCHIC, NO_PARTY_SCREEN, Route101_EventScript_RouteSign_HasTorchic
    msgbox Route101_EventScript_Text_NoTorchic, MSGBOX_DEFAULT
    release
    end

Route101_EventScript_RouteSign_HasTorchic::
    msgbox Route101_EventScript_Text_HasTorchic, MSGBOX_DEFAULT
    release
    end

Route101_EventScript_Text_NoTorchic:
    .string "You don't have a TORCHIC.$"

Route101_EventScript_Text_HasTorchic:
    .string "You have a TORCHIC!$"

getobjectfacingdirection

Credits to Lunos.

This command will let you check the direction at which an object event on a map is facing, which in turn allows you to handle edge cases in overworld scripts that deal with lots of movement.

  1. First, our scripting macro:
    @ Gets the facing direction of a given event object and stores it in the variable \dest.
    .macro getobjectfacingdirection evObjId:req, dest:req
    setvar VAR_TEMP_1, \evObjId
    specialvar \dest, Script_GetObjectFacingDirection
    .endm
  1. Next, a special function that will return the facing direction of the event object whose Local ID (i.e. the object's ID on the current map) is stored in the temporary variable VAR_TEMP_1.
u8 Script_GetObjectFacingDirection(void)
{
    u8 objId = GetObjectEventIdByLocalId(VarGet(VAR_TEMP_1));
    return gObjectEvents[objId].facingDirection;
}

NOTE: This function can be added in src/event_object_movement.c directly, or one can turn GetObjectEventIdByLocalId into a global function and put it elsewhere.

And we're done.

This macro is as easy to use as the other ones in this document.

You just invoke getobjectfacingdirection, feed it an event object's Local ID and then a variable to store their facing directon.

Here's a quick example of its usage:

Route101_EventScript_RouteSign::
    getobjectfacingdirection OBJ_EVENT_ID_PLAYER, VAR_TEMP_1
    goto_if_eq VAR_TEMP_1, DIR_NORTH, Route101_EventScript_RouteSign_North
    goto_if_eq VAR_TEMP_1, DIR_SOUTH, Route101_EventScript_RouteSign_South
    goto_if_eq VAR_TEMP_1, DIR_WEST, Route101_EventScript_RouteSign_West
    goto_if_eq VAR_TEMP_1, DIR_EAST, Route101_EventScript_RouteSign_East
    end

Route101_EventScript_RouteSign_North:
    msgbox Route101_Text_RouteSign_North, MSGBOX_DEFAULT
    release
    end

Route101_EventScript_RouteSign_South:
    msgbox Route101_Text_RouteSign_South, MSGBOX_DEFAULT
    release
    end

Route101_EventScript_RouteSign_West:
    msgbox Route101_Text_RouteSign_West, MSGBOX_DEFAULT
    release
    end

Route101_EventScript_RouteSign_East:
    msgbox Route101_Text_RouteSign_East, MSGBOX_DEFAULT
    release
    end

Route101_Text_RouteSign_North:
    .string "The Player is facing North!$"

Route101_Text_RouteSign_South:
    .string "The Player is facing South!$"

Route101_Text_RouteSign_West:
    .string "The Player is facing West!$"

Route101_Text_RouteSign_East:
    .string "The Player is facing East!$"

mGBA_20221107_192254264