Skip to content
ghoulslash edited this page Jul 3, 2020 · 14 revisions

Run Script On Trainer Sight

Credit to ghoulslash. Pull from his repo or follow the guide below.

Default Pokemon Trainers require the first script command to be trainerbattle to function correctly. This feature allows you to run any script while including the "trainer spotting" effect (see the bottom of the page for an example)

Modify Trainer Sight

1. Open src/trainer_see.c. Find the function CheckForTrainersWantingBattle. Modify it to the following:

bool8 CheckForTrainersWantingBattle(void)
{
    u8 i;
    u8 numTrainers;

    gNoOfApproachingTrainers = 0;
    gApproachingTrainerId = 0;

    for (i = 0; i < OBJECT_EVENTS_COUNT; i++)
    {
        if (!gObjectEvents[i].active)
            continue;
        
        if (gObjectEvents[i].trainerType == TRAINER_TYPE_NONE || gObjectEvents[i].trainerType == TRAINER_TYPE_SEE_ALL_DIRECTIONS)
            continue;

        numTrainers = CheckTrainer(i);
        if (numTrainers == 0xFF)    //run script
            break;
        
        if (numTrainers == 2)
            break;

        if (numTrainers == 0)
            continue;

        if (gNoOfApproachingTrainers > 1)
            break;
        if (GetMonsStateToDoubles_2() != 0) // one trainer found and cant have a double battle
            break;
    }
        
    if (numTrainers == 0xFF)
    {
        u8 objectEventId = gApproachingTrainers[gNoOfApproachingTrainers - 1].objectEventId;
        
        gSelectedObjectEvent = objectEventId;
        gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId;
        ScriptContext1_SetupScript(EventScript_ObjectApproachPlayer);
        ScriptContext2_Enable();
        return TRUE;
    }
    
    if (gNoOfApproachingTrainers == 1)
    {
        ResetTrainerOpponentIds();
        ConfigureAndSetUpOneTrainerBattle(gApproachingTrainers[gNoOfApproachingTrainers - 1].objectEventId,
                                          gApproachingTrainers[gNoOfApproachingTrainers - 1].trainerScriptPtr);
        gTrainerApproachedPlayer = TRUE;
        return TRUE;
    }
    else if (gNoOfApproachingTrainers == 2)
    {
        ResetTrainerOpponentIds();
        for (i = 0; i < gNoOfApproachingTrainers; i++, gApproachingTrainerId++)
        {
            ConfigureTwoTrainersBattle(gApproachingTrainers[i].objectEventId,
                                       gApproachingTrainers[i].trainerScriptPtr);
        }
        SetUpTwoTrainersBattle();
        gApproachingTrainerId = 0;
        gTrainerApproachedPlayer = TRUE;
        return TRUE;
    }
    else
    {
        gTrainerApproachedPlayer = FALSE;
        return FALSE;
    }
}

2. Next, find CheckTrainer and change it to:

static u8 CheckTrainer(u8 objectEventId)
{
    const u8 *scriptPtr;
    u8 ret = 1;
    u8 approachDistance;
    u16 scriptFlag = GetObjectEventTrainerSightFlagByObjectEventId(objectEventId);
    
    if (InTrainerHill() == TRUE)
        scriptPtr = GetTrainerHillTrainerScript();
    else
        scriptPtr = GetObjectEventScriptPointerByObjectEventId(objectEventId);

    if (InBattlePyramid())
    {
        if (GetBattlePyramidTrainerFlag(objectEventId))
            return 0;
    }
    else if (InTrainerHill() == TRUE)
    {
        if (GetHillTrainerFlag(objectEventId))
            return 0;
    }
    else
    {
        if (GetTrainerFlagFromScriptPointer(scriptPtr))
            return 0;
    }

    approachDistance = GetTrainerApproachDistance(&gObjectEvents[objectEventId]);

    if (approachDistance != 0)
    {
        if (scriptFlag >= TRAINER_TYPE_RUN_SCRIPT)
        {
            if (!FlagGet(scriptFlag) && scriptPtr != NULL)
            {
                // TRAINER_TYPE_RUN_SCRIPT
                FlagSet(scriptFlag);
                ret = 0xFF;
            }
            else
            {
                return 0;
            }
        }
        else
        {
            if (scriptPtr[1] == TRAINER_BATTLE_DOUBLE
                || scriptPtr[1] == TRAINER_BATTLE_REMATCH_DOUBLE
                || scriptPtr[1] == TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE)
            {
                if (GetMonsStateToDoubles_2() != 0)
                    return 0;

                ret = 2;
            }
        }

        gApproachingTrainers[gNoOfApproachingTrainers].objectEventId = objectEventId;
        gApproachingTrainers[gNoOfApproachingTrainers].trainerScriptPtr = scriptPtr;
        gApproachingTrainers[gNoOfApproachingTrainers].radius = approachDistance;
        TrainerApproachPlayer(&gObjectEvents[objectEventId], approachDistance - 1);
        gNoOfApproachingTrainers++;

        return ret;
    }

    return 0;
}

4. Create the function GetObjectEventTrainerSightFlagByObjectEventId First, open src/event_object_movement.c. Somewhere, add the function GetObjectEventTrainerSightFlagByObjectEventId:

u16 GetObjectEventTrainerSightFlagByObjectEventId(u8 objEventId)
{
    // ideally, would use a the last two bytes of the object event template
    return GetObjectEventTemplateByLocalIdAndMap(gObjectEvents[objEventId].localId, gObjectEvents[objEventId].mapNum, gObjectEvents[objEventId].mapGroup)->trainerType;
}

Next, define the new function in include/event_object_movement.h. Add u16 GetObjectEventTrainerSightFlagByObjectEventId(u8 objEventId); somewhere in the file.

Define TRAINER_TYPE_RUN_SCRIPT

add #define TRAINER_TYPE_RUN_SCRIPT 4 to include/constants/trainer_types.h

Make a new approach script

Open data/scripts/trainer_script.inc and add the following to the bottom

EventScript_ObjectApproachPlayer::
	lock
	special EndTrainerApproach
	waitstate
	gotonative LoadTrainerObjectScript
	end

gotonative LoadTrainerObjectScript will allow us to dynamically branch to our objects own script.

Next, add extern const u8 EventScript_ObjectApproachPlayer[]; somewhere to include/event_scripts.h

Create LoadTrainerObjectScript

Open src/script.c and add the following function:


### How to Set Up:
Clone this wiki locally