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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
|
Credit to BluRose for writing this in pokeruby, Lunos for porting this to pokeemerald, and ExpoSeed for modifying it.
THIS TUTORIAL IS NOT COMPLETE YET, DO NOT USE IT YET
This tutorial will add the new Exp All mechanics from Gen 6. This entails making it a toggleable key item, modifying the associated text, distributing all the exp in battle, and displaying the proper message.
## Contents
1. [Edit behavior out of battle](#1-edit-behavior-out-of-battle)
2. [Edit behavior in battle](#2-edit-behavior-in-battle)
## 1. Edit behavior out of battle
First, we will need some new strings.
Add these declarations to [include/strings.h](../blob/master/include/strings.h):
```c
extern const u8 gText_ExpShareOn[];
extern const u8 gText_ExpShareOff[];
```
And define it as such in [src/strings.h](../blob/master/src/strings.h):
```c
const u8 gText_ExpShareOn[] = _("Turned on the Exp. Share.\pParty will now gain a portion\nof the Experience Points.{PAUSE_UNTIL_PRESS}");
const u8 gText_ExpShareOff[] = _("Turned off the Exp. Share.\pParty will no longer gain a portion\nof any Experience Points.{PAUSE_UNTIL_PRESS}");
```
We also need to change the Exp Share's item description. Edit [src/data/text/item_descriptions.h](../blob/master/src/data/text/item_descriptions.h):
```diff
static const u8 sExpShareDesc[] = _(
- "A hold item that\n"
- "gets EXP. points\n"
- "from battles.");
+ "A device that\n"
+ "shares EXP. points\n"
+ "to the party.");
```
Now that out of battle strings are taken care of, let's work on the item properties. Edit [src/data/items.h](../blob/master/src/data/items.h):
```diff
[ITEM_EXP_SHARE] =
{
.name = _("EXP. SHARE"),
.itemId = ITEM_EXP_SHARE,
- .price = 3000,
+ .price = 0,
.holdEffect = HOLD_EFFECT_EXP_SHARE,
.description = sExpShareDesc,
- .pocket = POCKET_ITEMS,
- .type = 4,
- .fieldUseFunc = ItemUseOutOfBattle_CannotUse,
+ .importance = 1,
+ .pocket = POCKET_KEY_ITEMS,
+ .type = 2,
+ .fieldUseFunc = ItemUseOutOfBattle_ExpShare,
.secondaryId = 0,
},
```
We've changed the pocket, set the type to be the same type as other key items (this value only gets read by specific field callbacks like medicines, stones, and the bike anyways, so it's irrelevant), given the item an importance of 1 (so it cannot be tossed or deposited in the PC), and also gave it a function for field use. We will define the field use function next.
Edit [include/global.h](../blob/master/include/global.h):
```diff
struct SaveBlock2
{
/*0x00*/ u8 playerName[PLAYER_NAME_LENGTH + 1];
/*0x08*/ u8 playerGender; // MALE, FEMALE
/*0x09*/ u8 specialSaveWarpFlags;
/*0x0A*/ u8 playerTrainerId[TRAINER_ID_LENGTH];
/*0x0E*/ u16 playTimeHours;
/*0x10*/ u8 playTimeMinutes;
/*0x11*/ u8 playTimeSeconds;
/*0x12*/ u8 playTimeVBlanks;
/*0x13*/ u8 optionsButtonMode; // OPTIONS_BUTTON_MODE_[NORMAL/LR/L_EQUALS_A]
/*0x14*/ u16 optionsTextSpeed:3; // OPTIONS_TEXT_SPEED_[SLOW/MID/FAST]
u16 optionsWindowFrameType:5; // Specifies one of the 20 decorative borders for text boxes
u16 optionsSound:1; // OPTIONS_SOUND_[MONO/STEREO]
u16 optionsBattleStyle:1; // OPTIONS_BATTLE_STYLE_[SHIFT/SET]
u16 optionsBattleSceneOff:1; // whether battle animations are disabled
u16 regionMapZoom:1; // whether the map is zoomed in
+ u16 expShare:1;
/*0x18*/ struct Pokedex pokedex;
/*0x90*/ u8 filler_90[0x8];
/*0x98*/ struct Time localTimeOffset;
/*0xA0*/ struct Time lastBerryTreeUpdate;
/*0xA8*/ u32 gcnLinkFlags; // Read by Pokemon Colosseum/XD
/*0xAC*/ u32 encryptionKey;
/*0xB0*/ struct PlayersApprentice playerApprentice;
/*0xDC*/ struct Apprentice apprentices[APPRENTICE_COUNT];
/*0x1EC*/ struct BerryCrush berryCrush;
/*0x1FC*/ struct PokemonJumpResults pokeJump;
/*0x20C*/ struct BerryPickingResults berryPick;
/*0x21C*/ struct RankingHall1P hallRecords1P[HALL_FACILITIES_COUNT][2][3]; // From record mixing.
/*0x57C*/ struct RankingHall2P hallRecords2P[2][3]; // From record mixing.
/*0x624*/ u16 contestLinkResults[5][4]; // 4 positions for 5 categories.
/*0x64C*/ struct BattleFrontier frontier;
}; // sizeof=0xF2C
```
Declare a new function in [include/item_use.h](../blob/master/include/item_use.h):
```c
void ItemUseOutOfBattle_ExpShare(u8);
```
Insert this function into [src/item_use.c](../blob/master/src/item_use.c):
```c
void ItemUseOutOfBattle_ExpShare(u8 taskId)
{
if (!gSaveBlock2Ptr->expShare)
{
PlaySE(SE_EXPMAX);
if (!gTasks[taskId].data[2]) // to account for pressing select in the overworld
{
DisplayItemMessageOnField(taskId, gText_ExpShareOn, Task_CloseCantUseKeyItemMessage);
}
else
{
DisplayItemMessage(taskId, 1, gText_ExpShareOn, BagMenu_InitListsMenu);
}
}
else
{
PlaySE(SE_PC_OFF);
if (!gTasks[taskId].data[2]) // to account for pressing select in the overworld
{
DisplayItemMessageOnField(taskId, gText_ExpShareOff, Task_CloseCantUseKeyItemMessage);
}
else
{
DisplayItemMessage(taskId, 1, gText_ExpShareOff, BagMenu_InitListsMenu);
}
}
gSaveBlock2Ptr->expShare = !gSaveBlock2Ptr->expShare;
}
```
This function will toggle `expShare` on or off and display the appropriate message. It is also possible to register the item, so we need to account for that as well.
Now, all our changes out of battle are complete. In the next step, we will focus on having the new sharing functionality work properly.
## 2. Edit behavior in battle
First, we will need some new text for the new Exp. Share. We need to define a new id. Edit [include/constants/battle_string_ids.h](../blob/master/include/constants/battle_string_ids.h):
```diff
-#define BATTLESTRINGS_COUNT 369
+#define BATTLESTRINGS_COUNT 370
```
```diff
+#define STRINGID_TEAMGAINEDEXP 381
```
Add this to [src/battle_message.c](../blob/master/src/battle_message.c). This can be done anywhere as long as it is before `gBattleStringsTable`:
```c
static const u8 sText_TeamGainedEXP[] = _("The rest of the team gained EXP. Points\nthanks to the EXP. SHARE!\p");
```
Then add this to `gBattleStringsTable`:
```diff
[STRINGID_PKMNBOXLANETTESPCFULL - 12] = gText_PkmnTransferredLanettesPCBoxFull,
[STRINGID_TRAINER1WINTEXT - 12] = sText_Trainer1WinText,
[STRINGID_TRAINER2WINTEXT - 12] = sText_Trainer2WinText,
+ [STRINGID_TEAMGAINEDEXP - 12] = sText_TeamGainedEXP,
};
```
Now, the rest of our changes will be in `Cmd_getexp` of [src/battle_script_commands.c](../blob/master/src/battle_script_commands.c):
```diff
case 1: // calculate experience points to redistribute
{
u16 calculatedExp;
s32 viaSentIn;
for (viaSentIn = 0, i = 0; i < PARTY_SIZE; i++)
{
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) == SPECIES_NONE || GetMonData(&gPlayerParty[i], MON_DATA_HP) == 0)
continue;
if (gBitTable[i] & sentIn)
viaSentIn++;
item = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM);
if (item == ITEM_ENIGMA_BERRY)
holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
else
holdEffect = ItemId_GetHoldEffect(item);
if (holdEffect == HOLD_EFFECT_EXP_SHARE)
viaExpShare++;
}
calculatedExp = gBaseStats[gBattleMons[gBattlerFainted].species].expYield * gBattleMons[gBattlerFainted].level / 7;
if (viaExpShare) // at least one mon is getting exp via exp share
{
*exp = calculatedExp / 2 / viaSentIn;
if (*exp == 0)
*exp = 1;
gExpShareExp = calculatedExp / 2 / viaExpShare;
if (gExpShareExp == 0)
gExpShareExp = 1;
}
else
{
*exp = calculatedExp / viaSentIn;
if (*exp == 0)
*exp = 1;
gExpShareExp = 0;
}
gBattleScripting.getexpState++;
gBattleStruct->expGetterMonId = 0;
gBattleStruct->sentInPokes = sentIn;
}
// fall through
case 2: // set exp value to the poke in expgetter_id and print message
if (gBattleControllerExecFlags == 0)
{
item = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HELD_ITEM);
if (item == ITEM_ENIGMA_BERRY)
holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
else
holdEffect = ItemId_GetHoldEffect(item);
if (holdEffect != HOLD_EFFECT_EXP_SHARE && !(gBattleStruct->sentInPokes & 1))
{
*(&gBattleStruct->sentInPokes) >>= 1;
gBattleScripting.getexpState = 5;
gBattleMoveDamage = 0; // used for exp
}
else if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL) == MAX_LEVEL)
{
*(&gBattleStruct->sentInPokes) >>= 1;
gBattleScripting.getexpState = 5;
gBattleMoveDamage = 0; // used for exp
}
else
{
// music change in wild battle after fainting a poke
if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER) && gBattleMons[0].hp && !gBattleStruct->wildVictorySong)
{
BattleStopLowHpSound();
PlayBGM(MUS_KACHI2);
gBattleStruct->wildVictorySong++;
}
if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HP))
{
if (gBattleStruct->sentInPokes & 1)
gBattleMoveDamage = *exp;
else
gBattleMoveDamage = 0;
if (holdEffect == HOLD_EFFECT_EXP_SHARE)
gBattleMoveDamage += gExpShareExp;
if (holdEffect == HOLD_EFFECT_LUCKY_EGG)
gBattleMoveDamage = (gBattleMoveDamage * 150) / 100;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
gBattleMoveDamage = (gBattleMoveDamage * 150) / 100;
```
|