#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 "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 u8 GetMaxPowerTier(u16 move); // Constant Data static const u16 sMaxMoveTable[NUMBER_OF_MON_TYPES] = { [TYPE_NORMAL] = MOVE_MAX_STRIKE, [TYPE_FIGHTING] = MOVE_MAX_KNUCKLE, [TYPE_FLYING] = MOVE_MAX_AIRSTREAM, [TYPE_POISON] = MOVE_MAX_OOZE, [TYPE_GROUND] = MOVE_MAX_QUAKE, [TYPE_ROCK] = MOVE_MAX_ROCKFALL, [TYPE_BUG] = MOVE_MAX_FLUTTERBY, [TYPE_GHOST] = MOVE_MAX_PHANTASM, [TYPE_STEEL] = MOVE_MAX_STEELSPIKE, [TYPE_FIRE] = MOVE_MAX_FLARE, [TYPE_WATER] = MOVE_MAX_GEYSER, [TYPE_GRASS] = MOVE_MAX_OVERGROWTH, [TYPE_ELECTRIC] = MOVE_MAX_LIGHTNING, [TYPE_PSYCHIC] = MOVE_MAX_MINDSTORM, [TYPE_ICE] = MOVE_MAX_HAILSTORM, [TYPE_DRAGON] = MOVE_MAX_WYRMWIND, [TYPE_DARK] = MOVE_MAX_DARKNESS, [TYPE_FAIRY] = MOVE_MAX_STARFALL, }; struct GMaxMove { u16 species; u8 moveType; u16 gmaxMove; }; static const struct GMaxMove sGMaxMoveTable[] = { {SPECIES_VENUSAUR_GIGANTAMAX, TYPE_GRASS, MOVE_G_MAX_VINE_LASH}, {SPECIES_BLASTOISE_GIGANTAMAX, TYPE_WATER, MOVE_G_MAX_CANNONADE}, {SPECIES_CHARIZARD_GIGANTAMAX, TYPE_FIRE, MOVE_G_MAX_WILDFIRE}, {SPECIES_BUTTERFREE_GIGANTAMAX, TYPE_BUG, MOVE_G_MAX_BEFUDDLE}, {SPECIES_PIKACHU_GIGANTAMAX, TYPE_ELECTRIC, MOVE_G_MAX_VOLT_CRASH}, {SPECIES_MEOWTH_GIGANTAMAX, TYPE_NORMAL, MOVE_G_MAX_GOLD_RUSH}, {SPECIES_MACHAMP_GIGANTAMAX, TYPE_FIGHTING, MOVE_G_MAX_CHI_STRIKE}, {SPECIES_GENGAR_GIGANTAMAX, TYPE_GHOST, MOVE_G_MAX_TERROR}, {SPECIES_KINGLER_GIGANTAMAX, TYPE_WATER, MOVE_G_MAX_FOAM_BURST}, {SPECIES_LAPRAS_GIGANTAMAX, TYPE_ICE, MOVE_G_MAX_RESONANCE}, {SPECIES_EEVEE_GIGANTAMAX, TYPE_NORMAL, MOVE_G_MAX_CUDDLE}, {SPECIES_SNORLAX_GIGANTAMAX, TYPE_NORMAL, MOVE_G_MAX_REPLENISH}, {SPECIES_GARBODOR_GIGANTAMAX, TYPE_POISON, MOVE_G_MAX_MALODOR}, {SPECIES_MELMETAL_GIGANTAMAX, TYPE_STEEL, MOVE_G_MAX_MELTDOWN}, {SPECIES_RILLABOOM_GIGANTAMAX, TYPE_GRASS, MOVE_G_MAX_DRUM_SOLO}, {SPECIES_CINDERACE_GIGANTAMAX, TYPE_FIRE, MOVE_G_MAX_FIREBALL}, {SPECIES_INTELEON_GIGANTAMAX, TYPE_WATER, MOVE_G_MAX_HYDROSNIPE}, {SPECIES_CORVIKNIGHT_GIGANTAMAX, TYPE_FLYING, MOVE_G_MAX_WIND_RAGE}, {SPECIES_ORBEETLE_GIGANTAMAX, TYPE_PSYCHIC, MOVE_G_MAX_GRAVITAS}, {SPECIES_DREDNAW_GIGANTAMAX, TYPE_WATER, MOVE_G_MAX_STONESURGE}, {SPECIES_COALOSSAL_GIGANTAMAX, TYPE_ROCK, MOVE_G_MAX_VOLCALITH}, {SPECIES_FLAPPLE_GIGANTAMAX, TYPE_GRASS, MOVE_G_MAX_TARTNESS}, {SPECIES_APPLETUN_GIGANTAMAX, TYPE_GRASS, MOVE_G_MAX_SWEETNESS}, {SPECIES_SANDACONDA_GIGANTAMAX, TYPE_GROUND, MOVE_G_MAX_SANDBLAST}, {SPECIES_TOXTRICITY_AMPED_GIGANTAMAX, TYPE_ELECTRIC, MOVE_G_MAX_STUN_SHOCK}, {SPECIES_TOXTRICITY_LOW_KEY_GIGANTAMAX, TYPE_ELECTRIC, MOVE_G_MAX_STUN_SHOCK}, {SPECIES_CENTISKORCH_GIGANTAMAX, TYPE_FIRE, MOVE_G_MAX_CENTIFERNO}, {SPECIES_HATTERENE_GIGANTAMAX, TYPE_FAIRY, MOVE_G_MAX_SMITE}, {SPECIES_GRIMMSNARL_GIGANTAMAX, TYPE_DARK, MOVE_G_MAX_SNOOZE}, {SPECIES_ALCREMIE_GIGANTAMAX, TYPE_FAIRY, MOVE_G_MAX_FINALE}, {SPECIES_COPPERAJAH_GIGANTAMAX, TYPE_STEEL, MOVE_G_MAX_STEELSURGE}, {SPECIES_DURALUDON_GIGANTAMAX, TYPE_DRAGON, MOVE_G_MAX_DEPLETION}, {SPECIES_URSHIFU_SINGLE_STRIKE_STYLE_GIGANTAMAX,TYPE_DARK, MOVE_G_MAX_ONE_BLOW}, {SPECIES_URSHIFU_RAPID_STRIKE_STYLE_GIGANTAMAX, TYPE_WATER, MOVE_G_MAX_RAPID_FLOW}, }; // forward declarations static void SpriteCb_DynamaxTrigger(struct Sprite *); // Returns whether a battler is Dynamaxed. bool32 IsDynamaxed(u16 battlerId) { if (gBattleStruct->dynamax.dynamaxed[battlerId] /*|| IsRaidBoss(battlerId)*/) return TRUE; return FALSE; } // Returns whether a battler can Dynamax. bool32 CanDynamax(u16 battlerId) { u16 species = gBattleMons[battlerId].species; u16 holdEffect = ItemId_GetHoldEffect(gBattleMons[battlerId].item); // Check if Dynamax battle flag is set. This needs to be defined in include/config/battle.h #if B_FLAG_DYNAMAX_BATTLE != 0 if (!FlagGet(B_FLAG_DYNAMAX_BATTLE)) #endif return FALSE; // Check if Player has a Dynamax Band. if ((GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battlerId) == B_POSITION_PLAYER_RIGHT)) && !CheckBagHasItem(ITEM_DYNAMAX_BAND, 1)) 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; // Cannot Dynamax if you can Mega Evolve or use a Z-Move if (holdEffect == HOLD_EFFECT_MEGA_STONE || holdEffect == HOLD_EFFECT_Z_CRYSTAL) return FALSE; // Cannot Dynamax if your side has already or will Dynamax. if (gBattleStruct->dynamax.alreadyDynamaxed[GetBattlerSide(battlerId)] || gBattleStruct->dynamax.dynamaxed[BATTLE_PARTNER(battlerId)] || gBattleStruct->dynamax.toDynamax & gBitTable[BATTLE_PARTNER(battlerId)]) return FALSE; // TODO: Cannot Dynamax in a Max Raid if you don't have Dynamax Energy. // if (gBattleTypeFlags & BATTLE_TYPE_RAID && gBattleStruct->raid.dynamaxEnergy != battlerId) // return FALSE; // No checks failed, all set! return TRUE; } // Returns whether a battler is transformed into a Gigantamax form. bool32 IsGigantamaxed(u16 battlerId) { struct Pokemon *mon = &GetSideParty(GetBattlerSide(battlerId))[gBattlerPartyIndexes[battlerId]]; if ((gSpeciesInfo[gBattleMons[battlerId].species].isGigantamax) && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR)) return TRUE; return FALSE; } // Applies the HP Multiplier for Dynamaxed Pokemon and Raid Bosses. void ApplyDynamaxHPMultiplier(u32 battler, struct Pokemon* mon) { if (GetMonData(mon, MON_DATA_SPECIES) == SPECIES_SHEDINJA) return; else { u32 scale = 150 + 5 * GetMonData(mon, MON_DATA_DYNAMAX_LEVEL); u32 hp = (GetMonData(mon, MON_DATA_HP) * scale + 99) / 100; u32 maxHP = (GetMonData(mon, MON_DATA_MAX_HP) * scale + 99) / 100; SetMonData(mon, MON_DATA_HP, &hp); SetMonData(mon, MON_DATA_MAX_HP, &maxHP); } } // Returns the non-Dynamax HP of a Pokemon. u16 GetNonDynamaxHP(u16 battlerId) { if (!IsDynamaxed(battlerId) || gBattleMons[battlerId].species == SPECIES_SHEDINJA) return gBattleMons[battlerId].hp; else { u16 mult = UQ_4_12(1.0/1.5); // placeholder u16 hp = UQ_4_12_TO_INT((gBattleMons[battlerId].hp * mult) + UQ_4_12_ROUND); return hp; } } // Returns the non-Dynamax Max HP of a Pokemon. u16 GetNonDynamaxMaxHP(u32 battlerId) { if (!IsDynamaxed(battlerId) || gBattleMons[battlerId].species == SPECIES_SHEDINJA) return gBattleMons[battlerId].maxHP; else { u16 mult = UQ_4_12(1.0/1.5); // placeholder u16 maxHP = UQ_4_12_TO_INT((gBattleMons[battlerId].maxHP * mult) + UQ_4_12_ROUND); return maxHP; } } // Sets flags used for Dynamaxing and checks Gigantamax forms. void PrepareBattlerForDynamax(u16 battlerId) { u8 side = GetBattlerSide(battlerId); gBattleStruct->dynamax.alreadyDynamaxed[side] = TRUE; gBattleStruct->dynamax.dynamaxed[battlerId] = TRUE; gBattleStruct->dynamax.dynamaxTurns[battlerId] = DYNAMAX_TURNS_COUNT; // Substitute is removed upon Dynamaxing. gBattleMons[battlerId].status2 &= ~STATUS2_SUBSTITUTE; ClearBehindSubstituteBit(battlerId); // Choiced Moves are reset upon Dynamaxing. gBattleStruct->choicedMove[battlerId] = MOVE_NONE; // Try Gigantamax form change. if (!(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED)) // Ditto cannot Gigantamax. TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_GIGANTAMAX); } // Unsets the flags used for Dynamaxing and reverts max HP if needed. void UndoDynamax(u16 battlerId) { u8 side = GetBattlerSide(battlerId); u8 monId = gBattlerPartyIndexes[battlerId]; // Revert HP if battler is still Dynamaxed. if (IsDynamaxed(battlerId)) { struct Pokemon *mon = (side == B_SIDE_PLAYER) ? &gPlayerParty[monId] : &gEnemyParty[monId]; u16 mult = UQ_4_12(1.0/1.5); // placeholder gBattleMons[battlerId].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[battlerId].hp); } // Makes sure there are no Dynamax flags set, including on switch / faint. gBattleStruct->dynamax.dynamaxed[battlerId] = FALSE; gBattleStruct->dynamax.dynamaxTurns[battlerId] = 0; // Undo form change if needed. if (IsGigantamaxed(battlerId)) TryBattleFormChange(battlerId, FORM_CHANGE_END_BATTLE); } // Certain moves are blocked by Max Guard that normally ignore protection. bool32 IsMoveBlockedByMaxGuard(u16 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(u16 move) { // TODO: Certain moves are banned in raids. switch (gBattleMoves[move].effect) { case EFFECT_HEAT_CRASH: case EFFECT_LOW_KICK: return TRUE; } return FALSE; } // Returns whether a move should be converted into a Max Move. bool32 ShouldUseMaxMove(u16 battlerId, u16 baseMove) { // TODO: Raid bosses do not always use Max Moves. // if (IsRaidBoss(battlerId)) // return !IsRaidBossUsingRegularMove(battlerId, baseMove); return IsDynamaxed(battlerId) || gBattleStruct->dynamax.toDynamax & gBitTable[battlerId]; } static u16 GetTypeBasedMaxMove(u16 battlerId, u16 type) { // Gigantamax check u32 i; u16 species = gBattleMons[battlerId].species; u16 targetSpecies = SPECIES_NONE; if (!gSpeciesInfo[species].isGigantamax) targetSpecies = GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_BATTLE_GIGANTAMAX); if (targetSpecies != SPECIES_NONE) 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 (sMaxMoveTable[type] == MOVE_NONE) // failsafe return sMaxMoveTable[0]; return sMaxMoveTable[type]; } // Returns the appropriate Max Move or G-Max Move for a battler to use. u16 GetMaxMove(u16 battlerId, u16 baseMove) { u16 move = baseMove; if (baseMove == MOVE_NONE) // for move display { return MOVE_NONE; } else if (baseMove == MOVE_STRUGGLE) { return MOVE_STRUGGLE; } else if (gBattleMoves[baseMove].category == BATTLE_CATEGORY_STATUS) { move = MOVE_MAX_GUARD; } else if (gBattleStruct->dynamicMoveType) { move = GetTypeBasedMaxMove(battlerId, gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK); gBattleStruct->dynamax.categories[battlerId] = gBattleMoves[baseMove].category; } else { move = GetTypeBasedMaxMove(battlerId, gBattleMoves[baseMove].type); gBattleStruct->dynamax.categories[battlerId] = gBattleMoves[baseMove].category; } return move; } // 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. u8 GetMaxMovePower(u16 move) { u8 tier; // G-Max Drum Solo, G-Max Hydrosnipe, and G-Max Fireball always have 160 base power. if (gBattleMoves[GetMaxMove(gBattlerAttacker, move)].argument == MAX_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); if (gBattleMoves[move].type == TYPE_FIGHTING || gBattleMoves[move].type == 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 u8 GetMaxPowerTier(u16 move) { if (gBattleMoves[move].strikeCount >= 2 && gBattleMoves[move].strikeCount <= 5) { switch(gBattleMoves[move].power) { 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 (gBattleMoves[move].effect) { 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: //case EFFECT_DRAGON_DARTS: 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_WRING_OUT: return MAX_POWER_TIER_6; case EFFECT_FLAIL: case EFFECT_LOW_KICK: return MAX_POWER_TIER_7; case EFFECT_MULTI_HIT: switch(gBattleMoves[move].power) { 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 (gBattleMoves[move].power) { 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(u16 move) { return move >= FIRST_MAX_MOVE && move <= LAST_MAX_MOVE; } // Returns the full name of a Max Move for the move usage text. const u8 *GetMaxMoveName(u16 move) { if (IsMaxMove(move)) return gMaxMoveNames[move - FIRST_MAX_MOVE]; else return gMaxMoveNames[0]; // Failsafe } // 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; } } // Returns the status effect that should be applied by a G-Max Move. static u32 GetMaxMoveStatusEffect(u16 move) { u8 maxEffect = gBattleMoves[move].argument; switch (maxEffect) { // Status 1 case MAX_EFFECT_PARALYZE_FOES: return STATUS1_PARALYSIS; case MAX_EFFECT_POISON_FOES: return STATUS1_POISON; case MAX_EFFECT_POISON_PARALYZE_FOES: { static const u8 sStunShockEffects[] = {STATUS1_PARALYSIS, STATUS1_POISON}; return RandomElement(RNG_G_MAX_STUN_SHOCK, sStunShockEffects); } case MAX_EFFECT_EFFECT_SPORE_FOES: { static const u8 sBefuddleEffects[] = {STATUS1_PARALYSIS, STATUS1_POISON, STATUS1_SLEEP}; return RandomElement(RNG_G_MAX_BEFUDDLE, sBefuddleEffects); } // Status 2 case MAX_EFFECT_CONFUSE_FOES: case MAX_EFFECT_CONFUSE_FOES_PAY_DAY: return STATUS2_CONFUSION; case MAX_EFFECT_INFATUATE_FOES: return STATUS2_INFATUATION; case MAX_EFFECT_MEAN_LOOK: return STATUS2_ESCAPE_PREVENTION; case MAX_EFFECT_TORMENT_FOES: return STATUS2_TORMENT; default: return STATUS1_NONE; } } // CALLNATIVE FUNCTIONS #define CMD_ARGS(...) const struct __attribute__((packed)) { u8 opcode; MEMBERS(__VA_ARGS__) const u8 nextInstr[0]; } *const cmd = (const void *)gBattlescriptCurrInstr #define NATIVE_ARGS(...) CMD_ARGS(void (*func)(void), ##__VA_ARGS__) #define MEMBERS(...) VARARG_8(MEMBERS_, __VA_ARGS__) #define MEMBERS_0() #define MEMBERS_1(a) a; #define MEMBERS_2(a, b) a; b; #define MEMBERS_3(a, b, c) a; b; c; #define MEMBERS_4(a, b, c, d) a; b; c; d; #define MEMBERS_5(a, b, c, d, e) a; b; c; d; e; #define MEMBERS_6(a, b, c, d, e, f) a; b; c; d; e; f; #define MEMBERS_7(a, b, c, d, e, f, g) a; b; c; d; e; f; g; #define MEMBERS_8(a, b, c, d, e, f, g, h) a; b; c; d; e; f; g; h; // Updates Dynamax HP multipliers and healthboxes. void BS_UpdateDynamax(void) { NATIVE_ARGS(); u16 battler = gBattleScripting.battler; struct Pokemon *mon = &GetSideParty(GetBattlerSide(battler))[gBattlerPartyIndexes[battler]]; if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change. RecalcBattlerStats(battler, mon); UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL); gBattlescriptCurrInstr = cmd->nextInstr; } // Activates the secondary effect of a Max Move. void BS_SetMaxMoveEffect(void) { NATIVE_ARGS(); u16 effect = 0; u8 maxEffect = gBattleMoves[gCurrentMove].argument; // Don't continue if the move didn't land. if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) { gBattlescriptCurrInstr = cmd->nextInstr; return; } switch (maxEffect) { case MAX_EFFECT_RAISE_TEAM_ATTACK: case MAX_EFFECT_RAISE_TEAM_DEFENSE: case MAX_EFFECT_RAISE_TEAM_SPEED: case MAX_EFFECT_RAISE_TEAM_SP_ATK: case MAX_EFFECT_RAISE_TEAM_SP_DEF: if (!NoAliveMonsForEitherParty()) { // Max Effects are ordered by stat ID. SET_STATCHANGER(gBattleMoves[gCurrentMove].argument, 1, FALSE); BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectRaiseStatAllies; effect++; } break; case MAX_EFFECT_LOWER_ATTACK: case MAX_EFFECT_LOWER_DEFENSE: case MAX_EFFECT_LOWER_SPEED: case MAX_EFFECT_LOWER_SP_ATK: case MAX_EFFECT_LOWER_SP_DEF: case MAX_EFFECT_LOWER_SPEED_2_FOES: case MAX_EFFECT_LOWER_EVASIVENESS_FOES: if (!NoAliveMonsForEitherParty()) { u8 statId = 0; u8 stage = 1; switch (maxEffect) { case MAX_EFFECT_LOWER_SPEED_2_FOES: statId = STAT_SPEED; stage = 2; break; case MAX_EFFECT_LOWER_EVASIVENESS_FOES: statId = STAT_EVASION; break; default: // Max Effects are ordered by stat ID. statId = gBattleMoves[gCurrentMove].argument - MAX_EFFECT_LOWER_ATTACK + 1; break; } SET_STATCHANGER(statId, stage, TRUE); BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectLowerStatFoes; effect++; } break; case MAX_EFFECT_SUN: case MAX_EFFECT_RAIN: case MAX_EFFECT_SANDSTORM: case MAX_EFFECT_HAIL: { u8 weather, msg; switch (maxEffect) { case MAX_EFFECT_SUN: weather = ENUM_WEATHER_SUN; msg = B_MSG_STARTED_SUNLIGHT; break; case MAX_EFFECT_RAIN: weather = ENUM_WEATHER_RAIN; msg = B_MSG_STARTED_RAIN; break; case MAX_EFFECT_SANDSTORM: weather = ENUM_WEATHER_SANDSTORM; msg = B_MSG_STARTED_SANDSTORM; break; case MAX_EFFECT_HAIL: weather = ENUM_WEATHER_HAIL; msg = B_MSG_STARTED_HAIL; break; } if (TryChangeBattleWeather(gBattlerAttacker, weather, FALSE)) { gBattleCommunication[MULTISTRING_CHOOSER] = msg; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectSetWeather; effect++; } break; } case MAX_EFFECT_MISTY_TERRAIN: case MAX_EFFECT_GRASSY_TERRAIN: case MAX_EFFECT_ELECTRIC_TERRAIN: case MAX_EFFECT_PSYCHIC_TERRAIN: { u32 statusFlag = 0; switch (gBattleMoves[gCurrentMove].argument) { case MAX_EFFECT_MISTY_TERRAIN: statusFlag = STATUS_FIELD_MISTY_TERRAIN; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; break; case MAX_EFFECT_GRASSY_TERRAIN: statusFlag = STATUS_FIELD_GRASSY_TERRAIN; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_GRASSY; break; case MAX_EFFECT_ELECTRIC_TERRAIN: statusFlag = STATUS_FIELD_ELECTRIC_TERRAIN; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; break; case MAX_EFFECT_PSYCHIC_TERRAIN: statusFlag = STATUS_FIELD_PSYCHIC_TERRAIN; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC; break; } if (!(gFieldStatuses & statusFlag) && statusFlag != 0) { gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; gFieldStatuses |= statusFlag; if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_TERRAIN_EXTENDER) gFieldTimers.terrainTimer = 8; else gFieldTimers.terrainTimer = 5; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectSetTerrain; effect++; } break; } case MAX_EFFECT_VINE_LASH: case MAX_EFFECT_CANNONADE: case MAX_EFFECT_WILDFIRE: case MAX_EFFECT_VOLCALITH: { u8 side = GetBattlerSide(gBattlerTarget); if (!(gSideStatuses[side] & SIDE_STATUS_DAMAGE_NON_TYPES)) { gSideStatuses[side] |= SIDE_STATUS_DAMAGE_NON_TYPES; gSideTimers[side].damageNonTypesTimer = 5; // damage is dealt for 4 turns, ends on 5th gSideTimers[side].damageNonTypesType = gBattleMoves[gCurrentMove].type; BattleScriptPush(gBattlescriptCurrInstr + 1); ChooseDamageNonTypesString(gBattleMoves[gCurrentMove].type); gBattlescriptCurrInstr = BattleScript_DamageNonTypesStarts; effect++; } break; } case MAX_EFFECT_STEALTH_ROCK: if (!(gSideStatuses[GetBattlerSide(gBattlerTarget)] & SIDE_STATUS_STEALTH_ROCK)) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_POINTEDSTONESFLOAT; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectStonesurge; effect++; } break; case MAX_EFFECT_STEELSURGE: if (!(gSideStatuses[GetBattlerSide(gBattlerTarget)] & SIDE_STATUS_STEELSURGE)) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SHARPSTEELFLOATS; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectSteelsurge; effect++; } break; case MAX_EFFECT_DEFOG: if (gSideStatuses[GetBattlerSide(gBattlerTarget)] & SIDE_STATUS_SCREEN_ANY || gSideStatuses[GetBattlerSide(gBattlerTarget)] & SIDE_STATUS_HAZARDS_ANY || gSideStatuses[GetBattlerSide(gBattlerAttacker)] & SIDE_STATUS_HAZARDS_ANY || gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) { BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_DefogTryHazards; effect++; } break; case MAX_EFFECT_AURORA_VEIL: if (!(gSideStatuses[GetBattlerSide(gBattlerAttacker)] & SIDE_STATUS_AURORA_VEIL)) { gSideStatuses[GetBattlerSide(gBattlerAttacker)] |= SIDE_STATUS_AURORA_VEIL; if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LIGHT_CLAY) gSideTimers[GetBattlerSide(gBattlerAttacker)].auroraVeilTimer = 8; else gSideTimers[GetBattlerSide(gBattlerAttacker)].auroraVeilTimer = 5; gSideTimers[GetBattlerSide(gBattlerAttacker)].auroraVeilBattlerId = gBattlerAttacker; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_SAFEGUARD; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectAuroraVeilSuccess; effect++; } break; case MAX_EFFECT_GRAVITY: if (!(gFieldStatuses & STATUS_FIELD_GRAVITY)) { gFieldStatuses |= STATUS_FIELD_GRAVITY; gFieldTimers.gravityTimer = 5; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectGravitySuccess; effect++; } break; case MAX_EFFECT_SANDBLAST_FOES: case MAX_EFFECT_FIRE_SPIN_FOES: { // Affects both opponents, but doesn't print strings so we can handle it here. u8 battler; for (battler = 0; battler < MAX_BATTLERS_COUNT; ++battler) { if (GetBattlerSide(battler) != GetBattlerSide(gBattlerTarget)) continue; if (!(gBattleMons[battler].status2 & STATUS2_WRAPPED)) { gBattleMons[battler].status2 |= STATUS2_WRAPPED; if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_GRIP_CLAW) #if B_BINDING_TURNS >= GEN_5 gDisableStructs[battler].wrapTurns = 7; else gDisableStructs[battler].wrapTurns = (Random() % 2) + 4; #else gDisableStructs[battler].wrapTurns = 5; else gDisableStructs[battler].wrapTurns = (Random() % 4) + 2; #endif // The Wrap effect does not expire when the user switches, so here's some cheese. gBattleStruct->wrappedBy[battler] = gBattlerTarget; if (maxEffect == MAX_EFFECT_SANDBLAST_FOES) gBattleStruct->wrappedMove[battler] = MOVE_SAND_TOMB; else gBattleStruct->wrappedMove[battler] = MOVE_FIRE_SPIN; } } break; } case MAX_EFFECT_YAWN_FOE: { static const u8 sSnoozeEffects[] = {TRUE, FALSE}; if (!(gStatuses3[gBattlerTarget] & STATUS3_YAWN) && CanSleep(gBattlerTarget) && RandomElement(RNG_G_MAX_SNOOZE, sSnoozeEffects)) // 50% chance of success { gStatuses3[gBattlerTarget] |= STATUS3_YAWN_TURN(2); BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectYawnSuccess; effect++; } break; } case MAX_EFFECT_SPITE: if (gLastMoves[gBattlerTarget] != MOVE_NONE && gLastMoves[gBattlerTarget] != MOVE_UNAVAILABLE) { BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectTryReducePP; effect++; } break; case MAX_EFFECT_PARALYZE_FOES: case MAX_EFFECT_POISON_FOES: case MAX_EFFECT_POISON_PARALYZE_FOES: case MAX_EFFECT_EFFECT_SPORE_FOES: BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectStatus1Foes; effect++; break; case MAX_EFFECT_CONFUSE_FOES_PAY_DAY: if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) { u16 payday = gPaydayMoney; gPaydayMoney += (gBattleMons[gBattlerAttacker].level * 100); if (payday > gPaydayMoney) gPaydayMoney = 0xFFFF; gBattleCommunication[CURSOR_POSITION] = 1; // add "Coins scattered." message } // fall through case MAX_EFFECT_CONFUSE_FOES: case MAX_EFFECT_INFATUATE_FOES: case MAX_EFFECT_TORMENT_FOES: case MAX_EFFECT_MEAN_LOOK: BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectStatus2Foes; effect++; break; case MAX_EFFECT_CRIT_PLUS: gBattleStruct->bonusCritStages[gBattlerAttacker]++; gBattleStruct->bonusCritStages[BATTLE_PARTNER(gBattlerAttacker)]++; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectRaiseCritAlliesAnim; effect++; break; case MAX_EFFECT_HEAL_TEAM: BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectHealOneSixthAllies; effect++; break; case MAX_EFFECT_AROMATHERAPY: BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectCureStatusAllies; effect++; break; case MAX_EFFECT_RECYCLE_BERRIES: { static const u8 sReplenishEffects[] = {TRUE, FALSE}; if (RandomElement(RNG_G_MAX_REPLENISH, sReplenishEffects)) // 50% chance of success { BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_EffectRecycleBerriesAllies; effect++; } break; } } if (!effect) gBattlescriptCurrInstr = cmd->nextInstr; } // Sets up sharp steel on the target's side. void BS_SetSteelsurge(void) { NATIVE_ARGS(const u8 *failInstr); u8 targetSide = GetBattlerSide(gBattlerTarget); if (gSideStatuses[targetSide] & SIDE_STATUS_STEELSURGE) { gBattlescriptCurrInstr = cmd->failInstr; } else { gSideStatuses[targetSide] |= SIDE_STATUS_STEELSURGE; gSideTimers[targetSide].steelsurgeAmount = 1; gBattlescriptCurrInstr = cmd->nextInstr; } } // Applies the status1 effect associated with a given G-Max Move. // Could be expanded to function for any move. void BS_TrySetStatus1(void) { NATIVE_ARGS(const u8 *failInstr); u8 effect = 0; u32 status1 = GetMaxMoveStatusEffect(gCurrentMove); switch (status1) { case STATUS1_POISON: if (CanBePoisoned(gBattlerAttacker, gBattlerTarget)) { gBattleMons[gBattlerTarget].status1 |= STATUS1_POISON; gBattleCommunication[MULTISTRING_CHOOSER] = 0; effect++; } break; case STATUS1_PARALYSIS: if (CanBeParalyzed(gBattlerTarget)) { gBattleMons[gBattlerTarget].status1 |= STATUS1_PARALYSIS; gBattleCommunication[MULTISTRING_CHOOSER] = 3; effect++; } break; case STATUS1_SLEEP: if (CanSleep(gBattlerTarget)) { #if B_SLEEP_TURNS >= GEN_5 gBattleMons[gBattlerTarget].status1 |= STATUS1_SLEEP_TURN((Random() % 3) + 2); #else gBattleMons[gBattlerTarget].status1 |= STATUS1_SLEEP_TURN((Random() % 4) + 3); #endif gBattleCommunication[MULTISTRING_CHOOSER] = 4; effect++; } break; } if (effect) { gEffectBattler = gBattlerTarget; BtlController_EmitSetMonData(gBattlerTarget, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1); MarkBattlerForControllerExec(gBattlerTarget); gBattlescriptCurrInstr = cmd->nextInstr; } else { gBattlescriptCurrInstr = cmd->failInstr; } } // Applies the status2 effect associated with a given G-Max Move. void BS_TrySetStatus2(void) { NATIVE_ARGS(const u8 *failInstr); u8 effect = 0; u32 status2 = GetMaxMoveStatusEffect(gCurrentMove); switch (status2) { case STATUS2_CONFUSION: if (CanBeConfused(gBattlerTarget)) { gBattleMons[gBattlerTarget].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); gBattleCommunication[MULTISTRING_CHOOSER] = 0; gBattleCommunication[MULTIUSE_STATE] = 1; effect++; } break; case STATUS2_INFATUATION: { u8 atkGender = GetGenderFromSpeciesAndPersonality(gBattleMons[gBattlerAttacker].species, gBattleMons[gBattlerAttacker].personality); u8 defGender = GetGenderFromSpeciesAndPersonality(gBattleMons[gBattlerTarget].species, gBattleMons[gBattlerTarget].personality); if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_INFATUATION) && gBattleMons[gBattlerTarget].ability != ABILITY_OBLIVIOUS && !IsAbilityOnSide(gBattlerTarget, ABILITY_AROMA_VEIL) && atkGender != defGender && atkGender != MON_GENDERLESS && defGender != MON_GENDERLESS) { gBattleMons[gBattlerTarget].status2 |= STATUS2_INFATUATED_WITH(gBattlerAttacker); gBattleCommunication[MULTISTRING_CHOOSER] = 1; gBattleCommunication[MULTIUSE_STATE] = 2; effect++; } break; } case STATUS2_ESCAPE_PREVENTION: if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_ESCAPE_PREVENTION)) { gBattleMons[gBattlerTarget].status2 |= STATUS2_ESCAPE_PREVENTION; gDisableStructs[gBattlerTarget].battlerPreventingEscape = gBattlerAttacker; gBattleCommunication[MULTISTRING_CHOOSER] = 2; effect++; } break; case STATUS2_TORMENT: if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_TORMENT) && !IsAbilityOnSide(gBattlerTarget, ABILITY_AROMA_VEIL)) { gBattleMons[gBattlerTarget].status2 |= STATUS2_TORMENT; gDisableStructs[gBattlerTarget].tormentTimer = 3; // 3 turns excluding current turn gBattleCommunication[MULTISTRING_CHOOSER] = 3; effect++; } break; } if (effect) { gEffectBattler = gBattlerTarget; gBattlescriptCurrInstr = cmd->nextInstr; } else { gBattlescriptCurrInstr = cmd->failInstr; } } // Applies the endturn damage effect associated with the "Damage Non-" G-Max moves. void BS_DamageNonTypes(void) { NATIVE_ARGS(); u8 side = GetBattlerSide(gBattlerAttacker); gBattleMoveDamage = 0; if (gSideTimers[side].damageNonTypesTimer && !IS_BATTLER_OF_TYPE(gBattlerAttacker, gSideTimers[side].damageNonTypesType) && IsBattlerAlive(gBattlerAttacker) && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) { gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 6; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; } gBattlescriptCurrInstr = cmd->nextInstr; } // Heals one-sixth of the target's HP, including for Dynamaxed targets. void BS_HealOneSixth(void) { NATIVE_ARGS(const u8* failInstr); gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 6; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP) gBattlescriptCurrInstr = cmd->failInstr; // fail else gBattlescriptCurrInstr = cmd->nextInstr; // can heal } // Recycles the target's item if it is specifically holding a berry. void BS_TryRecycleBerry(void) { NATIVE_ARGS(const u8 *failInstr); u16* usedHeldItem = &gBattleStruct->usedHeldItems[gBattlerPartyIndexes[gBattlerTarget]][GetBattlerSide(gBattlerTarget)]; if (gBattleMons[gBattlerTarget].item == ITEM_NONE && gBattleStruct->changedItems[gBattlerTarget] == ITEM_NONE // Will not inherit an item && ItemId_GetPocket(*usedHeldItem) == POCKET_BERRIES) { gLastUsedItem = *usedHeldItem; *usedHeldItem = ITEM_NONE; gBattleMons[gBattlerTarget].item = gLastUsedItem; BtlController_EmitSetMonData(gBattlerTarget, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item); MarkBattlerForControllerExec(gBattlerTarget); gBattlescriptCurrInstr = cmd->nextInstr; } else { gBattlescriptCurrInstr = cmd->failInstr; } } // Goes to the jump instruction if the target is Dynamaxed. void BS_JumpIfDynamaxed(void) { NATIVE_ARGS(const u8 *jumpInstr); if (IsDynamaxed(gBattlerTarget)) gBattlescriptCurrInstr = cmd->jumpInstr; else gBattlescriptCurrInstr = cmd->nextInstr; } // DYNAMAX TRIGGER: static const u8 ALIGNED(4) sDynamaxTriggerGfx[] = INCBIN_U8("graphics/battle_interface/dynamax_trigger.4bpp"); static const u16 sDynamaxTriggerPal[] = INCBIN_U16("graphics/battle_interface/dynamax_trigger.gbapal"); static const struct SpriteSheet sSpriteSheet_DynamaxTrigger = { sDynamaxTriggerGfx, sizeof(sDynamaxTriggerGfx), TAG_DYNAMAX_TRIGGER_TILE }; static const struct SpritePalette sSpritePalette_DynamaxTrigger = { sDynamaxTriggerPal, TAG_DYNAMAX_TRIGGER_PAL }; static const struct OamData sOamData_DynamaxTrigger = { .y = 0, .affineMode = 0, .objMode = 0, .mosaic = 0, .bpp = 0, .shape = ST_OAM_SQUARE, .x = 0, .matrixNum = 0, .size = 2, .tileNum = 0, .priority = 1, .paletteNum = 0, .affineParam = 0, }; static const union AnimCmd sSpriteAnim_DynamaxTriggerOff[] = { ANIMCMD_FRAME(0, 0), ANIMCMD_END }; static const union AnimCmd sSpriteAnim_DynamaxTriggerOn[] = { ANIMCMD_FRAME(16, 0), ANIMCMD_END }; static const union AnimCmd *const sSpriteAnimTable_DynamaxTrigger[] = { sSpriteAnim_DynamaxTriggerOff, sSpriteAnim_DynamaxTriggerOn, }; static const struct SpriteTemplate sSpriteTemplate_DynamaxTrigger = { .tileTag = TAG_DYNAMAX_TRIGGER_TILE, .paletteTag = TAG_DYNAMAX_TRIGGER_PAL, .oam = &sOamData_DynamaxTrigger, .anims = sSpriteAnimTable_DynamaxTrigger, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCb_DynamaxTrigger }; // Dynamax Evolution Trigger icon functions. void ChangeDynamaxTriggerSprite(u8 spriteId, u8 animId) { StartSpriteAnim(&gSprites[spriteId], animId); } #define SINGLES_DYNAMAX_TRIGGER_POS_X_OPTIMAL (30) #define SINGLES_DYNAMAX_TRIGGER_POS_X_PRIORITY (31) #define SINGLES_DYNAMAX_TRIGGER_POS_X_SLIDE (15) #define SINGLES_DYNAMAX_TRIGGER_POS_Y_DIFF (-11) #define DOUBLES_DYNAMAX_TRIGGER_POS_X_OPTIMAL (30) #define DOUBLES_DYNAMAX_TRIGGER_POS_X_PRIORITY (31) #define DOUBLES_DYNAMAX_TRIGGER_POS_X_SLIDE (15) #define DOUBLES_DYNAMAX_TRIGGER_POS_Y_DIFF (-4) #define tBattler data[0] #define tHide data[1] void CreateDynamaxTriggerSprite(u8 battlerId, u8 palId) { LoadSpritePalette(&sSpritePalette_DynamaxTrigger); if (GetSpriteTileStartByTag(TAG_DYNAMAX_TRIGGER_TILE) == 0xFFFF) LoadSpriteSheet(&sSpriteSheet_DynamaxTrigger); if (gBattleStruct->dynamax.triggerSpriteId == 0xFF) { if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) gBattleStruct->dynamax.triggerSpriteId = CreateSprite(&sSpriteTemplate_DynamaxTrigger, gSprites[gHealthboxSpriteIds[battlerId]].x - DOUBLES_DYNAMAX_TRIGGER_POS_X_SLIDE, gSprites[gHealthboxSpriteIds[battlerId]].y - DOUBLES_DYNAMAX_TRIGGER_POS_Y_DIFF, 0); else gBattleStruct->dynamax.triggerSpriteId = CreateSprite(&sSpriteTemplate_DynamaxTrigger, gSprites[gHealthboxSpriteIds[battlerId]].x - SINGLES_DYNAMAX_TRIGGER_POS_X_SLIDE, gSprites[gHealthboxSpriteIds[battlerId]].y - SINGLES_DYNAMAX_TRIGGER_POS_Y_DIFF, 0); } gSprites[gBattleStruct->dynamax.triggerSpriteId].tBattler = battlerId; gSprites[gBattleStruct->dynamax.triggerSpriteId].tHide = FALSE; ChangeDynamaxTriggerSprite(gBattleStruct->dynamax.triggerSpriteId, palId); } static void SpriteCb_DynamaxTrigger(struct Sprite *sprite) { s32 xSlide, xPriority, xOptimal; s32 yDiff; if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { xSlide = DOUBLES_DYNAMAX_TRIGGER_POS_X_SLIDE; xPriority = DOUBLES_DYNAMAX_TRIGGER_POS_X_PRIORITY; xOptimal = DOUBLES_DYNAMAX_TRIGGER_POS_X_OPTIMAL; yDiff = DOUBLES_DYNAMAX_TRIGGER_POS_Y_DIFF; } else { xSlide = SINGLES_DYNAMAX_TRIGGER_POS_X_SLIDE; xPriority = SINGLES_DYNAMAX_TRIGGER_POS_X_PRIORITY; xOptimal = SINGLES_DYNAMAX_TRIGGER_POS_X_OPTIMAL; yDiff = SINGLES_DYNAMAX_TRIGGER_POS_Y_DIFF; } if (sprite->tHide) { if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide) sprite->x++; if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority) sprite->oam.priority = 2; else sprite->oam.priority = 1; sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff; sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff; if (sprite->x == gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide) DestroyDynamaxTriggerSprite(); } else { if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xOptimal) sprite->x--; if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority) sprite->oam.priority = 2; else sprite->oam.priority = 1; sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff; sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff; } } bool32 IsDynamaxTriggerSpriteActive(void) { if (GetSpriteTileStartByTag(TAG_DYNAMAX_TRIGGER_TILE) == 0xFFFF) return FALSE; else if (IndexOfSpritePaletteTag(TAG_DYNAMAX_TRIGGER_PAL) != 0xFF) return TRUE; else return FALSE; } void HideDynamaxTriggerSprite(void) { if (gBattleStruct->dynamax.triggerSpriteId >= MAX_SPRITES) return; ChangeDynamaxTriggerSprite(gBattleStruct->dynamax.triggerSpriteId, 0); gSprites[gBattleStruct->dynamax.triggerSpriteId].tHide = TRUE; } void DestroyDynamaxTriggerSprite(void) { FreeSpritePaletteByTag(TAG_DYNAMAX_TRIGGER_PAL); FreeSpriteTilesByTag(TAG_DYNAMAX_TRIGGER_TILE); if (gBattleStruct->dynamax.triggerSpriteId != 0xFF) DestroySprite(&gSprites[gBattleStruct->dynamax.triggerSpriteId]); gBattleStruct->dynamax.triggerSpriteId = 0xFF; } #undef tBattler #undef tHide // data fields for healthboxMain // oam.affineParam holds healthboxRight spriteId #define hMain_DynamaxIndicatorId data[3] #define hMain_HealthBarSpriteId data[5] #define hMain_Battler data[6] #define hMain_Data7 data[7] // data fields for healthboxRight #define hOther_HealthBoxSpriteId data[5] // data fields for healthbar #define hBar_HealthBoxSpriteId data[5]