forked from rh-hideout/pokeemerald-expansion
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbattle_dynamax.c
496 lines (449 loc) · 17.2 KB
/
battle_dynamax.c
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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
#include "global.h"
#include "battle.h"
#include "battle_anim.h"
#include "battle_controllers.h"
#include "battle_interface.h"
#include "battle_scripts.h"
#include "battle_script_commands.h"
#include "battle_gimmick.h"
#include "data.h"
#include "event_data.h"
#include "graphics.h"
#include "item.h"
#include "pokemon.h"
#include "random.h"
#include "sprite.h"
#include "string_util.h"
#include "util.h"
#include "constants/abilities.h"
#include "constants/battle_move_effects.h"
#include "constants/battle_string_ids.h"
#include "constants/flags.h"
#include "constants/hold_effects.h"
#include "constants/items.h"
#include "constants/moves.h"
static u32 GetMaxPowerTier(u32 move);
struct GMaxMove
{
u16 species;
u8 moveType;
u16 gmaxMove;
};
static const struct GMaxMove sGMaxMoveTable[] =
{
{SPECIES_VENUSAUR_GMAX, TYPE_GRASS, MOVE_G_MAX_VINE_LASH},
{SPECIES_BLASTOISE_GMAX, TYPE_WATER, MOVE_G_MAX_CANNONADE},
{SPECIES_CHARIZARD_GMAX, TYPE_FIRE, MOVE_G_MAX_WILDFIRE},
{SPECIES_BUTTERFREE_GMAX, TYPE_BUG, MOVE_G_MAX_BEFUDDLE},
{SPECIES_PIKACHU_GMAX, TYPE_ELECTRIC, MOVE_G_MAX_VOLT_CRASH},
{SPECIES_MEOWTH_GMAX, TYPE_NORMAL, MOVE_G_MAX_GOLD_RUSH},
{SPECIES_MACHAMP_GMAX, TYPE_FIGHTING, MOVE_G_MAX_CHI_STRIKE},
{SPECIES_GENGAR_GMAX, TYPE_GHOST, MOVE_G_MAX_TERROR},
{SPECIES_KINGLER_GMAX, TYPE_WATER, MOVE_G_MAX_FOAM_BURST},
{SPECIES_LAPRAS_GMAX, TYPE_ICE, MOVE_G_MAX_RESONANCE},
{SPECIES_EEVEE_GMAX, TYPE_NORMAL, MOVE_G_MAX_CUDDLE},
{SPECIES_SNORLAX_GMAX, TYPE_NORMAL, MOVE_G_MAX_REPLENISH},
{SPECIES_GARBODOR_GMAX, TYPE_POISON, MOVE_G_MAX_MALODOR},
{SPECIES_MELMETAL_GMAX, TYPE_STEEL, MOVE_G_MAX_MELTDOWN},
{SPECIES_RILLABOOM_GMAX, TYPE_GRASS, MOVE_G_MAX_DRUM_SOLO},
{SPECIES_CINDERACE_GMAX, TYPE_FIRE, MOVE_G_MAX_FIREBALL},
{SPECIES_INTELEON_GMAX, TYPE_WATER, MOVE_G_MAX_HYDROSNIPE},
{SPECIES_CORVIKNIGHT_GMAX, TYPE_FLYING, MOVE_G_MAX_WIND_RAGE},
{SPECIES_ORBEETLE_GMAX, TYPE_PSYCHIC, MOVE_G_MAX_GRAVITAS},
{SPECIES_DREDNAW_GMAX, TYPE_WATER, MOVE_G_MAX_STONESURGE},
{SPECIES_COALOSSAL_GMAX, TYPE_ROCK, MOVE_G_MAX_VOLCALITH},
{SPECIES_FLAPPLE_GMAX, TYPE_GRASS, MOVE_G_MAX_TARTNESS},
{SPECIES_APPLETUN_GMAX, TYPE_GRASS, MOVE_G_MAX_SWEETNESS},
{SPECIES_SANDACONDA_GMAX, TYPE_GROUND, MOVE_G_MAX_SANDBLAST},
{SPECIES_TOXTRICITY_AMPED_GMAX, TYPE_ELECTRIC, MOVE_G_MAX_STUN_SHOCK},
{SPECIES_TOXTRICITY_LOW_KEY_GMAX, TYPE_ELECTRIC, MOVE_G_MAX_STUN_SHOCK},
{SPECIES_CENTISKORCH_GMAX, TYPE_FIRE, MOVE_G_MAX_CENTIFERNO},
{SPECIES_HATTERENE_GMAX, TYPE_FAIRY, MOVE_G_MAX_SMITE},
{SPECIES_GRIMMSNARL_GMAX, TYPE_DARK, MOVE_G_MAX_SNOOZE},
{SPECIES_ALCREMIE_GMAX, TYPE_FAIRY, MOVE_G_MAX_FINALE},
{SPECIES_COPPERAJAH_GMAX, TYPE_STEEL, MOVE_G_MAX_STEELSURGE},
{SPECIES_DURALUDON_GMAX, TYPE_DRAGON, MOVE_G_MAX_DEPLETION},
{SPECIES_URSHIFU_SINGLE_STRIKE_GMAX,TYPE_DARK, MOVE_G_MAX_ONE_BLOW},
{SPECIES_URSHIFU_RAPID_STRIKE_GMAX, TYPE_WATER, MOVE_G_MAX_RAPID_FLOW},
};
// Returns whether a battler can Dynamax.
bool32 CanDynamax(u32 battler)
{
#if (!TESTING)
return FALSE;
#endif
u16 species = gBattleMons[battler].species;
u16 holdEffect = GetBattlerHoldEffect(battler, FALSE);
// Prevents Zigzagoon from dynamaxing in vanilla.
if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE && GetBattlerSide(battler) == B_SIDE_OPPONENT)
return FALSE;
// Check if Player has a Dynamax Band.
if (!TESTING && (GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT
|| (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)))
{
if (!CheckBagHasItem(ITEM_DYNAMAX_BAND, 1))
return FALSE;
if (B_FLAG_DYNAMAX_BATTLE == 0 || (B_FLAG_DYNAMAX_BATTLE != 0 && !FlagGet(B_FLAG_DYNAMAX_BATTLE)))
return FALSE;
}
// Check if species isn't allowed to Dynamax.
if (GET_BASE_SPECIES_ID(species) == SPECIES_ZACIAN
|| GET_BASE_SPECIES_ID(species) == SPECIES_ZAMAZENTA
|| GET_BASE_SPECIES_ID(species) == SPECIES_ETERNATUS)
return FALSE;
// Check if Trainer has already Dynamaxed.
if (HasTrainerUsedGimmick(battler, GIMMICK_DYNAMAX))
return FALSE;
// Check if AI battler is intended to Dynamaxed.
if (!ShouldTrainerBattlerUseGimmick(battler, GIMMICK_DYNAMAX))
return FALSE;
// Check if battler has another gimmick active.
if (GetActiveGimmick(battler) != GIMMICK_NONE)
return FALSE;
// Check if battler is holding a Z-Crystal or Mega Stone.
if (!TESTING && (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE)) // tests make this check already
return FALSE;
// TODO: Cannot Dynamax in a Max Raid if you don't have Dynamax Energy.
// if (gBattleTypeFlags & BATTLE_TYPE_NO_RUNNING && gBattleStruct->raid.dynamaxEnergy != battler)
// return FALSE;
// No checks failed, all set!
return TRUE;
}
// Returns whether a battler is transformed into a Gigantamax form.
bool32 IsGigantamaxed(u32 battler)
{
struct Pokemon *mon = GetPartyBattlerData(battler);
if ((gSpeciesInfo[gBattleMons[battler].species].isGigantamax) && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR))
return TRUE;
return FALSE;
}
// Applies the HP Multiplier for Dynamaxed Pokemon and Raid Bosses.
void ApplyDynamaxHPMultiplier(struct Pokemon* mon)
{
if (GetMonData(mon, MON_DATA_SPECIES) == SPECIES_SHEDINJA)
return;
else
{
uq4_12_t multiplier = GetDynamaxLevelHPMultiplier(GetMonData(mon, MON_DATA_DYNAMAX_LEVEL), FALSE);
u32 hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * multiplier) + UQ_4_12_ROUND);
u32 maxHP = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_MAX_HP) * multiplier) + UQ_4_12_ROUND);
SetMonData(mon, MON_DATA_HP, &hp);
SetMonData(mon, MON_DATA_MAX_HP, &maxHP);
}
}
// Returns the non-Dynamax HP of a Pokemon.
u16 GetNonDynamaxHP(u32 battler)
{
if (GetActiveGimmick(battler) != GIMMICK_DYNAMAX || gBattleMons[battler].species == SPECIES_SHEDINJA)
return gBattleMons[battler].hp;
else
{
struct Pokemon *mon = GetPartyBattlerData(battler);
uq4_12_t mult = GetDynamaxLevelHPMultiplier(GetMonData(mon, MON_DATA_DYNAMAX_LEVEL), TRUE);
u32 hp = UQ_4_12_TO_INT((gBattleMons[battler].hp * mult) + UQ_4_12_ROUND);
return hp;
}
}
// Returns the non-Dynamax Max HP of a Pokemon.
u16 GetNonDynamaxMaxHP(u32 battler)
{
if (GetActiveGimmick(battler) != GIMMICK_DYNAMAX || gBattleMons[battler].species == SPECIES_SHEDINJA)
return gBattleMons[battler].maxHP;
else
{
struct Pokemon *mon = GetPartyBattlerData(battler);
uq4_12_t mult = GetDynamaxLevelHPMultiplier(GetMonData(mon, MON_DATA_DYNAMAX_LEVEL), TRUE);
u32 maxHP = UQ_4_12_TO_INT((gBattleMons[battler].maxHP * mult) + UQ_4_12_ROUND);
return maxHP;
}
}
// Sets flags used for Dynamaxing and checks Gigantamax forms.
void ActivateDynamax(u32 battler)
{
// Set appropriate use flags.
SetActiveGimmick(battler, GIMMICK_DYNAMAX);
SetGimmickAsActivated(battler, GIMMICK_DYNAMAX);
gBattleStruct->dynamax.dynamaxTurns[battler] = DYNAMAX_TURNS_COUNT;
// Substitute is removed upon Dynamaxing.
gBattleMons[battler].status2 &= ~STATUS2_SUBSTITUTE;
ClearBehindSubstituteBit(battler);
// Choiced Moves are reset upon Dynamaxing.
gBattleStruct->choicedMove[battler] = MOVE_NONE;
// Try Gigantamax form change.
if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)) // Ditto cannot Gigantamax.
TryBattleFormChange(battler, FORM_CHANGE_BATTLE_GIGANTAMAX);
BattleScriptExecute(BattleScript_DynamaxBegins);
}
// Unsets the flags used for Dynamaxing and reverts max HP if needed.
void UndoDynamax(u32 battler)
{
// Revert HP if battler is still Dynamaxed.
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
{
struct Pokemon *mon = GetPartyBattlerData(battler);
uq4_12_t mult = GetDynamaxLevelHPMultiplier(GetMonData(mon, MON_DATA_DYNAMAX_LEVEL), TRUE);
gBattleMons[battler].hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * mult + 1) + UQ_4_12_ROUND); // round up
SetMonData(mon, MON_DATA_HP, &gBattleMons[battler].hp);
CalculateMonStats(mon);
}
// Makes sure there are no Dynamax flags set, including on switch / faint.
SetActiveGimmick(battler, GIMMICK_NONE);
gBattleStruct->dynamax.dynamaxTurns[battler] = 0;
// Undo form change if needed.
if (IsGigantamaxed(battler))
TryBattleFormChange(battler, FORM_CHANGE_END_BATTLE);
}
// Certain moves are blocked by Max Guard that normally ignore protection.
bool32 IsMoveBlockedByMaxGuard(u32 move)
{
switch (move)
{
case MOVE_BLOCK:
case MOVE_FLOWER_SHIELD:
case MOVE_GEAR_UP:
case MOVE_MAGNETIC_FLUX:
case MOVE_PHANTOM_FORCE:
case MOVE_PSYCH_UP:
case MOVE_SHADOW_FORCE:
case MOVE_TEATIME:
case MOVE_TRANSFORM:
return TRUE;
}
return FALSE;
}
// Weight-based moves (and some other moves in Raids) are blocked by Dynamax.
bool32 IsMoveBlockedByDynamax(u32 move)
{
// TODO: Certain moves are banned in raids.
switch (GetMoveEffect(move))
{
case EFFECT_HEAT_CRASH:
case EFFECT_LOW_KICK:
return TRUE;
}
return FALSE;
}
static u16 GetTypeBasedMaxMove(u32 battler, u32 type)
{
// Gigantamax check
u32 i;
u32 species = gBattleMons[battler].species;
u32 targetSpecies = species;
if (!gSpeciesInfo[species].isGigantamax)
targetSpecies = GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_GIGANTAMAX);
if (targetSpecies != species)
species = targetSpecies;
if (gSpeciesInfo[species].isGigantamax)
{
for (i = 0; i < ARRAY_COUNT(sGMaxMoveTable); i++)
{
if (sGMaxMoveTable[i].species == species && sGMaxMoveTable[i].moveType == type)
return sGMaxMoveTable[i].gmaxMove;
}
}
// Regular Max Move
if (gTypesInfo[type].maxMove == MOVE_NONE) // failsafe
return gTypesInfo[0].maxMove;
return gTypesInfo[type].maxMove;
}
// Returns the appropriate Max Move or G-Max Move for a battler to use.
u16 GetMaxMove(u32 battler, u32 baseMove)
{
u32 moveType;
SetTypeBeforeUsingMove(baseMove, battler);
moveType = GetBattleMoveType(baseMove);
if (baseMove == MOVE_NONE) // for move display
{
return MOVE_NONE;
}
else if (baseMove == MOVE_STRUGGLE)
{
return MOVE_STRUGGLE;
}
else if (GetMoveCategory(baseMove) == DAMAGE_CATEGORY_STATUS)
{
return MOVE_MAX_GUARD;
}
else
{
return GetTypeBasedMaxMove(battler, moveType);
}
}
// First value is for Fighting, Poison and Multi-Attack. The second is for everything else.
enum
{
MAX_POWER_TIER_1, // 70 or 90 damage
MAX_POWER_TIER_2, // 75 or 100 damage
MAX_POWER_TIER_3, // 80 or 110 damage
MAX_POWER_TIER_4, // 85 or 120 damage
MAX_POWER_TIER_5, // 90 or 130 damage
MAX_POWER_TIER_6, // 95 or 140 damage
MAX_POWER_TIER_7, // 100 or 130 damage
MAX_POWER_TIER_8, // 100 or 150 damage
};
// Gets the base power of a Max Move.
u32 GetMaxMovePower(u32 move)
{
u32 tier;
// G-Max Drum Solo, G-Max Hydrosnipe, and G-Max Fireball always have 160 base power.
if (MoveHasAdditionalEffect(move, MOVE_EFFECT_FIXED_POWER))
return 160;
// Exceptions to all other rules below:
switch (move)
{
case MOVE_TRIPLE_KICK: return 80;
case MOVE_GEAR_GRIND: return 100;
case MOVE_DUAL_WINGBEAT: return 100;
case MOVE_TRIPLE_AXEL: return 140;
}
tier = GetMaxPowerTier(move);
u32 moveType = GetMoveType(move);
if (moveType == TYPE_FIGHTING
|| moveType == TYPE_POISON
|| move == MOVE_MULTI_ATTACK)
{
switch (tier)
{
default:
case MAX_POWER_TIER_1: return 70;
case MAX_POWER_TIER_2: return 75;
case MAX_POWER_TIER_3: return 80;
case MAX_POWER_TIER_4: return 85;
case MAX_POWER_TIER_5: return 90;
case MAX_POWER_TIER_6: return 95;
case MAX_POWER_TIER_7: return 100;
case MAX_POWER_TIER_8: return 100;
}
}
else
{
switch (tier)
{
default:
case MAX_POWER_TIER_1: return 90;
case MAX_POWER_TIER_2: return 100;
case MAX_POWER_TIER_3: return 110;
case MAX_POWER_TIER_4: return 120;
case MAX_POWER_TIER_5: return 130;
case MAX_POWER_TIER_6: return 140;
case MAX_POWER_TIER_7: return 130;
case MAX_POWER_TIER_8: return 150;
}
}
}
static u32 GetMaxPowerTier(u32 move)
{
u32 strikeCount = GetMoveStrikeCount(move);
if (strikeCount >= 2 && strikeCount <= 5)
{
switch(GetMovePower(move))
{
case 0 ... 25: return MAX_POWER_TIER_2;
case 26 ... 30: return MAX_POWER_TIER_3;
case 31 ... 35: return MAX_POWER_TIER_4;
case 36 ... 50: return MAX_POWER_TIER_5;
default:
case 51 ... 60: return MAX_POWER_TIER_6;
}
}
switch (GetMoveEffect(move))
{
case EFFECT_BIDE:
case EFFECT_SUPER_FANG:
case EFFECT_LEVEL_DAMAGE:
case EFFECT_PSYWAVE:
case EFFECT_COUNTER:
case EFFECT_PRESENT:
case EFFECT_BEAT_UP:
case EFFECT_WEATHER_BALL:
case EFFECT_FLING:
case EFFECT_ELECTRO_BALL:
case EFFECT_METAL_BURST:
case EFFECT_TERRAIN_PULSE:
case EFFECT_PUNISHMENT:
case EFFECT_TRUMP_CARD:
case EFFECT_FIXED_DAMAGE_ARG:
case EFFECT_SPIT_UP:
case EFFECT_NATURAL_GIFT:
case EFFECT_MIRROR_COAT:
case EFFECT_FINAL_GAMBIT:
return MAX_POWER_TIER_2;
case EFFECT_OHKO:
case EFFECT_RETURN:
case EFFECT_FRUSTRATION:
case EFFECT_HEAT_CRASH:
case EFFECT_STORED_POWER:
case EFFECT_GYRO_BALL:
return MAX_POWER_TIER_5;
case EFFECT_MAGNITUDE:
case EFFECT_POWER_BASED_ON_TARGET_HP:
return MAX_POWER_TIER_6;
case EFFECT_FLAIL:
case EFFECT_LOW_KICK:
return MAX_POWER_TIER_7;
case EFFECT_MULTI_HIT:
switch(GetMovePower(move))
{
case 0 ... 15: return MAX_POWER_TIER_1;
case 16 ... 18: return MAX_POWER_TIER_2;
case 19 ... 20: return MAX_POWER_TIER_4;
default:
case 21 ... 25: return MAX_POWER_TIER_5;
}
}
switch (GetMovePower(move))
{
case 0 ... 40: return MAX_POWER_TIER_1;
case 45 ... 50: return MAX_POWER_TIER_2;
case 55 ... 60: return MAX_POWER_TIER_3;
case 65 ... 70: return MAX_POWER_TIER_4;
case 75 ... 100: return MAX_POWER_TIER_5;
case 110 ... 140: return MAX_POWER_TIER_6;
default:
case 150 ... 250: return MAX_POWER_TIER_8;
}
}
// Returns whether a move is a Max Move or not.
bool32 IsMaxMove(u32 move)
{
return move >= FIRST_MAX_MOVE && move <= LAST_MAX_MOVE;
}
// Assigns the multistring to use for the "Damage Non- Types" G-Max effect.
void ChooseDamageNonTypesString(u8 type)
{
switch (type)
{
case TYPE_GRASS:
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TRAPPED_WITH_VINES;
break;
case TYPE_WATER:
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CAUGHT_IN_VORTEX;
break;
case TYPE_FIRE:
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SURROUNDED_BY_FIRE;
break;
case TYPE_ROCK:
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SURROUNDED_BY_ROCKS;
break;
}
}
// Updates Dynamax HP multipliers and healthboxes.
void BS_UpdateDynamax(void)
{
NATIVE_ARGS();
u32 battler = gBattleScripting.battler;
struct Pokemon *mon = GetPartyBattlerData(battler);
if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change.
RecalcBattlerStats(battler, mon, GetActiveGimmick(battler) == GIMMICK_DYNAMAX);
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL);
gBattlescriptCurrInstr = cmd->nextInstr;
}
// Goes to the jump instruction if the target is Dynamaxed.
void BS_JumpIfDynamaxed(void)
{
NATIVE_ARGS(const u8 *jumpInstr);
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}