diff --git a/.all-contributorsrc b/.all-contributorsrc index c4ed2ea558..7d2e4d4bbf 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -348,6 +348,15 @@ "doc", "code" ] + }, + { + "login": "Ddaretrogamer", + "name": "Phantonomy", + "avatar_url": "https://avatars.githubusercontent.com/u/131238004?v=4", + "profile": "https://github.com/Ddaretrogamer", + "contributions": [ + "design" + ] } ], "contributorsPerLine": 7, diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index dc900e224e..21785498fb 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -24,6 +24,7 @@ + diff --git a/CREDITS.md b/CREDITS.md index 17809a3c3f..4c0b6a3d3e 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -60,6 +60,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d lordraindance2
lordraindance2

💻 Pablo Pena
Pablo Pena

💻 tustin2121
tustin2121

📖 💻 + Phantonomy
Phantonomy

🎨 diff --git a/INSTALL.md b/INSTALL.md index d265d588a1..072d43a858 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -14,8 +14,8 @@ After completing the install instructions for your OS, proceed to [Building poke On Windows, the project can be built using the following systems: - WSL2, fastest - WSL1, 7 times slower than WSL2 -- Msys2, 20 times slower than WSL2 (**NOTE**: Currently broken on pret upstream) -- Cygwin, 30 timer slower than WSL2 (**NOTE**: Currently broken on pret upstream) +- Msys2, 20 times slower than WSL2 +- Cygwin, 30 timer slower than WSL2 **NOTE**: Only WSL systems are recommended. diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 32eef8a357..c0f5bd4db5 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -2082,9 +2082,8 @@ .byte \id .endm - .macro arenawaitmessage id:req + .macro arenawaitmessage callnative BS_ArenaWaitMessage - .byte \id .endm .macro waitcry diff --git a/asm/macros/event.inc b/asm/macros/event.inc index d2329a6922..d56450b024 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -986,7 +986,7 @@ @ Gives the player a Pokémon of the specified species and level, and allows to customize extra parameters. @ VAR_RESULT will be set to MON_GIVEN_TO_PARTY, MON_GIVEN_TO_PC, or MON_CANT_GIVE depending on the outcome. - .macro givemon species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, isShiny, gmaxFactor, teraType, dmaxLevel + .macro givemon species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, shinyMode, gmaxFactor, teraType, dmaxLevel callnative ScrCmd_createmon, requests_effects=1 .byte 0 .byte PARTY_SIZE @ assign to first empty slot @@ -1014,7 +1014,7 @@ .ifnb \move2; .set givemon_flags, givemon_flags | (1 << 18); .endif .ifnb \move3; .set givemon_flags, givemon_flags | (1 << 19); .endif .ifnb \move4; .set givemon_flags, givemon_flags | (1 << 20); .endif - .ifnb \isShiny; .set givemon_flags, givemon_flags | (1 << 21); .endif + .ifnb \shinyMode; .set givemon_flags, givemon_flags | (1 << 21); .endif .ifnb \gmaxFactor; .set givemon_flags, givemon_flags | (1 << 22); .endif .ifnb \teraType; .set givemon_flags, givemon_flags | (1 << 23); .endif .ifnb \dmaxLevel; .set givemon_flags, givemon_flags | (1 << 24); .endif @@ -1040,7 +1040,7 @@ .ifnb \move2; .2byte \move2; .endif .ifnb \move3; .2byte \move3; .endif .ifnb \move4; .2byte \move4; .endif - .ifnb \isShiny; .2byte \isShiny; .endif + .ifnb \shinyMode; .2byte \shinyMode; .endif .ifnb \gmaxFactor; .2byte \gmaxFactor; .endif .ifnb \teraType; .2byte \teraType; .endif .ifnb \dmaxLevel; .2byte \dmaxLevel; .endif @@ -1048,7 +1048,7 @@ @ creates a mon for a given party and slot @ otherwise - .macro createmon side:req, slot:req, species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, isShiny, gmaxFactor, teraType, dmaxLevel + .macro createmon side:req, slot:req, species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, shinyMode, gmaxFactor, teraType, dmaxLevel callnative ScrCmd_createmon, requests_effects=1 .byte \side @ 0 - player, 1 - opponent .byte \slot @ 0-5 @@ -1076,7 +1076,7 @@ .ifnb \move2; .set givemon_flags, givemon_flags | (1 << 18); .endif .ifnb \move3; .set givemon_flags, givemon_flags | (1 << 19); .endif .ifnb \move4; .set givemon_flags, givemon_flags | (1 << 20); .endif - .ifnb \isShiny; .set givemon_flags, givemon_flags | (1 << 21); .endif + .ifnb \shinyMode; .set givemon_flags, givemon_flags | (1 << 21); .endif .ifnb \gmaxFactor; .set givemon_flags, givemon_flags | (1 << 22); .endif .ifnb \teraType; .set givemon_flags, givemon_flags | (1 << 23); .endif .ifnb \dmaxLevel; .set givemon_flags, givemon_flags | (1 << 24); .endif @@ -1102,7 +1102,7 @@ .ifnb \move2; .2byte \move2; .endif .ifnb \move3; .2byte \move3; .endif .ifnb \move4; .2byte \move4; .endif - .ifnb \isShiny; .2byte \isShiny; .endif + .ifnb \shinyMode; .2byte \shinyMode; .endif .ifnb \gmaxFactor; .2byte \gmaxFactor; .endif .ifnb \teraType; .2byte \teraType; .endif .ifnb \dmaxLevel; .2byte \dmaxLevel; .endif diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index de752be59b..2fc4b89ea4 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -171,7 +171,7 @@ gBattleAnimMove_Brine:: playsewithpan SE_M_DIVE, -64 waitforvisualfinish delay 16 - createvisualtask AnimTask_WaterSpoutRain, 5 + createvisualtask AnimTask_BrineRain, 5 playsewithpan SE_M_SURF, +63 clearmonbg ANIM_DEF_PARTNER blendoff @@ -366,6 +366,7 @@ gBattleAnimMove_UTurn:: gBattleAnimMove_CloseCombat:: loadspritegfx ANIM_TAG_IMPACT loadspritegfx ANIM_TAG_HANDS_AND_FEET + monbg ANIM_DEF_PARTNER call SetHighSpeedBg createsprite gFistFootRandomPosSpriteTemplate, ANIM_TARGET, 3, 1, 10, 0 createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 2, 0, 7, 1 @@ -440,7 +441,7 @@ gBattleAnimMove_CloseCombat:: playsewithpan SE_M_MEGA_KICK2, +63 delay 1 call UnsetHighSpeedBg - clearmonbg ANIM_TARGET + clearmonbg ANIM_DEF_PARTNER blendoff delay 1 setarg 7, 0x1000 @@ -969,6 +970,7 @@ gBattleAnimMove_HeartSwap:: loadspritegfx ANIM_TAG_RED_HEART loadspritegfx ANIM_TAG_PINKVIO_ORB loadspritegfx ANIM_TAG_SPARKLE_2 + monbg ANIM_TARGET createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 0, 8, RGB(31, 24, 26) createvisualtask AnimTask_HeartSwap, 3, ANIM_TARGET createvisualtask AnimTask_BlendMonInAndOut, 5, ANIM_TARGET, RGB_WHITE, 12, 3, 1 @@ -1001,7 +1003,6 @@ gBattleAnimMove_HeartSwap:: createsprite gRedHeartCharmSpriteTemplate, ANIM_ATTACKER, 3, 20, 20 playsewithpan SE_M_CHARM, SOUND_PAN_ATTACKER waitforvisualfinish - clearmonbg ANIM_ATTACKER clearmonbg ANIM_TARGET blendoff end @@ -1607,7 +1608,6 @@ gBattleAnimMove_DragonRush:: createsprite gRockFragmentSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, -30, -18, 8, 2 createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 0, 3, 7, 1 waitforvisualfinish - clearmonbg ANIM_DEF_PARTNER blendoff end @@ -2310,7 +2310,6 @@ gBattleAnimMove_MirrorShot:: createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 10, 0, RGB_WHITEALPHA createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14 waitforvisualfinish - clearmonbg ANIM_ATTACKER blendoff end @@ -3357,6 +3356,7 @@ gBattleAnimMove_AquaJet:: call RisingWaterHitEffect waitforvisualfinish createvisualtask AnimTask_ExtremeSpeedMonReappear, 2 + setarg 0x7, 0x1000 waitforvisualfinish visible ANIM_ATTACKER clearmonbg ANIM_DEF_PARTNER @@ -10991,7 +10991,6 @@ gBattleAnimMove_SmartStrike:: setalpha 12, 8 call SonicBoomProjectile createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 3, 0, 10, 1 - loadspritegfx ANIM_TAG_FLASH_CANNON_BALL createsprite gSmartStrikeImpactTemplate, ANIM_TARGET, 4, 0x0, 0x0, 0x8, 0x1, 0x0 playsewithpan SE_M_VITAL_THROW2, SOUND_PAN_TARGET createsprite gSmartStrikeGemTemplate, ANIM_TARGET, 2, 0x1, 0x1, 0x0, 0xffe8, 0xa @@ -11006,7 +11005,6 @@ gBattleAnimMove_SmartStrike:: clearmonbg ANIM_DEF_PARTNER blendoff waitforvisualfinish - clearmonbg ANIM_ATTACKER blendoff waitforvisualfinish end diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index c79f91a87c..b5dadafa0a 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6239,7 +6239,7 @@ BattleScript_PowderMoveNoEffectWaitMsg: BattleScript_MoveUsedFlinched:: printstring STRINGID_PKMNFLINCHED waitmessage B_WAIT_TIME_LONG - waitmessage B_WAIT_TIME_LONG + jumpifability BS_ATTACKER, ABILITY_STEADFAST, BattleScript_TryActivateSteadFast BattleScript_MoveUsedFlinchedEnd: goto BattleScript_MoveEnd BattleScript_TryActivateSteadFast: @@ -6249,7 +6249,7 @@ BattleScript_TryActivateSteadFast: copybyte gBattlerAbility, gBattlerAttacker call BattleScript_AbilityPopUp statbuffchange BS_ATTACKER, STAT_CHANGE_ALLOW_PTR, BattleScript_MoveUsedFlinchedEnd - setbyte gBattleCommunication STAT_SPEED + setbyte gBattleCommunication, STAT_SPEED stattextbuffer printstring STRINGID_ATTACKERABILITYSTATRAISE waitmessage B_WAIT_TIME_LONG @@ -7993,7 +7993,7 @@ BattleScript_ArenaTurnBeginning:: playse SE_ARENA_TIMEUP1 drawarenareftextbox arenajudgmentstring B_MSG_REF_COMMENCE_BATTLE - arenawaitmessage B_MSG_REF_COMMENCE_BATTLE + arenawaitmessage pause B_WAIT_TIME_LONG erasearenareftextbox volumeup @@ -8011,26 +8011,26 @@ BattleScript_ArenaDoJudgment:: pause B_WAIT_TIME_LONG drawarenareftextbox arenajudgmentstring B_MSG_REF_THATS_IT - arenawaitmessage B_MSG_REF_THATS_IT + arenawaitmessage pause B_WAIT_TIME_LONG setbyte gBattleCommunication, 0 @ Reset state for arenajudgmentwindow arenajudgmentwindow pause B_WAIT_TIME_LONG arenajudgmentwindow arenajudgmentstring B_MSG_REF_JUDGE_MIND - arenawaitmessage B_MSG_REF_JUDGE_MIND + arenawaitmessage arenajudgmentwindow arenajudgmentstring B_MSG_REF_JUDGE_SKILL - arenawaitmessage B_MSG_REF_JUDGE_SKILL + arenawaitmessage arenajudgmentwindow arenajudgmentstring B_MSG_REF_JUDGE_BODY - arenawaitmessage B_MSG_REF_JUDGE_BODY + arenawaitmessage arenajudgmentwindow jumpifbyte CMP_EQUAL, gBattleCommunication + 1, ARENA_RESULT_PLAYER_LOST, BattleScript_ArenaJudgmentPlayerLoses jumpifbyte CMP_EQUAL, gBattleCommunication + 1, ARENA_RESULT_TIE, BattleScript_ArenaJudgmentDraw @ ARENA_RESULT_PLAYER_WON arenajudgmentstring B_MSG_REF_PLAYER_WON - arenawaitmessage B_MSG_REF_PLAYER_WON + arenawaitmessage arenajudgmentwindow erasearenareftextbox printstring STRINGID_DEFEATEDOPPONENTBYREFEREE @@ -8045,7 +8045,7 @@ BattleScript_ArenaDoJudgment:: BattleScript_ArenaJudgmentPlayerLoses: arenajudgmentstring B_MSG_REF_OPPONENT_WON - arenawaitmessage B_MSG_REF_OPPONENT_WON + arenawaitmessage arenajudgmentwindow erasearenareftextbox printstring STRINGID_LOSTTOOPPONENTBYREFEREE @@ -8060,11 +8060,12 @@ BattleScript_ArenaJudgmentPlayerLoses: BattleScript_ArenaJudgmentDraw: arenajudgmentstring B_MSG_REF_DRAW - arenawaitmessage B_MSG_REF_DRAW + arenawaitmessage arenajudgmentwindow erasearenareftextbox printstring STRINGID_TIEDOPPONENTBYREFEREE waitmessage B_WAIT_TIME_LONG + arenabothmonslost playfaintcry BS_PLAYER1 waitcry dofaintanimation BS_PLAYER1 @@ -8075,7 +8076,6 @@ BattleScript_ArenaJudgmentDraw: dofaintanimation BS_OPPONENT1 cleareffectsonfaint BS_OPPONENT1 waitanimation - arenabothmonslost end2 BattleScript_AskIfWantsToForfeitMatch:: diff --git a/data/battle_scripts_2.s b/data/battle_scripts_2.s old mode 100644 new mode 100755 index b46b30a443..586a79c5ef --- a/data/battle_scripts_2.s +++ b/data/battle_scripts_2.s @@ -212,9 +212,9 @@ BattleScript_WallyBallThrow:: BattleScript_ShakeBallThrow:: animatewildpokemonafterfailedpokeball BS_TARGET - waitstate waitmessage B_WAIT_TIME_LONG printfromtable gBallEscapeStringIds + waitanimation waitmessage B_WAIT_TIME_LONG jumpifword CMP_NO_COMMON_BITS, gBattleTypeFlags, BATTLE_TYPE_SAFARI, BattleScript_ShakeBallThrowEnd jumpifbyte CMP_NOT_EQUAL, gNumSafariBalls, 0, BattleScript_ShakeBallThrowEnd diff --git a/docs/fix_links.py b/docs/fix_links.py index 6e2eaec485..7b6b01b995 100644 --- a/docs/fix_links.py +++ b/docs/fix_links.py @@ -36,6 +36,7 @@ def proc_items(items): s = s.replace('](README.md)', '](./)') s = s.replace('](/INSTALL.md', '](INSTALL.md') s = s.replace('](docs/', '](') + s = s.replace('](/docs/', '](/') s = URL_RE.sub(handle_url, s) item['Chapter']['content'] = ANCHOR_RE.sub(handle_anchor, s) proc_items(item['Chapter']['sub_items']) diff --git a/graphics/pokemon/pikachu/starter/icon.png b/graphics/pokemon/pikachu/starter/icon.png index 737fababd7..891f0d1b3e 100644 Binary files a/graphics/pokemon/pikachu/starter/icon.png and b/graphics/pokemon/pikachu/starter/icon.png differ diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index 9e0698b75d..e87aeda9f9 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -349,6 +349,12 @@ enum EvoSpinDirections { SPIN_EITHER, // Player spins either clockwise or counter-clockwise }; +enum ShinyMode { + SHINY_MODE_ALWAYS, + SHINY_MODE_RANDOM, + SHINY_MODE_NEVER +}; + #define MON_PIC_WIDTH 64 #define MON_PIC_HEIGHT 64 #define MON_PIC_SIZE (MON_PIC_WIDTH * MON_PIC_HEIGHT / 2) diff --git a/include/pokeball.h b/include/pokeball.h index 530e168f93..51949206e2 100644 --- a/include/pokeball.h +++ b/include/pokeball.h @@ -45,6 +45,7 @@ enum { extern const struct CompressedSpriteSheet gBallSpriteSheets[]; extern const struct SpritePalette gBallSpritePalettes[]; extern const struct SpriteTemplate gBallSpriteTemplates[]; +extern const u16 gBallItemIds[]; #define POKEBALL_PLAYER_SENDOUT 0xFF #define POKEBALL_OPPONENT_SENDOUT 0xFE diff --git a/include/pokedex.h b/include/pokedex.h index 0dfc802312..c558cd1b07 100644 --- a/include/pokedex.h +++ b/include/pokedex.h @@ -8,6 +8,7 @@ void ResetPokedex(void); u16 GetNationalPokedexCount(u8 caseID); u16 GetHoennPokedexCount(u8 caseID); u8 DisplayCaughtMonDexPage(u16 species, bool32 isShiny, u32 personality); +u32 Pokedex_CreateCaughtMonSprite(u32 species, s32 x, s32 y); s8 GetSetPokedexFlag(enum NationalDexOrder nationalDexNo, u8 caseID); void DrawFootprint(u8 windowId, u16 species); u16 CreateMonSpriteFromNationalDexNumber(enum NationalDexOrder nationalNum, s16 x, s16 y, u16 paletteSlot); diff --git a/include/pokemon.h b/include/pokemon.h index 0d3add4941..08313fb95b 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -635,6 +635,13 @@ struct FormChange u16 param3; }; +enum FusionExtraMoveHandling +{ + FORGET_EXTRA_MOVES, + SWAP_EXTRA_MOVES_KYUREM_WHITE, + SWAP_EXTRA_MOVES_KYUREM_BLACK +}; + struct Fusion { u16 fusionStorageIndex; @@ -643,11 +650,22 @@ struct Fusion u16 targetSpecies2; u16 fusingIntoMon; u16 fusionMove; - u16 unfuseForgetMove; + enum FusionExtraMoveHandling extraMoveHandling; }; extern const struct Fusion *const gFusionTablePointers[NUM_SPECIES]; +#if P_FUSION_FORMS +#if P_FAMILY_KYUREM +#if P_FAMILY_RESHIRAM +extern const u16 gKyurenWhiteSwapMoveTable[][2]; +#endif //P_FAMILY_RESHIRAM +#if P_FAMILY_ZEKROM +extern const u16 gKyurenBlackSwapMoveTable[][2]; +#endif //P_FAMILY_ZEKROM +#endif //P_FAMILY_KYUREM +#endif //P_FUSION_FORMS + #define NUM_UNOWN_FORMS 28 #define GET_UNOWN_LETTER(personality) (( \ diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 034ca30fc9..e84d7313a6 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -316,7 +316,7 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler) gAiBattleData->chosenTarget[battler] = gBattlerTarget; } -bool32 BattlerChoseNonMoveAction(void) +bool32 BattlerChooseNonMoveAction(void) { if (gAiThinkingStruct->aiAction & AI_ACTION_FLEE) { @@ -358,10 +358,6 @@ void ComputeBattlerDecisions(u32 battler) bool32 isAiBattler = (gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart()) && (BattlerHasAi(battler) && !(gBattleTypeFlags & BATTLE_TYPE_PALACE)); if (isAiBattler || CanAiPredictMove()) { - // If ai is about to flee or chosen to watch player, no need to calc anything - if (isAiBattler && BattlerChoseNonMoveAction()) - return; - // Risky AI switches aggressively even mid battle enum SwitchType switchType = (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_RISKY) ? SWITCH_AFTER_KO : SWITCH_MID_BATTLE; @@ -382,6 +378,8 @@ void ComputeBattlerDecisions(u32 battler) // AI's move scoring gAiBattleData->chosenMoveIndex[battler] = BattleAI_ChooseMoveIndex(battler); // Calculate score and chose move index + if (isAiBattler) + BattlerChooseNonMoveAction(); ModifySwitchAfterMoveScoring(battler); gAiLogicData->aiCalcInProgress = FALSE; diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 8099ea9c0d..62a68cf800 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1995,7 +1995,10 @@ static s32 GetMaxDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposingBattle { damageTaken = AI_CalcPartyMonDamage(playerMove, opposingBattler, battler, battleMon, &effectiveness, AI_DEFENDING); if (playerMove == gBattleStruct->choicedMove[opposingBattler]) // If player is choiced, only care about the choice locked move + { + *bestPlayerMove = playerMove; return damageTaken; + } if (damageTaken > maxDamageTaken) { maxDamageTaken = damageTaken; @@ -2016,13 +2019,19 @@ static s32 GetMaxPriorityDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposi for (i = 0; i < MAX_MON_MOVES; i++) { + // If player is choiced into a non-priority move, AI understands that it can't deal priority damage + if (gBattleStruct->choicedMove[opposingBattler] !=MOVE_NONE && GetMovePriority(gBattleStruct->choicedMove[opposingBattler]) < 1) + break; playerMove = SMART_SWITCHING_OMNISCIENT ? gBattleMons[opposingBattler].moves[i] : playerMoves[i]; if (GetBattleMovePriority(opposingBattler, gAiLogicData->abilities[opposingBattler], playerMove) > 0 && playerMove != MOVE_NONE && !IsBattleMoveStatus(playerMove) && GetMoveEffect(playerMove) != EFFECT_FOCUS_PUNCH && gBattleMons[opposingBattler].pp[i] > 0) { damageTaken = AI_CalcPartyMonDamage(playerMove, opposingBattler, battler, battleMon, &effectiveness, AI_DEFENDING); if (playerMove == gBattleStruct->choicedMove[opposingBattler]) // If player is choiced, only care about the choice locked move + { + *bestPlayerPriorityMove = playerMove; return damageTaken; + } if (damageTaken > maxDamageTaken) { maxDamageTaken = damageTaken; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 20b15d6cfe..c735913af3 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -3267,14 +3267,16 @@ enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, enum Ability defAbility u32 battlerToSwitch; u32 predictedMoveSpeedCheck = GetIncomingMoveSpeedCheck(battlerAtk, battlerDef, gAiLogicData); - battlerToSwitch = gBattleStruct->AI_monToSwitchIntoId[battlerAtk]; - // Palafin always wants to activate Zero to Hero if (gBattleMons[battlerAtk].species == SPECIES_PALAFIN_ZERO && gBattleMons[battlerAtk].ability == ABILITY_ZERO_TO_HERO && CountUsablePartyMons(battlerAtk) != 0) return SHOULD_PIVOT; + battlerToSwitch = gAiLogicData->mostSuitableMonId[battlerAtk]; + // This shouldn't ever happen, but it's there to make sure we don't accidentally read past the gParty array. + if (battlerToSwitch >= PARTY_SIZE) + battlerToSwitch = 0; if (PartyBattlerShouldAvoidHazards(battlerAtk, battlerToSwitch)) return DONT_PIVOT; diff --git a/src/battle_anim_water.c b/src/battle_anim_water.c index 490136d116..5674cd5cc7 100644 --- a/src/battle_anim_water.c +++ b/src/battle_anim_water.c @@ -45,6 +45,7 @@ static void AnimTask_RunSinAnimTimer(u8); static void AnimTask_CreateSurfWave_Step1(u8); static void AnimTask_CreateSurfWave_Step2(u8); static void AnimTask_SurfWaveScanlineEffect(u8); +static void AnimTask_BrineRain_Step(u8); static void AnimTask_WaterSpoutLaunch_Step(u8); static void AnimTask_WaterSpoutRain_Step(u8); static u8 GetWaterSpoutPowerForAnim(void); @@ -1442,24 +1443,97 @@ static void AnimSmallWaterOrb(struct Sprite *sprite) } } +#define tRainState data[0] +#define tWaterSpoutPower data[1] +#define tDropTaskDelay data[2] +#define tDropInitialXPos data[4] +#define tDropXRange data[5] +#define tDropEndYPos data[6] +#define tDropXPos data[7] +#define tSineTableIndex data[8] +#define tCurrentDropSprites data[9] +#define tDropHasHit data[10] +#define tCreatedDropSprites data[11] +#define tMaxDropSprites data[12] +#define tShakeTasksCreated data[13] +#define tDropInitialYPos data[14] + +void AnimTask_BrineRain(u8 taskId) +{ + struct Task *task = &gTasks[taskId]; + + if (IsOnPlayerSide(gBattleAnimAttacker)) + { + task->tDropEndYPos = 40; + task->tDropInitialYPos = 0; + } + else + { + task->tDropEndYPos = 90; + task->tDropInitialYPos = 40; + } + task->tDropInitialXPos = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2); + task->tDropXRange = 40; + task->tDropXPos = task->tDropInitialXPos; + task->tMaxDropSprites = 10; + task->func = AnimTask_BrineRain_Step; +} + +static void AnimTask_BrineRain_Step(u8 taskId) +{ + struct Task *task = &gTasks[taskId]; + u8 taskId2; + + switch (task->tRainState) + { + case 0: + if (++task->tDropTaskDelay > 2) + { + task->tDropTaskDelay = 0; + CreateWaterSpoutRainDroplet(task, taskId); + } + if (task->tDropHasHit != FALSE && task->tShakeTasksCreated == FALSE) + { + gBattleAnimArgs[0] = ANIM_TARGET; + gBattleAnimArgs[1] = 0; + gBattleAnimArgs[2] = 12; + taskId2 = CreateTask(AnimTask_HorizontalShake, 80); + if (taskId2 != TASK_NONE) + { + gTasks[taskId2].func(taskId2); + gAnimVisualTaskCount++; + } + task->tShakeTasksCreated = TRUE; + } + if (task->tCreatedDropSprites >= task->tMaxDropSprites) + task->tRainState++; + break; + case 1: + if (task->tCurrentDropSprites == 0) + DestroyAnimVisualTask(taskId); + break; + } +} + void AnimTask_WaterSpoutRain(u8 taskId) { struct Task *task = &gTasks[taskId]; - task->data[1] = GetWaterSpoutPowerForAnim(); + task->tWaterSpoutPower = GetWaterSpoutPowerForAnim(); if (IsOnPlayerSide(gBattleAnimAttacker)) { - task->data[4] = 136; - task->data[6] = 40; + task->tDropInitialXPos = 136; + task->tDropEndYPos = 40; } else { - task->data[4] = 16; - task->data[6] = 80; + task->tDropInitialXPos = 16; + task->tDropEndYPos = 80; } - task->data[5] = 98; - task->data[7] = task->data[4] + 49; - task->data[12] = task->data[1] * 5 + 5; + task->tDropXRange = 98; + task->tDropXPos = task->tDropInitialXPos + 49; + task->tMaxDropSprites = task->tWaterSpoutPower * 5 + 5; + task->tDropInitialYPos = 0; task->func = AnimTask_WaterSpoutRain_Step; } @@ -1468,15 +1542,15 @@ static void AnimTask_WaterSpoutRain_Step(u8 taskId) struct Task *task = &gTasks[taskId]; u8 taskId2; - switch (task->data[0]) + switch (task->tRainState) { case 0: - if (++task->data[2] > 2) + if (++task->tDropTaskDelay > 2) { - task->data[2] = 0; + task->tDropTaskDelay = 0; CreateWaterSpoutRainDroplet(task, taskId); } - if (task->data[10] != 0 && task->data[13] == 0) + if (task->tDropHasHit != FALSE && task->tShakeTasksCreated == FALSE) { gBattleAnimArgs[0] = ANIM_TARGET; gBattleAnimArgs[1] = 0; @@ -1494,13 +1568,13 @@ static void AnimTask_WaterSpoutRain_Step(u8 taskId) gTasks[taskId2].func(taskId2); gAnimVisualTaskCount++; } - task->data[13] = 1; + task->tShakeTasksCreated = TRUE; } - if (task->data[11] >= task->data[12]) - task->data[0]++; + if (task->tCreatedDropSprites >= task->tMaxDropSprites) + task->tRainState++; break; case 1: - if (task->data[9] == 0) + if (task->tCurrentDropSprites == 0) DestroyAnimVisualTask(taskId); break; } @@ -1508,8 +1582,8 @@ static void AnimTask_WaterSpoutRain_Step(u8 taskId) static void CreateWaterSpoutRainDroplet(struct Task *task, u8 taskId) { - u16 yPosArg = ((gSineTable[task->data[8]] + 3) >> 4) + task->data[6]; - u8 spriteId = CreateSprite(&gSmallWaterOrbSpriteTemplate, task->data[7], 0, 0); + u16 yPosArg = ((gSineTable[task->tSineTableIndex] + 3) >> 4) + task->tDropEndYPos; + u8 spriteId = CreateSprite(&gSmallWaterOrbSpriteTemplate, task->tDropXPos, task->tDropInitialYPos, 0); if (spriteId != MAX_SPRITES) { @@ -1517,11 +1591,11 @@ static void CreateWaterSpoutRainDroplet(struct Task *task, u8 taskId) gSprites[spriteId].data[5] = yPosArg; gSprites[spriteId].data[6] = taskId; gSprites[spriteId].data[7] = 9; - task->data[9]++; + task->tCurrentDropSprites++; } - task->data[11]++; - task->data[8] = (task->data[8] + 39) & 0xFF; - task->data[7] = (ISO_RANDOMIZE2(task->data[7]) % task->data[5]) + task->data[4]; + task->tCreatedDropSprites++; + task->tSineTableIndex = (task->tSineTableIndex + 39) & 0xFF; + task->tDropXPos = (ISO_RANDOMIZE2(task->tDropXPos) % task->tDropXRange) + task->tDropInitialXPos; } static void AnimWaterSpoutRain(struct Sprite *sprite) @@ -1531,7 +1605,7 @@ static void AnimWaterSpoutRain(struct Sprite *sprite) sprite->y += 8; if (sprite->y >= sprite->data[5]) { - gTasks[sprite->data[6]].data[10] = 1; + gTasks[sprite->data[6]].tDropHasHit = TRUE; sprite->data[1] = CreateSprite(&gWaterHitSplatSpriteTemplate, sprite->x, sprite->y, 1); if (sprite->data[1] != MAX_SPRITES) { @@ -1560,6 +1634,21 @@ static void AnimWaterSpoutRainHit(struct Sprite *sprite) } } +#undef tRainState +#undef tWaterSpoutPower +#undef tDropTaskDelay +#undef tDropInitialXPos +#undef tDropXRange +#undef tDropEndYPos +#undef tDropXPos +#undef tSineTableIndex +#undef tCurrentDropSprites +#undef tDropHasHit +#undef tCreatedDropSprites +#undef tMaxDropSprites +#undef tShakeTasksCreated +#undef tDropInitialYPos + void AnimTask_WaterSport(u8 taskId) { struct Task *task = &gTasks[taskId]; diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 15985aa0b4..0588d9208b 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -535,6 +535,12 @@ static void OpponentHandleChoosePokemon(u32 battler) { if (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) || gAiLogicData->ejectButtonSwitch || gAiLogicData->ejectPackSwitch) switchType = SWITCH_MID_BATTLE; + + // reset the AI data to consider the correct on-field state at time of switch + SetBattlerAiData(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT), gAiLogicData); + if (IsDoubleBattle()) + SetBattlerAiData(GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT), gAiLogicData); + chosenMonId = GetMostSuitableMonToSwitchInto(battler, switchType); if (chosenMonId == PARTY_SIZE) { diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 3b572198c3..b5a5e7abec 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -2000,7 +2000,7 @@ static void PlayerHandleChooseAction(u32 battler) else if (gAiBattleData->chosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_PLAYER_LEFT) StringAppend(gStringVar1, COMPOUND_STRING(" {DOWN_ARROW}-")); else if (gAiBattleData->chosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_PLAYER_RIGHT) - StringAppend(gStringVar1, COMPOUND_STRING(" {DOWN_ARROW}-")); + StringAppend(gStringVar1, COMPOUND_STRING(" -{DOWN_ARROW}")); } else if (moveTarget == MOVE_TARGET_BOTH) { diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index bddf1b6d19..50c458803c 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -331,6 +331,7 @@ static u8 GetBattlePalaceMoveGroup(u8 battler, u16 move) case MOVE_TARGET_RANDOM: case MOVE_TARGET_BOTH: case MOVE_TARGET_FOES_AND_ALLY: + case MOVE_TARGET_ALL_BATTLERS: if (IsBattleMoveStatus(move)) return PALACE_MOVE_GROUP_SUPPORT; else @@ -338,6 +339,7 @@ static u8 GetBattlePalaceMoveGroup(u8 battler, u16 move) break; case MOVE_TARGET_DEPENDS: case MOVE_TARGET_OPPONENTS_FIELD: + case MOVE_TARGET_ALLY: return PALACE_MOVE_GROUP_SUPPORT; case MOVE_TARGET_USER: return PALACE_MOVE_GROUP_DEFENSE; diff --git a/src/battle_interface.c b/src/battle_interface.c index d20b4b9aae..bcdd56839b 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -2967,7 +2967,8 @@ void TryAddLastUsedBallItemSprites(void) static void DestroyLastUsedBallWinGfx(struct Sprite *sprite) { FreeSpriteTilesByTag(TAG_LAST_BALL_WINDOW); - FreeSpritePaletteByTag(TAG_ABILITY_POP_UP); + if (GetSpriteTileStartByTag(MOVE_INFO_WINDOW_TAG) == 0xFFFF) + FreeSpritePaletteByTag(TAG_ABILITY_POP_UP); DestroySprite(sprite); gBattleStruct->ballSpriteIds[1] = MAX_SPRITES; } @@ -3004,7 +3005,8 @@ void TryToHideMoveInfoWindow(void) static void DestroyMoveInfoWinGfx(struct Sprite *sprite) { FreeSpriteTilesByTag(MOVE_INFO_WINDOW_TAG); - FreeSpritePaletteByTag(TAG_ABILITY_POP_UP); + if (GetSpriteTileStartByTag(TAG_LAST_BALL_WINDOW) == 0xFFFF) + FreeSpritePaletteByTag(TAG_ABILITY_POP_UP); DestroySprite(sprite); gBattleStruct->moveInfoSpriteId = MAX_SPRITES; } diff --git a/src/battle_main.c b/src/battle_main.c index 1685a4d606..62f4b95ca9 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5895,11 +5895,11 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, enum MonState enum HoldEffect holdEffect; enum Gimmick gimmick = GetActiveGimmick(battler); - if (move == MOVE_STRUGGLE) - return TYPE_NORMAL; - if (state == MON_IN_BATTLE) { + if (moveEffect == EFFECT_STRUGGLE) + return TYPE_MYSTERY; + species = gBattleMons[battler].species; heldItem = gBattleMons[battler].item; holdEffect = GetBattlerHoldEffect(battler); @@ -6152,8 +6152,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battler) && GetBattleMoveType(move) == GetItemSecondaryId(heldItem) && effect != EFFECT_PLEDGE && effect != EFFECT_OHKO - && effect != EFFECT_SHEER_COLD - && effect != EFFECT_STRUGGLE) + && effect != EFFECT_SHEER_COLD) { gSpecialStatuses[battler].gemParam = GetBattlerHoldEffectParam(battler); gSpecialStatuses[battler].gemBoost = TRUE; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 8b6e331424..a690295833 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1848,13 +1848,13 @@ static void Cmd_adjustdamage(void) gBattleStruct->moveResultFlags[battlerDef] &= ~(MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE); gBattleStruct->moveDamage[battlerDef] = 0; gSpecialStatuses[battlerDef].enduredDamage = TRUE; - RecordAbilityBattle(gBattlerTarget, ABILITY_ICE_FACE); + RecordAbilityBattle(battlerDef, ABILITY_ICE_FACE); gDisableStructs[battlerDef].iceFaceActivationPrevention = TRUE; // Form change will be done after attack animation in Cmd_resultmessage. continue; } - if (gBattleMons[gBattlerTarget].hp > gBattleStruct->moveDamage[battlerDef]) + if (gBattleMons[battlerDef].hp > gBattleStruct->moveDamage[battlerDef]) continue; holdEffect = GetBattlerHoldEffect(battlerDef); @@ -5647,11 +5647,13 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) return FALSE; u32 effect = FALSE; + u32 side = GetBattlerSide(gBattlerTarget); switch (moveEffect) { case EFFECT_KNOCK_OFF: if (gBattleMons[gBattlerTarget].item != ITEM_NONE && IsBattlerAlive(gBattlerAttacker) + && !(B_KNOCK_OFF_REMOVAL >= GEN_5 && side == B_SIDE_PLAYER && !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) && IsBattlerTurnDamaged(gBattlerTarget) && !DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove) && CanBattlerGetOrLoseItem(gBattlerTarget, gBattleMons[gBattlerTarget].item) @@ -5673,11 +5675,13 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE; CheckSetUnburden(gBattlerTarget); - // In Gen 5+, Knock Off removes the target's item rather than rendering it unusable. + // In Gen 5+, Knock Off removes the target's item rather than rendering it unusable if (B_KNOCK_OFF_REMOVAL >= GEN_5) { BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item); MarkBattlerForControllerExec(gBattlerTarget); + // Mark item as stolen so it will be restored after battle + gBattleStruct->itemLost[side][gBattlerPartyIndexes[gBattlerTarget]].stolen = TRUE; } else { @@ -5837,7 +5841,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) } break; case EFFECT_STONE_AXE: - if (!IsHazardOnSide(GetBattlerSide(gBattlerTarget), HAZARDS_STEALTH_ROCK) + if (!IsHazardOnSide(side, HAZARDS_STEALTH_ROCK) && IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) { @@ -5848,7 +5852,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) } break; case EFFECT_CEASELESS_EDGE: - if (gSideTimers[GetBattlerSide(gBattlerTarget)].spikesAmount < 3 + if (gSideTimers[side].spikesAmount < 3 && IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) { @@ -15038,30 +15042,48 @@ void BS_SetTerrain(void) switch (GetMoveEffect(gCurrentMove)) { case EFFECT_MISTY_TERRAIN: - statusFlag = STATUS_FIELD_MISTY_TERRAIN; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; + if (!(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + { + statusFlag = STATUS_FIELD_MISTY_TERRAIN; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; + } break; case EFFECT_GRASSY_TERRAIN: - statusFlag = STATUS_FIELD_GRASSY_TERRAIN; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_GRASSY; + if (!(gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)) + { + statusFlag = STATUS_FIELD_GRASSY_TERRAIN; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_GRASSY; + } break; case EFFECT_ELECTRIC_TERRAIN: - statusFlag = STATUS_FIELD_ELECTRIC_TERRAIN; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; + if (!(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)) + { + statusFlag = STATUS_FIELD_ELECTRIC_TERRAIN; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; + } break; case EFFECT_PSYCHIC_TERRAIN: - statusFlag = STATUS_FIELD_PSYCHIC_TERRAIN; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC; + if (!(gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)) + { + statusFlag = STATUS_FIELD_PSYCHIC_TERRAIN; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC; + } break; default: break; } - enum HoldEffect atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker); - - gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; - gFieldStatuses |= statusFlag; - gFieldTimers.terrainTimer = gBattleTurnCounter + (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5; - gBattlescriptCurrInstr = cmd->nextInstr; + if (statusFlag) + { + enum HoldEffect atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker); + gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; + gFieldStatuses |= statusFlag; + gFieldTimers.terrainTimer = gBattleTurnCounter + (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5; + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + gBattlescriptCurrInstr = cmd->jumpInstr; + } } void BS_JumpIfTerrainAffected(void) @@ -16838,13 +16860,12 @@ void BS_EraseArenaRefTextBox(void) void BS_ArenaJudgmentString(void) { - CMD_ARGS(u8 id); + NATIVE_ARGS(u8 id); BattleStringExpandPlaceholdersToDisplayedString(gRefereeStringsTable[cmd->id]); BattlePutTextOnWindow(gDisplayedStringBattle, ARENA_WIN_JUDGMENT_TEXT); gBattlescriptCurrInstr = cmd->nextInstr; } -// Argument passed but no use void BS_ArenaWaitMessage(void) { NATIVE_ARGS(); diff --git a/src/battle_util.c b/src/battle_util.c index 2bb0c032e9..53ec033938 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -570,8 +570,7 @@ void HandleAction_UseMove(void) gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; } } - - if (IsBattlerAlly(gBattlerAttacker, gBattlerTarget) && !IsBattlerAlive(gBattlerTarget)) + else if (IsBattlerAlly(gBattlerAttacker, gBattlerTarget) && !IsBattlerAlive(gBattlerTarget)) { gBattlescriptCurrInstr = BattleScript_FailedFromAtkCanceler; } @@ -4892,8 +4891,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, enum Ability ability, u32 spec && gDisableStructs[gBattlerAttacker].disabledMove == MOVE_NONE && IsBattlerAlive(gBattlerAttacker) && !IsAbilityOnSide(gBattlerAttacker, ABILITY_AROMA_VEIL) - && gBattleMons[gBattlerAttacker].pp[gChosenMovePos] != 0 - && !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) // TODO: Max Moves don't make contact, useless? + && gChosenMove != MOVE_STRUGGLE && RandomPercentage(RNG_CURSED_BODY, 30)) { gDisableStructs[gBattlerAttacker].disabledMove = gChosenMove; diff --git a/src/data/abilities.h b/src/data/abilities.h index b3a65d564f..39a0644c4f 100644 --- a/src/data/abilities.h +++ b/src/data/abilities.h @@ -1286,6 +1286,7 @@ const struct AbilityInfo gAbilitiesInfo[ABILITIES_COUNT] = { .name = _("Bulletproof"), .description = COMPOUND_STRING("Avoids some projectiles."), + .breakable = TRUE, .aiRating = 7, }, diff --git a/src/data/moves_info.h b/src/data/moves_info.h index ed4097ab81..1f2ee5f6a8 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -9079,7 +9079,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = "A life-risking tackle that\n" "slightly hurts the user."), #endif - .effect = EFFECT_HIT, + .effect = EFFECT_RECOIL, .power = 120, .type = TYPE_ELECTRIC, .accuracy = 100, @@ -19331,7 +19331,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .power = 100, .type = TYPE_ICE, .accuracy = 85, - .pp = 5, + .pp = B_UPDATED_MOVE_DATA >= GEN_9 ? 10 : 5, .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, diff --git a/src/data/pokemon/form_change_table_pointers.h b/src/data/pokemon/form_change_table_pointers.h index ea2cdfd2a2..ad2e6a040d 100644 --- a/src/data/pokemon/form_change_table_pointers.h +++ b/src/data/pokemon/form_change_table_pointers.h @@ -34,3 +34,22 @@ const struct Fusion *const gFusionTablePointers[NUM_SPECIES] = #endif //P_FAMILY_CALYREX #endif //P_FUSION_FORMS }; + +#if P_FUSION_FORMS +#if P_FAMILY_KYUREM +#if P_FAMILY_RESHIRAM +const u16 gKyurenWhiteSwapMoveTable[][2] = +{ + {MOVE_SCARY_FACE, MOVE_FUSION_FLARE}, + {MOVE_GLACIATE, MOVE_ICE_BURN}, +}; +#endif //P_FAMILY_RESHIRAM +#if P_FAMILY_ZEKROM +const u16 gKyurenBlackSwapMoveTable[][2] = +{ + {MOVE_SCARY_FACE, MOVE_FUSION_BOLT}, + {MOVE_GLACIATE, MOVE_FREEZE_SHOCK}, +}; +#endif //P_FAMILY_ZEKROM +#endif //P_FAMILY_KYUREM +#endif //P_FUSION_FORMS diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index ed80ae2178..ad8a7e90ca 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -755,8 +755,8 @@ static const struct FormChange sLandorusFormChangeTable[] = { #if P_FAMILY_KYUREM static const struct Fusion sKyuremFusionTable[] = { - {0, ITEM_DNA_SPLICERS, SPECIES_KYUREM, SPECIES_RESHIRAM, SPECIES_KYUREM_WHITE}, - {0, ITEM_DNA_SPLICERS, SPECIES_KYUREM, SPECIES_ZEKROM, SPECIES_KYUREM_BLACK}, + {0, ITEM_DNA_SPLICERS, SPECIES_KYUREM, SPECIES_RESHIRAM, SPECIES_KYUREM_WHITE, MOVE_NONE, SWAP_EXTRA_MOVES_KYUREM_WHITE}, + {0, ITEM_DNA_SPLICERS, SPECIES_KYUREM, SPECIES_ZEKROM, SPECIES_KYUREM_BLACK, MOVE_NONE, SWAP_EXTRA_MOVES_KYUREM_BLACK}, {FUSION_TERMINATOR}, }; #endif //P_FAMILY_KYUREM @@ -998,8 +998,8 @@ static const struct FormChange sMimikyuTotemFormChangeTable[] = { #if P_FAMILY_NECROZMA static const struct Fusion sNecrozmaFusionTable[] = { - {1, ITEM_N_SOLARIZER, SPECIES_NECROZMA, SPECIES_SOLGALEO, SPECIES_NECROZMA_DUSK_MANE, MOVE_SUNSTEEL_STRIKE, MOVE_CONFUSION}, - {2, ITEM_N_LUNARIZER, SPECIES_NECROZMA, SPECIES_LUNALA, SPECIES_NECROZMA_DAWN_WINGS, MOVE_MOONGEIST_BEAM, MOVE_CONFUSION}, + {1, ITEM_N_SOLARIZER, SPECIES_NECROZMA, SPECIES_SOLGALEO, SPECIES_NECROZMA_DUSK_MANE, MOVE_SUNSTEEL_STRIKE, FORGET_EXTRA_MOVES}, + {2, ITEM_N_LUNARIZER, SPECIES_NECROZMA, SPECIES_LUNALA, SPECIES_NECROZMA_DAWN_WINGS, MOVE_MOONGEIST_BEAM, FORGET_EXTRA_MOVES}, {FUSION_TERMINATOR}, }; @@ -1266,8 +1266,8 @@ static const struct FormChange sUrshifuRapidStrikeFormChangeTable[] = { #if P_FAMILY_CALYREX static const struct Fusion sCalyrexFusionTable[] = { - {3, ITEM_REINS_OF_UNITY, SPECIES_CALYREX, SPECIES_GLASTRIER, SPECIES_CALYREX_ICE, MOVE_GLACIAL_LANCE, MOVE_CONFUSION}, - {3, ITEM_REINS_OF_UNITY, SPECIES_CALYREX, SPECIES_SPECTRIER, SPECIES_CALYREX_SHADOW, MOVE_ASTRAL_BARRAGE, MOVE_CONFUSION}, + {3, ITEM_REINS_OF_UNITY, SPECIES_CALYREX, SPECIES_GLASTRIER, SPECIES_CALYREX_ICE, MOVE_GLACIAL_LANCE, FORGET_EXTRA_MOVES}, + {3, ITEM_REINS_OF_UNITY, SPECIES_CALYREX, SPECIES_SPECTRIER, SPECIES_CALYREX_SHADOW, MOVE_ASTRAL_BARRAGE, FORGET_EXTRA_MOVES}, {FUSION_TERMINATOR}, }; #endif //P_FAMILY_CALYREX diff --git a/src/debug.c b/src/debug.c index d6632810ca..5779401735 100644 --- a/src/debug.c +++ b/src/debug.c @@ -55,6 +55,7 @@ #include "strings.h" #include "string_util.h" #include "task.h" +#include "tv.h" #include "pokemon_summary_screen.h" #include "wild_encounter.h" #include "constants/abilities.h" @@ -2612,11 +2613,11 @@ static void DebugAction_Give_Pokemon_SelectDynamaxLevel(u8 taskId) } } -static void Debug_Display_StatInfo(const u8* text, u32 stat, u32 value, u32 digit, u8 windowId) +static void Debug_Display_StatInfo(const u8* text, u32 stat, u32 value, u32 digit, u8 windowId, u32 maxValue) { StringCopy(gStringVar1, gStatNamesTable[stat]); StringCopy(gStringVar2, gText_DigitIndicator[digit]); - ConvertIntToDecimalStringN(gStringVar3, value, STR_CONV_MODE_LEADING_ZEROS, 2); + ConvertIntToDecimalStringN(gStringVar3, value, STR_CONV_MODE_LEADING_ZEROS, CountDigits(maxValue)); StringCopyPadded(gStringVar3, gStringVar3, CHAR_SPACE, 15); StringExpandPlaceholders(gStringVar4, text); AddTextPrinterParameterized(windowId, DEBUG_MENU_FONT, gStringVar4, 0, 0, 0, NULL); @@ -2636,7 +2637,7 @@ static void DebugAction_Give_Pokemon_SelectGigantamaxFactor(u8 taskId) sDebugMonData->gmaxFactor = gTasks[taskId].tInput; gTasks[taskId].tInput = 0; gTasks[taskId].tDigit = 0; - Debug_Display_StatInfo(sDebugText_IVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId); + Debug_Display_StatInfo(sDebugText_IVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_IVS); gTasks[taskId].func = DebugAction_Give_Pokemon_SelectIVs; } else if (JOY_NEW(B_BUTTON)) @@ -2653,7 +2654,7 @@ static void DebugAction_Give_Pokemon_SelectIVs(u8 taskId) { PlaySE(SE_SELECT); Debug_HandleInput_Numeric(taskId, 0, MAX_PER_STAT_IVS, 3); - Debug_Display_StatInfo(sDebugText_IVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId); + Debug_Display_StatInfo(sDebugText_IVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_IVS); } //If A or B button @@ -2669,7 +2670,7 @@ static void DebugAction_Give_Pokemon_SelectIVs(u8 taskId) gTasks[taskId].tInput = 0; gTasks[taskId].tDigit = 0; - Debug_Display_StatInfo(sDebugText_IVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId); + Debug_Display_StatInfo(sDebugText_IVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_IVS); gTasks[taskId].func = DebugAction_Give_Pokemon_SelectIVs; } else @@ -2678,7 +2679,7 @@ static void DebugAction_Give_Pokemon_SelectIVs(u8 taskId) gTasks[taskId].tDigit = 0; gTasks[taskId].tIterator = 0; - Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId); + Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_EVS); gTasks[taskId].func = DebugAction_Give_Pokemon_SelectEVs; } } @@ -2726,7 +2727,7 @@ static void DebugAction_Give_Pokemon_SelectEVs(u8 taskId) { PlaySE(SE_SELECT); Debug_HandleInput_Numeric(taskId, 0, MAX_PER_STAT_EVS, 4); - Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId); + Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_EVS); } //If A or B button @@ -2741,7 +2742,7 @@ static void DebugAction_Give_Pokemon_SelectEVs(u8 taskId) gTasks[taskId].tIterator++; gTasks[taskId].tInput = 0; gTasks[taskId].tDigit = 0; - Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId); + Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_EVS); gTasks[taskId].func = DebugAction_Give_Pokemon_SelectEVs; } else @@ -2758,7 +2759,7 @@ static void DebugAction_Give_Pokemon_SelectEVs(u8 taskId) } PlaySE(SE_FAILURE); - Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId); + Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_EVS); gTasks[taskId].func = DebugAction_Give_Pokemon_SelectEVs; } else @@ -3191,12 +3192,10 @@ static void DebugAction_PCBag_Fill_PocketItems(u8 taskId) static void DebugAction_PCBag_Fill_PocketPokeBalls(u8 taskId) { - u16 ballId; - - for (ballId = BALL_STRANGE; ballId < POKEBALL_COUNT; ballId++) + for (enum PokeBall ballId = BALL_STRANGE; ballId < POKEBALL_COUNT; ballId++) { if (CheckBagHasSpace(ballId, MAX_BAG_ITEM_CAPACITY)) - AddBagItem(ballId, MAX_BAG_ITEM_CAPACITY); + AddBagItem(gBallItemIds[ballId], MAX_BAG_ITEM_CAPACITY); } } diff --git a/src/dexnav.c b/src/dexnav.c index e1fc4a497f..fe4b267a70 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -472,8 +472,8 @@ static void AddSearchWindow(u8 width) } #define WINDOW_COL_0 (SPECIES_ICON_X + 4) -#define WINDOW_COL_1 (WINDOW_COL_0 + (GetFontAttribute(sDexNavSearchDataPtr->windowId, FONTATTR_MAX_LETTER_WIDTH) * (POKEMON_NAME_LENGTH))) -#define WINDOW_MOVE_NAME_X (WINDOW_COL_1 + (GetFontAttribute(sDexNavSearchDataPtr->windowId, FONTATTR_MAX_LETTER_WIDTH) * 6)) +#define WINDOW_COL_1 (WINDOW_COL_0 + (GetFontAttribute(FONT_SMALL, FONTATTR_MAX_LETTER_WIDTH) * (POKEMON_NAME_LENGTH))) +#define WINDOW_MOVE_NAME_X (WINDOW_COL_1 + (GetFontAttribute(FONT_SMALL, FONTATTR_MAX_LETTER_WIDTH) * 6)) #define SEARCH_ARROW_X (WINDOW_MOVE_NAME_X + 90) #define SEARCH_ARROW_Y 0 @@ -485,19 +485,19 @@ static void AddSearchWindowText(u16 species, u8 proximity, u8 searchLevel, bool8 if (hidden) { StringCopy(gStringVar4, sText_ThreeQmarks); - AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); return; } else { StringCopy(gStringVar1, GetSpeciesName(species)); - AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1); } //level - always present ConvertIntToDecimalStringN(gStringVar1, sDexNavSearchDataPtr->monLevel, STR_CONV_MODE_LEFT_ALIGN, 3); StringExpandPlaceholders(gStringVar4, sText_MonLevel); - AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, WINDOW_COL_1, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, WINDOW_COL_1, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); if (proximity <= SNEAKING_PROXIMITY) { @@ -507,21 +507,21 @@ static void AddSearchWindowText(u16 species, u8 proximity, u8 searchLevel, bool8 { StringCopy(gStringVar1, GetMoveName(sDexNavSearchDataPtr->moves[0])); StringExpandPlaceholders(gStringVar4, sText_EggMove); - AddTextPrinterParameterized3(windowId, 0, WINDOW_MOVE_NAME_X, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(windowId, FONT_SMALL, WINDOW_MOVE_NAME_X, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); } if (searchLevel > 2) { // ability name StringCopy(gStringVar1, gAbilitiesInfo[GetAbilityBySpecies(species, sDexNavSearchDataPtr->abilityNum)].name); - AddTextPrinterParameterized3(windowId, 0, WINDOW_COL_1 + 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1); + AddTextPrinterParameterized3(windowId, FONT_SMALL, WINDOW_COL_1 + 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1); // item name if (sDexNavSearchDataPtr->heldItem) { CopyItemName(sDexNavSearchDataPtr->heldItem, gStringVar1); StringExpandPlaceholders(gStringVar4, sText_HeldItem); - AddTextPrinterParameterized3(windowId, 0, WINDOW_COL_0, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(windowId, FONT_SMALL, WINDOW_COL_0, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); } } } @@ -532,7 +532,7 @@ static void AddSearchWindowText(u16 species, u8 proximity, u8 searchLevel, bool8 StringExpandPlaceholders(gStringVar4, sText_DexNavChainLong); else StringExpandPlaceholders(gStringVar4, sText_DexNavChain); - AddTextPrinterParameterized3(windowId, 0, SEARCH_ARROW_X - 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(windowId, FONT_SMALL, SEARCH_ARROW_X - 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); CopyWindowToVram(sDexNavSearchDataPtr->windowId, 2); } @@ -959,7 +959,7 @@ static void DexNavUpdateDirectionArrow(void) str = sText_ArrowDown; //player above } - AddTextPrinterParameterized3(windowId, 1, SEARCH_ARROW_X, SEARCH_ARROW_Y, sSearchFontColor, TEXT_SKIP_DRAW, str); + AddTextPrinterParameterized3(windowId, FONT_NORMAL, SEARCH_ARROW_X, SEARCH_ARROW_Y, sSearchFontColor, TEXT_SKIP_DRAW, str); CopyWindowToVram(windowId, 2); } @@ -2133,9 +2133,9 @@ static void PrintCurrentSpeciesInfo(void) //species name if (species == SPECIES_NONE) - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SPECIES_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, SPECIES_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); else - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SPECIES_INFO_Y, sFontColor_Black, 0, GetSpeciesName(species)); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, SPECIES_INFO_Y, sFontColor_Black, 0, GetSpeciesName(species)); //type icon(s) type1 = GetSpeciesType(species, 0); @@ -2157,34 +2157,34 @@ static void PrintCurrentSpeciesInfo(void) //search level if (species == SPECIES_NONE) { - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); } else { ConvertIntToDecimalStringN(gStringVar4, GetSearchLevel(species), 0, 4); - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, gStringVar4); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, gStringVar4); } //hidden ability if (species == SPECIES_NONE) { - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); } else if (GetSetPokedexFlag(dexNum, FLAG_GET_CAUGHT)) { if (GetSpeciesAbility(species, 2) != ABILITY_NONE) - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, gAbilitiesInfo[GetSpeciesAbility(species, 2)].name); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, HA_INFO_Y, sFontColor_Black, 0, gAbilitiesInfo[GetSpeciesAbility(species, 2)].name); else - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, gText_None); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, HA_INFO_Y, sFontColor_Black, 0, gText_None); } else { - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_CaptureToSee); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_CaptureToSee); } //current chain ConvertIntToDecimalStringN(gStringVar1, gSaveBlock3Ptr->dexNavChain, STR_CONV_MODE_LEFT_ALIGN, 3); - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, CHAIN_BONUS_Y, sFontColor_Black, 0, gStringVar1); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, CHAIN_BONUS_Y, sFontColor_Black, 0, gStringVar1); CopyWindowToVram(WINDOW_INFO, 3); PutWindowTilemap(WINDOW_INFO); @@ -2193,7 +2193,7 @@ static void PrintCurrentSpeciesInfo(void) static void PrintMapName(void) { GetMapName(gStringVar3, GetCurrentRegionMapSectionId(), 0); - AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 108 + + AddTextPrinterParameterized3(WINDOW_REGISTERED, FONT_NORMAL, 108 + GetStringRightAlignXOffset(1, gStringVar3, MAP_NAME_LENGTH * GetFontAttribute(1, FONTATTR_MAX_LETTER_WIDTH)), 0, sFontColor_White, 0, gStringVar3); CopyWindowToVram(WINDOW_REGISTERED, 3); @@ -2205,13 +2205,13 @@ static void PrintSearchableSpecies(u16 species) PutWindowTilemap(WINDOW_REGISTERED); if (species == SPECIES_NONE) { - AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, sText_DexNav_PressRToRegister); + AddTextPrinterParameterized3(WINDOW_REGISTERED, FONT_NORMAL, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, sText_DexNav_PressRToRegister); } else { StringCopy(gStringVar1, GetSpeciesName(species)); StringExpandPlaceholders(gStringVar4, sText_DexNav_SearchForRegisteredSpecies); - AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(WINDOW_REGISTERED, FONT_NORMAL, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, gStringVar4); } PrintMapName(); @@ -2651,11 +2651,11 @@ static void DrawSearchIcon(void) static void DrawHiddenSearchWindow(u8 width) { AddSearchWindow(width); - AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, SPECIES_ICON_X + 4, 0, sSearchFontColor, TEXT_SKIP_DRAW, sText_ThreeQmarks); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, SPECIES_ICON_X + 4, 0, sSearchFontColor, TEXT_SKIP_DRAW, sText_ThreeQmarks); ConvertIntToDecimalStringN(gStringVar1, sDexNavSearchDataPtr->searchLevel, STR_CONV_MODE_LEFT_ALIGN, 2); StringExpandPlaceholders(gStringVar4, sText_SearchLevel); - AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, SPECIES_ICON_X + 4, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, SPECIES_ICON_X + 4, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); CopyWindowToVram(sDexNavSearchDataPtr->windowId, 2); } diff --git a/src/field_player_avatar.c b/src/field_player_avatar.c index 4633e38d82..386d917355 100644 --- a/src/field_player_avatar.c +++ b/src/field_player_avatar.c @@ -1163,17 +1163,18 @@ void PlayerSetAnimId(u8 movementActionId, u8 copyableMovement) // slow stairs (from FRLG--faster than slow) static void PlayerWalkSlowStairs(u8 direction) { - PlayerSetAnimId(GetWalkSlowStairsMovementAction(direction), 2); + PlayerSetAnimId(GetWalkSlowStairsMovementAction(direction), COPY_MOVE_WALK); } // slow static void UNUSED PlayerWalkSlow(u8 direction) { - PlayerSetAnimId(GetWalkSlowMovementAction(direction), 2); + PlayerSetAnimId(GetWalkSlowMovementAction(direction), COPY_MOVE_WALK); } + static void PlayerRunSlow(u8 direction) { - PlayerSetAnimId(GetPlayerRunSlowMovementAction(direction), 2); + PlayerSetAnimId(GetPlayerRunSlowMovementAction(direction), COPY_MOVE_WALK); } // normal speed (1 speed) @@ -1205,7 +1206,7 @@ static void PlayerRun(u8 direction) void PlayerOnBikeCollide(u8 direction) { PlayCollisionSoundIfNotFacingWarp(direction); - PlayerSetAnimId(GetWalkInPlaceNormalMovementAction(direction), COPY_MOVE_WALK); + PlayerSetAnimId(GetWalkInPlaceNormalMovementAction(direction), COPY_MOVE_FACE); // Edge case: If the player stops at the top of a mud slide, but the NPC follower is still on a mud slide tile, // move the follower into the player and hide them. if (PlayerHasFollowerNPC()) @@ -1226,18 +1227,18 @@ void PlayerOnBikeCollide(u8 direction) void PlayerOnBikeCollideWithFarawayIslandMew(u8 direction) { - PlayerSetAnimId(GetWalkInPlaceNormalMovementAction(direction), COPY_MOVE_WALK); + PlayerSetAnimId(GetWalkInPlaceNormalMovementAction(direction), COPY_MOVE_FACE); } static void PlayerNotOnBikeCollide(u8 direction) { PlayCollisionSoundIfNotFacingWarp(direction); - PlayerSetAnimId(GetWalkInPlaceSlowMovementAction(direction), COPY_MOVE_WALK); + PlayerSetAnimId(GetWalkInPlaceSlowMovementAction(direction), COPY_MOVE_FACE); } static void PlayerNotOnBikeCollideWithFarawayIslandMew(u8 direction) { - PlayerSetAnimId(GetWalkInPlaceSlowMovementAction(direction), COPY_MOVE_WALK); + PlayerSetAnimId(GetWalkInPlaceSlowMovementAction(direction), COPY_MOVE_FACE); } void PlayerFaceDirection(u8 direction) diff --git a/src/follower_npc.c b/src/follower_npc.c index 11b066c12c..8bc3a61697 100644 --- a/src/follower_npc.c +++ b/src/follower_npc.c @@ -187,6 +187,7 @@ static void TurnNPCIntoFollower(u32 localId, u32 followerFlags, u32 setScript, c u32 npcY = gObjectEvents[eventObjId].currentCoords.y; const u8 *script; u32 flag; + u16 facingDirection = gObjectEvents[eventObjId].facingDirection; flag = GetObjectEventFlagIdByLocalIdAndMap(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); // If the object does not have an event flag, don't create follower. @@ -210,7 +211,7 @@ static void TurnNPCIntoFollower(u32 localId, u32 followerFlags, u32 setScript, c SetFollowerNPCData(FNPC_DATA_OBJ_ID, TrySpawnObjectEventTemplate(&npc, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, npcX, npcY)); follower = &gObjectEvents[GetFollowerNPCData(FNPC_DATA_OBJ_ID)]; MoveObjectEventToMapCoords(follower, npcX, npcY); - ObjectEventTurn(follower, gObjectEvents[eventObjId].facingDirection); + ObjectEventTurn(follower, facingDirection); follower->movementType = MOVEMENT_TYPE_NONE; gSprites[follower->spriteId].callback = MovementType_None; diff --git a/src/item.c b/src/item.c index a9522a723c..53633bbe66 100644 --- a/src/item.c +++ b/src/item.c @@ -235,9 +235,9 @@ bool32 HasAtLeastOneBerry(void) bool32 HasAtLeastOnePokeBall(void) { - for (u32 ballId = BALL_STRANGE; ballId < POKEBALL_COUNT; ballId++) + for (enum PokeBall ballId = BALL_STRANGE; ballId < POKEBALL_COUNT; ballId++) { - if (CheckBagHasItem(ballId, 1) == TRUE) + if (CheckBagHasItem(gBallItemIds[ballId], 1) == TRUE) return TRUE; } return FALSE; diff --git a/src/overworld.c b/src/overworld.c index 7ce561bdae..e07d48b78c 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1561,7 +1561,7 @@ const struct BlendSettings gTimeOfDayBlend[] = }; #define DEFAULT_WEIGHT 256 -#define TIME_BLEND_WEIGHT(begin, end) (DEFAULT_WEIGHT - (DEFAULT_WEIGHT * SAFE_DIV(((hours - begin) * MINUTES_PER_HOUR + minutes), ((end - begin) * MINUTES_PER_HOUR)))) +#define TIME_BLEND_WEIGHT(begin, end) (DEFAULT_WEIGHT - SAFE_DIV((DEFAULT_WEIGHT * ((hours - begin) * MINUTES_PER_HOUR + minutes)), ((end - begin) * MINUTES_PER_HOUR))) #define MORNING_HOUR_MIDDLE (MORNING_HOUR_BEGIN + ((MORNING_HOUR_END - MORNING_HOUR_BEGIN) / 2)) diff --git a/src/party_menu.c b/src/party_menu.c index e6568c34bf..3b05551cb2 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -6113,13 +6113,14 @@ void ItemUseCB_EvolutionStone(u8 taskId, TaskFunc task) #define tAnimWait data[2] #define tNextFunc 3 -#define fusionType data[7] -#define firstFusion data[8] -#define firstFusionSlot data[9] -#define fusionResult data[10] -#define secondFusionSlot data[11] -#define unfuseSecondMon data[12] -#define moveToLearn data[13] +#define fusionType data[6] +#define firstFusion data[7] +#define firstFusionSlot data[8] +#define fusionResult data[9] +#define secondFusionSlot data[10] +#define unfuseSecondMon data[11] +#define moveToLearn data[12] +#define tExtraMoveHandling data[13] #define forgetMove data[14] #define storageIndex data[15] @@ -6254,6 +6255,75 @@ static void RestoreFusionMon(struct Pokemon *mon) } } +static void DeleteInvalidFusionMoves(struct Pokemon *mon, u32 species) +{ + for (u32 i = 0; i < MAX_MON_MOVES; i++) + { + u32 move = GetMonData(mon, MON_DATA_MOVE1 + i); + bool32 toDelete = TRUE; + const struct LevelUpMove *learnset = GetSpeciesLevelUpLearnset(species); + for (u32 j = 0; learnset[j].move != LEVEL_UP_MOVE_END;j++) + { + if (learnset[j].move == move) + { + toDelete = FALSE; + break; + } + } + if (!toDelete) + continue; + const u16 *learnset2 = GetSpeciesTeachableLearnset(species); + for (u32 j = 0; learnset2[j] != MOVE_UNAVAILABLE;j++) + { + if (learnset2[j] == move) + { + toDelete = FALSE; + break; + } + } + if (!toDelete) + continue; + const u16 *learnset3 = GetSpeciesEggMoves(species); + for (u32 j = 0; learnset3[j] != MOVE_UNAVAILABLE;j++) + { + if (learnset3[j] == move) + { + toDelete = FALSE; + break; + } + } + if (toDelete) + DeleteMove(mon, move); + } +} + +static void SwapFusionMonMoves(struct Pokemon *mon, const u16 moveTable[][2], u32 mode) +{ + u32 oldMoveIndex, newMoveIndex; + if (mode == FUSE_MON) + { + oldMoveIndex = 0; + newMoveIndex = 1; + } + else //mode == UNFUSE_MON + { + oldMoveIndex = 1; + newMoveIndex = 0; + } + for (u32 i = 0; i < MAX_MON_MOVES; i++) + { + u32 move = GetMonData(mon, MON_DATA_MOVE1 + i); + for (u32 j = 0; j < 2; j++) + { + if (move == moveTable[j][oldMoveIndex]) + { + SetMonData(mon, MON_DATA_MOVE1 + i, &moveTable[j][newMoveIndex]); + SetMonData(mon, MON_DATA_PP1 + i, &gMovesInfo[moveTable[j][newMoveIndex]].pp); + } + } + } + +} static void Task_TryItemUseFusionChange(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gTasks[taskId].firstFusionSlot]; @@ -6345,15 +6415,38 @@ static void Task_TryItemUseFusionChange(u8 taskId) case 6: if (!IsPartyMenuTextPrinterActive()) { - if (gTasks[taskId].moveToLearn != 0) + if (gTasks[taskId].fusionType == FUSE_MON) { - if (gTasks[taskId].fusionType == FUSE_MON) +#if P_FAMILY_KYUREM +#if P_FAMILY_RESHIRAM + if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE) + SwapFusionMonMoves(mon, gKyurenWhiteSwapMoveTable, FUSE_MON); +#endif //P_FAMILY_RESHIRAM +#if P_FAMILY_ZEKROM + if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_BLACK) + SwapFusionMonMoves(mon, gKyurenBlackSwapMoveTable, FUSE_MON); +#endif //P_FAMILY_ZEKROM +#endif //P_FAMILY_KYUREM + if (gTasks[taskId].moveToLearn != 0) FormChangeTeachMove(taskId, gTasks[taskId].moveToLearn, gTasks[taskId].firstFusionSlot); - else + } + else //(gTasks[taskId].fusionType == UNFUSE_MON) + { +#if P_FAMILY_KYUREM +#if P_FAMILY_RESHIRAM + if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE) + SwapFusionMonMoves(mon, gKyurenWhiteSwapMoveTable, UNFUSE_MON); +#endif //P_FAMILY_RESHIRAM +#if P_FAMILY_ZEKROM + if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_BLACK) + SwapFusionMonMoves(mon, gKyurenBlackSwapMoveTable, UNFUSE_MON); +#endif //P_FAMILY_ZEKROM +#endif //P_FAMILY_KYUREM + if ( gTasks[taskId].tExtraMoveHandling == FORGET_EXTRA_MOVES) { - DeleteMove(mon, gTasks[taskId].forgetMove); + DeleteInvalidFusionMoves(mon, gTasks[taskId].fusionResult); if (!DoesMonHaveAnyMoves(mon)) - FormChangeTeachMove(taskId, gTasks[taskId].moveToLearn, gTasks[taskId].firstFusionSlot); + FormChangeTeachMove(taskId, MOVE_CONFUSION, gTasks[taskId].firstFusionSlot); } } gTasks[taskId].tState++; @@ -6400,7 +6493,7 @@ void ItemUseCB_Fusion(u8 taskId, TaskFunc taskFunc) task->storageIndex = itemFusion[i].fusionStorageIndex; task->fusionResult = itemFusion[i].targetSpecies1; task->unfuseSecondMon = itemFusion[i].targetSpecies2; - task->moveToLearn = itemFusion[i].unfuseForgetMove; + task->tExtraMoveHandling = itemFusion[i].extraMoveHandling; task->forgetMove = itemFusion[i].fusionMove; TryItemUseFusionChange(taskId, taskFunc); return; @@ -6440,6 +6533,7 @@ void ItemUseCB_Fusion(u8 taskId, TaskFunc taskFunc) task->fusionResult = itemFusion[i].fusingIntoMon; task->secondFusionSlot = gPartyMenu.slotId; task->moveToLearn = itemFusion[i].fusionMove; + task->tExtraMoveHandling = itemFusion[i].extraMoveHandling; // Start Fusion TryItemUseFusionChange(taskId, taskFunc); return; diff --git a/src/pokeball.c b/src/pokeball.c index 5608de9647..5b32aa7e1d 100644 --- a/src/pokeball.c +++ b/src/pokeball.c @@ -544,6 +544,38 @@ const struct SpriteTemplate gBallSpriteTemplates[POKEBALL_COUNT] = #define tBattler data[3] #define tOpponentBattler data[4] +const u16 gBallItemIds[POKEBALL_COUNT] = +{ + [BALL_STRANGE] = ITEM_STRANGE_BALL, + [BALL_POKE] = ITEM_POKE_BALL, + [BALL_GREAT] = ITEM_GREAT_BALL, + [BALL_ULTRA] = ITEM_ULTRA_BALL, + [BALL_MASTER] = ITEM_MASTER_BALL, + [BALL_PREMIER] = ITEM_PREMIER_BALL, + [BALL_HEAL] = ITEM_HEAL_BALL, + [BALL_NET] = ITEM_NET_BALL, + [BALL_NEST] = ITEM_NEST_BALL, + [BALL_DIVE] = ITEM_DIVE_BALL, + [BALL_DUSK] = ITEM_DUSK_BALL, + [BALL_TIMER] = ITEM_TIMER_BALL, + [BALL_QUICK] = ITEM_QUICK_BALL, + [BALL_REPEAT] = ITEM_REPEAT_BALL, + [BALL_LUXURY] = ITEM_LUXURY_BALL, + [BALL_LEVEL] = ITEM_LEVEL_BALL, + [BALL_LURE] = ITEM_LURE_BALL, + [BALL_MOON] = ITEM_MOON_BALL, + [BALL_FRIEND] = ITEM_FRIEND_BALL, + [BALL_LOVE] = ITEM_LOVE_BALL, + [BALL_FAST] = ITEM_FAST_BALL, + [BALL_HEAVY] = ITEM_HEAVY_BALL, + [BALL_DREAM] = ITEM_DREAM_BALL, + [BALL_SAFARI] = ITEM_SAFARI_BALL, + [BALL_SPORT] = ITEM_SPORT_BALL, + [BALL_PARK] = ITEM_PARK_BALL, + [BALL_BEAST] = ITEM_BEAST_BALL, + [BALL_CHERISH] = ITEM_CHERISH_BALL, +}; + u8 DoPokeballSendOutAnimation(u32 battler, s16 pan, u8 kindOfThrow) { u8 taskId; diff --git a/src/pokedex.c b/src/pokedex.c index 91fe39367b..7f2ebe2d9c 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -1,5 +1,6 @@ #include "global.h" #include "battle_main.h" +#include "battle_script_commands.h" #include "bg.h" #include "data.h" #include "decompress.h" @@ -4021,8 +4022,8 @@ static void HighlightSubmenuScreenSelectBarItem(u8 a, u16 b) #define tPalTimer data[2] #define tMonSpriteId data[3] #define tIsShiny data[13] -#define tPersonalityLo data[14] -#define tPersonalityHi data[15] +#define tPersonalityLo 14 +#define tPersonalityHi 15 u8 DisplayCaughtMonDexPage(u16 species, bool32 isShiny, u32 personality) { @@ -4035,11 +4036,29 @@ u8 DisplayCaughtMonDexPage(u16 species, bool32 isShiny, u32 personality) gTasks[taskId].tState = 0; gTasks[taskId].tSpecies = species; gTasks[taskId].tIsShiny = isShiny; - gTasks[taskId].tPersonalityLo = personality; - gTasks[taskId].tPersonalityHi = personality >> 16; + gTasks[taskId].data[tPersonalityLo] = personality; + gTasks[taskId].data[tPersonalityHi] = personality >> 16; return taskId; } +static void LoadDexMonPalette(u32 taskId, bool32 isShiny) +{ + const u16 *paletteData = GetMonSpritePalFromSpeciesAndPersonality(gTasks[taskId].tSpecies, isShiny, GetWordTaskArg(taskId, tPersonalityLo)); + u32 paletteNum = gSprites[gTasks[taskId].tMonSpriteId].oam.paletteNum; + LoadPalette(paletteData, OBJ_PLTT_ID(paletteNum), PLTT_SIZE_4BPP); +} + +u32 Pokedex_CreateCaughtMonSprite(u32 species, s32 x, s32 y) +{ + u32 spriteId; + + SetMultiuseSpriteTemplateToPokemon(species, GetCatchingBattler()); + spriteId = CreateSprite(&gMultiuseSpriteTemplate, x, y, 0); + gSprites[spriteId].oam.priority = 0; + gSprites[spriteId].callback = SpriteCallbackDummy; + return spriteId; +} + static void Task_DisplayCaughtMonDexPage(u8 taskId) { u8 spriteId; @@ -4087,11 +4106,13 @@ static void Task_DisplayCaughtMonDexPage(u8 taskId) gTasks[taskId].tState++; break; case 4: - spriteId = CreateMonPicSprite(species, FALSE, ((u16)gTasks[taskId].tPersonalityHi << 16) | (u16)gTasks[taskId].tPersonalityLo, TRUE, MON_PAGE_X, MON_PAGE_Y, 0, TAG_NONE); + // We're using a different mon sprite creation method, because we don't have enough memory to safely use CreateMonPicSprite. + spriteId = Pokedex_CreateCaughtMonSprite(species, MON_PAGE_X, MON_PAGE_Y); + gTasks[taskId].tMonSpriteId = spriteId; + LoadDexMonPalette(taskId, FALSE); gSprites[spriteId].oam.priority = 0; BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK); SetVBlankCallback(gPokedexVBlankCB); - gTasks[taskId].tMonSpriteId = spriteId; gTasks[taskId].tState++; break; case 5: @@ -4138,9 +4159,6 @@ static void Task_ExitCaughtMonPage(u8 taskId) if (!gPaletteFade.active) { bool32 isShiny; - u32 personality; - u8 paletteNum; - const u16 *paletteData; void *buffer; SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_1D_MAP | DISPCNT_OBJ_ON); @@ -4153,10 +4171,7 @@ static void Task_ExitCaughtMonPage(u8 taskId) Free(buffer); isShiny = (bool8)gTasks[taskId].tIsShiny; - personality = ((u16)gTasks[taskId].tPersonalityHi << 16) | (u16)gTasks[taskId].tPersonalityLo; - paletteNum = gSprites[gTasks[taskId].tMonSpriteId].oam.paletteNum; - paletteData = GetMonSpritePalFromSpeciesAndPersonality(gTasks[taskId].tSpecies, isShiny, personality); - LoadPalette(paletteData, OBJ_PLTT_ID(paletteNum), PLTT_SIZE_4BPP); + LoadDexMonPalette(taskId, isShiny); DestroyTask(taskId); } } diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index 850aac3cf4..9a28f13a54 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -2421,6 +2421,8 @@ static bool8 LoadPokedexListPage(u8 page) // when returning to search results after selecting an evo, we have to restore // the original dexNum because the search results page doesn't rebuild the list sPokedexListItem->dexNum = sPokedexView->originalSearchSelectionNum; + sPokedexListItem->seen = GetSetPokedexFlag(sPokedexView->originalSearchSelectionNum, FLAG_GET_SEEN); + sPokedexListItem->owned = GetSetPokedexFlag(sPokedexView->originalSearchSelectionNum, FLAG_GET_CAUGHT); sPokedexView->originalSearchSelectionNum = 0; } CreateMonSpritesAtPos(sPokedexView->selectedPokemon, 0xE); @@ -4148,13 +4150,18 @@ void Task_DisplayCaughtMonDexPageHGSS(u8 taskId) gTasks[taskId].tState++; break; case 4: - spriteId = CreateMonSpriteFromNationalDexNumberHGSS(dexNum, MON_PAGE_X, MON_PAGE_Y, 0); - gSprites[spriteId].oam.priority = 0; + { + u32 personality = ((u16)gTasks[taskId].tPersonalityHi << 16) | (u16)gTasks[taskId].tPersonalityLo; + const u16 *paletteData = GetMonSpritePalFromSpeciesAndPersonality(species, FALSE, personality); + + spriteId = Pokedex_CreateCaughtMonSprite(species, MON_PAGE_X, MON_PAGE_Y); + LoadPalette(paletteData, OBJ_PLTT_ID(gSprites[spriteId].oam.paletteNum), PLTT_SIZE_4BPP); BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK); SetVBlankCallback(gPokedexVBlankCB); gTasks[taskId].tMonSpriteId = spriteId; gTasks[taskId].tState++; break; + } case 5: SetGpuReg(REG_OFFSET_BLDCNT, 0); SetGpuReg(REG_OFFSET_BLDALPHA, 0); @@ -4532,8 +4539,8 @@ static u32 GetPokedexMonPersonality(u16 species) static u16 CreateMonSpriteFromNationalDexNumberHGSS(u16 nationalNum, s16 x, s16 y, u16 paletteSlot) { - nationalNum = NationalPokedexNumToSpeciesHGSS(nationalNum); - return CreateMonPicSprite(nationalNum, FALSE, GetPokedexMonPersonality(nationalNum), TRUE, x, y, paletteSlot, TAG_NONE); + u32 species = NationalPokedexNumToSpeciesHGSS(nationalNum); + return CreateMonPicSprite(species, FALSE, GetPokedexMonPersonality(species), TRUE, x, y, paletteSlot, TAG_NONE); } static u16 GetPokemonScaleFromNationalDexNumber(u16 nationalNum) @@ -6306,6 +6313,17 @@ static void HandlePreEvolutionSpeciesPrint(u8 taskId, u16 preSpecies, u16 specie } } +static bool32 HasTwoPreEvolutions(u32 species) +{ + switch (species) + { + case SPECIES_GHOLDENGO: + return TRUE; + default: + return FALSE; + } +} + static u8 PrintPreEvolutions(u8 taskId, u16 species) { u16 i; @@ -6357,13 +6375,39 @@ static u8 PrintPreEvolutions(u8 taskId, u16 species) { if (evolutions[j].targetSpecies == species) { - preEvolutionOne = i; - numPreEvolutions += 1; - break; + if (numPreEvolutions == 0) + { + preEvolutionOne = i; + numPreEvolutions += 1; + if (!HasTwoPreEvolutions(species)) + break; + } + else + { + preEvolutionTwo = i; + numPreEvolutions += 1; + break; + } } } } + if (HasTwoPreEvolutions(species)) + { + CreateCaughtBallEvolutionScreen(preEvolutionOne, base_x - 9, base_y + base_y_offset*0, 0); + HandlePreEvolutionSpeciesPrint(taskId, preEvolutionOne, species, base_x, base_y, base_y_offset, 0); + + CreateCaughtBallEvolutionScreen(preEvolutionTwo, base_x - 9, base_y + base_y_offset*(numPreEvolutions - 1), 0); + HandlePreEvolutionSpeciesPrint(taskId, preEvolutionTwo, species, base_x, base_y, base_y_offset, numPreEvolutions - 1); + + sPokedexView->sEvoScreenData.targetSpecies[0] = preEvolutionOne; + sPokedexView->sEvoScreenData.targetSpecies[1] = preEvolutionTwo; + + sPokedexView->numPreEvolutions = numPreEvolutions; + sPokedexView->sEvoScreenData.numAllEvolutions += numPreEvolutions; + return numPreEvolutions; + } + //Calculate if previous evolution also has a previous evolution if (numPreEvolutions != 0) { diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 359ab75e41..d4fba87859 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -332,7 +332,7 @@ void SetTeraType(struct ScriptContext *ctx) * if side/slot are assigned, it will create the mon at the assigned party location * if slot == PARTY_SIZE, it will give the mon to first available party or storage slot */ -static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u16 item, enum PokeBall ball, u8 nature, u8 abilityNum, u8 gender, u8 *evs, u8 *ivs, u16 *moves, bool8 isShiny, bool8 gmaxFactor, u8 teraType, u8 dmaxLevel) +static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u16 item, enum PokeBall ball, u8 nature, u8 abilityNum, u8 gender, u8 *evs, u8 *ivs, u16 *moves, enum ShinyMode shinyMode, bool8 gmaxFactor, u8 teraType, u8 dmaxLevel) { enum NationalDexOrder nationalDexNum; int sentToPc; @@ -340,6 +340,7 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u u32 i; u8 genderRatio = gSpeciesInfo[species].genderRatio; u16 targetSpecies; + bool32 isShiny; // check whether to use a specific nature or a random one if (nature >= NUM_NATURES) @@ -360,10 +361,13 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u CreateMonWithNature(&mon, species, level, 32, nature); // shininess - if (P_FLAG_FORCE_SHINY != 0 && FlagGet(P_FLAG_FORCE_SHINY)) + if (shinyMode == SHINY_MODE_ALWAYS || (P_FLAG_FORCE_SHINY != 0 && FlagGet(P_FLAG_FORCE_SHINY))) isShiny = TRUE; - else if (P_FLAG_FORCE_NO_SHINY != 0 && FlagGet(P_FLAG_FORCE_NO_SHINY)) + else if (shinyMode == SHINY_MODE_NEVER || (P_FLAG_FORCE_NO_SHINY != 0 && FlagGet(P_FLAG_FORCE_NO_SHINY))) isShiny = FALSE; + else + isShiny = GetMonData(&mon, MON_DATA_IS_SHINY); + SetMonData(&mon, MON_DATA_IS_SHINY, &isShiny); // gigantamax factor @@ -479,7 +483,7 @@ u32 ScriptGiveMon(u16 species, u8 level, u16 item) MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1}; // ScriptGiveMonParameterized won't touch the stats' IV. u16 moves[MAX_MON_MOVES] = {MOVE_NONE, MOVE_NONE, MOVE_NONE, MOVE_NONE}; - return ScriptGiveMonParameterized(0, PARTY_SIZE, species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_PERSONALITY, MON_GENDERLESS, evs, ivs, moves, FALSE, FALSE, NUMBER_OF_MON_TYPES, 0); + return ScriptGiveMonParameterized(0, PARTY_SIZE, species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_PERSONALITY, MON_GENDERLESS, evs, ivs, moves, SHINY_MODE_RANDOM, FALSE, NUMBER_OF_MON_TYPES, 0); } #define PARSE_FLAG(n, default_) (flags & (1 << (n))) ? VarGet(ScriptReadHalfword(ctx)) : (default_) @@ -542,20 +546,20 @@ void ScrCmd_createmon(struct ScriptContext *ctx) } } } - hpIv = PARSE_FLAG(11, hpIv); - atkIv = PARSE_FLAG(12, atkIv); - defIv = PARSE_FLAG(13, defIv); - speedIv = PARSE_FLAG(14, speedIv); - spAtkIv = PARSE_FLAG(15, spAtkIv); - spDefIv = PARSE_FLAG(16, spDefIv); - u16 move1 = PARSE_FLAG(17, MOVE_NONE); - u16 move2 = PARSE_FLAG(18, MOVE_NONE); - u16 move3 = PARSE_FLAG(19, MOVE_NONE); - u16 move4 = PARSE_FLAG(20, MOVE_NONE); - bool8 isShiny = PARSE_FLAG(21, FALSE); - bool8 gmaxFactor = PARSE_FLAG(22, FALSE); - u8 teraType = PARSE_FLAG(23, NUMBER_OF_MON_TYPES); - u8 dmaxLevel = PARSE_FLAG(24, 0); + hpIv = PARSE_FLAG(11, hpIv); + atkIv = PARSE_FLAG(12, atkIv); + defIv = PARSE_FLAG(13, defIv); + speedIv = PARSE_FLAG(14, speedIv); + spAtkIv = PARSE_FLAG(15, spAtkIv); + spDefIv = PARSE_FLAG(16, spDefIv); + u16 move1 = PARSE_FLAG(17, MOVE_NONE); + u16 move2 = PARSE_FLAG(18, MOVE_NONE); + u16 move3 = PARSE_FLAG(19, MOVE_NONE); + u16 move4 = PARSE_FLAG(20, MOVE_NONE); + enum ShinyMode shinyMode = PARSE_FLAG(21, SHINY_MODE_RANDOM); + bool8 gmaxFactor = PARSE_FLAG(22, FALSE); + u8 teraType = PARSE_FLAG(23, NUMBER_OF_MON_TYPES); + u8 dmaxLevel = PARSE_FLAG(24, 0); u8 evs[NUM_STATS] = {hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv}; u8 ivs[NUM_STATS] = {hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv}; @@ -566,7 +570,7 @@ void ScrCmd_createmon(struct ScriptContext *ctx) else Script_RequestEffects(SCREFF_V1); - gSpecialVar_Result = ScriptGiveMonParameterized(side, slot, species, level, item, ball, nature, abilityNum, gender, evs, ivs, moves, isShiny, gmaxFactor, teraType, dmaxLevel); + gSpecialVar_Result = ScriptGiveMonParameterized(side, slot, species, level, item, ball, nature, abilityNum, gender, evs, ivs, moves, shinyMode, gmaxFactor, teraType, dmaxLevel); } #undef PARSE_FLAG diff --git a/src/window.c b/src/window.c index 65419ff77b..51b473e89c 100644 --- a/src/window.c +++ b/src/window.c @@ -5,14 +5,10 @@ #include "blit.h" #include "decompress.h" -// This global is set to 0 and never changed. -COMMON_DATA u8 gTransparentTileNumber = 0; COMMON_DATA void *gWindowBgTilemapBuffers[NUM_BACKGROUNDS] = {0}; extern u32 gWindowTileAutoAllocEnabled; EWRAM_DATA struct Window gWindows[WINDOWS_MAX] = {0}; -EWRAM_DATA static struct Window *sWindowPtr = NULL; -EWRAM_DATA static u16 sWindowSize = 0; static u32 GetNumActiveWindowsOnBg(u32 bgId); static u32 GetNumActiveWindowsOnBg8Bit(u32 bgId); @@ -103,7 +99,6 @@ bool32 InitWindows(const struct WindowTemplate *templates) } } - gTransparentTileNumber = 0; return TRUE; } @@ -375,7 +370,7 @@ void ClearWindowTilemap(u32 windowId) FillBgTilemapBufferRect( windowLocal.window.bg, - gTransparentTileNumber, + 0, windowLocal.window.tilemapLeft, windowLocal.window.tilemapTop, windowLocal.window.width, @@ -698,20 +693,20 @@ void BlitBitmapRectToWindow4BitTo8Bit(u32 windowId, const u8 *pixels, u16 srcX, void CopyWindowToVram8Bit(u32 windowId, u8 mode) { - sWindowPtr = &gWindows[windowId]; - sWindowSize = 64 * (sWindowPtr->window.width * sWindowPtr->window.height); + struct Window *window = &gWindows[windowId]; + u16 windowSize = 64 * (window->window.width * window->window.height); switch (mode) { case COPYWIN_MAP: - CopyBgTilemapBufferToVram(sWindowPtr->window.bg); + CopyBgTilemapBufferToVram(window->window.bg); break; case COPYWIN_GFX: - LoadBgTiles(sWindowPtr->window.bg, sWindowPtr->tileData, sWindowSize, sWindowPtr->window.baseBlock); + LoadBgTiles(window->window.bg, window->tileData, windowSize, window->window.baseBlock); break; case COPYWIN_FULL: - LoadBgTiles(sWindowPtr->window.bg, sWindowPtr->tileData, sWindowSize, sWindowPtr->window.baseBlock); - CopyBgTilemapBufferToVram(sWindowPtr->window.bg); + LoadBgTiles(window->window.bg, window->tileData, windowSize, window->window.baseBlock); + CopyBgTilemapBufferToVram(window->window.bg); break; } } diff --git a/test/battle/ability/battery.c b/test/battle/ability/battery.c index cecfa81d0c..7ddc0b837c 100644 --- a/test/battle/ability/battery.c +++ b/test/battle/ability/battery.c @@ -1,5 +1,51 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Battery increases Sp. Attack damage of allies by ~30%"); // 5325/4096 -TO_DO_BATTLE_TEST("Battery does not increase its own Sp. Attack damage"); +DOUBLE_BATTLE_TEST("Battery increases Sp. Attack damage of allies by ~30%") +{ + s16 damage[2]; + + GIVEN { + PLAYER(SPECIES_CHARJABUG) { Speed(1); Ability(ABILITY_BATTERY); } + PLAYER(SPECIES_WOBBUFFET) { Speed(2); Moves(MOVE_SHOCK_WAVE); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(4); Moves(MOVE_CELEBRATE, MOVE_GASTRO_ACID); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(3); } + } WHEN { + TURN { MOVE(playerRight, MOVE_SHOCK_WAVE, target: opponentLeft); } + TURN { MOVE(opponentLeft, MOVE_GASTRO_ACID, target: playerLeft); MOVE(playerRight, MOVE_SHOCK_WAVE, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, playerRight); + HP_BAR(opponentLeft, captureDamage: &damage[0]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_GASTRO_ACID, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, playerRight); + HP_BAR(opponentLeft, captureDamage: &damage[1]); + } THEN { + EXPECT_MUL_EQ(damage[1], UQ_4_12(1.3), damage[0]); + } + +} + +DOUBLE_BATTLE_TEST("Battery does not increase its own Sp. Attack damage") +{ + s16 damage[2]; + + GIVEN { + PLAYER(SPECIES_CHARJABUG) { Speed(1); Ability(ABILITY_BATTERY); } + PLAYER(SPECIES_WOBBUFFET) { Speed(2); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(4); Moves(MOVE_CELEBRATE, MOVE_GASTRO_ACID); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(3); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_SHOCK_WAVE, target: opponentLeft); } + TURN { MOVE(opponentLeft, MOVE_GASTRO_ACID, target: playerLeft); MOVE(playerLeft, MOVE_SHOCK_WAVE, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[0]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_GASTRO_ACID, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[1]); + } THEN { + EXPECT_EQ(damage[0], damage[1]); + } +} diff --git a/test/battle/ability/color_change.c b/test/battle/ability/color_change.c index 4c07926502..49eaa3e916 100644 --- a/test/battle/ability/color_change.c +++ b/test/battle/ability/color_change.c @@ -154,6 +154,26 @@ SINGLE_BATTLE_TEST("Color Change changes the type to Normal when a Pokemon is hi } } +SINGLE_BATTLE_TEST("Color Change does not change the type to Normal when a Pokemon is hit by Struggle") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_SOAK); } + TURN { MOVE(player, MOVE_STRUGGLE); } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SOAK, player); + MESSAGE("The opposing Kecleon transformed into the Water type!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("The opposing Kecleon's Color Change made it the Normal type!"); + } + } +} + SINGLE_BATTLE_TEST("Color Change does not activate if move is boosted by Sheer Force") { GIVEN { diff --git a/test/battle/ability/cursed_body.c b/test/battle/ability/cursed_body.c index 25dbdc4b5b..b3bc886a3d 100644 --- a/test/battle/ability/cursed_body.c +++ b/test/battle/ability/cursed_body.c @@ -16,10 +16,77 @@ SINGLE_BATTLE_TEST("Cursed Body triggers 30% of the time") } } -TO_DO_BATTLE_TEST("Cursed Body cannot disable Struggle") -TO_DO_BATTLE_TEST("Cursed Body can trigger if the attacker is behind a Substitute") -TO_DO_BATTLE_TEST("Cursed Body cannot trigger if the target is behind a Substitute") -TO_DO_BATTLE_TEST("Cursed Body does not stop a multistrike move mid-execution") +SINGLE_BATTLE_TEST("Cursed Body cannot disable Struggle") +{ + GIVEN { + ASSUME(GetItemHoldEffect(ITEM_CHOICE_SCARF) == HOLD_EFFECT_CHOICE_SCARF); + ASSUME(GetMoveEffect(MOVE_TAUNT) == EFFECT_TAUNT); + ASSUME(GetMoveCategory(MOVE_CELEBRATE) == DAMAGE_CATEGORY_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_CHOICE_SCARF); Moves(MOVE_CELEBRATE); } + OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TAUNT); } + TURN { FORCED_MOVE(player); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_CURSED_BODY); + MESSAGE("Wobbuffet's Struggle was disabled by the opposing Frillish's Cursed Body!"); + } + } +} + +SINGLE_BATTLE_TEST("Cursed Body can trigger if the attacker is behind a Substitute") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_SUBSTITUTE); } + TURN { MOVE(player, MOVE_AQUA_JET); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_JET, player); + ABILITY_POPUP(opponent, ABILITY_CURSED_BODY); + MESSAGE("Wobbuffet's Aqua Jet was disabled by the opposing Frillish's Cursed Body!"); + } +} + +SINGLE_BATTLE_TEST("Cursed Body cannot trigger if the target is behind a Substitute") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); } + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); } + TURN { MOVE(player, MOVE_AQUA_JET); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_JET, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_CURSED_BODY); + MESSAGE("Wobbuffet's Aqua Jet was disabled by the opposing Frillish's Cursed Body!"); + } + } +} + +SINGLE_BATTLE_TEST("Cursed Body does not stop a multistrike move mid-execution") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ROCK_BLAST) == EFFECT_MULTI_HIT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_ROCK_BLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_BLAST, player); + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_CURSED_BODY); + MESSAGE("Wobbuffet's Rock Blast was disabled by the opposing Frillish's Cursed Body!"); + HP_BAR(opponent); + } +} + TO_DO_BATTLE_TEST("Cursed Body disables the move that called another move instead of the called move") TO_DO_BATTLE_TEST("Cursed Body disables damaging Z-Moves, but not the base move") // Rotom Powers can restore Z-Moves TO_DO_BATTLE_TEST("Cursed Body disables the base move of a status Z-Move") diff --git a/test/battle/ability/protean.c b/test/battle/ability/protean.c index c5d141d244..fe4ae25e60 100644 --- a/test/battle/ability/protean.c +++ b/test/battle/ability/protean.c @@ -54,3 +54,19 @@ SINGLE_BATTLE_TEST("Protean changes the type of the user only once per switch in ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent); } } + +SINGLE_BATTLE_TEST("Protean does not change the user's type when using Struggle") +{ + GIVEN { + PLAYER(SPECIES_REGIROCK); + OPPONENT(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); } + } WHEN { + TURN { MOVE(opponent, MOVE_STRUGGLE); } + } SCENE { + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_PROTEAN); + MESSAGE("The opposing Greninja transformed into the Normal type!"); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, opponent); + } +} diff --git a/test/battle/ability/steadfast.c b/test/battle/ability/steadfast.c index fc35e94278..2d6fb5d901 100644 --- a/test/battle/ability/steadfast.c +++ b/test/battle/ability/steadfast.c @@ -1,4 +1,56 @@ #include "global.h" #include "test/battle.h" +SINGLE_BATTLE_TEST("Steadfast boosts Speed when the user attempts to move but is flinched") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffectWithChance(MOVE_FAKE_OUT, MOVE_EFFECT_FLINCH, 100)); + PLAYER(SPECIES_LUCARIO) { Ability(ABILITY_STEADFAST); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_FAKE_OUT); MOVE(player, MOVE_CELEBRATE); } + } SCENE { + ABILITY_POPUP(player, ABILITY_STEADFAST); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("Steadfast doesn't activate if the user wasn't flinched") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffectWithChance(MOVE_FAKE_OUT, MOVE_EFFECT_FLINCH, 100)); + ASSUME(GetItemHoldEffect(ITEM_COVERT_CLOAK) == HOLD_EFFECT_COVERT_CLOAK); + PLAYER(SPECIES_LUCARIO) { Ability(ABILITY_STEADFAST); Item(ITEM_COVERT_CLOAK); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_FAKE_OUT); MOVE(player, MOVE_CELEBRATE); } + } SCENE { + NOT ABILITY_POPUP(player, ABILITY_STEADFAST); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + } +} + +DOUBLE_BATTLE_TEST("Steadfast doesn't activate if the user has already moved") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_BITE, MOVE_EFFECT_FLINCH)); + ASSUME(GetMoveEffect(MOVE_INSTRUCT) == EFFECT_INSTRUCT); + PLAYER(SPECIES_LUCARIO) { Ability(ABILITY_STEADFAST); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_SWORDS_DANCE); MOVE(opponentLeft, MOVE_BITE, target: playerLeft); MOVE(playerRight, MOVE_INSTRUCT, target: playerLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, playerLeft); + NOT ABILITY_POPUP(playerLeft, ABILITY_STEADFAST); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + } +} + TO_DO_BATTLE_TEST("TODO: Write Steadfast (Ability) test titles") diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c index f531f07f4d..1e10b7fc1c 100644 --- a/test/battle/ai/ai_switching.c +++ b/test/battle/ai/ai_switching.c @@ -40,6 +40,28 @@ AI_SINGLE_BATTLE_TEST("AI switches if Perish Song is about to kill") } } +AI_SINGLE_BATTLE_TEST("AI sees on-field player ability correctly and does not see previous Pokémon's ability after player uses a pivot move when choosing a post-KO switch") +{ + u32 testAbility; + PARAMETRIZE { testAbility = ABILITY_WATER_ABSORB; } + PARAMETRIZE { testAbility = ABILITY_VOLT_ABSORB; } + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_PIKACHU) {Level(100); Moves(MOVE_VOLT_SWITCH, MOVE_SPARKLY_SWIRL); Ability(ABILITY_LIGHTNING_ROD); }; + PLAYER(SPECIES_LANTURN) {Level(44); Moves(MOVE_SCALD); Ability(testAbility); }; + OPPONENT(SPECIES_SOBBLE) {Level(44); Moves(MOVE_SCRATCH); } + OPPONENT(SPECIES_BOMBIRDIER) {Level(42); Moves(MOVE_ROCK_SLIDE); } + OPPONENT(SPECIES_IRON_THORNS) {Level(43); Moves(MOVE_SUPERCELL_SLAM, MOVE_ICE_PUNCH); } + } WHEN { + TURN { + MOVE(player, MOVE_VOLT_SWITCH); + SEND_OUT(player, 1); + EXPECT_MOVE(opponent, MOVE_SCRATCH); + testAbility == ABILITY_WATER_ABSORB ? EXPECT_SEND_OUT(opponent, 2) : EXPECT_SEND_OUT(opponent, 1); + } + } +} + AI_DOUBLE_BATTLE_TEST("AI will not try to switch for the same Pokémon for 2 spots in a double battle (all bad moves)") { u32 flags; @@ -1400,3 +1422,20 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: Fake Out style moves won't confu TURN { MOVE(player, MOVE_SCRATCH); EXPECT_MOVE(opponent, MOVE_CLOSE_COMBAT); } } } + +AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will consider choice-locked player priority when determining which mon to send out") +{ + u32 item; + PARAMETRIZE { item = ITEM_NONE; } + PARAMETRIZE { item = ITEM_CHOICE_BAND; } + GIVEN { + ASSUME(gItemsInfo[ITEM_CHOICE_BAND].holdEffect == HOLD_EFFECT_CHOICE_BAND); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_LYCANROC) { Speed(5); Moves(MOVE_ACCELEROCK, MOVE_MIGHTY_CLEAVE); Item(item); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(4); HP(1); Moves(MOVE_TACKLE); } + OPPONENT(SPECIES_DECIDUEYE_HISUI) { Speed(4); Moves(MOVE_LEAF_BLADE); } + OPPONENT(SPECIES_PHEROMOSA) { Speed(6); HP(1); Moves(MOVE_EARTHQUAKE); } + } WHEN { + TURN { MOVE(player, MOVE_MIGHTY_CLEAVE); EXPECT_MOVE(opponent, MOVE_TACKLE); item == ITEM_NONE ? EXPECT_SEND_OUT(opponent, 1) : EXPECT_SEND_OUT(opponent, 2); } + } +} diff --git a/test/battle/hold_effect/gems.c b/test/battle/hold_effect/gems.c index b2d843e1d5..b597beeab7 100644 --- a/test/battle/hold_effect/gems.c +++ b/test/battle/hold_effect/gems.c @@ -26,6 +26,34 @@ SINGLE_BATTLE_TEST("Gem is consumed when it corresponds to the type of a move") } } +SINGLE_BATTLE_TEST("Gem is not consumed when using Struggle", s16 damage) +{ + u32 item = 0; + + PARAMETRIZE { item = ITEM_NONE; } + PARAMETRIZE { item = ITEM_NORMAL_GEM; } + + GIVEN { + if (item != ITEM_NONE) { + ASSUME(GetItemHoldEffect(item) == HOLD_EFFECT_GEMS); + ASSUME(GetItemSecondaryId(item) == GetMoveType(MOVE_STRUGGLE)); + } + PLAYER(SPECIES_WOBBUFFET) { Item(item); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_STRUGGLE); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("The Normal Gem strengthened Wobbuffet's power!"); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} + SINGLE_BATTLE_TEST("Gem boost is only applied once") { s16 boostedHit; diff --git a/test/battle/hold_effect/type_power.c b/test/battle/hold_effect/type_power.c index 5afe2a39c8..c03d2487d5 100644 --- a/test/battle/hold_effect/type_power.c +++ b/test/battle/hold_effect/type_power.c @@ -53,3 +53,27 @@ SINGLE_BATTLE_TEST("Type-enhancing items increase the base power of moves by 20% } } } + +SINGLE_BATTLE_TEST("Type-enhancing items do not increase the power of Struggle", s16 damage) +{ + u32 item = 0; + + PARAMETRIZE { item = ITEM_NONE; } + PARAMETRIZE { item = ITEM_SILK_SCARF; } + + GIVEN { + if (item != ITEM_NONE) { + ASSUME(GetItemHoldEffect(item) == HOLD_EFFECT_TYPE_POWER); + ASSUME(GetItemSecondaryId(item) == GetMoveType(MOVE_STRUGGLE)); + } + PLAYER(SPECIES_WOBBUFFET) { Item(item); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_STRUGGLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} diff --git a/test/battle/hold_effect/weakness_berry.c b/test/battle/hold_effect/weakness_berry.c new file mode 100644 index 0000000000..17adb08aa3 --- /dev/null +++ b/test/battle/hold_effect/weakness_berry.c @@ -0,0 +1,124 @@ +#include "global.h" +#include "test/battle.h" + +static const u16 sMoveItemTable[][4] = +{ + { TYPE_NORMAL, MOVE_SCRATCH, ITEM_CHILAN_BERRY, SPECIES_WOBBUFFET }, + { TYPE_FIGHTING, MOVE_KARATE_CHOP, ITEM_CHOPLE_BERRY, SPECIES_RAMPARDOS }, + { TYPE_FLYING, MOVE_WING_ATTACK, ITEM_COBA_BERRY, SPECIES_HARIYAMA }, + { TYPE_POISON, MOVE_POISON_STING, ITEM_KEBIA_BERRY, SPECIES_GOGOAT }, + { TYPE_GROUND, MOVE_MUD_SHOT, ITEM_SHUCA_BERRY, SPECIES_RAMPARDOS }, + { TYPE_ROCK, MOVE_ROCK_THROW, ITEM_CHARTI_BERRY, SPECIES_CORVISQUIRE }, + { TYPE_BUG, MOVE_BUG_BITE, ITEM_TANGA_BERRY, SPECIES_WOBBUFFET }, + { TYPE_GHOST, MOVE_SHADOW_PUNCH, ITEM_KASIB_BERRY, SPECIES_WOBBUFFET }, + { TYPE_STEEL, MOVE_METAL_CLAW, ITEM_BABIRI_BERRY, SPECIES_RAMPARDOS }, + { TYPE_FIRE, MOVE_EMBER, ITEM_OCCA_BERRY, SPECIES_GOGOAT }, + { TYPE_WATER, MOVE_WATER_GUN, ITEM_PASSHO_BERRY, SPECIES_RAMPARDOS }, + { TYPE_GRASS, MOVE_VINE_WHIP, ITEM_RINDO_BERRY, SPECIES_RAMPARDOS }, + { TYPE_ELECTRIC, MOVE_THUNDER_SHOCK, ITEM_WACAN_BERRY, SPECIES_CORVISQUIRE }, + { TYPE_PSYCHIC, MOVE_CONFUSION, ITEM_PAYAPA_BERRY, SPECIES_HARIYAMA }, + { TYPE_ICE, MOVE_AURORA_BEAM, ITEM_YACHE_BERRY, SPECIES_DRAGONAIR }, + { TYPE_DRAGON, MOVE_DRAGON_BREATH, ITEM_HABAN_BERRY, SPECIES_DRAGONAIR }, + { TYPE_DARK, MOVE_BITE, ITEM_COLBUR_BERRY, SPECIES_WOBBUFFET }, + { TYPE_FAIRY, MOVE_DISARMING_VOICE, ITEM_ROSELI_BERRY, SPECIES_DRAGONAIR }, +}; + +SINGLE_BATTLE_TEST("Weakness berries decrease the base power of moves by half", s16 damage) +{ + u32 move = 0, item = 0, type = 0, defender = 0; + + for (u32 j = 0; j < ARRAY_COUNT(sMoveItemTable); j++) + { + PARAMETRIZE { type = sMoveItemTable[j][0]; move = sMoveItemTable[j][1]; defender = sMoveItemTable[j][3]; item = ITEM_NONE; } + PARAMETRIZE { type = sMoveItemTable[j][0]; move = sMoveItemTable[j][1]; defender = sMoveItemTable[j][3]; item = sMoveItemTable[j][2]; } + } + + GIVEN { + ASSUME(GetMovePower(move) > 0); + ASSUME(GetMoveType(move) == type); + ASSUME(GetSpeciesType(defender, 0) == GetSpeciesType(defender, 1)); + if (type != TYPE_NORMAL) { + ASSUME(gTypeEffectivenessTable[type][GetSpeciesType(defender, 0)] > UQ_4_12(1.0)); + } + if (item != ITEM_NONE) { + ASSUME(GetItemHoldEffect(item) == HOLD_EFFECT_RESIST_BERRY); + ASSUME(GetItemHoldEffectParam(item) == type); + } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(defender) { Item(item); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + if (1 == i % 2) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + } + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + for (u32 j = 0; j < ARRAY_COUNT(sMoveItemTable); j++) { + EXPECT_MUL_EQ(results[j*2].damage, Q_4_12(0.5), results[(j*2)+1].damage); + } + } +} + +SINGLE_BATTLE_TEST("Weakness berries do not activate unless a move is super effective", s16 damage) +{ + u32 move = 0, item = 0, type = 0, defender = 0; + + for (u32 j = 0; j < ARRAY_COUNT(sMoveItemTable); j++) + { + if (TYPE_NORMAL == type) + { + // ITEM_CHILAN_BERRY activates without a weakness + } + else if (TYPE_FAIRY == type) + { + PARAMETRIZE { type = sMoveItemTable[j][0]; move = sMoveItemTable[j][1]; item = sMoveItemTable[j][2]; defender = SPECIES_WOBBUFFET; } + } + else + { + PARAMETRIZE { type = sMoveItemTable[j][0]; move = sMoveItemTable[j][1]; item = sMoveItemTable[j][2]; defender = SPECIES_SABLEYE; } + } + } + + GIVEN { + ASSUME(GetMovePower(move) > 0); + ASSUME(uq4_12_multiply(gTypeEffectivenessTable[type][GetSpeciesType(defender, 0)], + gTypeEffectivenessTable[type][GetSpeciesType(defender, 1)]) <= UQ_4_12(1.0)); + ASSUME(GetItemHoldEffect(item) == HOLD_EFFECT_RESIST_BERRY); + ASSUME(GetItemHoldEffectParam(item) == type); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(defender) { Item(item); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + } +} + +SINGLE_BATTLE_TEST("Weakness berries do not decrease the power of Struggle", s16 damage) +{ + u32 item = 0; + + PARAMETRIZE { item = ITEM_NONE; } + PARAMETRIZE { item = ITEM_CHILAN_BERRY; } + + GIVEN { + if (item != ITEM_NONE) { + ASSUME(GetItemHoldEffect(item) == HOLD_EFFECT_RESIST_BERRY); + ASSUME(GetItemHoldEffectParam(item) == TYPE_NORMAL); + } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_STRUGGLE); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + MESSAGE("The Chilan Berry weakened the damage to the opposing Wobbuffet!"); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} diff --git a/test/battle/move_effect/knock_off.c b/test/battle/move_effect/knock_off.c index 3fdeadcecc..eb0874d7bf 100644 --- a/test/battle/move_effect/knock_off.c +++ b/test/battle/move_effect/knock_off.c @@ -6,6 +6,33 @@ ASSUMPTIONS ASSUME(GetMoveEffect(MOVE_KNOCK_OFF) == EFFECT_KNOCK_OFF); } +WILD_BATTLE_TEST("Knock Off does not remove item when used by Wild Pokemon (Gen 5+)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LEFTOVERS); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_EVIOLITE); } + } WHEN { + TURN { MOVE(opponent, MOVE_KNOCK_OFF); } + TURN { MOVE(player, MOVE_KNOCK_OFF); } + } SCENE { + // Turn 1 + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, opponent); + if (B_KNOCK_OFF_REMOVAL >= GEN_5) + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player); + else + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player); + // Turn 2 + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, opponent); + } THEN { + EXPECT(player->item == ITEM_LEFTOVERS); + if (B_KNOCK_OFF_REMOVAL >= GEN_5) + EXPECT(opponent->item == ITEM_NONE); + else + EXPECT(opponent->item == ITEM_EVIOLITE); + } +} + SINGLE_BATTLE_TEST("Knock Off knocks a healing berry before it has the chance to activate") { GIVEN { diff --git a/test/battle/move_effect/misty_terrain.c b/test/battle/move_effect/misty_terrain.c index b96f0c650d..cd9a92eb37 100644 --- a/test/battle/move_effect/misty_terrain.c +++ b/test/battle/move_effect/misty_terrain.c @@ -87,3 +87,16 @@ SINGLE_BATTLE_TEST("Misty Terrain lasts for 5 turns") MESSAGE("The mist disappeared from the battlefield."); } } + +SINGLE_BATTLE_TEST("Misty Terrain will fail if there is one already on the field") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MISTY_TERRAIN); MOVE(opponent, MOVE_MISTY_TERRAIN); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MISTY_TERRAIN, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_MISTY_TERRAIN, opponent); + } +} diff --git a/test/battle/move_effect/struggle.c b/test/battle/move_effect/struggle.c new file mode 100644 index 0000000000..5bf9de6207 --- /dev/null +++ b/test/battle/move_effect/struggle.c @@ -0,0 +1,75 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Struggle deals recoil 1/4 of damage dealt (Gen 2-3)") + +SINGLE_BATTLE_TEST("Struggle deals recoil 1/4 of user's hp (Gen 4+)") +{ + ASSUME(GetMoveEffect(MOVE_STRUGGLE) == EFFECT_STRUGGLE); + + s16 recoil; + u32 atkStat = 0; + u32 hpStat = 0; + + PARAMETRIZE { atkStat = 100; hpStat = 200; } + PARAMETRIZE { atkStat = 50; hpStat = 200; } + PARAMETRIZE { atkStat = 100; hpStat = 300; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { MaxHP(hpStat); HP(hpStat); Attack(atkStat); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_STRUGGLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + HP_BAR(player, captureDamage: &recoil); + } THEN { + EXPECT_MUL_EQ(hpStat, Q_4_12(0.25), recoil); + } +} + +SINGLE_BATTLE_TEST("Struggle can hit ghost types") +{ + ASSUME(GetSpeciesType(SPECIES_DRIFBLIM, 0) == TYPE_GHOST); + + s16 damage; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_DRIFBLIM); + } WHEN { + TURN { MOVE(player, MOVE_STRUGGLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + HP_BAR(opponent, captureDamage: &damage); + } THEN { + EXPECT_NE(0, damage); + } +} + +SINGLE_BATTLE_TEST("Struggle does not receive normal-type STAB") +{ + // Compare with Cut, which does receive normal-type STAB + ASSUME(GetSpeciesType(SPECIES_ZANGOOSE, 0) == GetMoveType(MOVE_STRUGGLE)); + ASSUME(GetMovePower(MOVE_CUT) == GetMovePower(MOVE_STRUGGLE)); + ASSUME(GetMoveCategory(MOVE_CUT) == GetMoveCategory(MOVE_STRUGGLE)); + ASSUME(GetMoveType(MOVE_CUT) == GetMoveType(MOVE_STRUGGLE)); + + s16 cutDamage; + s16 struggleDamage; + + GIVEN { + PLAYER(SPECIES_ZANGOOSE); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CUT); } + TURN { MOVE(player, MOVE_STRUGGLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CUT, player); + HP_BAR(opponent, captureDamage: &cutDamage); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + HP_BAR(opponent, captureDamage: &struggleDamage); + } THEN { + EXPECT_MUL_EQ(struggleDamage, Q_4_12(1.5), cutDamage); + } +} diff --git a/test/pokemon.c b/test/pokemon.c index 61c9e86c04..6079bd28bd 100644 --- a/test/pokemon.c +++ b/test/pokemon.c @@ -304,7 +304,7 @@ TEST("givemon [all]") ZeroPlayerPartyMons(); RUN_OVERWORLD_SCRIPT( - givemon SPECIES_WOBBUFFET, 100, item=ITEM_LEFTOVERS, ball=ITEM_MASTER_BALL, nature=NATURE_BOLD, abilityNum=2, gender=MON_MALE, hpEv=1, atkEv=2, defEv=3, speedEv=4, spAtkEv=5, spDefEv=6, hpIv=7, atkIv=8, defIv=9, speedIv=10, spAtkIv=11, spDefIv=12, move1=MOVE_SCRATCH, move2=MOVE_SPLASH, move3=MOVE_CELEBRATE, move4=MOVE_EXPLOSION, isShiny=TRUE, gmaxFactor=TRUE, teraType=TYPE_FIRE, dmaxLevel=7; + givemon SPECIES_WOBBUFFET, 100, item=ITEM_LEFTOVERS, ball=ITEM_MASTER_BALL, nature=NATURE_BOLD, abilityNum=2, gender=MON_MALE, hpEv=1, atkEv=2, defEv=3, speedEv=4, spAtkEv=5, spDefEv=6, hpIv=7, atkIv=8, defIv=9, speedIv=10, spAtkIv=11, spDefIv=12, move1=MOVE_SCRATCH, move2=MOVE_SPLASH, move3=MOVE_CELEBRATE, move4=MOVE_EXPLOSION, shinyMode=SHINY_MODE_ALWAYS, gmaxFactor=TRUE, teraType=TYPE_FIRE, dmaxLevel=7; ); EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_WOBBUFFET); @@ -363,13 +363,13 @@ TEST("givemon [vars]") VarSet(VAR_TEMP_6, MOVE_SPLASH); VarSet(VAR_TEMP_7, MOVE_CELEBRATE); VarSet(VAR_TEMP_8, MOVE_EXPLOSION); - VarSet(VAR_TEMP_9, TRUE); + VarSet(VAR_TEMP_9, SHINY_MODE_ALWAYS); VarSet(VAR_TEMP_A, TRUE); VarSet(VAR_TEMP_B, TYPE_FIRE); VarSet(VAR_TEMP_E, 7); RUN_OVERWORLD_SCRIPT( - givemon VAR_TEMP_C, VAR_TEMP_D, item=VAR_0x8000, ball=VAR_0x8001, nature=VAR_0x8002, abilityNum=VAR_0x8003, gender=VAR_0x8004, hpEv=VAR_0x8005, atkEv=VAR_0x8006, defEv=VAR_0x8007, speedEv=VAR_0x8008, spAtkEv=VAR_0x8009, spDefEv=VAR_0x800A, hpIv=VAR_0x800B, atkIv=VAR_TEMP_0, defIv=VAR_TEMP_1, speedIv=VAR_TEMP_2, spAtkIv=VAR_TEMP_3, spDefIv=VAR_TEMP_4, move1=VAR_TEMP_5, move2=VAR_TEMP_6, move3=VAR_TEMP_7, move4=VAR_TEMP_8, isShiny=VAR_TEMP_9, gmaxFactor=VAR_TEMP_A, teraType=VAR_TEMP_B, dmaxLevel=VAR_TEMP_E; + givemon VAR_TEMP_C, VAR_TEMP_D, item=VAR_0x8000, ball=VAR_0x8001, nature=VAR_0x8002, abilityNum=VAR_0x8003, gender=VAR_0x8004, hpEv=VAR_0x8005, atkEv=VAR_0x8006, defEv=VAR_0x8007, speedEv=VAR_0x8008, spAtkEv=VAR_0x8009, spDefEv=VAR_0x800A, hpIv=VAR_0x800B, atkIv=VAR_TEMP_0, defIv=VAR_TEMP_1, speedIv=VAR_TEMP_2, spAtkIv=VAR_TEMP_3, spDefIv=VAR_TEMP_4, move1=VAR_TEMP_5, move2=VAR_TEMP_6, move3=VAR_TEMP_7, move4=VAR_TEMP_8, shinyMode=VAR_TEMP_9, gmaxFactor=VAR_TEMP_A, teraType=VAR_TEMP_B, dmaxLevel=VAR_TEMP_E; ); EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_WOBBUFFET);