summaryrefslogtreecommitdiff
path: root/Trainer-Scripts.md
blob: 1305413d26a33b97bf3c685ee0e942e16df9ee86 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
## Run Script On Trainer Sight

Credit to ghoulslash. You can pull from the [repo](https://github.com/ghoulslash/pokeemerald/tree/trainer_see_scripts) or follow the tutorial below. The bottom of the page has set up instructions.

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. Here's an example:

<a href="https://imgur.com/JJ81c6i"><img src="https://i.imgur.com/JJ81c6i.gif" title="source: imgur.com" /></a>

### Modify Trainer Sight
**1. Open [src/trainer_see.c](../blob/master/src/trainer_see.c).** Find the function `CheckForTrainersWantingBattle`. Modify it to the following:
```c
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:**
```c
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 (scriptFlag < TRAINER_TYPE_RUN_SCRIPT)
    {
        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;
}
```

**3. Make the trainers only see in 1 direction**
Find the function `GetTrainerApproachDistance`. Replace the line:
```c
if (trainerObj->trainerType == TRAINER_TYPE_NORMAL)
```
with:
```c
if (trainerObj->trainerType == TRAINER_TYPE_NORMAL || trainerObj->trainerType >= TRAINER_TYPE_RUN_SCRIPT)
```

**4. Create the function `GetObjectEventTrainerSightFlagByObjectEventId`**
First, open [src/event_object_movement.c](../blob/master/src/event_object_movement.c). Somewhere, add the function `GetObjectEventTrainerSightFlagByObjectEventId`:
```c
u16 GetObjectEventTrainerSightFlagByObjectEventId(u8 objEventId)
{
    return GetObjectEventTemplateByLocalIdAndMap(gObjectEvents[objEventId].localId, gObjectEvents[objEventId].mapNum, gObjectEvents[objEventId].mapGroup)->trainerType;
}
```

Next, define the new function in [include/event_object_movement.h](../blob/master/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](../blob/master/include/constants/trainer_types.h)

This is actually superfluous, but is a nice way to demonstrate that any value >= 4 will allow you to run any script.


### Make a new approach script
Open [data/scripts/trainer_script.inc](../blob/master/data/scripts/trainer_script.inc) and add the following to the bottom
```c
EventScript_ObjectApproachPlayer::
	lock
	special DoTrainerApproach
	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](../blob/master/include/event_scripts.h)

### Create `LoadTrainerObjectScript`
Open [src/script.c](../blob/master/src/script.c) and add the following function:
```c
bool8 LoadTrainerObjectScript(void)
{
    sScriptContext1.scriptPtr = gApproachingTrainers[gNoOfApproachingTrainers - 1].trainerScriptPtr;
    return TRUE;
}
```

Also, add `#include "trainer_see.h"` to the top of the file for this function to recognize `gApproachingTrainers`

Finally, add `#include/event_scripts.h` at the top of [src/trainer_see.c](../blob/master/src/trainer_see.c)

### How to Set Up:
Rather than make a new object event template field, we can use the fact that `.trainerType` is conveniently 16 bits. Set up your object event the following way:
<a href="https://imgur.com/oYsv8ZB"><img src="https://i.imgur.com/oYsv8ZB.png" title="source: imgur.com" /></a>

Here's a brief run-down of the important fields:
* `Script`: the script you want the object to run upon approaching the player.
* `Trainer Type:` the flag that allows the script to run. This must be >3. The `ObjectEvent` structure `trainerType` field is only `u8`. You could expand this to `u16`, but that takes up saveblock space (4 more bytes due to alignment per object event, so 64 more saveblock bytes). Alternatively, just use a flag with ID <= 0xFF.
* `Sight Radius:` the same as for regular trainers

**Note:** there are two bytes free at the end of ObjectEventTemplate. You could easily add the trainer script flag here (don't forget to change the porymap json, GetObjectEventTrainerSightFlagByObjectEventId, and global.fieldmap.h!), and keep the trainer type field as intended.