#include "global.h" #include "pokemon.h" #include "battle.h" #include "battle_anim.h" #include "battle_tv.h" #include "constants/battle_string_ids.h" #include "constants/battle_anim.h" #include "constants/moves.h" #include "battle_message.h" #include "tv.h" #include "constants/battle_move_effects.h" // this file's functions static bool8 IsNotSpecialBattleString(u16 stringId); static void AddMovePoints(u8 caseId, u16 arg1, u8 arg2, u8 arg3); static void TrySetBattleSeminarShow(void); static void AddPointsOnFainting(bool8 targetFainted); static void AddPointsBasedOnWeather(u16 weatherFlags, u16 moveId, u8 moveSlot); static bool8 ShouldCalculateDamage(u16 moveId, s32 *dmg, u16 *powerOverride); #define TABLE_END ((u16)-1) enum { PTS_MOVE_EFFECT, PTS_EFFECTIVENESS, PTS_SET_UP, // Broadly. Used by Wish, Future Sight, Ingrain, etc. PTS_RAIN, PTS_SUN, PTS_SANDSTORM, PTS_HAIL, PTS_ELECTRIC, PTS_STATUS_DMG, PTS_STATUS, PTS_SPIKES, PTS_WATER_SPORT, PTS_MUD_SPORT, PTS_REFLECT, PTS_LIGHT_SCREEN, PTS_SAFEGUARD, PTS_MIST, PTS_BREAK_WALL, PTS_CRITICAL_HIT, PTS_FAINT, PTS_FAINT_SET_UP, PTS_FLINCHED, PTS_STAT_INCREASE_1, PTS_STAT_INCREASE_2, PTS_STAT_DECREASE_SELF, PTS_STAT_DECREASE_1, PTS_STAT_DECREASE_2, PTS_STAT_INCREASE_NOT_SELF, }; enum { FNT_NONE, FNT_CURSE, FNT_LEECH_SEED, FNT_POISON, FNT_BURN, FNT_NIGHTMARE, FNT_WRAP, FNT_SPIKES, FNT_FUTURE_SIGHT, FNT_DOOM_DESIRE, FNT_PERISH_SONG, FNT_DESTINY_BOND, FNT_CONFUSION, FNT_EXPLOSION, FNT_RECOIL, FNT_OTHER, }; // const rom data static const u16 sVariableDmgMoves[] = { MOVE_COUNTER, MOVE_FISSURE, MOVE_BIDE, MOVE_MIRROR_COAT, MOVE_HORN_DRILL, MOVE_FLAIL, MOVE_REVERSAL, MOVE_HIDDEN_POWER, MOVE_SHEER_COLD, MOVE_FOCUS_PUNCH, MOVE_ERUPTION, MOVE_WATER_SPOUT, MOVE_DREAM_EATER, MOVE_WEATHER_BALL, MOVE_SNORE, MOVE_PAIN_SPLIT, MOVE_GUILLOTINE, MOVE_FRUSTRATION, MOVE_RETURN, MOVE_ENDEAVOR, MOVE_PRESENT, MOVE_REVENGE, TABLE_END, // those are handled by the function itself MOVE_MAGNITUDE, MOVE_PSYWAVE, TABLE_END }; static const u16 sPoints_MoveEffect[NUM_BATTLE_MOVE_EFFECTS] = { [EFFECT_HIT] = 1, [EFFECT_SLEEP] = 1, [EFFECT_POISON_HIT] = 1, [EFFECT_ABSORB] = 4, [EFFECT_BURN_HIT] = 1, [EFFECT_FREEZE_HIT] = 1, [EFFECT_PARALYZE_HIT] = 1, [EFFECT_EXPLOSION] = 0, [EFFECT_DREAM_EATER] = 5, [EFFECT_MIRROR_MOVE] = 1, [EFFECT_ATTACK_UP] = 1, [EFFECT_DEFENSE_UP] = 1, [EFFECT_SPEED_UP] = 1, [EFFECT_SPECIAL_ATTACK_UP] = 1, [EFFECT_SPECIAL_DEFENSE_UP] = 1, [EFFECT_ACCURACY_UP] = 1, [EFFECT_EVASION_UP] = 1, [EFFECT_ALWAYS_HIT] = 2, [EFFECT_ATTACK_DOWN] = 1, [EFFECT_DEFENSE_DOWN] = 1, [EFFECT_SPEED_DOWN] = 1, [EFFECT_SPECIAL_ATTACK_DOWN] = 1, [EFFECT_SPECIAL_DEFENSE_DOWN] = 1, [EFFECT_ACCURACY_DOWN] = 1, [EFFECT_EVASION_DOWN] = 1, [EFFECT_HAZE] = 5, [EFFECT_BIDE] = 5, [EFFECT_RAMPAGE] = 4, [EFFECT_ROAR] = 5, [EFFECT_MULTI_HIT] = 1, [EFFECT_CONVERSION] = 3, [EFFECT_FLINCH_HIT] = 1, [EFFECT_RESTORE_HP] = 3, [EFFECT_TOXIC] = 5, [EFFECT_PAY_DAY] = 1, [EFFECT_LIGHT_SCREEN] = 7, [EFFECT_TRI_ATTACK] = 1, [EFFECT_REST] = 7, [EFFECT_OHKO] = 7, [EFFECT_RAZOR_WIND] = 1, [EFFECT_SUPER_FANG] = 5, [EFFECT_DRAGON_RAGE] = 2, [EFFECT_TRAP] = 4, [EFFECT_HIGH_CRITICAL] = 1, [EFFECT_DOUBLE_HIT] = 1, [EFFECT_RECOIL_IF_MISS] = 1, [EFFECT_MIST] = 5, [EFFECT_FOCUS_ENERGY] = 1, [EFFECT_RECOIL] = 2, [EFFECT_CONFUSE] = 4, [EFFECT_ATTACK_UP_2] = 1, [EFFECT_DEFENSE_UP_2] = 1, [EFFECT_SPEED_UP_2] = 1, [EFFECT_SPECIAL_ATTACK_UP_2] = 1, [EFFECT_SPECIAL_DEFENSE_UP_2] = 1, [EFFECT_ACCURACY_UP_2] = 1, [EFFECT_EVASION_UP_2] = 1, [EFFECT_TRANSFORM] = 0, [EFFECT_ATTACK_DOWN_2] = 1, [EFFECT_DEFENSE_DOWN_2] = 1, [EFFECT_SPEED_DOWN_2] = 1, [EFFECT_SPECIAL_ATTACK_DOWN_2] = 1, [EFFECT_SPECIAL_DEFENSE_DOWN_2] = 1, [EFFECT_ACCURACY_DOWN_2] = 1, [EFFECT_EVASION_DOWN_2] = 1, [EFFECT_REFLECT] = 7, [EFFECT_POISON] = 4, [EFFECT_PARALYZE] = 4, [EFFECT_ATTACK_DOWN_HIT] = 1, [EFFECT_DEFENSE_DOWN_HIT] = 1, [EFFECT_SPEED_DOWN_HIT] = 1, [EFFECT_SPECIAL_ATTACK_DOWN_HIT] = 1, [EFFECT_SPECIAL_DEFENSE_DOWN_HIT] = 1, [EFFECT_ACCURACY_DOWN_HIT] = 1, [EFFECT_EVASION_DOWN_HIT] = 1, [EFFECT_SKY_ATTACK] = 4, [EFFECT_CONFUSE_HIT] = 1, [EFFECT_TWINEEDLE] = 1, [EFFECT_VITAL_THROW] = 1, [EFFECT_SUBSTITUTE] = 4, [EFFECT_RECHARGE] = 5, [EFFECT_RAGE] = 2, [EFFECT_MIMIC] = 4, [EFFECT_METRONOME] = 1, [EFFECT_LEECH_SEED] = 4, [EFFECT_SPLASH] = 1, [EFFECT_DISABLE] = 7, [EFFECT_LEVEL_DAMAGE] = 2, [EFFECT_PSYWAVE] = 1, [EFFECT_COUNTER] = 5, [EFFECT_ENCORE] = 7, [EFFECT_PAIN_SPLIT] = 3, [EFFECT_SNORE] = 3, [EFFECT_CONVERSION_2] = 4, [EFFECT_LOCK_ON] = 3, [EFFECT_SKETCH] = 3, [EFFECT_UNUSED_60] = 3, [EFFECT_SLEEP_TALK] = 3, [EFFECT_DESTINY_BOND] = 3, [EFFECT_FLAIL] = 2, [EFFECT_SPITE] = 4, [EFFECT_FALSE_SWIPE] = 1, [EFFECT_HEAL_BELL] = 5, [EFFECT_QUICK_ATTACK] = 1, [EFFECT_TRIPLE_KICK] = 1, [EFFECT_THIEF] = 4, [EFFECT_MEAN_LOOK] = 5, [EFFECT_NIGHTMARE] = 3, [EFFECT_MINIMIZE] = 1, [EFFECT_CURSE] = 2, [EFFECT_UNUSED_6E] = 1, [EFFECT_PROTECT] = 5, [EFFECT_SPIKES] = 4, [EFFECT_FORESIGHT] = 3, [EFFECT_PERISH_SONG] = 6, [EFFECT_SANDSTORM] = 4, [EFFECT_ENDURE] = 3, [EFFECT_ROLLOUT] = 3, [EFFECT_SWAGGER] = 3, [EFFECT_FURY_CUTTER] = 2, [EFFECT_ATTRACT] = 4, [EFFECT_RETURN] = 1, [EFFECT_PRESENT] = 1, [EFFECT_FRUSTRATION] = 1, [EFFECT_SAFEGUARD] = 5, [EFFECT_THAW_HIT] = 1, [EFFECT_MAGNITUDE] = 1, [EFFECT_BATON_PASS] = 7, [EFFECT_PURSUIT] = 2, [EFFECT_RAPID_SPIN] = 2, [EFFECT_SONICBOOM] = 1, [EFFECT_UNUSED_83] = 1, [EFFECT_MORNING_SUN] = 4, [EFFECT_SYNTHESIS] = 4, [EFFECT_MOONLIGHT] = 4, [EFFECT_HIDDEN_POWER] = 1, [EFFECT_RAIN_DANCE] = 4, [EFFECT_SUNNY_DAY] = 4, [EFFECT_DEFENSE_UP_HIT] = 1, [EFFECT_ATTACK_UP_HIT] = 1, [EFFECT_ALL_STATS_UP_HIT] = 1, [EFFECT_UNUSED_8D] = 1, [EFFECT_BELLY_DRUM] = 7, [EFFECT_PSYCH_UP] = 7, [EFFECT_MIRROR_COAT] = 6, [EFFECT_SKULL_BASH] = 3, [EFFECT_TWISTER] = 1, [EFFECT_EARTHQUAKE] = 1, [EFFECT_FUTURE_SIGHT] = 1, [EFFECT_GUST] = 1, [EFFECT_FLINCH_MINIMIZE_HIT] = 1, [EFFECT_SOLAR_BEAM] = 1, [EFFECT_THUNDER] = 1, [EFFECT_TELEPORT] = 1, [EFFECT_BEAT_UP] = 2, [EFFECT_SEMI_INVULNERABLE] = 3, [EFFECT_DEFENSE_CURL] = 1, [EFFECT_SOFTBOILED] = 1, [EFFECT_FAKE_OUT] = 4, [EFFECT_UPROAR] = 4, [EFFECT_STOCKPILE] = 3, [EFFECT_SPIT_UP] = 3, [EFFECT_SWALLOW] = 3, [EFFECT_UNUSED_A3] = 1, [EFFECT_HAIL] = 4, [EFFECT_TORMENT] = 7, [EFFECT_FLATTER] = 7, [EFFECT_WILL_O_WISP] = 5, [EFFECT_MEMENTO] = 7, [EFFECT_FACADE] = 1, [EFFECT_FOCUS_PUNCH] = 7, [EFFECT_SMELLINGSALT] = 1, [EFFECT_FOLLOW_ME] = 5, [EFFECT_NATURE_POWER] = 0, [EFFECT_CHARGE] = 4, [EFFECT_TAUNT] = 4, [EFFECT_HELPING_HAND] = 4, [EFFECT_TRICK] = 4, [EFFECT_ROLE_PLAY] = 4, [EFFECT_WISH] = 2, [EFFECT_ASSIST] = 2, [EFFECT_INGRAIN] = 6, [EFFECT_SUPERPOWER] = 3, [EFFECT_MAGIC_COAT] = 6, [EFFECT_RECYCLE] = 4, [EFFECT_REVENGE] = 4, [EFFECT_BRICK_BREAK] = 2, [EFFECT_YAWN] = 5, [EFFECT_KNOCK_OFF] = 2, [EFFECT_ENDEAVOR] = 1, [EFFECT_ERUPTION] = 1, [EFFECT_SKILL_SWAP] = 6, [EFFECT_IMPRISON] = 6, [EFFECT_REFRESH] = 6, [EFFECT_GRUDGE] = 1, [EFFECT_SNATCH] = 1, [EFFECT_LOW_KICK] = 1, [EFFECT_SECRET_POWER] = 1, [EFFECT_DOUBLE_EDGE] = 2, [EFFECT_TEETER_DANCE] = 6, [EFFECT_BLAZE_KICK] = 1, [EFFECT_MUD_SPORT] = 4, [EFFECT_POISON_FANG] = 1, [EFFECT_WEATHER_BALL] = 1, [EFFECT_OVERHEAT] = 3, [EFFECT_TICKLE] = 1, [EFFECT_COSMIC_POWER] = 1, [EFFECT_SKY_UPPERCUT] = 1, [EFFECT_BULK_UP] = 1, [EFFECT_POISON_TAIL] = 1, [EFFECT_WATER_SPORT] = 4, [EFFECT_CALM_MIND] = 1, [EFFECT_DRAGON_DANCE] = 1, [EFFECT_CAMOUFLAGE] = 3 }; static const u16 sPoints_Effectiveness[] = { 4, // Super Effective -3, // Not Very Effective -6 // No Effect }; static const u16 sPoints_SetUp[] = { 4, // Future Sight 4, // Doom Desire 6, 6, // Wish 7, // Grudge 6, 2 // Ingrain }; static const u16 sPoints_RainMoves[] = { MOVE_BUBBLE, 3, MOVE_WHIRLPOOL, 3, MOVE_OCTAZOOKA, 3, MOVE_CLAMP, 3, MOVE_WITHDRAW, 3, MOVE_CRABHAMMER, 3, MOVE_WATER_SPOUT, 3, MOVE_DIVE, 3, MOVE_WATERFALL, 3, MOVE_MUDDY_WATER, 3, MOVE_SURF, 3, MOVE_HYDRO_CANNON, 3, MOVE_HYDRO_PUMP, 3, MOVE_BUBBLE_BEAM, 3, MOVE_WATER_SPORT, 0, // Unnecessary, unlisted moves are already given 0 points MOVE_WATER_GUN, 3, MOVE_WATER_PULSE, 3, MOVE_WEATHER_BALL, 3, MOVE_THUNDER, 3, MOVE_SOLAR_BEAM, -4, MOVE_OVERHEAT, -4, MOVE_FLAME_WHEEL, -4, MOVE_FLAMETHROWER, -4, MOVE_SACRED_FIRE, -4, MOVE_FIRE_BLAST, -4, MOVE_HEAT_WAVE, -4, MOVE_EMBER, -4, MOVE_BLAST_BURN, -4, MOVE_BLAZE_KICK, -4, MOVE_ERUPTION, -4, MOVE_FIRE_SPIN, -4, MOVE_FIRE_PUNCH, -4, MOVE_SOLAR_BEAM, -4, // Repeated TABLE_END, 0 }; static const u16 sPoints_SunMoves[] = { MOVE_OVERHEAT, 3, MOVE_FLAME_WHEEL, 3, MOVE_FLAMETHROWER, 3, MOVE_SACRED_FIRE, 3, MOVE_FIRE_BLAST, 3, MOVE_HEAT_WAVE, 3, MOVE_EMBER, 3, MOVE_BLAST_BURN, 3, MOVE_BLAZE_KICK, 3, MOVE_ERUPTION, 3, MOVE_FIRE_SPIN, 3, MOVE_FIRE_PUNCH, 3, MOVE_SOLAR_BEAM, 5, MOVE_SYNTHESIS, 3, MOVE_MORNING_SUN, 3, MOVE_MOONLIGHT, 3, MOVE_WEATHER_BALL, 3, TABLE_END, 0 }; static const u16 sPoints_SandstormMoves[] = { MOVE_WEATHER_BALL, 3, MOVE_SOLAR_BEAM, -3, TABLE_END, 0 }; static const u16 sPoints_HailMoves[] = { MOVE_WEATHER_BALL, 3, MOVE_SOLAR_BEAM, -3, TABLE_END, 0 }; static const u16 sPoints_ElectricMoves[] = { MOVE_THUNDERBOLT, 3, MOVE_THUNDER_PUNCH, 3, MOVE_SPARK, 3, MOVE_THUNDER_SHOCK, 3, MOVE_ZAP_CANNON, 3, MOVE_SHOCK_WAVE, 3, MOVE_THUNDER_WAVE, 0, // Unnecessary, unlisted moves are already given 0 points MOVE_THUNDER, 3, MOVE_VOLT_TACKLE, 3, TABLE_END, 0 }; static const u16 sPoints_StatusDmg[] = { 5, // Curse 3, // Leech Seed 3, // Poison 3, // Toxic 3, // Burn 3, // Nightmare 3 // Wrap (Trapping move) }; static const u16 sPoints_Status[] = { 5, // Attraction 5, // Confusion 5, // Paralysis 5, // Sleep 5 // Freeze }; static const u16 sPoints_Spikes[] = { 4 }; static const u16 sPoints_WaterSport[] = { 5 }; static const u16 sPoints_MudSport[] = { 5 }; static const u16 sPoints_Reflect[] = { 3 }; static const u16 sPoints_LightScreen[] = { 3 }; static const u16 sPoints_Safeguard[] = { 4 }; static const u16 sPoints_Mist[] = { 3 }; static const u16 sPoints_BreakWall[] = { 6 }; static const u16 sPoints_CriticalHit[] = { 6 }; static const u16 sPoints_Faint[] = { 6 }; static const u16 sPoints_Flinched[] = { 4 }; static const u16 sPoints_StatIncrease1[NUM_BATTLE_STATS - 1] = { [STAT_ATK - 1] = 2, [STAT_DEF - 1] = 2, [STAT_SPEED - 1] = 2, [STAT_SPATK - 1] = 2, [STAT_SPDEF - 1] = 2, [STAT_ACC - 1] = 2, [STAT_EVASION - 1] = 2 }; static const u16 sPoints_StatIncrease2[NUM_BATTLE_STATS - 1] = { [STAT_ATK - 1] = 4, [STAT_DEF - 1] = 4, [STAT_SPEED - 1] = 4, [STAT_SPATK - 1] = 4, [STAT_SPDEF - 1] = 4, [STAT_ACC - 1] = 4, [STAT_EVASION - 1] = 4 }; static const u16 sPoints_StatDecreaseSelf[NUM_BATTLE_STATS - 1] = { [STAT_ATK - 1] = -1, [STAT_DEF - 1] = -1, [STAT_SPEED - 1] = -1, [STAT_SPATK - 1] = -1, [STAT_SPDEF - 1] = -1, [STAT_ACC - 1] = -1, [STAT_EVASION - 1] = -1 }; static const u16 sPoints_StatDecrease1[NUM_BATTLE_STATS - 1] = { [STAT_ATK - 1] = 2, [STAT_DEF - 1] = 2, [STAT_SPEED - 1] = 2, [STAT_SPATK - 1] = 2, [STAT_SPDEF - 1] = 2, [STAT_ACC - 1] = 2, [STAT_EVASION - 1] = 2 }; static const u16 sPoints_StatDecrease2[NUM_BATTLE_STATS - 1] = { [STAT_ATK - 1] = 4, [STAT_DEF - 1] = 4, [STAT_SPEED - 1] = 4, [STAT_SPATK - 1] = 4, [STAT_SPDEF - 1] = 4, [STAT_ACC - 1] = 4, [STAT_EVASION - 1] = 4 }; static const u16 sPoints_StatIncreaseNotSelf[NUM_BATTLE_STATS - 1] = { [STAT_ATK - 1] = -2, [STAT_DEF - 1] = -2, [STAT_SPEED - 1] = -2, [STAT_SPATK - 1] = -2, [STAT_SPDEF - 1] = -2, [STAT_ACC - 1] = -2, [STAT_EVASION - 1] = -2 }; static const u16 *const sPointsArray[] = { [PTS_MOVE_EFFECT] = sPoints_MoveEffect, [PTS_EFFECTIVENESS] = sPoints_Effectiveness, [PTS_SET_UP] = sPoints_SetUp, [PTS_RAIN] = sPoints_RainMoves, [PTS_SUN] = sPoints_SunMoves, [PTS_SANDSTORM] = sPoints_SandstormMoves, [PTS_HAIL] = sPoints_HailMoves, [PTS_ELECTRIC] = sPoints_ElectricMoves, [PTS_STATUS_DMG] = sPoints_StatusDmg, [PTS_STATUS] = sPoints_Status, [PTS_SPIKES] = sPoints_Spikes, [PTS_WATER_SPORT] = sPoints_WaterSport, [PTS_MUD_SPORT] = sPoints_MudSport, [PTS_REFLECT] = sPoints_Reflect, [PTS_LIGHT_SCREEN] = sPoints_LightScreen, [PTS_SAFEGUARD] = sPoints_Safeguard, [PTS_MIST] = sPoints_Mist, [PTS_BREAK_WALL] = sPoints_BreakWall, [PTS_CRITICAL_HIT] = sPoints_CriticalHit, [PTS_FAINT] = sPoints_Faint, [PTS_FAINT_SET_UP] = sPoints_Faint, [PTS_FLINCHED] = sPoints_Flinched, [PTS_STAT_INCREASE_1] = sPoints_StatIncrease1, [PTS_STAT_INCREASE_2] = sPoints_StatIncrease2, [PTS_STAT_DECREASE_SELF] = sPoints_StatDecreaseSelf, [PTS_STAT_DECREASE_1] = sPoints_StatDecrease1, [PTS_STAT_DECREASE_2] = sPoints_StatDecrease2, [PTS_STAT_INCREASE_NOT_SELF] = sPoints_StatIncreaseNotSelf }; // Points will always be calculated for these messages // even if current pokemon does not have corresponding move static const u16 sSpecialBattleStrings[] = { STRINGID_PKMNPERISHCOUNTFELL, STRINGID_PKMNWISHCAMETRUE, STRINGID_PKMNLOSTPPGRUDGE, STRINGID_PKMNTOOKFOE, STRINGID_PKMNABSORBEDNUTRIENTS, STRINGID_PKMNANCHOREDITSELF, STRINGID_PKMNAFFLICTEDBYCURSE, STRINGID_PKMNSAPPEDBYLEECHSEED, STRINGID_PKMNLOCKEDINNIGHTMARE, STRINGID_PKMNHURTBY, STRINGID_PKMNHURTBYBURN, STRINGID_PKMNHURTBYPOISON, STRINGID_PKMNHURTBYSPIKES, STRINGID_ATTACKERFAINTED, STRINGID_TARGETFAINTED, STRINGID_PKMNHITWITHRECOIL, STRINGID_PKMNCRASHED, TABLE_END }; // code void BattleTv_SetDataBasedOnString(u16 stringId) { struct BattleTv *tvPtr; u32 atkSide, defSide, effSide, scriptingSide; struct Pokemon *atkMon, *defMon; u8 moveSlot; u32 atkFlank, defFlank, effFlank; u8 *perishCount; u16 *statStringId, *finishedMoveId; if (!(gBattleTypeFlags & BATTLE_TYPE_LINK) && stringId != STRINGID_ITDOESNTAFFECT && stringId != STRINGID_NOTVERYEFFECTIVE) return; tvPtr = &gBattleStruct->tv; atkSide = GetBattlerSide(gBattlerAttacker); defSide = GetBattlerSide(gBattlerTarget); effSide = GetBattlerSide(gEffectBattler); scriptingSide = GetBattlerSide(gBattleMsgDataPtr->scrActive); if (atkSide == B_SIDE_PLAYER) atkMon = &gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]]; else atkMon = &gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker]]; if (defSide == B_SIDE_PLAYER) defMon = &gPlayerParty[gBattlerPartyIndexes[gBattlerTarget]]; else defMon = &gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]; moveSlot = GetBattlerMoveSlotId(gBattlerAttacker, gBattleMsgDataPtr->currentMove); if (moveSlot >= MAX_MON_MOVES && IsNotSpecialBattleString(stringId) && stringId > BATTLESTRINGS_ID_ADDER) { tvPtr->side[atkSide].faintCause = FNT_OTHER; return; } perishCount = (u8 *)(gBattleTextBuff1 + 4); statStringId = (u16 *)(gBattleTextBuff2 + 2); finishedMoveId = (u16 *)(gBattleTextBuff1 + 2); atkFlank = GetBattlerPosition(gBattlerAttacker) / 2; defFlank = GetBattlerPosition(gBattlerTarget) / 2; effFlank = GetBattlerPosition(gEffectBattler) / 2; switch (stringId) { case STRINGID_ITDOESNTAFFECT: AddMovePoints(PTS_EFFECTIVENESS, moveSlot, 2, 0); if (!(gBattleTypeFlags & BATTLE_TYPE_LINK)) TrySetBattleSeminarShow(); break; case STRINGID_NOTVERYEFFECTIVE: AddMovePoints(PTS_EFFECTIVENESS, moveSlot, 1, 0); if (!(gBattleTypeFlags & BATTLE_TYPE_LINK) && GetMonData(defMon, MON_DATA_HP, NULL) != 0) TrySetBattleSeminarShow(); break; case STRINGID_SUPEREFFECTIVE: AddMovePoints(PTS_EFFECTIVENESS, moveSlot, 0, 0); break; case STRINGID_PKMNFORESAWATTACK: tvPtr->side[atkSide].futureSightMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide].futureSightMoveSlot = moveSlot; break; case STRINGID_PKMNCHOSEXASDESTINY: tvPtr->side[atkSide].doomDesireMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide].doomDesireMoveSlot = moveSlot; break; case STRINGID_FAINTINTHREE: tvPtr->side[atkSide].perishSongMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide].perishSongMoveSlot = moveSlot; tvPtr->side[atkSide].perishSong = 1; break; case STRINGID_PKMNPERISHCOUNTFELL: if (*perishCount == 0) tvPtr->side[atkSide].faintCause = FNT_PERISH_SONG; break; case STRINGID_PKMNWISHCAMETRUE: if (tvPtr->side[defSide].wishMonId != 0) { AddMovePoints(PTS_SET_UP, 3, defSide, (tvPtr->side[defSide].wishMonId - 1) * 4 + tvPtr->side[defSide].wishMoveSlot); } break; case STRINGID_PKMNWANTSGRUDGE: tvPtr->side[atkSide].grudgeMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide].grudgeMoveSlot = moveSlot; break; case STRINGID_PKMNLOSTPPGRUDGE: if (tvPtr->side[defSide].grudgeMonId != 0) { AddMovePoints(PTS_SET_UP, 4, defSide, (tvPtr->side[defSide].grudgeMonId - 1) * 4 + tvPtr->side[defSide].grudgeMoveSlot); } break; case STRINGID_PKMNTRYINGTOTAKEFOE: tvPtr->side[atkSide].destinyBondMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide].destinyBondMoveSlot = moveSlot; break; case STRINGID_PKMNTOOKFOE: if (tvPtr->side[defSide].destinyBondMonId != 0) tvPtr->side[atkSide].faintCause = FNT_DESTINY_BOND; break; case STRINGID_PKMNPLANTEDROOTS: tvPtr->pos[atkSide][atkFlank].ingrainMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->pos[atkSide][atkFlank].ingrainMoveSlot = moveSlot; break; case STRINGID_PKMNABSORBEDNUTRIENTS: if (tvPtr->pos[atkSide][atkFlank].ingrainMonId != 0) { AddMovePoints(PTS_SET_UP, 6, atkSide, (tvPtr->pos[atkSide][atkFlank].ingrainMonId - 1) * 4 + tvPtr->pos[atkSide][atkFlank].ingrainMoveSlot); } break; case STRINGID_PKMNANCHOREDITSELF: if (tvPtr->pos[defSide][defFlank].ingrainMonId != 0) { AddMovePoints(PTS_SET_UP, 6, defSide, (tvPtr->pos[defSide][defFlank].ingrainMonId - 1) * 4 + tvPtr->pos[defSide][defFlank].ingrainMoveSlot); } break; case STRINGID_PKMNTRANSFORMEDINTO: gBattleStruct->anyMonHasTransformed = TRUE; break; case STRINGID_CRITICALHIT: AddMovePoints(PTS_CRITICAL_HIT, moveSlot, 0, 0); break; case STRINGID_ATTACKERSSTATROSE: if (gBattleTextBuff1[2] != 0) { if (*statStringId == STRINGID_STATSHARPLY) AddMovePoints(PTS_STAT_INCREASE_2, moveSlot, gBattleTextBuff1[2] - 1, 0); else AddMovePoints(PTS_STAT_INCREASE_1, moveSlot, gBattleTextBuff1[2] - 1, 0); } break; case STRINGID_DEFENDERSSTATROSE: if (gBattleTextBuff1[2] != 0) { if (gBattlerAttacker == gBattlerTarget) { if (*statStringId == STRINGID_STATSHARPLY) AddMovePoints(PTS_STAT_INCREASE_2, moveSlot, gBattleTextBuff1[2] - 1, 0); else AddMovePoints(PTS_STAT_INCREASE_1, moveSlot, gBattleTextBuff1[2] - 1, 0); } else { AddMovePoints(PTS_STAT_INCREASE_NOT_SELF, moveSlot, gBattleTextBuff1[2] - 1, 0); } } break; case STRINGID_ATTACKERSSTATFELL: if (gBattleTextBuff1[2] != 0) AddMovePoints(PTS_STAT_DECREASE_SELF, moveSlot, gBattleTextBuff1[2] - 1, 0); break; case STRINGID_DEFENDERSSTATFELL: if (gBattleTextBuff1[2] != 0) { if (*statStringId == STRINGID_STATHARSHLY) AddMovePoints(PTS_STAT_DECREASE_2, moveSlot, gBattleTextBuff1[2] - 1, 0); else AddMovePoints(PTS_STAT_DECREASE_1, moveSlot, gBattleTextBuff1[2] - 1, 0); } break; case STRINGID_PKMNLAIDCURSE: tvPtr->pos[defSide][defFlank].curseMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->pos[defSide][defFlank].curseMoveSlot = moveSlot; break; case STRINGID_PKMNAFFLICTEDBYCURSE: if (GetMonData(atkMon, MON_DATA_HP, NULL) && tvPtr->pos[atkSide][atkFlank].curseMonId != 0) { AddMovePoints(PTS_STATUS_DMG, 0, tvPtr->pos[atkSide][atkFlank].curseMonId - 1, tvPtr->pos[atkSide][atkFlank].curseMoveSlot); tvPtr->side[atkSide].faintCause = FNT_CURSE; tvPtr->side[atkSide].faintCauseMonId = atkFlank; } break; case STRINGID_PKMNSEEDED: tvPtr->pos[defSide][defFlank].leechSeedMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->pos[defSide][defFlank].leechSeedMoveSlot = moveSlot; break; case STRINGID_PKMNSAPPEDBYLEECHSEED: if (tvPtr->pos[atkSide][atkFlank].leechSeedMonId != 0) { AddMovePoints(PTS_STATUS_DMG, 1, tvPtr->pos[atkSide][atkFlank].leechSeedMonId - 1, tvPtr->pos[atkSide][atkFlank].leechSeedMoveSlot); tvPtr->side[atkSide].faintCause = FNT_LEECH_SEED; tvPtr->side[atkSide].faintCauseMonId = atkFlank; } break; case STRINGID_PKMNFELLINTONIGHTMARE: tvPtr->pos[defSide][defFlank].nightmareMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->pos[defSide][defFlank].nightmareMoveSlot = moveSlot; break; case STRINGID_PKMNLOCKEDINNIGHTMARE: if (GetMonData(atkMon, MON_DATA_HP, NULL) != 0 && tvPtr->pos[atkSide][atkFlank].nightmareMonId != 0) { AddMovePoints(PTS_STATUS_DMG, 5, tvPtr->pos[atkSide][atkFlank].nightmareMonId - 1, tvPtr->pos[atkSide][atkFlank].nightmareMoveSlot); tvPtr->side[atkSide].faintCause = FNT_NIGHTMARE; tvPtr->side[atkSide].faintCauseMonId = atkFlank; } break; case STRINGID_PKMNSQUEEZEDBYBIND: case STRINGID_PKMNTRAPPEDINVORTEX: case STRINGID_PKMNWRAPPEDBY: case STRINGID_PKMNCLAMPED: case STRINGID_PKMNTRAPPEDBYSANDTOMB: tvPtr->pos[defSide][defFlank].wrapMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->pos[defSide][defFlank].wrapMoveSlot = moveSlot; break; case STRINGID_PKMNHURTBY: if (GetMonData(atkMon, MON_DATA_HP, NULL) != 0 && tvPtr->pos[atkSide][atkFlank].wrapMonId != 0) { AddMovePoints(PTS_STATUS_DMG, 6, tvPtr->pos[atkSide][atkFlank].wrapMonId - 1, tvPtr->pos[atkSide][atkFlank].wrapMoveSlot); tvPtr->side[atkSide].faintCause = FNT_WRAP; tvPtr->side[atkSide].faintCauseMonId = atkFlank; } break; case STRINGID_PKMNWASBURNED: tvPtr->mon[effSide][gBattlerPartyIndexes[gEffectBattler]].brnMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->mon[effSide][gBattlerPartyIndexes[gEffectBattler]].brnMoveSlot = moveSlot; break; case STRINGID_PKMNHURTBYBURN: if (GetMonData(atkMon, MON_DATA_HP, NULL) != 0) { if (tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].brnMonId != 0) AddMovePoints(PTS_STATUS_DMG, 4, tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].brnMonId - 1, tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].brnMoveSlot); tvPtr->side[atkSide].faintCause = FNT_BURN; tvPtr->side[atkSide].faintCauseMonId = gBattlerPartyIndexes[gBattlerAttacker]; } break; case STRINGID_PKMNWASPOISONED: tvPtr->mon[effSide][gBattlerPartyIndexes[gEffectBattler]].psnMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->mon[effSide][gBattlerPartyIndexes[gEffectBattler]].psnMoveSlot = moveSlot; break; case STRINGID_PKMNBADLYPOISONED: tvPtr->mon[effSide][gBattlerPartyIndexes[gEffectBattler]].badPsnMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->mon[effSide][gBattlerPartyIndexes[gEffectBattler]].badPsnMoveSlot = moveSlot; break; case STRINGID_PKMNHURTBYPOISON: if (GetMonData(atkMon, MON_DATA_HP, NULL) != 0) { if (tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].psnMonId != 0) AddMovePoints(PTS_STATUS_DMG, 2, tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].psnMonId - 1, tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].psnMoveSlot); if (tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].badPsnMonId != 0) AddMovePoints(PTS_STATUS_DMG, 3, tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].badPsnMonId - 1, tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].badPsnMoveSlot); tvPtr->side[atkSide].faintCause = FNT_POISON; tvPtr->side[atkSide].faintCauseMonId = gBattlerPartyIndexes[gBattlerAttacker]; } break; case STRINGID_PKMNFELLINLOVE: tvPtr->pos[defSide][defFlank].attractMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->pos[defSide][defFlank].attractMoveSlot = moveSlot; break; case STRINGID_PKMNIMMOBILIZEDBYLOVE: if (tvPtr->pos[atkSide][atkFlank].attractMonId != 0) AddMovePoints(PTS_STATUS, 0, tvPtr->pos[atkSide][atkFlank].attractMonId - 1, tvPtr->pos[atkSide][atkFlank].attractMoveSlot); break; case STRINGID_PKMNWASPARALYZED: tvPtr->mon[effSide][gBattlerPartyIndexes[gEffectBattler]].prlzMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->mon[effSide][gBattlerPartyIndexes[gEffectBattler]].prlzMoveSlot = moveSlot; break; case STRINGID_PKMNISPARALYZED: if (tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].prlzMonId != 0) AddMovePoints(PTS_STATUS, 2, tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].prlzMonId - 1, tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].prlzMoveSlot); break; case STRINGID_PKMNFELLASLEEP: tvPtr->mon[effSide][gBattlerPartyIndexes[gEffectBattler]].slpMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->mon[effSide][gBattlerPartyIndexes[gEffectBattler]].slpMoveSlot = moveSlot; break; case STRINGID_PKMNFASTASLEEP: if (tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].slpMonId != 0 && gBattleMsgDataPtr->currentMove != MOVE_SNORE && gBattleMsgDataPtr->currentMove != MOVE_SLEEP_TALK) AddMovePoints(PTS_STATUS, 3, tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].slpMonId - 1, tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].slpMoveSlot); break; case STRINGID_PKMNWASFROZEN: tvPtr->mon[effSide][gBattlerPartyIndexes[gEffectBattler]].frzMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->mon[effSide][gBattlerPartyIndexes[gEffectBattler]].frzMoveSlot = moveSlot; break; case STRINGID_PKMNISFROZEN: if (tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].frzMonId != 0) AddMovePoints(PTS_STATUS, 4, tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].frzMonId - 1, tvPtr->mon[atkSide][gBattlerPartyIndexes[gBattlerAttacker]].frzMoveSlot); break; case STRINGID_PKMNWASCONFUSED: tvPtr->pos[effSide][effFlank].confusionMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->pos[effSide][effFlank].confusionMoveSlot = moveSlot; break; case STRINGID_ITHURTCONFUSION: if (tvPtr->pos[atkSide][atkFlank].confusionMonId != 0) AddMovePoints(PTS_STATUS, 1, tvPtr->pos[atkSide][atkFlank].confusionMonId - 1, tvPtr->pos[atkSide][atkFlank].confusionMoveSlot); tvPtr->side[atkSide].faintCause = FNT_CONFUSION; break; case STRINGID_SPIKESSCATTERED: tvPtr->side[defSide].spikesMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[defSide].spikesMoveSlot = moveSlot; break; case STRINGID_PKMNHURTBYSPIKES: if (tvPtr->side[scriptingSide].spikesMonId != 0) { AddMovePoints(PTS_SPIKES, scriptingSide ^ BIT_SIDE, tvPtr->side[scriptingSide].spikesMonId - 1, tvPtr->side[scriptingSide].spikesMoveSlot); tvPtr->side[scriptingSide].faintCause = FNT_SPIKES; } break; case STRINGID_PKMNBLEWAWAYSPIKES: tvPtr->side[atkSide].spikesMonId = 0; tvPtr->side[atkSide].spikesMoveSlot = 0; break; case STRINGID_FIREWEAKENED: tvPtr->pos[atkSide][atkFlank].waterSportMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->pos[atkSide][atkFlank].waterSportMoveSlot = moveSlot; break; case STRINGID_ELECTRICITYWEAKENED: tvPtr->pos[atkSide][atkFlank].mudSportMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->pos[atkSide][atkFlank].mudSportMoveSlot = moveSlot; break; case STRINGID_ATTACKERFAINTED: AddPointsOnFainting(FALSE); case STRINGID_RETURNMON: if (tvPtr->pos[atkSide][atkFlank].waterSportMonId != 0) { tvPtr->pos[atkSide][atkFlank].waterSportMonId = 0; tvPtr->pos[atkSide][atkFlank].waterSportMoveSlot = 0; } if (tvPtr->pos[atkSide][atkFlank].mudSportMonId != 0) { tvPtr->pos[atkSide][atkFlank].mudSportMonId = 0; tvPtr->pos[atkSide][atkFlank].mudSportMoveSlot = 0; } break; case STRINGID_TARGETFAINTED: AddPointsOnFainting(TRUE); if (tvPtr->pos[atkSide][defFlank].waterSportMonId != 0) { tvPtr->pos[atkSide][defFlank].waterSportMonId = 0; tvPtr->pos[atkSide][defFlank].waterSportMoveSlot = 0; } if (tvPtr->pos[atkSide][defFlank].mudSportMonId != 0) { tvPtr->pos[atkSide][defFlank].mudSportMonId = 0; tvPtr->pos[atkSide][defFlank].mudSportMoveSlot = 0; } break; case STRINGID_PKMNRAISEDDEF: case STRINGID_PKMNRAISEDDEFALITTLE: tvPtr->side[atkSide].reflectMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide].reflectMoveSlot = moveSlot; break; case STRINGID_PKMNRAISEDSPDEF: case STRINGID_PKMNRAISEDSPDEFALITTLE: tvPtr->side[atkSide].lightScreenMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide].lightScreenMoveSlot = moveSlot; break; case STRINGID_PKMNSXWOREOFF: if (*finishedMoveId == MOVE_REFLECT) { tvPtr->side[atkSide].reflectMonId = 0; tvPtr->side[atkSide].reflectMoveSlot = 0; } if (*finishedMoveId == MOVE_LIGHT_SCREEN) { tvPtr->side[atkSide].lightScreenMonId = 0; tvPtr->side[atkSide].lightScreenMoveSlot = 0; } if (*finishedMoveId == MOVE_MIST) { tvPtr->side[atkSide].mistMonId = 0; tvPtr->side[atkSide].mistMoveSlot = 0; } break; case STRINGID_PKMNCOVEREDBYVEIL: tvPtr->side[atkSide].safeguardMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide].safeguardMoveSlot = moveSlot; break; case STRINGID_PKMNUSEDSAFEGUARD: if (tvPtr->side[defSide].safeguardMonId != 0) AddMovePoints(PTS_SAFEGUARD, 0, tvPtr->side[defSide].safeguardMonId - 1, tvPtr->side[defSide].safeguardMoveSlot); break; case STRINGID_PKMNSAFEGUARDEXPIRED: tvPtr->side[atkSide].safeguardMonId = 0; tvPtr->side[atkSide].safeguardMoveSlot = 0; break; case STRINGID_PKMNSHROUDEDINMIST: tvPtr->side[atkSide].mistMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide].mistMoveSlot = moveSlot; break; case STRINGID_PKMNPROTECTEDBYMIST: if (tvPtr->side[defSide].mistMonId != 0) AddMovePoints(PTS_MIST, 0, tvPtr->side[defSide].mistMonId - 1, tvPtr->side[defSide].mistMoveSlot); break; case STRINGID_THEWALLSHATTERED: tvPtr->side[defSide].reflectMonId = 0; tvPtr->side[defSide].reflectMoveSlot = 0; tvPtr->side[defSide].lightScreenMonId = 0; tvPtr->side[defSide].lightScreenMoveSlot = 0; AddMovePoints(PTS_BREAK_WALL, 0, gBattlerPartyIndexes[gBattlerAttacker], moveSlot); break; case STRINGID_PKMNFLINCHED: if (tvPtr->pos[atkSide][0].attackedByMonId != 0) AddMovePoints(PTS_FLINCHED, 0, tvPtr->pos[atkSide][0].attackedByMonId - 1, tvPtr->pos[atkSide][0].attackedByMoveSlot); if (tvPtr->pos[atkSide][1].attackedByMonId != 0) AddMovePoints(PTS_FLINCHED, 0, tvPtr->pos[atkSide][1].attackedByMonId - 1, tvPtr->pos[atkSide][1].attackedByMoveSlot); break; case STRINGID_PKMNCRASHED: case STRINGID_PKMNHITWITHRECOIL: tvPtr->side[atkSide].faintCause = FNT_RECOIL; break; } } static bool8 IsNotSpecialBattleString(u16 stringId) { s32 i = 0; do { if (sSpecialBattleStrings[i] == stringId) break; i++; } while (sSpecialBattleStrings[i] != TABLE_END); if (sSpecialBattleStrings[i] == TABLE_END) return TRUE; else return FALSE; } void BattleTv_SetDataBasedOnMove(u16 move, u16 weatherFlags, struct DisableStruct *disableStructPtr) { struct BattleTv *tvPtr; u32 atkSide, defSide; u8 moveSlot; if (!(gBattleTypeFlags & BATTLE_TYPE_LINK)) return; tvPtr = &gBattleStruct->tv; atkSide = GetBattlerSide(gBattlerAttacker); defSide = GetBattlerSide(gBattlerTarget); moveSlot = GetBattlerMoveSlotId(gBattlerAttacker, move); if (moveSlot >= MAX_MON_MOVES) { tvPtr->side[atkSide].faintCause = FNT_OTHER; return; } tvPtr->pos[defSide][GetBattlerPosition(gBattlerAttacker) / 2].attackedByMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->pos[defSide][GetBattlerPosition(gBattlerAttacker) / 2].attackedByMoveSlot = moveSlot; tvPtr->side[atkSide].usedMoveSlot = moveSlot; AddMovePoints(PTS_MOVE_EFFECT, moveSlot, gBattleMoves[move].effect, 0); AddPointsBasedOnWeather(weatherFlags, move, moveSlot); if (disableStructPtr->chargeTimer != 0) AddMovePoints(PTS_ELECTRIC, move, moveSlot, 0); if (move == MOVE_WISH) { tvPtr->side[atkSide].wishMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide].wishMoveSlot = moveSlot; } if (move == MOVE_SELF_DESTRUCT || move == MOVE_EXPLOSION) { tvPtr->side[atkSide ^ BIT_SIDE].explosionMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide ^ BIT_SIDE].explosionMoveSlot = moveSlot; tvPtr->side[atkSide ^ BIT_SIDE].faintCause = FNT_EXPLOSION; tvPtr->side[atkSide ^ BIT_SIDE].explosion = TRUE; } AddMovePoints(PTS_REFLECT, gBattleMoves[move].type, gBattleMoves[move].power, 0); AddMovePoints(PTS_LIGHT_SCREEN, gBattleMoves[move].type, gBattleMoves[move].power, 0); AddMovePoints(PTS_WATER_SPORT, gBattleMoves[move].type, 0, 0); AddMovePoints(PTS_MUD_SPORT, gBattleMoves[move].type, 0, 0); } void BattleTv_SetDataBasedOnAnimation(u8 animationId) { struct BattleTv *tvPtr; u32 atkSide; if (!(gBattleTypeFlags & BATTLE_TYPE_LINK)) return; tvPtr = &gBattleStruct->tv; atkSide = GetBattlerSide(gBattlerAttacker); switch (animationId) { case B_ANIM_FUTURE_SIGHT_HIT: if (tvPtr->side[atkSide].futureSightMonId != 0) { AddMovePoints(PTS_SET_UP, 0, atkSide, (tvPtr->side[atkSide].futureSightMonId - 1) * 4 + tvPtr->side[atkSide].futureSightMoveSlot); tvPtr->side[atkSide].faintCause = FNT_FUTURE_SIGHT; } break; case B_ANIM_DOOM_DESIRE_HIT: if (tvPtr->side[atkSide].doomDesireMonId != 0) { AddMovePoints(PTS_SET_UP, 1, atkSide, (tvPtr->side[atkSide].doomDesireMonId - 1) * 4 + tvPtr->side[atkSide].doomDesireMoveSlot); tvPtr->side[atkSide].faintCause = FNT_DOOM_DESIRE; } break; } } void TryPutLinkBattleTvShowOnAir(void) { u16 playerBestSpecies = 0, opponentBestSpecies = 0; s16 playerBestSum = 0, opponentBestSum = SHRT_MAX; u8 playerBestMonId = 0, opponentBestMonId = 0; struct BattleTvMovePoints *movePoints = NULL; u8 countPlayer = 0, countOpponent = 0; s16 sum = 0; u16 species = 0; u16 moveId = 0; s32 i, j; int zero = 0, one = 1; //needed for matching if (gBattleStruct->anyMonHasTransformed) return; movePoints = &gBattleStruct->tvMovePoints; for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) != SPECIES_NONE) countPlayer++; if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES, NULL) != SPECIES_NONE) countOpponent++; } if (!(gBattleTypeFlags & BATTLE_TYPE_LINK) || countPlayer != countOpponent) return; for (i = 0; i < PARTY_SIZE; i++) { species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL); if (species != SPECIES_NONE && !GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG, NULL)) { for (sum = 0, j = 0; j < MAX_MON_MOVES; j++) sum += movePoints->points[zero][i * 4 + j]; if (playerBestSum < sum) { playerBestMonId = i; playerBestSum = sum; playerBestSpecies = species; } } species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES, NULL); if (species != SPECIES_NONE && !GetMonData(&gEnemyParty[i], MON_DATA_IS_EGG, NULL)) { for (sum = 0, j = 0; j < MAX_MON_MOVES; j++) sum += movePoints->points[one][i * 4 + j]; if (opponentBestSum == sum) { if (GetMonData(&gEnemyParty[i], MON_DATA_EXP, NULL) > GetMonData(&gEnemyParty[opponentBestMonId], MON_DATA_EXP, NULL)) { opponentBestMonId = i; opponentBestSum = sum; opponentBestSpecies = species; } } else if (opponentBestSum > sum) { opponentBestMonId = i; opponentBestSum = sum; opponentBestSpecies = species; } } } for (sum = 0, i = 0, j = 0; j < MAX_MON_MOVES; j++) { if (sum < movePoints->points[zero][playerBestMonId * 4 + j]) { sum = movePoints->points[zero][playerBestMonId * 4 + j]; i = j; } } moveId = GetMonData(&gPlayerParty[playerBestMonId], MON_DATA_MOVE1 + i, NULL); if (playerBestSum == 0 || moveId == 0) return; if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { if ((playerBestMonId < MULTI_PARTY_SIZE && !GetLinkTrainerFlankId(gBattleScripting.multiplayerId)) || (playerBestMonId >= MULTI_PARTY_SIZE && GetLinkTrainerFlankId(gBattleScripting.multiplayerId))) { j = (opponentBestMonId < MULTI_PARTY_SIZE) ? FALSE : TRUE; PutBattleUpdateOnTheAir(GetOpposingLinkMultiBattlerId(j, gBattleScripting.multiplayerId), moveId, playerBestSpecies, opponentBestSpecies); } } else { PutBattleUpdateOnTheAir(gBattleScripting.multiplayerId ^ 1, moveId, playerBestSpecies, opponentBestSpecies); } } static void AddMovePoints(u8 caseId, u16 arg1, u8 arg2, u8 arg3) { struct BattleTvMovePoints *movePoints = &gBattleStruct->tvMovePoints; struct BattleTv *tvPtr = &gBattleStruct->tv; u32 atkSide = GetBattlerSide(gBattlerAttacker); u32 defSide = GetBattlerSide(gBattlerTarget); const u16 *ptr; s32 i; switch (caseId) { case PTS_MOVE_EFFECT: case PTS_EFFECTIVENESS: case PTS_CRITICAL_HIT: case PTS_STAT_INCREASE_1: case PTS_STAT_INCREASE_2: case PTS_STAT_DECREASE_SELF: case PTS_STAT_DECREASE_1: case PTS_STAT_DECREASE_2: case PTS_STAT_INCREASE_NOT_SELF: movePoints->points[atkSide][gBattlerPartyIndexes[gBattlerAttacker] * 4 + arg1] += sPointsArray[caseId][arg2]; break; #define move arg1 case PTS_RAIN: case PTS_SUN: case PTS_SANDSTORM: case PTS_HAIL: case PTS_ELECTRIC: i = 0; ptr = sPointsArray[caseId]; do { if (move == ptr[i]) { movePoints->points[atkSide][gBattlerPartyIndexes[gBattlerAttacker] * 4 + arg2] += ptr[i+1]; break; } i += 2; } while (ptr[i] != TABLE_END); break; #undef move case PTS_FAINT: tvPtr->side[arg2 ^ 1].faintCause = FNT_NONE; movePoints->points[arg2][0 * 4 + arg3] += sPointsArray[caseId][arg1]; break; case PTS_FAINT_SET_UP: tvPtr->side[arg2].faintCause = FNT_NONE; // fallthrough case PTS_SET_UP: movePoints->points[arg2][0 * 4 + arg3] += sPointsArray[caseId][arg1]; break; case PTS_BREAK_WALL: movePoints->points[atkSide][arg2 * 4 + arg3] += sPointsArray[caseId][arg1]; break; case PTS_STATUS_DMG: case PTS_STATUS: case PTS_SAFEGUARD: case PTS_MIST: case PTS_FLINCHED: movePoints->points[atkSide ^ BIT_SIDE][arg2 * 4 + arg3] += sPointsArray[caseId][arg1]; break; case PTS_SPIKES: movePoints->points[arg1][arg2 * 4 + arg3] += sPointsArray[caseId][0]; break; #define type arg1 #define power arg2 case PTS_WATER_SPORT: // If used fire move during Water Sport if (tvPtr->pos[defSide][0].waterSportMonId != -(tvPtr->pos[defSide][1].waterSportMonId) && type == TYPE_FIRE) { if (tvPtr->pos[defSide][0].waterSportMonId != 0) { u32 id = (tvPtr->pos[defSide][0].waterSportMonId - 1) * 4; movePoints->points[defSide][id + tvPtr->pos[defSide][0].waterSportMoveSlot] += sPointsArray[caseId][0]; } if (tvPtr->pos[defSide][1].waterSportMonId != 0) { u32 id = (tvPtr->pos[defSide][1].waterSportMonId - 1) * 4; movePoints->points[defSide][id + tvPtr->pos[defSide][1].waterSportMoveSlot] += sPointsArray[caseId][0]; } } break; case PTS_MUD_SPORT: // If used Electric move during Mud Sport if (tvPtr->pos[defSide][0].mudSportMonId != -(tvPtr->pos[defSide][1].mudSportMonId) && type == TYPE_ELECTRIC) { if (tvPtr->pos[defSide][0].mudSportMonId != 0) { u32 id = (tvPtr->pos[defSide][0].mudSportMonId - 1) * 4; movePoints->points[defSide][id + tvPtr->pos[defSide][0].mudSportMoveSlot] += sPointsArray[caseId][0]; } if (tvPtr->pos[defSide][1].mudSportMonId != 0) { u32 id = (tvPtr->pos[defSide][1].mudSportMonId - 1) * 4; movePoints->points[defSide][id + tvPtr->pos[defSide][1].mudSportMoveSlot] += sPointsArray[caseId][0]; } } break; case PTS_REFLECT: // If hit Reflect with damaging physical move if (IS_TYPE_PHYSICAL(type) && power != 0 && tvPtr->side[defSide].reflectMonId != 0) { u32 id = (tvPtr->side[defSide].reflectMonId - 1) * 4; movePoints->points[defSide][id + tvPtr->side[defSide].reflectMoveSlot] += sPointsArray[caseId][0]; } break; case PTS_LIGHT_SCREEN: // If hit Light Screen with damaging special move if (!IS_TYPE_PHYSICAL(type) && power != 0 && tvPtr->side[defSide].lightScreenMonId != 0) { u32 id = (tvPtr->side[defSide].lightScreenMonId - 1) * 4; movePoints->points[defSide][id + tvPtr->side[defSide].lightScreenMoveSlot] += sPointsArray[caseId][0]; } break; #undef type #undef power } } static void AddPointsOnFainting(bool8 targetFainted) { struct BattleTv *tvPtr = &gBattleStruct->tv; u32 atkSide = GetBattlerSide(gBattlerAttacker); u32 defSide = GetBattlerSide(gBattlerTarget); u32 atkArrId = tvPtr->side[atkSide].faintCauseMonId; s32 i; if (tvPtr->side[atkSide].faintCause != FNT_NONE) { switch (tvPtr->side[atkSide].faintCause) { case FNT_CURSE: if (tvPtr->pos[atkSide][atkArrId].curseMonId != 0) { AddMovePoints(PTS_FAINT, 0, atkSide ^ BIT_SIDE, (tvPtr->pos[atkSide][atkArrId].curseMonId - 1) * 4 + tvPtr->pos[atkSide][atkArrId].curseMoveSlot); } break; case FNT_LEECH_SEED: if (tvPtr->pos[atkSide][atkArrId].leechSeedMonId != 0) { AddMovePoints(PTS_FAINT, 0, atkSide ^ BIT_SIDE, (tvPtr->pos[atkSide][atkArrId].leechSeedMonId - 1) * 4 + tvPtr->pos[atkSide][atkArrId].leechSeedMoveSlot); } break; case FNT_POISON: if (tvPtr->mon[atkSide][atkArrId].psnMonId != 0) { AddMovePoints(PTS_FAINT, 0, atkSide ^ BIT_SIDE, (tvPtr->mon[atkSide][atkArrId].psnMonId - 1) * 4 + tvPtr->mon[atkSide][atkArrId].psnMoveSlot); } if (tvPtr->mon[atkSide][atkArrId].badPsnMonId != 0) { AddMovePoints(PTS_FAINT, 0, atkSide ^ BIT_SIDE, (tvPtr->mon[atkSide][atkArrId].badPsnMonId - 1) * 4 + tvPtr->mon[atkSide][atkArrId].badPsnMoveSlot); } break; case FNT_BURN: if (tvPtr->mon[atkSide][atkArrId].brnMonId != 0) { AddMovePoints(PTS_FAINT, 0, atkSide ^ BIT_SIDE, (tvPtr->mon[atkSide][atkArrId].brnMonId - 1) * 4 + tvPtr->mon[atkSide][atkArrId].brnMoveSlot); } break; case FNT_NIGHTMARE: if (tvPtr->pos[atkSide][atkArrId].nightmareMonId != 0) { AddMovePoints(PTS_FAINT, 0, atkSide ^ BIT_SIDE, (tvPtr->pos[atkSide][atkArrId].nightmareMonId - 1) * 4 + tvPtr->pos[atkSide][atkArrId].nightmareMoveSlot); } break; case FNT_WRAP: if (tvPtr->pos[atkSide][atkArrId].wrapMonId != 0) { AddMovePoints(PTS_FAINT, 0, atkSide ^ BIT_SIDE, (tvPtr->pos[atkSide][atkArrId].wrapMonId - 1) * 4 + tvPtr->pos[atkSide][atkArrId].wrapMoveSlot); } break; case FNT_SPIKES: if (tvPtr->side[atkSide].spikesMonId != 0) { AddMovePoints(PTS_FAINT, 0, atkSide ^ BIT_SIDE, (tvPtr->side[atkSide].spikesMonId - 1) * 4 + tvPtr->side[atkSide].spikesMoveSlot); } break; case FNT_FUTURE_SIGHT: if (tvPtr->side[atkSide].futureSightMonId != 0) { AddMovePoints(PTS_FAINT_SET_UP, 0, atkSide, (tvPtr->side[atkSide].futureSightMonId - 1) * 4 + tvPtr->side[atkSide].futureSightMoveSlot); } break; case FNT_DOOM_DESIRE: if (tvPtr->side[atkSide].doomDesireMonId != 0) { AddMovePoints(PTS_FAINT_SET_UP, 0, atkSide, (tvPtr->side[atkSide].doomDesireMonId - 1) * 4 + tvPtr->side[atkSide].doomDesireMoveSlot); } break; case FNT_PERISH_SONG: if (tvPtr->side[atkSide].perishSong && tvPtr->side[atkSide].perishSongMonId - 1 != gBattlerPartyIndexes[gBattlerAttacker]) { AddMovePoints(PTS_FAINT, 0, atkSide, (tvPtr->side[atkSide].perishSongMonId - 1) * 4 + tvPtr->side[atkSide].perishSongMoveSlot); } if (tvPtr->side[atkSide ^ BIT_SIDE].perishSong) { AddMovePoints(PTS_FAINT, 0, atkSide ^ BIT_SIDE, (tvPtr->side[atkSide ^ BIT_SIDE].perishSongMonId - 1) * 4 + tvPtr->side[atkSide ^ BIT_SIDE].perishSongMoveSlot); } break; case FNT_DESTINY_BOND: if (tvPtr->side[atkSide ^ BIT_SIDE].destinyBondMonId != 0) { AddMovePoints(PTS_FAINT, 0, atkSide ^ BIT_SIDE, (tvPtr->side[atkSide ^ BIT_SIDE].destinyBondMonId - 1) * 4 + tvPtr->side[atkSide ^ BIT_SIDE].destinyBondMoveSlot); } break; case FNT_CONFUSION: for (i = 0; i < 2; i++) { if (tvPtr->pos[atkSide][i].confusionMonId != 0) { AddMovePoints(PTS_FAINT, 0, atkSide ^ BIT_SIDE, (tvPtr->pos[atkSide][i].confusionMonId - 1) * 4 + tvPtr->pos[atkSide][i].confusionMoveSlot); } } break; case FNT_EXPLOSION: if (tvPtr->side[atkSide].explosion) { AddMovePoints(PTS_FAINT, 0, atkSide, (tvPtr->side[atkSide].explosionMonId - 1) * 4 + tvPtr->side[atkSide].explosionMoveSlot); } if (tvPtr->side[atkSide ^ BIT_SIDE].explosion) { AddMovePoints(PTS_FAINT, 0, atkSide ^ BIT_SIDE, (tvPtr->side[atkSide ^ BIT_SIDE].explosionMonId - 1) * 4 + tvPtr->side[atkSide ^ BIT_SIDE].explosionMoveSlot); } break; case FNT_RECOIL: if (targetFainted == TRUE) { AddMovePoints(PTS_FAINT_SET_UP, 0, atkSide, (gBattlerPartyIndexes[gBattlerAttacker]) * 4 + tvPtr->side[atkSide].usedMoveSlot); } break; case FNT_OTHER: break; } } else { if (tvPtr->side[defSide].faintCause == FNT_SPIKES) { if (tvPtr->side[defSide].spikesMonId != 0) { AddMovePoints(PTS_FAINT, 0, defSide ^ BIT_SIDE, (tvPtr->side[defSide].spikesMonId - 1) * 4 + tvPtr->side[defSide].spikesMoveSlot); } } else { AddMovePoints(PTS_FAINT_SET_UP, 0, atkSide, (gBattlerPartyIndexes[gBattlerAttacker]) * 4 + tvPtr->side[atkSide].usedMoveSlot); } } } static void TrySetBattleSeminarShow(void) { s32 i; s32 dmgByMove[MAX_MON_MOVES]; u16 powerOverride; u16 currMoveSaved; if (gBattleTypeFlags & (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) return; else if (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT) return; else if (gBattleMons[gBattlerAttacker].statStages[STAT_ACC] < DEFAULT_STAT_STAGE) return; else if (gBattleMons[gBattlerTarget].statStages[STAT_EVASION] > DEFAULT_STAT_STAGE) return; else if (gCurrentMove == MOVE_HIDDEN_POWER || gCurrentMove == MOVE_WEATHER_BALL) return; else if (gBattleTypeFlags & (BATTLE_TYPE_PALACE | BATTLE_TYPE_PIKE | BATTLE_TYPE_PYRAMID)) return; else if (gBattleMoves[gBattleMons[gBattlerAttacker].moves[gMoveSelectionCursor[gBattlerAttacker]]].power == 0) return; i = 0; currMoveSaved = gBattleMons[gBattlerAttacker].moves[gMoveSelectionCursor[gBattlerAttacker]]; do { if (currMoveSaved == sVariableDmgMoves[i]) break; i++; } while (sVariableDmgMoves[i] != TABLE_END); if (sVariableDmgMoves[i] != TABLE_END) return; dmgByMove[gMoveSelectionCursor[gBattlerAttacker]] = gBattleMoveDamage; currMoveSaved = gCurrentMove; for (i = 0; i < MAX_MON_MOVES; i++) { gCurrentMove = gBattleMons[gBattlerAttacker].moves[i]; powerOverride = 0; if (ShouldCalculateDamage(gCurrentMove, &dmgByMove[i], &powerOverride)) { u8 moveResultFlags; u16 sideStatus = gSideStatuses[GET_BATTLER_SIDE(gBattlerTarget)]; gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[gBattlerAttacker], &gBattleMons[gBattlerTarget], gCurrentMove, sideStatus, powerOverride, 0, gBattlerAttacker, gBattlerTarget); if (gStatuses3[gBattlerAttacker] & STATUS3_CHARGED_UP && gBattleMoves[gCurrentMove].type == TYPE_ELECTRIC) gBattleMoveDamage *= 2; if (gProtectStructs[gBattlerAttacker].helpingHand) gBattleMoveDamage = gBattleMoveDamage * 15 / 10; moveResultFlags = TypeCalc(gCurrentMove, gBattlerAttacker, gBattlerTarget); dmgByMove[i] = gBattleMoveDamage; if (dmgByMove[i] == 0 && !(moveResultFlags & MOVE_RESULT_NO_EFFECT)) dmgByMove[i] = 1; } } for (i = 0; i < MAX_MON_MOVES; i++) { if (i != gMoveSelectionCursor[gBattlerAttacker] && dmgByMove[i] > dmgByMove[gMoveSelectionCursor[gBattlerAttacker]]) { u16 opponentSpecies, playerSpecies; s32 bestMoveId; if (gMoveSelectionCursor[gBattlerAttacker] != 0) bestMoveId = 0; else bestMoveId = 1; for (i = 0; i < MAX_MON_MOVES; i++) { if (i != gMoveSelectionCursor[gBattlerAttacker] && dmgByMove[i] > dmgByMove[bestMoveId]) bestMoveId = i; } opponentSpecies = GetMonData(&gEnemyParty [gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_SPECIES, NULL); playerSpecies = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_SPECIES, NULL); TryPutBattleSeminarOnAir(opponentSpecies, playerSpecies, gMoveSelectionCursor[gBattlerAttacker], gBattleMons[gBattlerAttacker].moves, gBattleMons[gBattlerAttacker].moves[bestMoveId]); break; } } gBattleMoveDamage = dmgByMove[gMoveSelectionCursor[gBattlerAttacker]]; gCurrentMove = currMoveSaved; } static bool8 ShouldCalculateDamage(u16 moveId, s32 *dmg, u16 *powerOverride) { if (gBattleMoves[moveId].power == 0) { *dmg = 0; return FALSE; } else { s32 i = 0; do { if (moveId == sVariableDmgMoves[i]) break; i++; } while (sVariableDmgMoves[i] != TABLE_END); if (sVariableDmgMoves[i] != TABLE_END) { *dmg = 0; return FALSE; } else if (moveId == MOVE_PSYWAVE) { *dmg = gBattleMons[gBattlerAttacker].level; *dmg /= 2; return FALSE; } else if (moveId == MOVE_MAGNITUDE) { *powerOverride = 10; return TRUE; } else { return TRUE; } } } void BattleTv_ClearExplosionFaintCause(void) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) { struct BattleTv *tvPtr = &gBattleStruct->tv; tvPtr->side[B_SIDE_PLAYER].faintCause = FNT_NONE; tvPtr->side[B_SIDE_OPPONENT].faintCause = FNT_NONE; tvPtr->side[B_SIDE_PLAYER].faintCauseMonId = 0; tvPtr->side[B_SIDE_OPPONENT].faintCauseMonId = 0; tvPtr->side[B_SIDE_PLAYER].explosionMonId = 0; tvPtr->side[B_SIDE_OPPONENT].explosionMonId = 0; tvPtr->side[B_SIDE_PLAYER].explosionMoveSlot = 0; tvPtr->side[B_SIDE_OPPONENT].explosionMoveSlot = 0; tvPtr->side[B_SIDE_PLAYER].explosion = FALSE; tvPtr->side[B_SIDE_OPPONENT].explosion = FALSE; } } u8 GetBattlerMoveSlotId(u8 battlerId, u16 moveId) { s32 i; struct Pokemon *party; if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; i = 0; while (1) { if (i >= MAX_MON_MOVES) break; if (GetMonData(&party[gBattlerPartyIndexes[battlerId]], MON_DATA_MOVE1 + i, NULL) == moveId) break; i++; } return i; } static void AddPointsBasedOnWeather(u16 weatherFlags, u16 moveId, u8 moveSlot) { if (weatherFlags & B_WEATHER_RAIN) AddMovePoints(PTS_RAIN, moveId, moveSlot, 0); else if (weatherFlags & B_WEATHER_SUN) AddMovePoints(PTS_SUN, moveId, moveSlot, 0); else if (weatherFlags & B_WEATHER_SANDSTORM) AddMovePoints(PTS_SANDSTORM, moveId, moveSlot, 0); else if (weatherFlags & B_WEATHER_HAIL) AddMovePoints(PTS_HAIL, moveId, moveSlot, 0); }