diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml index 6493f4c11b..33af7d9649 100644 --- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml +++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml @@ -23,9 +23,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.7.3 (Latest release) + - 1.7.4 (Latest release) - master (default when pulling, unreleased bugfixes) - upcoming (Edge) + - 1.7.3 - 1.7.2 - 1.7.1 - 1.7.0 diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml index 3fa2d2dfd5..cabfda4990 100644 --- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml +++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml @@ -23,9 +23,11 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.7.2 (Latest release) + - 1.7.4 (Latest release) - master (default when pulling, unreleased bugfixes) - upcoming (Edge) + - 1.7.3 + - 1.7.2 - 1.7.1 - 1.7.0 - 1.6.2 diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml index e39eac4f56..33c85c15fb 100644 --- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml +++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml @@ -23,9 +23,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.7.3 (Latest release) + - 1.7.4 (Latest release) - master (default when pulling, unreleased bugfixes) - upcoming (Edge) + - 1.7.3 - 1.7.2 - 1.7.1 - 1.7.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index d4805a5d40..f8e0c66e20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Pokeemerald-Expansion Changelogs ## Version 1.7.x +### [Version 1.7.4](docs/changelogs/1.7.4.md) - Bugfix Release ### [Version 1.7.3](docs/changelogs/1.7.3.md) - Bugfix Release ### [Version 1.7.2](docs/changelogs/1.7.2.md) - Bugfix Release ### [Version 1.7.1](docs/changelogs/1.7.1.md) - Bugfix Release diff --git a/README.md b/README.md index 0804faf74f..dcb9861615 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ With this, you'll get the latest version of pokeemerald-expansion, plus a couple ## **How do I update my version of pokeemerald-expansion?** - If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`. -- Once you have your remote set up, run the command `git pull RHH expansion/1.7.3`. +- Once you have your remote set up, run the command `git pull RHH expansion/1.7.4`. ### Please consider crediting the entire [list of contributors](https://github.com/rh-hideout/pokeemerald-expansion/wiki/Credits) in your project, as they have all worked hard to develop this project :) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 6e26a267ac..d2a8115089 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1360,11 +1360,11 @@ .byte \battler .4byte \jumpInstr .endm - + .macro allyswitchswapbattlers callnative BS_AllySwitchSwapBattler .endm - + .macro allyswitchfailchance jumpInstr:req callnative BS_AllySwitchFailChance .4byte \jumpInstr @@ -1616,11 +1616,17 @@ callnative BS_TryUpperHand .4byte \failInstr .endm - + .macro tryupdaterecoiltracker callnative BS_TryUpdateRecoilTracker .endm + .macro trytidyup clear:req, jumpInstr:req + callnative BS_TryTidyUp + .byte \clear + .4byte \jumpInstr + .endm + @ various command changed to more readable macros .macro cancelmultiturnmoves battler:req various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES diff --git a/asm/macros/event.inc b/asm/macros/event.inc index cdff19c542..e424e7c538 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2139,3 +2139,34 @@ callnative ToggleGigantamaxFactor .2byte \slot .endm + + @ Sets VAR_RESULT to one of the arguments (via setorcopyvar). + .macro randomelement element:req, elements:vararg + .set _randomelement_n, 0 + .irp el, \element, \elements + .set _randomelement_n, _randomelement_n + 1 + .endr + random _randomelement_n + + .set _randomelement_n, 0 + .irp el, \element, \elements + goto_if_ne VAR_RESULT, _randomelement_n, 1f + setorcopyvar VAR_RESULT, \el + goto 2f + 1: + .set _randomelement_n, _randomelement_n + 1 + .endr + 2: + .endm + + @ Sets VAR_RESULT to TRUE with probability 'percent', and FALSE + @ with probability '100% - percent'. + .macro randompercentage percent:req + random 100 + goto_if_lt VAR_RESULT, \percent, 1f + setvar VAR_RESULT, FALSE + goto 2f + 1: + setvar VAR_RESULT, TRUE + 2: + .endm diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 398005f824..becdadac1a 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -1028,6 +1028,10 @@ gBattleAnims_General:: .4byte General_Rainbow @ B_ANIM_RAINBOW .4byte General_SeaOfFire @ B_ANIM_SEA_OF_FIRE .4byte General_Swamp @ B_ANIM_SWAMP + .4byte General_TrickRoom @ B_ANIM_TRICK_ROOM + .4byte General_WonderRoom @ B_ANIM_WONDER_ROOM + .4byte General_MagicRoom @ B_ANIM_MAGIC_ROOM + .4byte General_Tailwind @ B_ANIM_TAILLWIND .align 2 gBattleAnims_Special:: @@ -1316,6 +1320,35 @@ Move_TAILWIND: delay 1 end +General_Tailwind: + loadspritegfx ANIM_TAG_FLYING_DIRT + playsewithpan SE_M_GUST, SOUND_PAN_ATTACKER + call SetHighSpeedBg + setalpha 12, 8 + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 10, 2304, 96, 0 + delay 12 + playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 90, 2048, 96, 0 + delay 12 + playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 50, 2560, 96, 0 + delay 12 + playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 20, 2304, 96, 0 + delay 12 + playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 70, 1984, 96, 0 + delay 12 + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 0, 2816, 96, 0 + delay 10 + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 60, 2560, 96, 0 + waitforvisualfinish + stopsound + call UnsetHighSpeedBg + blendoff + delay 1 + end + Move_ACUPRESSURE: loadspritegfx ANIM_TAG_ACUPRESSURE loadspritegfx ANIM_TAG_SPARK_2 @@ -3303,6 +3336,8 @@ Move_DEFOG: Move_TRICK_ROOM:: call InitRoomAnimation +General_TrickRoom: + playsewithpan SE_M_SUPERSONIC, SOUND_PAN_TARGET fadetobg BG_TRICK_ROOM waitbgfadein delay 0x40 @@ -3312,7 +3347,6 @@ Move_TRICK_ROOM:: end InitRoomAnimation: setalpha 8, 8 - playsewithpan SE_M_SUPERSONIC, SOUND_PAN_TARGET createvisualtask AnimTask_ScaleMonAndRestore, 5, -6, -6, 15, ANIM_TARGET, 1 return @@ -4932,6 +4966,8 @@ PowerSplitLaunch: Move_WONDER_ROOM:: call InitRoomAnimation +General_WonderRoom: + playsewithpan SE_M_SUPERSONIC, SOUND_PAN_TARGET fadetobg BG_WONDER_ROOM waitbgfadein delay 0x40 @@ -5112,6 +5148,8 @@ Move_TELEKINESIS:: Move_MAGIC_ROOM:: call InitRoomAnimation +General_MagicRoom: + playsewithpan SE_M_SUPERSONIC, SOUND_PAN_TARGET fadetobg BG_MAGIC_ROOM waitbgfadein delay 0x40 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index d451cf566f..537d92ba52 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -20,6 +20,23 @@ .section script_data, "aw", %progbits +BattleScript_EffectTidyUp:: + attackcanceler + attackstring + pause B_WAIT_TIME_MED + ppreduce + waitstate + trytidyup FALSE, BattleScript_EffectTidyUpDoMoveAnimation + goto BattleScript_EffectDragonDanceFromStatUp + +BattleScript_EffectTidyUpDoMoveAnimation:: + attackanimation + waitanimation + trytidyup TRUE, NULL + printstring STRINGID_TIDYINGUPCOMPLETE + waitmessage B_WAIT_TIME_LONG + goto BattleScript_EffectDragonDanceFromStatUp + BattleScript_EffectUpperHand:: attackcanceler tryupperhand BattleScript_FailedFromAtkString @@ -2534,6 +2551,10 @@ BattleScript_EffectTrickRoom:: waitanimation printfromtable gRoomsStringIds waitmessage B_WAIT_TIME_LONG + call BattleScript_TryRoomServiceLoop + goto BattleScript_MoveEnd + +BattleScript_TryRoomServiceLoop: savetarget setbyte gBattlerTarget, 0 BattleScript_RoomServiceLoop: @@ -2544,7 +2565,7 @@ BattleScript_RoomServiceLoop_NextBattler: addbyte gBattlerTarget, 0x1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_RoomServiceLoop restoretarget - goto BattleScript_MoveEnd + return BattleScript_EffectWonderRoom:: BattleScript_EffectMagicRoom:: @@ -5265,62 +5286,6 @@ BattleScript_EffectRecoilHP25:: incrementgamestat GAME_STAT_USED_STRUGGLE goto BattleScript_EffectHit -BattleScript_EffectTeeterDance:: - attackcanceler - attackstring - ppreduce - setbyte gBattlerTarget, 0 -BattleScript_TeeterDanceLoop:: - movevaluescleanup - jumpifbyteequal gBattlerAttacker, gBattlerTarget, BattleScript_TeeterDanceLoopIncrement - jumpifability BS_TARGET, ABILITY_OWN_TEMPO, BattleScript_TeeterDanceOwnTempoPrevents - jumpifsubstituteblocks BattleScript_TeeterDanceSubstitutePrevents - jumpifstatus2 BS_TARGET, STATUS2_CONFUSION, BattleScript_TeeterDanceAlreadyConfused - jumpifhasnohp BS_TARGET, BattleScript_TeeterDanceLoopIncrement - accuracycheck BattleScript_TeeterDanceMissed, ACC_CURR_MOVE - jumpifsafeguard BattleScript_TeeterDanceSafeguardProtected - attackanimation - waitanimation - seteffectprimary MOVE_EFFECT_CONFUSION - resultmessage - waitmessage B_WAIT_TIME_LONG -BattleScript_TeeterDanceDoMoveEndIncrement:: - moveendto MOVEEND_NEXT_TARGET -BattleScript_TeeterDanceLoopIncrement:: - addbyte gBattlerTarget, 1 - jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_TeeterDanceLoop - end - -BattleScript_TeeterDanceOwnTempoPrevents:: - pause B_WAIT_TIME_SHORT - printstring STRINGID_PKMNPREVENTSCONFUSIONWITH - waitmessage B_WAIT_TIME_LONG - goto BattleScript_TeeterDanceDoMoveEndIncrement - -BattleScript_TeeterDanceSafeguardProtected:: - pause B_WAIT_TIME_SHORT - printstring STRINGID_PKMNUSEDSAFEGUARD - waitmessage B_WAIT_TIME_LONG - goto BattleScript_TeeterDanceDoMoveEndIncrement - -BattleScript_TeeterDanceSubstitutePrevents:: - pause B_WAIT_TIME_SHORT - printstring STRINGID_BUTITFAILED - waitmessage B_WAIT_TIME_LONG - goto BattleScript_TeeterDanceDoMoveEndIncrement - -BattleScript_TeeterDanceAlreadyConfused:: - setalreadystatusedmoveattempt BS_ATTACKER - pause B_WAIT_TIME_SHORT - printstring STRINGID_PKMNALREADYCONFUSED - waitmessage B_WAIT_TIME_LONG - goto BattleScript_TeeterDanceDoMoveEndIncrement - -BattleScript_TeeterDanceMissed:: - resultmessage - waitmessage B_WAIT_TIME_LONG - goto BattleScript_TeeterDanceDoMoveEndIncrement - BattleScript_EffectMudSport:: BattleScript_EffectWaterSport:: attackcanceler @@ -5456,6 +5421,7 @@ BattleScript_EffectDragonDance:: attackcanceler attackstring ppreduce +BattleScript_EffectDragonDanceFromStatUp:: jumpifstat BS_ATTACKER, CMP_LESS_THAN, STAT_ATK, MAX_STAT_STAGE, BattleScript_DragonDanceDoMoveAnim jumpifstat BS_ATTACKER, CMP_EQUAL, STAT_SPEED, MAX_STAT_STAGE, BattleScript_CantRaiseMultipleStats BattleScript_DragonDanceDoMoveAnim:: @@ -5983,6 +5949,19 @@ BattleScript_SunlightFaded:: call BattleScript_ActivateWeatherAbilities end2 +BattleScript_OverworldStatusStarts:: + printfromtable gStartingStatusStringIds + waitmessage B_WAIT_TIME_LONG + playanimation_var BS_ATTACKER, sB_ANIM_ARG1 + call BattleScript_OverworldStatusStarts_TryActivations + end3 + +BattleScript_OverworldStatusStarts_TryActivations: + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SET_TRICK_ROOM, BattleScript_TryRoomServiceLoop + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SET_TAILWIND_PLAYER, BattleScript_TryTailwindAbilitiesLoop + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SET_TAILWIND_OPPONENT, BattleScript_TryTailwindAbilitiesLoop + return + BattleScript_OverworldWeatherStarts:: printfromtable gWeatherStartsStringIds waitmessage B_WAIT_TIME_LONG diff --git a/data/maps/SootopolisCity_LotadAndSeedotHouse/scripts.inc b/data/maps/SootopolisCity_LotadAndSeedotHouse/scripts.inc index 52bdf3acee..0463906506 100644 --- a/data/maps/SootopolisCity_LotadAndSeedotHouse/scripts.inc +++ b/data/maps/SootopolisCity_LotadAndSeedotHouse/scripts.inc @@ -116,7 +116,7 @@ SootopolisCity_LotadAndSeedotHouse_Text_PleaseShowMeBigSeedot: .string "P-p-please, show me!$" SootopolisCity_LotadAndSeedotHouse_Text_GoshMightBeBiggerThanLotad: - .string "{STR_VAR_2} inches!\n" + .string "{STR_VAR_2}!\n" .string "Oh, my gosh, this is a big one!\p" .string "It might even beat the big LOTAD\n" .string "my younger brother saw!\p" @@ -132,7 +132,7 @@ SootopolisCity_LotadAndSeedotHouse_Text_BagCrammedFull1: .string "Your BAG is crammed full.$" SootopolisCity_LotadAndSeedotHouse_Text_SeenBiggerSeedot: - .string "{STR_VAR_2} inches, is it?\p" + .string "{STR_VAR_2}, is it?\p" .string "Hmm… I've seen a bigger SEEDOT\n" .string "than this one.$" @@ -148,7 +148,7 @@ SootopolisCity_LotadAndSeedotHouse_Text_DontHaveBigSeedot: SootopolisCity_LotadAndSeedotHouse_Text_BiggestSeedotInHistory: .string "The biggest SEEDOT in history!\n" - .string "{STR_VAR_2}'s {STR_VAR_3}-inch giant!\p" + .string "{STR_VAR_2}'s {STR_VAR_3} giant!\p" .string "A SEEDOT bigger than a LOTAD\n" .string "always wanted!$" @@ -164,7 +164,7 @@ SootopolisCity_LotadAndSeedotHouse_Text_PleaseShowMeBigLotad: .string "P-p-please show me!$" SootopolisCity_LotadAndSeedotHouse_Text_WowMightBeBiggerThanSeedot: - .string "{STR_VAR_2} inches!\n" + .string "{STR_VAR_2}!\n" .string "Wow, that is big!\p" .string "It might be even bigger than the huge\n" .string "SEEDOT my big brother saw.\p" @@ -180,7 +180,7 @@ SootopolisCity_LotadAndSeedotHouse_Text_BagCrammedFull2: .string "Your BAG is crammed full.$" SootopolisCity_LotadAndSeedotHouse_Text_SeenBiggerLotad: - .string "{STR_VAR_2} inches?\p" + .string "{STR_VAR_2}?\p" .string "Hmm… I've seen a bigger LOTAD\n" .string "than this one here.$" @@ -196,7 +196,7 @@ SootopolisCity_LotadAndSeedotHouse_Text_DontHaveBigLotad: SootopolisCity_LotadAndSeedotHouse_Text_BiggestLotadInHistory: .string "The biggest LOTAD in history!\n" - .string "{STR_VAR_2}'s {STR_VAR_3}-inch colossus!\p" + .string "{STR_VAR_2}'s {STR_VAR_3} colossus!\p" .string "A LOTAD bigger than a SEEDOT\n" .string "always wanted!$" diff --git a/docs/changelogs/1.7.4.md b/docs/changelogs/1.7.4.md new file mode 100644 index 0000000000..50556bec77 --- /dev/null +++ b/docs/changelogs/1.7.4.md @@ -0,0 +1,68 @@ +# Version 1.7.4 + +```md +## How to update +- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`. +- Once you have your remote set up, run the command `git pull RHH expansion/1.Y.Z`. +``` + +## 🧬 General 🧬 +### Fixed +* HGSS Dex fixes: + * Fixed inconsistent list tileset between capped and decapped modes by @ravepossum in https://github.com/rh-hideout/pokeemerald-expansion/pull/4126 + * Fixed screen select bar popping in too early for area screen by @ravepossum in https://github.com/rh-hideout/pokeemerald-expansion/pull/4094 +* Fixed Lotad/Seedot house using species weight instead of height by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4195 + +## 🐉 Pokémon 🐉 +### Fixed +* Fixed Ogerpon shiny palettes by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4165 +* Fixed Basculegion back sprite offset by @MelonSpeedruns in https://github.com/rh-hideout/pokeemerald-expansion/pull/4198 +* Fixed Greninja form animations by @MelonSpeedruns in https://github.com/rh-hideout/pokeemerald-expansion/pull/4198 +* Fixed compile error when `P_GEN_8_CROSS_EVOS` is enabled but not `P_GEN_9_CROSS_EVOS` due to Ursaluna's cry being labled as Gen 9 by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4210 +* Fixed Curly Tatsugiri and Green Plumage Squawkabilly icons not showing up properly in later versions of Porymap by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4212 + +## ⚔️ Battle General ⚔️ ## +### Fixed +* Fixed disobedience not resetting multihit moves by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4133 +* Fixed switch in interactions with hold effects abilities by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4091 + * Download activating on an empty field. + * Intimidate activating on an empty field and not activating upon new opponent switching in. + * Primal Reversion not happening immediately upon switching in from U-turn. + * Held items not being triggered when switching in fron U-turn and Intimidate being triggered beforehand. +* Fixed LastUsedBall issues not being saved and DisplayBall not being shown by @Wesmaster in https://github.com/rh-hideout/pokeemerald-expansion/pull/4209 + +## 🤹 Moves 🤹 +### Fixed +* Fixed move animations crashing on some emulators because of division by zero by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4121 + * Flip Turn, Anchor Shot, Snipe Shot and Incinerate were affected by this. +* Fixed Eerie Spell consuming double PP and showing its message twice by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4127 +* Fixed Fairy Lock animation by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4111 + +## 🎭 Abilities 🎭 +### Added +* Added Gen 5+ config for Soundproof no longer preventing Uproar status by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4174 +### Fixed +* Fixed Mycelium Might speed calculation by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4146 +* Fixed Stench triggering on non-damaging attacks by @HungryPickle in https://github.com/rh-hideout/pokeemerald-expansion/pull/4159 +* Fixed Disguise not ending the battle in the correct form by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4167 +* Fixed Opportunist accumulating stat changes by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4168 + +## 🧶 Items 🧶 +### Fixed +* Kee Berry fixes by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4149 + * Fixed triggering if the holder does a physical attack instead of receiving it. + * Fixed it boosting the target's defense if the attacker holds a Kee Berry, while still displaying the boost animation for the holder. +* Fixed poison-healing items not reseting Toxic Counter by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4135 + +## 🧹 Other Cleanup 🧹 +### Changed +* Clean up space/tabs difference by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4163 +* Deleted a space by @cmy2008 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4194 + +## New Contributors +* @cmy2008 made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/4194 +* @MelonSpeedruns made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/4198 + +**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.7.3...expansion/1.7.4 + + diff --git a/graphics/pokemon/squawkabilly/green_plumage/icon.png b/graphics/pokemon/squawkabilly/icon.png old mode 100755 new mode 100644 similarity index 100% rename from graphics/pokemon/squawkabilly/green_plumage/icon.png rename to graphics/pokemon/squawkabilly/icon.png diff --git a/graphics/pokemon/squawkabilly/green_plumage/normal.pal b/graphics/pokemon/squawkabilly/normal.pal similarity index 100% rename from graphics/pokemon/squawkabilly/green_plumage/normal.pal rename to graphics/pokemon/squawkabilly/normal.pal diff --git a/graphics/pokemon/squawkabilly/green_plumage/shiny.pal b/graphics/pokemon/squawkabilly/shiny.pal similarity index 100% rename from graphics/pokemon/squawkabilly/green_plumage/shiny.pal rename to graphics/pokemon/squawkabilly/shiny.pal diff --git a/graphics/pokemon/tatsugiri/curly/back.png b/graphics/pokemon/tatsugiri/back.png similarity index 100% rename from graphics/pokemon/tatsugiri/curly/back.png rename to graphics/pokemon/tatsugiri/back.png diff --git a/graphics/pokemon/tatsugiri/curly/front.png b/graphics/pokemon/tatsugiri/front.png similarity index 100% rename from graphics/pokemon/tatsugiri/curly/front.png rename to graphics/pokemon/tatsugiri/front.png diff --git a/graphics/pokemon/tatsugiri/curly/icon.png b/graphics/pokemon/tatsugiri/icon.png old mode 100755 new mode 100644 similarity index 100% rename from graphics/pokemon/tatsugiri/curly/icon.png rename to graphics/pokemon/tatsugiri/icon.png diff --git a/graphics/pokemon/tatsugiri/curly/normal.pal b/graphics/pokemon/tatsugiri/normal.pal similarity index 100% rename from graphics/pokemon/tatsugiri/curly/normal.pal rename to graphics/pokemon/tatsugiri/normal.pal diff --git a/graphics/pokemon/tatsugiri/curly/shiny.pal b/graphics/pokemon/tatsugiri/shiny.pal similarity index 100% rename from graphics/pokemon/tatsugiri/curly/shiny.pal rename to graphics/pokemon/tatsugiri/shiny.pal diff --git a/include/battle.h b/include/battle.h index 5628dd3146..360faace34 100644 --- a/include/battle.h +++ b/include/battle.h @@ -682,9 +682,12 @@ struct BattleStruct } multiBuffer; u8 wishPerishSongState; u8 wishPerishSongBattlerId; - bool8 overworldWeatherDone; - bool8 terrainDone; - u8 isAtkCancelerForCalledMove; // Certain cases in atk canceler should only be checked once, when the original move is called, however others need to be checked the twice. + u8 overworldWeatherDone:1; + u8 startingStatusDone:1; + u8 isAtkCancelerForCalledMove:1; // Certain cases in atk canceler should only be checked once, when the original move is called, however others need to be checked the twice. + u8 terrainDone:1; + u8 startingStatus; // status to apply at battle start. defined in constants/battle.h + u8 startingStatusTimer; u8 atkCancellerTracker; struct BattleTvMovePoints tvMovePoints; struct BattleTv tv; diff --git a/include/battle_ai_main.h b/include/battle_ai_main.h index 8f482c5812..18148a89b0 100644 --- a/include/battle_ai_main.h +++ b/include/battle_ai_main.h @@ -29,7 +29,8 @@ #define STAT_CHANGE_ACC 10 #define STAT_CHANGE_EVASION 11 -#define BEST_DAMAGE_MOVE 1 // Move with the most amount of hits with the best accuracy/effect +#define BEST_DAMAGE_MOVE 1 // Move with the most amount of hits with the best accuracy/effect +#define POWERFUL_STATUS_MOVE 10 // Moves with this score will be chosen over a move that faints target // Temporary scores that are added together to determine a final score at the at of AI_CalcMoveScore #define WEAK_EFFECT 1 diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index cfdb791e0e..ac7aea429b 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -99,7 +99,8 @@ bool32 HasMoveWithCategory(u32 battler, u32 category); bool32 HasMoveWithType(u32 battler, u32 type); bool32 HasMoveEffect(u32 battlerId, u32 moveEffect); bool32 HasMoveEffectANDArg(u32 battlerId, u32 effect, u32 argument); -bool32 HasMoveWithMoveEffect(u32 battlerId, u32 moveEffect); +bool32 HasMoveWithAdditionalEffect(u32 battlerId, u32 moveEffect); +bool32 HasMoveWithCriticalHitChance(u32 battlerId); bool32 HasMoveWithMoveEffectExcept(u32 battlerId, u32 moveEffect, u32 exception); bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect); bool32 IsAromaVeilProtectedMove(u32 move); @@ -135,7 +136,7 @@ bool32 HasSnatchAffectedMove(u32 battler); // status checks bool32 AI_CanBeBurned(u32 battler, u32 ability); bool32 AI_CanGetFrostbite(u32 battler, u32 ability); -bool32 AI_CanBeConfused(u32 battler, u32 ability); +bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability); bool32 AI_CanSleep(u32 battler, u32 ability); bool32 IsBattlerIncapacitated(u32 battler, u32 ability); bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove); @@ -189,6 +190,7 @@ s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct Battl s32 AI_CheckMoveEffects(u32 battlerAtk, u32 battlerDef, u32 move, s32 score, struct AiLogicData *aiData, u32 predictedMove, bool32 isDoubleBattle); s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle); bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef); -s32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData); +bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData); +void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score); #endif //GUARD_BATTLE_AI_UTIL_H diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 3ff275d3bf..319437c12f 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -54,6 +54,7 @@ extern const u8 BattleScript_DamagingWeatherContinues[]; extern const u8 BattleScript_SandStormHailSnowEnds[]; extern const u8 BattleScript_SunlightContinues[]; extern const u8 BattleScript_SunlightFaded[]; +extern const u8 BattleScript_OverworldStatusStarts[]; extern const u8 BattleScript_OverworldWeatherStarts[]; extern const u8 BattleScript_OverworldTerrain[]; extern const u8 BattleScript_SideStatusWoreOff[]; @@ -680,7 +681,6 @@ extern const u8 BattleScript_EffectImprison[]; extern const u8 BattleScript_EffectRefresh[]; extern const u8 BattleScript_EffectGrudge[]; extern const u8 BattleScript_EffectSnatch[]; -extern const u8 BattleScript_EffectTeeterDance[]; extern const u8 BattleScript_EffectHitEscape[]; extern const u8 BattleScript_EffectMudSport[]; extern const u8 BattleScript_EffectTickle[]; @@ -823,5 +823,6 @@ extern const u8 BattleScript_EffectDoodle[]; extern const u8 BattleScript_EffectFilletAway[]; extern const u8 BattleScript_EffectShedTail[]; extern const u8 BattleScript_EffectUpperHand[]; +extern const u8 BattleScript_EffectTidyUp[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/battle_util.h b/include/battle_util.h index 21595ea57c..62575da950 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -39,6 +39,7 @@ #define ABILITYEFFECT_SWITCH_IN_TERRAIN 16 #define ABILITYEFFECT_SWITCH_IN_WEATHER 17 #define ABILITYEFFECT_OPPORTUNIST 18 +#define ABILITYEFFECT_SWITCH_IN_STATUSES 19 // Special cases #define ABILITYEFFECT_MUD_SPORT 252 // Only used if B_SPORT_TURNS >= GEN_6 #define ABILITYEFFECT_WATER_SPORT 253 // Only used if B_SPORT_TURNS >= GEN_6 diff --git a/include/config.h b/include/config.h index f4be812643..41695a77b4 100644 --- a/include/config.h +++ b/include/config.h @@ -34,16 +34,6 @@ #define LOG_HANDLER (LOG_HANDLER_MGBA_PRINT) #endif -#define ENGLISH - -#ifdef ENGLISH -#define UNITS_IMPERIAL -#define CHAR_DEC_SEPARATOR CHAR_PERIOD // Period is used as a decimal separator only in the UK and the US. -#else -#define UNITS_METRIC -#define CHAR_DEC_SEPARATOR CHAR_COMMA -#endif - // Uncomment to fix some identified minor bugs #define BUGFIX @@ -81,4 +71,11 @@ #define SUMMARY_SCREEN_NATURE_COLORS TRUE // If TRUE, nature-based stat boosts and reductions will be red and blue in the summary screen. #define HQ_RANDOM TRUE // If TRUE, replaces the default RNG with an implementation of SFC32 RNG. May break code that relies on RNG. +// Measurement system constants to be used for UNITS +#define UNITS_IMPERIAL 0 // Inches, feet, pounds +#define UNITS_METRIC 1 // meters, kilograms + +#define UNITS UNITS_IMPERIAL +#define CHAR_DEC_SEPARATOR CHAR_PERIOD // CHAR_PERIOD is used as a decimal separator only in the UK and the US. The rest of the world uses CHAR_COMMA. + #endif // GUARD_CONFIG_H diff --git a/include/config/battle.h b/include/config/battle.h index 163e4c0f08..5ec007e686 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -51,6 +51,7 @@ // Turn settings #define B_BINDING_TURNS GEN_LATEST // In Gen5+, binding moves last for 4-5 turns instead of 2-5 turns. (With Grip Claw, 7 and 5 turns respectively.) #define B_UPROAR_TURNS GEN_LATEST // In Gen5+, Uproar lasts for 3 turns instead of 2-5 turns. +#define B_UPROAR_IGNORE_SOUNDPROOF GEN_LATEST // In Gen5+, Uproar status ignores Soundproof. #define B_DISABLE_TURNS GEN_LATEST // Disable's turns. See Cmd_disablelastusedattack. #define B_TAILWIND_TURNS GEN_LATEST // In Gen5+, Tailwind lasts 4 turns instead of 3. #define B_SLEEP_TURNS GEN_LATEST // In Gen5+, sleep lasts for 1-3 turns instead of 2-5 turns. @@ -180,8 +181,8 @@ // Var Settings // To use the following features in scripting, replace the 0s with the var ID you're assigning it to. // Eg: Replace with VAR_UNUSED_0x40F7 so you can use B_VAR_TERRAIN for that feature. -#define B_VAR_TERRAIN 0 // If this var has a value, assigning a STATUS_FIELD_xx_TERRAIN to it before battle causes the battle to start with that terrain active. -#define B_VAR_TERRAIN_TIMER 0 // If this var has a value greater or equal than 1 field terrains will last that number of turns, otherwise they will last until they're overwritten. +#define B_VAR_STARTING_STATUS 0 // If this var has a value, assigning a STATUS_FIELD_xx_TERRAIN to it before battle causes the battle to start with that terrain active. +#define B_VAR_STARTING_STATUS_TIMER 0 // If this var has a value greater or equal than 1 field terrains will last that number of turns, otherwise they will last until they're overwritten. #define B_VAR_WILD_AI_FLAGS 0 // If not 0, you can use this var to add to default wild AI flags. NOT usable with flags above (1 << 15) // Sky Battles @@ -229,6 +230,7 @@ #define B_WILD_NATURAL_ENEMIES TRUE // If set to TRUE, certain wild mon species will attack other species when partnered in double wild battles (eg. Zangoose vs Seviper) #define B_AFFECTION_MECHANICS TRUE // In Gen6+, there's a stat called affection that can trigger different effects in battle. From LGPE onwards, those effects use friendship instead. #define B_TRAINER_CLASS_POKE_BALLS GEN_LATEST // In Gen7+, trainers will use certain types of Poké Balls depending on their trainer class. +#define B_TRAINER_MON_RANDOM_ABILITY FALSE // If this is set to TRUE a random legal ability will be generated for a trainer mon #define B_OBEDIENCE_MECHANICS GEN_LATEST // In PLA+ (here Gen8+), obedience restrictions also apply to non-outsider Pokémon, albeit based on their level met rather than actual level #define B_USE_FROSTBITE FALSE // In PLA, Frostbite replaces Freeze. Enabling this flag does the same here. Moves can still be cherry-picked to either Freeze or Frostbite. Freeze-Dry, Secret Power & Tri Attack depend on this config. #define B_OVERWORLD_SNOW GEN_LATEST // In Gen9+, overworld Snow will summon snow instead of hail. diff --git a/include/constants/battle.h b/include/constants/battle.h index a353fbb357..9a3654195e 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -354,7 +354,7 @@ #define MOVE_EFFECT_PREVENT_ESCAPE 33 #define MOVE_EFFECT_NIGHTMARE 34 #define MOVE_EFFECT_ALL_STATS_UP 35 -#define MOVE_EFFECT_RAPIDSPIN 36 +#define MOVE_EFFECT_RAPID_SPIN 36 #define MOVE_EFFECT_REMOVE_STATUS 37 #define MOVE_EFFECT_ATK_DEF_DOWN 38 #define MOVE_EFFECT_ATK_PLUS_2 39 @@ -517,4 +517,17 @@ // Constants for Torment #define PERMANENT_TORMENT 0xF +// Constants for B_VAR_STARTING_STATUS +// Timer value controlled by B_VAR_STARTING_STATUS_TIMER +#define STARTING_STATUS_NONE 0 +#define STARTING_STATUS_ELECTRIC_TERRAIN 1 +#define STARTING_STATUS_MISTY_TERRAIN 2 +#define STARTING_STATUS_GRASSY_TERRAIN 3 +#define STARTING_STATUS_PSYCHIC_TERRAIN 4 +#define STARTING_STATUS_TRICK_ROOM 5 +#define STARTING_STATUS_MAGIC_ROOM 6 +#define STARTING_STATUS_WONDER_ROOM 7 +#define STARTING_STATUS_TAILWIND_PLAYER 8 +#define STARTING_STATUS_TAILWIND_OPPONENT 9 + #endif // GUARD_CONSTANTS_BATTLE_H diff --git a/include/constants/battle_ai.h b/include/constants/battle_ai.h index ad489a4dd6..2093312ea6 100644 --- a/include/constants/battle_ai.h +++ b/include/constants/battle_ai.h @@ -34,20 +34,20 @@ #define AI_FLAG_PREFER_BATON_PASS (1 << 6) #define AI_FLAG_DOUBLE_BATTLE (1 << 7) // removed, split between AI_FLAG_CHECK_BAD_MOVE & AI_FLAG_CHECK_GOOD_MOVE #define AI_FLAG_HP_AWARE (1 << 8) +#define AI_FLAG_POWERFUL_STATUS (1 << 9) // AI prefers moves that set up field effects or side statuses, even if the user can faint the target // New, Trainer Handicap Flags -#define AI_FLAG_NEGATE_UNAWARE (1 << 9) // AI is NOT aware of negating effects like wonder room, mold breaker, etc -#define AI_FLAG_WILL_SUICIDE (1 << 10) // AI will use explosion / self destruct / final gambit / etc +#define AI_FLAG_NEGATE_UNAWARE (1 << 10) // AI is NOT aware of negating effects like wonder room, mold breaker, etc +#define AI_FLAG_WILL_SUICIDE (1 << 11) // AI will use explosion / self destruct / final gambit / etc // New, Trainer Strategy Flags -#define AI_FLAG_HELP_PARTNER (1 << 11) // AI can try to help partner. If not set, will tend not to target partner -#define AI_FLAG_PREFER_STATUS_MOVES (1 << 12) // AI gets a score bonus for status moves. Should be combined with AI_FLAG_CHECK_BAD_MOVE to prevent using only status moves -#define AI_FLAG_STALL (1 << 13) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished -#define AI_FLAG_SCREENER (1 << 14) // AI prefers screening effects like reflect, mist, etc. TODO unfinished +#define AI_FLAG_HELP_PARTNER (1 << 12) // AI can try to help partner. If not set, will tend not to target partner +#define AI_FLAG_PREFER_STATUS_MOVES (1 << 13) // AI gets a score bonus for status moves. Should be combined with AI_FLAG_CHECK_BAD_MOVE to prevent using only status moves +#define AI_FLAG_STALL (1 << 14) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished #define AI_FLAG_SMART_SWITCHING (1 << 15) // AI includes a lot more switching checks #define AI_FLAG_ACE_POKEMON (1 << 16) // AI has an Ace Pokemon. The last Pokemon in the party will not be used until it's the last one remaining. #define AI_FLAG_OMNISCIENT (1 << 17) // AI has full knowledge of player moves, abilities, hold items #define AI_FLAG_SMART_MON_CHOICES (1 << 18) // AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are separate decisions. Pairs very well with AI_FLAG_SMART_SWITCHING. -#define AI_FLAG_COUNT 18 +#define AI_FLAG_COUNT 19 // 'other' ai logic flags #define AI_FLAG_ROAMING (1 << 29) diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index b085d305ab..8bc09ba530 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -563,6 +563,10 @@ #define B_ANIM_RAINBOW 42 #define B_ANIM_SEA_OF_FIRE 43 #define B_ANIM_SWAMP 44 +#define B_ANIM_TRICK_ROOM 45 +#define B_ANIM_WONDER_ROOM 46 +#define B_ANIM_MAGIC_ROOM 47 +#define B_ANIM_TAILWIND 48 // special animations table (gBattleAnims_Special) #define B_ANIM_LVL_UP 0 diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 41c1e82f1f..bd55b6954a 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -164,7 +164,6 @@ enum { EFFECT_GRUDGE, EFFECT_SNATCH, EFFECT_LOW_KICK, - EFFECT_TEETER_DANCE, EFFECT_HIT_ESCAPE, EFFECT_MUD_SPORT, EFFECT_WEATHER_BALL, @@ -350,6 +349,7 @@ enum { EFFECT_UPPER_HAND, EFFECT_DRAGON_CHEER, EFFECT_LAST_RESPECTS, + EFFECT_TIDY_UP, NUM_BATTLE_MOVE_EFFECTS, }; diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 852e9a26c6..7a64f6d6fd 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -703,8 +703,12 @@ #define STRINGID_SHEDITSTAIL 701 #define STRINGID_CLOAKEDINAHARSHLIGHT 702 #define STRINGID_SUPERSWEETAROMAWAFTS 703 +#define STRINGID_DIMENSIONSWERETWISTED 704 +#define STRINGID_BIZARREARENACREATED 705 +#define STRINGID_BIZARREAREACREATED 706 +#define STRINGID_TIDYINGUPCOMPLETE 707 -#define BATTLESTRINGS_COUNT 704 +#define BATTLESTRINGS_COUNT 708 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, @@ -967,6 +971,19 @@ #define B_MSG_TERRAIN_END_GRASSY 7 #define B_MSG_TERRAIN_COUNT 8 +// gStartingStatusStringIds +#define B_MSG_TERRAIN_SET_MISTY 0 +#define B_MSG_TERRAIN_SET_ELECTRIC 1 +#define B_MSG_TERRAIN_SET_PSYCHIC 2 +#define B_MSG_TERRAIN_SET_GRASSY 3 +#define B_MSG_SET_TRICK_ROOM 4 +#define B_MSG_SET_MAGIC_ROOM 5 +#define B_MSG_SET_WONDER_ROOM 6 +#define B_MSG_SET_TAILWIND_PLAYER 7 +#define B_MSG_SET_TAILWIND_OPPONENT 8 +#define B_MSG_STARTING_STATUS_COUNT 9 + + // gWrappedStringIds #define B_MSG_WRAPPED_BIND 0 #define B_MSG_WRAPPED_WRAP 1 diff --git a/include/constants/cries.h b/include/constants/cries.h index 40c6dafc1e..3213f62d3f 100644 --- a/include/constants/cries.h +++ b/include/constants/cries.h @@ -626,9 +626,9 @@ enum { #if P_FAMILY_TEDDIURSA CRY_TEDDIURSA, CRY_URSARING, -#if P_GEN_9_CROSS_EVOS +#if P_GEN_8_CROSS_EVOS CRY_URSALUNA, -#endif //P_GEN_9_CROSS_EVOS +#endif //P_GEN_8_CROSS_EVOS #endif //P_FAMILY_TEDDIURSA #if P_FAMILY_SLUGMA CRY_SLUGMA, diff --git a/include/constants/expansion.h b/include/constants/expansion.h index 7e6cf52b26..63f262aeee 100644 --- a/include/constants/expansion.h +++ b/include/constants/expansion.h @@ -3,7 +3,7 @@ #define EXPANSION_VERSION_MAJOR 1 #define EXPANSION_VERSION_MINOR 7 -#define EXPANSION_VERSION_PATCH 3 +#define EXPANSION_VERSION_PATCH 4 // FALSE if this this version of Expansion is not a tagged commit, i.e. // it contains unreleased changes. diff --git a/include/constants/pokedex.h b/include/constants/pokedex.h index c03b096f9a..8058f01e5c 100644 --- a/include/constants/pokedex.h +++ b/include/constants/pokedex.h @@ -1307,4 +1307,24 @@ enum { #define HOENN_DEX_COUNT (HOENN_DEX_DEOXYS + 1) +#define DECAGRAMS_IN_POUND 4536 +#define CM_PER_INCH 2.54 +#define CM_PER_INCH_FACTOR (CM_PER_INCH * 100) +#define INCHES_IN_FOOT 12 +#define INCHES_IN_ONE_AND_HALF_FOOT (INCHES_IN_FOOT * 1.5) +#define INCHES_IN_FOOT_FACTOR (INCHES_IN_FOOT * 10) + +#define WEIGHT_HEIGHT_STR_LEN 16 +#define WEIGHT_HEIGHT_STR_MEM (WEIGHT_HEIGHT_STR_LEN * sizeof(u8)) + +#define DEX_HEADER_X 96 +#define DEX_Y_TOP 57 +#define DEX_Y_BOTTOM 73 +#define DEX_MEASUREMENT_X 129 + +#define DEX_HGSS_HEADER_X_PADDING 59 +#define DEX_HGSS_Y_TOP_PADDING 7 +#define DEX_HGSS_Y_BOTTOM_PADDING 4 +#define DEX_HGSS_MEASUREMENT_X_PADDING 51 + #endif // GUARD_CONSTANTS_POKEDEX_H diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index 13b76ed328..186ef5de59 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -323,6 +323,9 @@ #define NUM_NORMAL_ABILITY_SLOTS 2 #define NUM_HIDDEN_ABILITY_SLOTS 1 +// Used as a signal for givemon to generate a default ability by personality. +#define NUM_ABILITY_PERSONALITY 0xFF + #define LEGENDARY_PERFECT_IV_COUNT 3 #endif // GUARD_CONSTANTS_POKEMON_H diff --git a/include/data.h b/include/data.h index dc1067b57f..ebe7c3f3c1 100644 --- a/include/data.h +++ b/include/data.h @@ -63,7 +63,6 @@ struct TrainerBacksprite #define GET_MON_COORDS_HEIGHT(size)((size & 0xF) * 8) #define TRAINER_PARTY_IVS(hp, atk, def, speed, spatk, spdef) (hp | (atk << 5) | (def << 10) | (speed << 15) | (spatk << 20) | (spdef << 25)) #define TRAINER_PARTY_EVS(hp, atk, def, speed, spatk, spdef) ((const u8[6]){hp,atk,def,spatk,spdef,speed}) -#define TRAINER_PARTY_NATURE(nature) (nature+1) struct TrainerMon { @@ -99,7 +98,7 @@ struct Trainer /*0x13*/ u8 trainerName[TRAINER_NAME_LENGTH + 1]; /*0x1E*/ bool8 doubleBattle:1; bool8 mugshotEnabled:1; - u8 padding:6; + u8 startingStatus:6; // this trainer starts a battle with a given status. see include/constants/battle.h for values /*0x1F*/ u8 mugshotColor; /*0x20*/ u8 partySize; }; diff --git a/include/pokedex.h b/include/pokedex.h index c53d3e013b..be861fe682 100644 --- a/include/pokedex.h +++ b/include/pokedex.h @@ -29,5 +29,8 @@ bool16 HasAllHoennMons(void); void ResetPokedexScrollPositions(void); bool16 HasAllMons(void); void CB2_OpenPokedex(void); +void PrintMonMeasurements(u16 species, u32 owned); +u8* ConvertMonHeightToString(u32 height); +u8* ConvertMonWeightToString(u32 weight); #endif // GUARD_POKEDEX_H diff --git a/include/strings.h b/include/strings.h index bb4c3f9de2..431f90df16 100644 --- a/include/strings.h +++ b/include/strings.h @@ -531,7 +531,9 @@ extern const u8 gText_PokedexRegistration[]; extern const u8 gText_NumberClear01[]; extern const u8 gText_5MarksPokemon[]; extern const u8 gText_UnkHeight[]; +extern const u8 gText_UnkHeightMetric[]; extern const u8 gText_UnkWeight[]; +extern const u8 gText_UnkWeightMetric[]; extern const u8 gText_HTHeight[]; extern const u8 gText_WTWeight[]; extern const u8 gText_SearchingPleaseWait[]; diff --git a/sound/cry_tables.inc b/sound/cry_tables.inc index 383dd2512e..450b9ee584 100644 --- a/sound/cry_tables.inc +++ b/sound/cry_tables.inc @@ -623,9 +623,9 @@ gCryTable:: .if P_FAMILY_TEDDIURSA == TRUE cry Cry_Teddiursa cry Cry_Ursaring -.if P_GEN_9_CROSS_EVOS == TRUE +.if P_GEN_8_CROSS_EVOS == TRUE cry Cry_Ursaluna -.endif @ P_GEN_9_CROSS_EVOS +.endif @ P_GEN_8_CROSS_EVOS .endif @ P_FAMILY_TEDDIURSA .if P_FAMILY_SLUGMA == TRUE cry Cry_Slugma @@ -3049,9 +3049,9 @@ gCryTable_Reverse:: .if P_FAMILY_TEDDIURSA == TRUE cry_reverse Cry_Teddiursa cry_reverse Cry_Ursaring -.if P_GEN_9_CROSS_EVOS == TRUE +.if P_GEN_8_CROSS_EVOS == TRUE cry_reverse Cry_Ursaluna -.endif @ P_GEN_9_CROSS_EVOS +.endif @ P_GEN_8_CROSS_EVOS .endif @ P_FAMILY_TEDDIURSA .if P_FAMILY_SLUGMA == TRUE cry_reverse Cry_Slugma diff --git a/sound/direct_sound_data.inc b/sound/direct_sound_data.inc index d73504a1ff..cfe5a3b9bb 100644 --- a/sound/direct_sound_data.inc +++ b/sound/direct_sound_data.inc @@ -1844,11 +1844,11 @@ Cry_Teddiursa:: Cry_Ursaring:: .incbin "sound/direct_sound_samples/cries/ursaring.bin" -.if P_GEN_9_CROSS_EVOS == TRUE +.if P_GEN_8_CROSS_EVOS == TRUE .align 2 Cry_Ursaluna:: .incbin "sound/direct_sound_samples/cries/ursaluna.bin" -.endif @ P_GEN_9_CROSS_EVOS +.endif @ P_GEN_8_CROSS_EVOS .endif @ P_FAMILY_TEDDIURSA .if P_FAMILY_SLUGMA == TRUE diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index d3ef2dca0e..2a0181d1d1 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -52,6 +52,7 @@ static s32 AI_Roaming(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); static s32 AI_Safari(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_PowerfulStatus(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) = @@ -65,10 +66,10 @@ static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) = [6] = AI_PreferBatonPass, // AI_FLAG_PREFER_BATON_PASS [7] = AI_DoubleBattle, // AI_FLAG_DOUBLE_BATTLE [8] = AI_HPAware, // AI_FLAG_HP_AWARE - [9] = NULL, // AI_FLAG_NEGATE_UNAWARE - [10] = NULL, // AI_FLAG_WILL_SUICIDE - [11] = NULL, // AI_FLAG_HELP_PARTNER - [12] = NULL, // Unused + [9] = AI_PowerfulStatus, // AI_FLAG_POWERFUL_STATUS + [10] = NULL, // AI_FLAG_NEGATE_UNAWARE + [11] = NULL, // AI_FLAG_WILL_SUICIDE + [12] = NULL, // AI_FLAG_HELP_PARTNER [13] = NULL, // Unused [14] = NULL, // Unused [15] = NULL, // Unused @@ -1762,7 +1763,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_FOLLOW_ME: case EFFECT_HELPING_HAND: - case EFFECT_DRAGON_CHEER: if (!isDoubleBattle || !IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove) @@ -1904,19 +1904,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) if (aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_DATA->moveAccuracy[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] < 75) ADJUST_SCORE(-6); break; - case EFFECT_TEETER_DANCE: - if (((gBattleMons[battlerDef].status2 & STATUS2_CONFUSION) - || (!DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move) && aiData->abilities[battlerDef] == ABILITY_OWN_TEMPO) - || (IsBattlerGrounded(battlerDef) && AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) - || (DoesSubstituteBlockMove(battlerAtk, battlerDef, move))) - && ((gBattleMons[BATTLE_PARTNER(battlerDef)].status2 & STATUS2_CONFUSION) - || (!DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move) && aiData->abilities[BATTLE_PARTNER(battlerDef)] == ABILITY_OWN_TEMPO) - || (IsBattlerGrounded(BATTLE_PARTNER(battlerDef)) && AI_IsTerrainAffected(BATTLE_PARTNER(battlerDef), STATUS_FIELD_MISTY_TERRAIN)) - || (DoesSubstituteBlockMove(battlerAtk, BATTLE_PARTNER(battlerDef), move)))) - { - ADJUST_SCORE(-10); - } - break; case EFFECT_TRANSFORM: if (gBattleMons[battlerAtk].status2 & STATUS2_TRANSFORMED || (gBattleMons[battlerDef].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE))) //Leave out Illusion b/c AI is supposed to be fooled @@ -2341,15 +2328,18 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { ADJUST_SCORE(-10); } - else if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM) // Trick Room Up + else if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_POWERFUL_STATUS)) { - if (GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) // Attacker side slower than target side - ADJUST_SCORE(-10); // Keep the Trick Room up - } - else - { - if (GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) // Attacker side faster than target side - ADJUST_SCORE(-10); // Keep the Trick Room down + if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM) // Trick Room Up + { + if (GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) // Attacker side slower than target side + ADJUST_SCORE(-10); // Keep the Trick Room up + } + else + { + if (GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) // Attacker side faster than target side + ADJUST_SCORE(-10); // Keep the Trick Room down + } } break; case EFFECT_MAGIC_ROOM: @@ -2787,7 +2777,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-5); else if (atkPartnerHoldEffect == HOLD_EFFECT_SCOPE_LENS || IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_DRAGON) - || gMovesInfo[aiData->partnerMove].criticalHitStage > 0) + || gMovesInfo[aiData->partnerMove].criticalHitStage > 0 + || HasMoveWithCriticalHitChance(battlerAtkPartner)) ADJUST_SCORE(GOOD_EFFECT); } // our effect relative to partner @@ -2950,7 +2941,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_SWAGGER: if (gBattleMons[battlerAtkPartner].statStages[STAT_ATK] < MAX_STAT_STAGE && HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL) - && (!AI_CanBeConfused(battlerAtkPartner, TRUE) + && (!AI_CanBeConfused(battlerAtk, battlerAtkPartner, move, TRUE) || atkPartnerHoldEffect == HOLD_EFFECT_CURE_CONFUSION || atkPartnerHoldEffect == HOLD_EFFECT_CURE_STATUS)) { @@ -2960,7 +2951,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_FLATTER: if (gBattleMons[battlerAtkPartner].statStages[STAT_SPATK] < MAX_STAT_STAGE && HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_SPECIAL) - && (!AI_CanBeConfused(battlerAtkPartner, TRUE) + && (!AI_CanBeConfused(battlerAtk, battlerAtkPartner, move, TRUE) || atkPartnerHoldEffect == HOLD_EFFECT_CURE_CONFUSION || atkPartnerHoldEffect == HOLD_EFFECT_CURE_STATUS)) { @@ -3489,8 +3480,6 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(BEST_EFFECT); if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_LIGHT_CLAY) ADJUST_SCORE(DECENT_EFFECT); - if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SCREENER) - ADJUST_SCORE(DECENT_EFFECT); } break; case EFFECT_REST: @@ -3518,10 +3507,6 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) if (ShouldTrap(battlerAtk, battlerDef, move)) ADJUST_SCORE(GOOD_EFFECT); break; - case EFFECT_MIST: - if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SCREENER) - ADJUST_SCORE(DECENT_EFFECT); - break; case EFFECT_FOCUS_ENERGY: case EFFECT_LASER_FOCUS: if (aiData->abilities[battlerAtk] == ABILITY_SUPER_LUCK @@ -3537,6 +3522,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) IncreaseParalyzeScore(battlerAtk, battlerDef, move, &score); break; case EFFECT_SUBSTITUTE: + ADJUST_SCORE(GOOD_EFFECT); if (gStatuses3[battlerDef] & STATUS3_PERISH_SONG) ADJUST_SCORE(GOOD_EFFECT); if (gBattleMons[battlerDef].status1 & (STATUS1_BURN | STATUS1_PSN_ANY | STATUS1_FROSTBITE)) @@ -3562,7 +3548,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_LEECH_SEED: if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || gStatuses3[battlerDef] & STATUS3_LEECHSEED - || HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPIDSPIN) + || HasMoveWithAdditionalEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) || aiData->abilities[battlerDef] == ABILITY_LIQUID_OOZE || aiData->abilities[battlerDef] == ABILITY_MAGIC_GUARD) break; @@ -3721,7 +3707,13 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_STEALTH_ROCK: case EFFECT_STICKY_WEB: case EFFECT_TOXIC_SPIKES: - score += AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData); + if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData)); + { + if (gDisableStructs[battlerAtk].isFirstTurn) + ADJUST_SCORE(BEST_EFFECT); + else + ADJUST_SCORE(DECENT_EFFECT); + } break; case EFFECT_FORESIGHT: if (aiData->abilities[battlerAtk] == ABILITY_SCRAPPY || aiData->abilities[battlerAtk] == ABILITY_MINDS_EYE) @@ -3846,7 +3838,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_FIRST_TURN_ONLY: if (ShouldFakeOut(battlerAtk, battlerDef, move)) ADJUST_SCORE(GOOD_EFFECT); - else if (gMovesInfo[move].priority >= 1 && gDisableStructs[battlerAtk].isFirstTurn && GetBestDmgMoveFromBattler(battlerAtk, battlerDef) == move) + else if (gMovesInfo[move].argument == MOVE_FIRST_IMPRESSION && gDisableStructs[battlerAtk].isFirstTurn && GetBestDmgMoveFromBattler(battlerAtk, battlerDef) == move) ADJUST_SCORE(BEST_EFFECT); break; case EFFECT_STOCKPILE: @@ -3861,7 +3853,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_FLATTER: if (HasMoveEffect(battlerAtk, EFFECT_FOUL_PLAY) || HasMoveEffect(battlerAtk, EFFECT_PSYCH_UP) - || HasMoveWithMoveEffect(battlerAtk, MOVE_EFFECT_SPECTRAL_THIEF)) + || HasMoveWithAdditionalEffect(battlerAtk, MOVE_EFFECT_SPECTRAL_THIEF)) ADJUST_SCORE(DECENT_EFFECT); if (aiData->abilities[battlerDef] == ABILITY_CONTRARY) ADJUST_SCORE(GOOD_EFFECT); @@ -4173,6 +4165,8 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK, &score); IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK, &score); break; + case EFFECT_TIDY_UP: + IncreaseTidyUpScore(battlerAtk, battlerDef, move, &score); case EFFECT_DRAGON_DANCE: case EFFECT_SHIFT_GEAR: IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPEED, &score); @@ -4254,10 +4248,13 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(GOOD_EFFECT); // Partner might use pledge move break; case EFFECT_TRICK_ROOM: - if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) - ADJUST_SCORE(GOOD_EFFECT); - else if ((gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) - ADJUST_SCORE(GOOD_EFFECT); + if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_POWERFUL_STATUS)) + { + if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) + ADJUST_SCORE(GOOD_EFFECT); + else if ((gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) + ADJUST_SCORE(GOOD_EFFECT); + } break; case EFFECT_MAGIC_ROOM: ADJUST_SCORE(WEAK_EFFECT); @@ -4505,7 +4502,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF, &score); } break; - case MOVE_EFFECT_RAPIDSPIN: + case MOVE_EFFECT_RAPID_SPIN: if ((gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerAtk) != 0) || (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].status2 & STATUS2_WRAPPED)) ADJUST_SCORE(GOOD_EFFECT); @@ -4638,7 +4635,13 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) break; case MOVE_EFFECT_STEALTH_ROCK: case MOVE_EFFECT_SPIKES: - score += AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData); + if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData)); + { + if (gDisableStructs[battlerAtk].isFirstTurn) + ADJUST_SCORE(BEST_EFFECT); + else + ADJUST_SCORE(DECENT_EFFECT); + } break; case MOVE_EFFECT_FEINT: if (gMovesInfo[predictedMove].effect == EFFECT_PROTECT) @@ -4654,7 +4657,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) } break; case MOVE_EFFECT_WRAP: - if (!HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPIDSPIN) && ShouldTrap(battlerAtk, battlerDef, move)) + if (!HasMoveWithAdditionalEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) && ShouldTrap(battlerAtk, battlerDef, move)) ADJUST_SCORE(BEST_EFFECT); break; } @@ -4757,7 +4760,6 @@ static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score case EFFECT_WILL_O_WISP: case EFFECT_INGRAIN: case EFFECT_IMPRISON: - case EFFECT_TEETER_DANCE: case EFFECT_TICKLE: case EFFECT_COSMIC_POWER: case EFFECT_BULK_UP: @@ -4781,6 +4783,7 @@ static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score case EFFECT_MAGIC_ROOM: case EFFECT_TAILWIND: case EFFECT_DRAGON_DANCE: + case EFFECT_TIDY_UP: case EFFECT_STICKY_WEB: case EFFECT_RAIN_DANCE: case EFFECT_SUNNY_DAY: @@ -4841,7 +4844,6 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_MIRROR_COAT: case EFFECT_FOCUS_PUNCH: case EFFECT_REVENGE: - case EFFECT_TEETER_DANCE: case EFFECT_FILLET_AWAY: if (Random() & 1) ADJUST_SCORE(DECENT_EFFECT); @@ -5124,6 +5126,96 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) return score; } +static s32 AI_PowerfulStatus(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) +{ + u32 moveEffect = gMovesInfo[move].effect; + + if (gMovesInfo[move].category != DAMAGE_CATEGORY_STATUS || gMovesInfo[AI_DATA->partnerMove].effect == moveEffect) + return score; + + switch (moveEffect) + { + case EFFECT_TAILWIND: + if (!gSideTimers[GetBattlerSide(battlerAtk)].tailwindTimer && !(gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer > 1)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_TRICK_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && !HasMoveEffect(battlerDef, EFFECT_TRICK_ROOM)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_MAGIC_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_MAGIC_ROOM) && !HasMoveEffect(battlerDef, EFFECT_MAGIC_ROOM)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_WONDER_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_WONDER_ROOM) && !HasMoveEffect(battlerDef, EFFECT_WONDER_ROOM)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_GRAVITY: + if (!(gFieldStatuses & STATUS_FIELD_GRAVITY)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SAFEGUARD: + if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_SAFEGUARD)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_MIST: + if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_MIST)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_LIGHT_SCREEN: + case EFFECT_REFLECT: + case EFFECT_AURORA_VEIL: + if (ShouldSetScreen(battlerAtk, battlerDef, moveEffect)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SPIKES: + case EFFECT_STEALTH_ROCK: + case EFFECT_STICKY_WEB: + case EFFECT_TOXIC_SPIKES: + if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, AI_DATA)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_GRASSY_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_ELECTRIC_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_PSYCHIC_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_MISTY_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SANDSTORM: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_SANDSTORM | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SUNNY_DAY: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_SUN | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_RAIN_DANCE: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_RAIN | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_HAIL: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SNOWSCAPE: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + } + + return score; +} + static void AI_Flee(void) { AI_THINKING_STRUCT->aiAction |= (AI_ACTION_DONE | AI_ACTION_FLEE | AI_ACTION_DO_NOT_ATTACK); diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index cb903dae54..0eefbe4722 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1134,7 +1134,6 @@ bool32 IsConfusionMoveEffect(u32 moveEffect) case EFFECT_CONFUSE: case EFFECT_SWAGGER: case EFFECT_FLATTER: - case EFFECT_TEETER_DANCE: return TRUE; default: return FALSE; @@ -1709,7 +1708,7 @@ bool32 HasMoveEffectANDArg(u32 battlerId, u32 effect, u32 argument) return FALSE; } -bool32 HasMoveWithMoveEffect(u32 battlerId, u32 moveEffect) +bool32 HasMoveWithAdditionalEffect(u32 battlerId, u32 moveEffect) { s32 i; u16 *moves = GetMovesArray(battlerId); @@ -1724,6 +1723,21 @@ bool32 HasMoveWithMoveEffect(u32 battlerId, u32 moveEffect) return FALSE; } +bool32 HasMoveWithCriticalHitChance(u32 battlerId) +{ + s32 i; + u16 *moves = GetMovesArray(battlerId); + + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE + && gMovesInfo[moves[i]].criticalHitStage > 0) + return TRUE; + } + + return FALSE; +} + bool32 HasMoveWithMoveEffectExcept(u32 battlerId, u32 moveEffect, u32 exception) { s32 i; @@ -2585,25 +2599,27 @@ bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, return TRUE; } -bool32 AI_CanBeConfused(u32 battler, u32 ability) +bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability) { - if ((gBattleMons[battler].status2 & STATUS2_CONFUSION) - || (ability == ABILITY_OWN_TEMPO) - || (IsBattlerGrounded(battler) && (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN))) + if ((gBattleMons[battlerDef].status2 & STATUS2_CONFUSION) + || (ability == ABILITY_OWN_TEMPO && !DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move)) + || (IsBattlerGrounded(battlerDef) && (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD + || DoesSubstituteBlockMove(battlerAtk, battlerDef, move)) return FALSE; return TRUE; } bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove) { - if (!AI_CanBeConfused(battlerDef, defAbility) - || AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0 - || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD - || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) - || DoesPartnerHaveSameMoveEffect(battlerAtkPartner, battlerDef, move, partnerMove)) - { + if (gMovesInfo[move].target == MOVE_TARGET_FOES_AND_ALLY + && AI_CanBeConfused(battlerAtk, battlerDef, move, defAbility) + && !AI_CanBeConfused(battlerAtk, BATTLE_PARTNER(battlerDef), move, AI_DATA->abilities[BATTLE_PARTNER(battlerDef)])) + return FALSE; + + if (!AI_CanBeConfused(battlerAtk, battlerDef, move, defAbility) + || DoesPartnerHaveSameMoveEffect(battlerAtkPartner, battlerDef, move, partnerMove)) return FALSE; - } return TRUE; } @@ -2860,6 +2876,10 @@ bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent) bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, u32 moveEffect) { u32 atkSide = GetBattlerSide(battlerAtk); + + if (HasMoveEffect(battlerDef, EFFECT_BRICK_BREAK)) // Don't waste a turn if screens will be broken + return FALSE; + switch (moveEffect) { case EFFECT_AURORA_VEIL: @@ -3297,7 +3317,7 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) return; // Don't set up if AI is dead to residual damage from weather - if (BattlerWillFaintFromWeather(battlerAtk, AI_DATA->abilities[battlerAtk])) + if (GetBattlerSecondaryDamage(battlerAtk) >= gBattleMons[battlerAtk].hp) return; // Don't increase stats if opposing battler has Opportunist @@ -3312,10 +3332,15 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) break; case STAT_CHANGE_DEF: if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL) || !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL)) - ADJUST_SCORE_PTR(DECENT_EFFECT); + { + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL) + ADJUST_SCORE_PTR(DECENT_EFFECT); + else + ADJUST_SCORE_PTR(WEAK_EFFECT); + } break; case STAT_CHANGE_SPEED: - if ((noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == 0) + if ((noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == UNKNOWN_NO_OF_HITS) ADJUST_SCORE_PTR(DECENT_EFFECT); break; case STAT_CHANGE_SPATK: @@ -3324,7 +3349,12 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) break; case STAT_CHANGE_SPDEF: if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL) || !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL)) - ADJUST_SCORE_PTR(DECENT_EFFECT); + { + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL) + ADJUST_SCORE_PTR(DECENT_EFFECT); + else + ADJUST_SCORE_PTR(WEAK_EFFECT); + } break; case STAT_CHANGE_ATK_2: if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_PHYSICAL) && shouldSetUp) @@ -3332,10 +3362,15 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) break; case STAT_CHANGE_DEF_2: if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL) || !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL)) - ADJUST_SCORE_PTR(GOOD_EFFECT); + { + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL) + ADJUST_SCORE_PTR(GOOD_EFFECT); + else + ADJUST_SCORE_PTR(DECENT_EFFECT); + } break; case STAT_CHANGE_SPEED_2: - if ((noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == 0) + if ((noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == UNKNOWN_NO_OF_HITS) ADJUST_SCORE_PTR(GOOD_EFFECT); break; case STAT_CHANGE_SPATK_2: @@ -3344,14 +3379,19 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) break; case STAT_CHANGE_SPDEF_2: if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL) || !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL)) - ADJUST_SCORE_PTR(GOOD_EFFECT); + { + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL) + ADJUST_SCORE_PTR(GOOD_EFFECT); + else + ADJUST_SCORE_PTR(DECENT_EFFECT); + } break; case STAT_CHANGE_ACC: if (gBattleMons[battlerAtk].statStages[STAT_ACC] <= 3) // Increase only if necessary ADJUST_SCORE_PTR(DECENT_EFFECT); break; case STAT_CHANGE_EVASION: - if (GetBattlerSecondaryDamage(battlerAtk) && ((noOfHitsToFaint > 3) || noOfHitsToFaint == 0)) + if (noOfHitsToFaint > 3 || noOfHitsToFaint == UNKNOWN_NO_OF_HITS) ADJUST_SCORE_PTR(GOOD_EFFECT); else ADJUST_SCORE_PTR(DECENT_EFFECT); @@ -3567,10 +3607,31 @@ bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef) } //TODO - track entire opponent party data to determine hazard effectiveness -s32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData) +bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData) { - if (aiData->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0) - return 0; + if (aiData->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE + || CountUsablePartyMons(battlerDef) == 0 + || HasMoveWithAdditionalEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) + || HasMoveEffect(battlerDef, EFFECT_DEFOG)) + return FALSE; - return 2 * gDisableStructs[battlerAtk].isFirstTurn; + return TRUE; +} + +void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) +{ + if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerAtk) != 0) + ADJUST_SCORE_PTR(GOOD_EFFECT); + if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerDef) != 0) + ADJUST_SCORE_PTR(-2); + + if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE && AI_STRIKES_FIRST(battlerAtk, battlerDef, move)) + ADJUST_SCORE_PTR(-10); + if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE) + ADJUST_SCORE_PTR(GOOD_EFFECT); + + if (gStatuses3[battlerAtk] & STATUS3_LEECHSEED) + ADJUST_SCORE_PTR(DECENT_EFFECT); + if (gStatuses3[battlerDef] & STATUS3_LEECHSEED) + ADJUST_SCORE_PTR(-2); } diff --git a/src/battle_interface.c b/src/battle_interface.c index ecd502c088..50de6945a8 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -3468,9 +3468,14 @@ void TryAddLastUsedBallItemSprites(void) || (gLastThrownBall != 0 && !CheckBagHasItem(gLastThrownBall, 1))) { // we're out of the last used ball, so just set it to the first ball in the bag + u16 firstBall; + // we have to compact the bag first bc it is typically only compacted when you open it CompactItemsInBagPocket(&gBagPockets[BALLS_POCKET]); - gBallToDisplay = gBagPockets[BALLS_POCKET].itemSlots[0].itemId; + + firstBall = gBagPockets[BALLS_POCKET].itemSlots[0].itemId; + if (firstBall > ITEM_NONE) + gBallToDisplay = firstBall; } if (!CanThrowLastUsedBall()) diff --git a/src/battle_main.c b/src/battle_main.c index 2b0f0cc3ef..ad3f106282 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -1928,7 +1928,7 @@ void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMon u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags) { u32 personalityValue; - s32 i, j; + s32 i; u8 monsCount; if (battleTypeFlags & BATTLE_TYPE_TRAINER && !(battleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER @@ -1956,6 +1956,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer const struct TrainerMon *partyData = trainer->party; u32 otIdType = OT_ID_RANDOM_NO_SHINY; u32 fixedOtId = 0; + u32 ability = 0; if (trainer->doubleBattle == TRUE) personalityValue = 0x80; @@ -1969,8 +1970,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[i].species); else if (partyData[i].gender == TRAINER_MON_FEMALE) personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species); - if (partyData[i].nature != 0) - ModifyPersonalityForNature(&personalityValue, partyData[i].nature - 1); + ModifyPersonalityForNature(&personalityValue, partyData[i].nature); if (partyData[i].isShiny) { otIdType = OT_ID_PRESET; @@ -1994,14 +1994,24 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer { const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species]; u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities); - for (j = 0; j < maxAbilities; ++j) + for (ability = 0; ability < maxAbilities; ++ability) { - if (speciesInfo->abilities[j] == partyData[i].ability) + if (speciesInfo->abilities[ability] == partyData[i].ability) break; } - if (j < maxAbilities) - SetMonData(&party[i], MON_DATA_ABILITY_NUM, &j); + if (ability >= maxAbilities) + ability = 0; } + else if (B_TRAINER_MON_RANDOM_ABILITY) + { + const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species]; + ability = personalityHash % 3; + while (speciesInfo->abilities[ability] == ABILITY_NONE) + { + ability--; + } + } + SetMonData(&party[i], MON_DATA_ABILITY_NUM, &ability); SetMonData(&party[i], MON_DATA_FRIENDSHIP, &(partyData[i].friendship)); if (partyData[i].ball != ITEM_NONE) { @@ -3724,6 +3734,24 @@ static void DoBattleIntro(void) gBattleStruct->overworldWeatherDone = FALSE; SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers Ai_InitPartyStruct(); // Save mons party counts, and first 2/4 mons on the battlefield. + + // Try to set a status to start the battle with + gBattleStruct->startingStatus = 0; + if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && gTrainers[gTrainerBattleOpponent_B].startingStatus) + { + gBattleStruct->startingStatus = gTrainers[gTrainerBattleOpponent_B].startingStatus; + gBattleStruct->startingStatusTimer = 0; // infinite + } + else if (gTrainers[gTrainerBattleOpponent_A].startingStatus) + { + gBattleStruct->startingStatus = gTrainers[gTrainerBattleOpponent_A].startingStatus; + gBattleStruct->startingStatusTimer = 0; // infinite + } + else if (B_VAR_STARTING_STATUS != 0) + { + gBattleStruct->startingStatus = VarGet(B_VAR_STARTING_STATUS); + gBattleStruct->startingStatusTimer = VarGet(B_VAR_STARTING_STATUS_TIMER); + } gBattleMainFunc = TryDoEventsBeforeFirstTurn; } break; @@ -3789,6 +3817,14 @@ static void TryDoEventsBeforeFirstTurn(void) return; } + if (!gBattleStruct->startingStatusDone + && gBattleStruct->startingStatus + && AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_STATUSES, 0, 0, ABILITYEFFECT_SWITCH_IN_STATUSES, 0) != 0) + { + gBattleStruct->startingStatusDone = TRUE; + return; + } + // Totem boosts for (i = 0; i < gBattlersCount; i++) { @@ -4977,7 +5013,7 @@ static void TurnValuesCleanUp(bool8 var0) else { memset(&gProtectStructs[i], 0, sizeof(struct ProtectStruct)); - memset(&gQueuedStatBoosts[i], 0, sizeof(gQueuedStatBoosts)); + memset(&gQueuedStatBoosts[i], 0, sizeof(struct QueuedStatBoost)); if (gDisableStructs[i].isFirstTurn) gDisableStructs[i].isFirstTurn--; diff --git a/src/battle_message.c b/src/battle_message.c index a99f3cde9d..c17997dab0 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -559,6 +559,7 @@ static const u8 sText_PkmnSwitchedStatChanges[] = _("{B_ATK_NAME_WITH_PREFIX} sw static const u8 sText_PkmnSurroundedWithVeilOfWater[] = _("{B_ATK_NAME_WITH_PREFIX} surrounded itself\nwith a veil of water!"); static const u8 sText_PkmnLevitatedOnElectromagnetism[] = _("{B_ATK_NAME_WITH_PREFIX} levitated on\nelectromagnetism!"); static const u8 sText_PkmnTwistedDimensions[] = _("{B_ATK_NAME_WITH_PREFIX} twisted\nthe dimensions!"); +static const u8 sText_DimensionsWereTwisted[] = _("The dimensions were\ntwisted!"); static const u8 sText_PointedStonesFloat[] =_("Pointed stones float in the air\naround {B_DEF_TEAM2} team!"); static const u8 sText_CloakedInMysticalMoonlight[] =_("It became cloaked in mystical\nmoonlight!"); static const u8 sText_TrappedBySwirlingMagma[] =_("{B_DEF_NAME_WITH_PREFIX} became\ntrapped by swirling magma!"); @@ -567,9 +568,11 @@ static const u8 sText_ProtectedTeam[] =_("{B_CURRENT_MOVE} protected\n{B_ATK_TEA static const u8 sText_SharedItsGuard[] =_("{B_ATK_NAME_WITH_PREFIX} shared its\nguard with the target!"); static const u8 sText_SharedItsPower[] =_("{B_ATK_NAME_WITH_PREFIX} shared its\npower with the target!"); static const u8 sText_SwapsDefAndSpDefOfAllPkmn[] =_("It created a bizarre area in which the\nDefense and Sp. Def stats are swapped!"); +static const u8 sText_BizzareAreaCreated[] =_("A bizarre area was created in which the\nDefense and Sp. Def stats are swapped!"); static const u8 sText_BecameNimble[] =_("{B_ATK_NAME_WITH_PREFIX} became nimble!"); static const u8 sText_HurledIntoTheAir[] =_("{B_DEF_NAME_WITH_PREFIX} was hurled\ninto the air!"); static const u8 sText_HeldItemsLoseEffects[] =_("It created a bizarre area in which\nPokémon's held items lose their effects!"); +static const u8 sText_BizarreArenaCreated[] =_("A bizarre area was created!\nHold items lost their effects!"); static const u8 sText_FellStraightDown[] =_("{B_DEF_NAME_WITH_PREFIX} fell\nstraight down!"); static const u8 sText_TargetChangedType[] =_("{B_DEF_NAME_WITH_PREFIX} transformed\ninto the {B_BUFF1} type!"); static const u8 sText_PkmnAcquiredSimple[] =_("{B_DEF_NAME_WITH_PREFIX} acquired\nSimple!"); @@ -840,9 +843,11 @@ static const u8 sText_ItemWasUsedUp[] = _("The {B_LAST_ITEM}\nwas used up..."); static const u8 sText_AttackerLostItsType[] = _("{B_ATK_NAME_WITH_PREFIX} lost\nits {B_BUFF1} type!"); static const u8 sText_ShedItsTail[] = _("{B_ATK_NAME_WITH_PREFIX} shed its tail\nto create a decoy!"); static const u8 sText_SupersweetAromaWafts[] = _("A supersweet aroma is wafting from\nthe syrup covering {B_ATK_NAME_WITH_PREFIX}!"); +static const u8 sText_TidyingUpComplete[] = _("Tidying up complete!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_TIDYINGUPCOMPLETE - BATTLESTRINGS_TABLE_START] = sText_TidyingUpComplete, [STRINGID_SUPERSWEETAROMAWAFTS - BATTLESTRINGS_TABLE_START] = sText_SupersweetAromaWafts, [STRINGID_SHEDITSTAIL - BATTLESTRINGS_TABLE_START] = sText_ShedItsTail, [STRINGID_ELECTROSHOTCHARGING - BATTLESTRINGS_TABLE_START] = sText_ElectroShotCharging, @@ -1384,6 +1389,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_PKMNSURROUNDEDWITHVEILOFWATER - BATTLESTRINGS_TABLE_START] = sText_PkmnSurroundedWithVeilOfWater, [STRINGID_PKMNLEVITATEDONELECTROMAGNETISM - BATTLESTRINGS_TABLE_START] = sText_PkmnLevitatedOnElectromagnetism, [STRINGID_PKMNTWISTEDDIMENSIONS - BATTLESTRINGS_TABLE_START] = sText_PkmnTwistedDimensions, + [STRINGID_DIMENSIONSWERETWISTED - BATTLESTRINGS_TABLE_START] = sText_DimensionsWereTwisted, [STRINGID_POINTEDSTONESFLOAT - BATTLESTRINGS_TABLE_START] = sText_PointedStonesFloat, [STRINGID_CLOAKEDINMYSTICALMOONLIGHT - BATTLESTRINGS_TABLE_START] = sText_CloakedInMysticalMoonlight, [STRINGID_TRAPPEDBYSWIRLINGMAGMA - BATTLESTRINGS_TABLE_START] = sText_TrappedBySwirlingMagma, @@ -1392,9 +1398,11 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_SHAREDITSGUARD - BATTLESTRINGS_TABLE_START] = sText_SharedItsGuard, [STRINGID_SHAREDITSPOWER - BATTLESTRINGS_TABLE_START] = sText_SharedItsPower, [STRINGID_SWAPSDEFANDSPDEFOFALLPOKEMON - BATTLESTRINGS_TABLE_START] = sText_SwapsDefAndSpDefOfAllPkmn, + [STRINGID_BIZARREAREACREATED - BATTLESTRINGS_TABLE_START] = sText_BizzareAreaCreated, [STRINGID_BECAMENIMBLE - BATTLESTRINGS_TABLE_START] = sText_BecameNimble, [STRINGID_HURLEDINTOTHEAIR - BATTLESTRINGS_TABLE_START] = sText_HurledIntoTheAir, [STRINGID_HELDITEMSLOSEEFFECTS - BATTLESTRINGS_TABLE_START] = sText_HeldItemsLoseEffects, + [STRINGID_BIZARREARENACREATED - BATTLESTRINGS_TABLE_START] = sText_BizarreArenaCreated, [STRINGID_FELLSTRAIGHTDOWN - BATTLESTRINGS_TABLE_START] = sText_FellStraightDown, [STRINGID_TARGETCHANGEDTYPE - BATTLESTRINGS_TABLE_START] = sText_TargetChangedType, [STRINGID_PKMNACQUIREDSIMPLE - BATTLESTRINGS_TABLE_START] = sText_PkmnAcquiredSimple, @@ -1563,6 +1571,19 @@ const u16 gMentalHerbCureStringIds[] = [B_MSG_MENTALHERBCURE_DISABLE] = STRINGID_PKMNMOVEDISABLEDNOMORE, }; +const u16 gStartingStatusStringIds[B_MSG_STARTING_STATUS_COUNT] = +{ + [B_MSG_TERRAIN_SET_MISTY] = STRINGID_TERRAINBECOMESMISTY, + [B_MSG_TERRAIN_SET_ELECTRIC] = STRINGID_TERRAINBECOMESELECTRIC, + [B_MSG_TERRAIN_SET_PSYCHIC] = STRINGID_TERRAINBECOMESPSYCHIC, + [B_MSG_TERRAIN_SET_GRASSY] = STRINGID_TERRAINBECOMESGRASSY, + [B_MSG_SET_TRICK_ROOM] = STRINGID_DIMENSIONSWERETWISTED, + [B_MSG_SET_MAGIC_ROOM] = STRINGID_BIZARREARENACREATED, + [B_MSG_SET_WONDER_ROOM] = STRINGID_BIZARREAREACREATED, + [B_MSG_SET_TAILWIND_PLAYER] = STRINGID_TAILWINDBLEW, + [B_MSG_SET_TAILWIND_OPPONENT] = STRINGID_TAILWINDBLEW, +}; + const u16 gTerrainStringIds[B_MSG_TERRAIN_COUNT] = { [B_MSG_TERRAIN_SET_MISTY] = STRINGID_TERRAINBECOMESMISTY, @@ -3189,7 +3210,7 @@ static const u8 *BattleStringGetPlayerName(u8 *text, u8 battler) if (DECAP_ENABLED && !DECAP_NICKNAMES && toCpy != text && *toCpy != CHAR_FIXED_CASE) { *text = CHAR_FIXED_CASE; - StringCopyN(text+1, toCpy, PLAYER_NAME_LENGTH); + StringCopyN(text+1, toCpy, PLAYER_NAME_LENGTH + 1); toCpy = text; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index c3c39bc10d..7a3dc7ff76 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2843,7 +2843,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) { case STATUS1_SLEEP: // check active uproar - if (battlerAbility != ABILITY_SOUNDPROOF) + if (battlerAbility != ABILITY_SOUNDPROOF || B_UPROAR_IGNORE_SOUNDPROOF >= GEN_5) { for (i = 0; i < gBattlersCount && !(gBattleMons[i].status2 & STATUS2_UPROAR); i++) ; @@ -3451,7 +3451,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) gBattlescriptCurrInstr = BattleScript_AllStatsUp; } break; - case MOVE_EFFECT_RAPIDSPIN: + case MOVE_EFFECT_RAPID_SPIN: BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_RapidSpinAway; break; @@ -8397,6 +8397,44 @@ static bool32 TryDefogClear(u32 battlerAtk, bool32 clear) return FALSE; } +static bool32 TryTidyUpClear(u32 battlerAtk, bool32 clear) +{ + s32 i; + u8 saveBattler = gBattlerAttacker; + + for (i = 0; i < NUM_BATTLE_SIDES; i++) + { + struct SideTimer *sideTimer = &gSideTimers[i]; + u32 *sideStatuses = &gSideStatuses[i]; + + gBattlerAttacker = i; // For correct battle string. Ally's / Foe's + DEFOG_CLEAR(SIDE_STATUS_SPIKES, spikesAmount, BattleScript_SpikesDefog, 0); + DEFOG_CLEAR(SIDE_STATUS_STEALTH_ROCK, stealthRockAmount, BattleScript_StealthRockDefog, 0); + DEFOG_CLEAR(SIDE_STATUS_TOXIC_SPIKES, toxicSpikesAmount, BattleScript_ToxicSpikesDefog, 0); + DEFOG_CLEAR(SIDE_STATUS_STICKY_WEB, stickyWebAmount, BattleScript_StickyWebDefog, 0); + } + + for (i = 0; i < MAX_BATTLERS_COUNT; i++) + { + if (gBattleMons[i].status2 & STATUS2_SUBSTITUTE) + { + if (clear) + { + gBattlerTarget = i; + gDisableStructs[i].substituteHP = 0; + gBattleMons[i].status2 &= ~STATUS2_SUBSTITUTE; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_SubstituteFade; + } + gBattlerAttacker = saveBattler; + return TRUE; + } + } + + gBattlerAttacker = saveBattler; + return FALSE; +} + u32 IsFlowerVeilProtected(u32 battler) { if (IS_BATTLER_OF_TYPE(battler, TYPE_GRASS)) @@ -11142,7 +11180,7 @@ bool8 UproarWakeUpCheck(u8 battler) for (i = 0; i < gBattlersCount; i++) { - if (!(gBattleMons[i].status2 & STATUS2_UPROAR) || GetBattlerAbility(battler) == ABILITY_SOUNDPROOF) + if (!(gBattleMons[i].status2 & STATUS2_UPROAR) || (GetBattlerAbility(battler) == ABILITY_SOUNDPROOF && B_UPROAR_IGNORE_SOUNDPROOF < GEN_5)) continue; gBattleScripting.battler = i; @@ -16671,3 +16709,23 @@ void BS_TryUpdateRecoilTracker(void) TryUpdateEvolutionTracker(EVO_LEVEL_RECOIL_DAMAGE_FEMALE, gBattleMoveDamage); gBattlescriptCurrInstr = cmd->nextInstr; } + +void BS_TryTidyUp(void) +{ + NATIVE_ARGS(u8 clear, const u8 *jumpInstr); + + if (cmd->clear) + { + if (TryTidyUpClear(gEffectBattler, TRUE)) + return; + else + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + if (TryTidyUpClear(gBattlerAttacker, FALSE)) + gBattlescriptCurrInstr = cmd->jumpInstr; + else + gBattlescriptCurrInstr = cmd->nextInstr; + } +} diff --git a/src/battle_tv.c b/src/battle_tv.c index cb70d1deed..80cb0020f8 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -942,7 +942,7 @@ static void AddMovePoints(u8 caseId, u16 arg1, u8 arg2, u8 arg3) // Various cases to add/remove points if (gMovesInfo[arg2].recoil > 0) baseFromEffect++; // Recoil moves - if (MoveHasMoveEffect(arg2, MOVE_EFFECT_RAPIDSPIN)) + if (MoveHasMoveEffect(arg2, MOVE_EFFECT_RAPID_SPIN)) baseFromEffect++; if (MoveHasMoveEffect(arg2, MOVE_EFFECT_SP_ATK_TWO_DOWN) || MoveHasMoveEffect(arg2, MOVE_EFFECT_ATK_DEF_DOWN)) baseFromEffect += 2; // Overheat, Superpower, etc. diff --git a/src/battle_util.c b/src/battle_util.c index 87ff4d05e5..7e3687448e 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1907,7 +1907,7 @@ u8 DoFieldEndTurnEffects(void) gBattlerAttacker = gSideTimers[side].tailwindBattlerId; if (gSideStatuses[side] & SIDE_STATUS_TAILWIND) { - if (--gSideTimers[side].tailwindTimer == 0) + if (gSideTimers[side].tailwindTimer > 0 && --gSideTimers[side].tailwindTimer == 0) { gSideStatuses[side] &= ~SIDE_STATUS_TAILWIND; BattleScriptExecute(BattleScript_TailwindEnds); @@ -2088,7 +2088,7 @@ u8 DoFieldEndTurnEffects(void) } break; case ENDTURN_TRICK_ROOM: - if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && --gFieldTimers.trickRoomTimer == 0) + if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer > 0 && --gFieldTimers.trickRoomTimer == 0) { gFieldStatuses &= ~STATUS_FIELD_TRICK_ROOM; BattleScriptExecute(BattleScript_TrickRoomEnds); @@ -2097,7 +2097,7 @@ u8 DoFieldEndTurnEffects(void) gBattleStruct->turnCountersTracker++; break; case ENDTURN_WONDER_ROOM: - if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM && --gFieldTimers.wonderRoomTimer == 0) + if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM && gFieldTimers.wonderRoomTimer > 0 && --gFieldTimers.wonderRoomTimer == 0) { gFieldStatuses &= ~STATUS_FIELD_WONDER_ROOM; BattleScriptExecute(BattleScript_WonderRoomEnds); @@ -2106,7 +2106,7 @@ u8 DoFieldEndTurnEffects(void) gBattleStruct->turnCountersTracker++; break; case ENDTURN_MAGIC_ROOM: - if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM && --gFieldTimers.magicRoomTimer == 0) + if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM && gFieldTimers.magicRoomTimer > 0 && --gFieldTimers.magicRoomTimer == 0) { gFieldStatuses &= ~STATUS_FIELD_MAGIC_ROOM; BattleScriptExecute(BattleScript_MagicRoomEnds); @@ -4000,78 +4000,155 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 switch (caseID) { - case ABILITYEFFECT_SWITCH_IN_TERRAIN: + case ABILITYEFFECT_SWITCH_IN_STATUSES: // starting field/side/etc statuses with a variable { - u8 varTerrainTimer = VarGet(B_VAR_TERRAIN_TIMER); + u8 timerVal = gBattleStruct->startingStatusTimer; gBattleScripting.battler = battler; - if (VarGet(B_VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY) + switch (gBattleStruct->startingStatus) { - u16 terrainFlags = VarGet(B_VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY; // only works for status flag (1 << 15) - - if (varTerrainTimer == 0) + case STARTING_STATUS_ELECTRIC_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)) { - gFieldStatuses = terrainFlags | STATUS_FIELD_TERRAIN_PERMANENT; // terrain is permanent - } - else - { - gFieldStatuses |= terrainFlags; - gFieldTimers.terrainTimer = varTerrainTimer; - } - - switch (VarGet(B_VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY) - { - case STATUS_FIELD_ELECTRIC_TERRAIN: gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; - break; - case STATUS_FIELD_MISTY_TERRAIN: + gFieldStatuses |= STATUS_FIELD_ELECTRIC_TERRAIN; + if (timerVal == 0) + gFieldStatuses |= STATUS_FIELD_TERRAIN_PERMANENT; + else + gFieldTimers.terrainTimer = timerVal; + effect = 2; + } + break; + case STARTING_STATUS_MISTY_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; - break; - case STATUS_FIELD_GRASSY_TERRAIN: + gFieldStatuses |= STATUS_FIELD_MISTY_TERRAIN; + if (timerVal == 0) + gFieldStatuses |= STATUS_FIELD_TERRAIN_PERMANENT; + else + gFieldTimers.terrainTimer = timerVal; + effect = 2; + } + break; + case STARTING_STATUS_GRASSY_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)) + { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_GRASSY; - break; - case STATUS_FIELD_PSYCHIC_TERRAIN: + gFieldStatuses |= STATUS_FIELD_GRASSY_TERRAIN; + if (timerVal == 0) + gFieldStatuses |= STATUS_FIELD_TERRAIN_PERMANENT; + else + gFieldTimers.terrainTimer = timerVal; + effect = 2; + } + break; + case STARTING_STATUS_PSYCHIC_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)) + { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC; - break; + gFieldStatuses |= STATUS_FIELD_PSYCHIC_TERRAIN; + if (timerVal == 0) + gFieldStatuses |= STATUS_FIELD_TERRAIN_PERMANENT; + else + gFieldTimers.terrainTimer = timerVal; + effect = 2; } - BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); - effect++; + break; + case STARTING_STATUS_TRICK_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_TRICK_ROOM; + gFieldStatuses |= STATUS_FIELD_TRICK_ROOM; + gBattleScripting.animArg1 = B_ANIM_TRICK_ROOM; + if (timerVal == 0) + gFieldTimers.trickRoomTimer = 0; // infinite + else + gFieldTimers.trickRoomTimer = 5; + effect = 1; + } + break; + case STARTING_STATUS_MAGIC_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_MAGIC_ROOM)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_MAGIC_ROOM; + gFieldStatuses |= STATUS_FIELD_MAGIC_ROOM; + gBattleScripting.animArg1 = B_ANIM_MAGIC_ROOM; + if (timerVal == 0) + gFieldTimers.magicRoomTimer = 0; // infinite + else + gFieldTimers.magicRoomTimer = 5; + effect = 1; + } + break; + case STARTING_STATUS_WONDER_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_WONDER_ROOM)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_WONDER_ROOM; + gFieldStatuses |= STATUS_FIELD_WONDER_ROOM; + gBattleScripting.animArg1 = B_ANIM_WONDER_ROOM; + if (timerVal == 0) + gFieldTimers.wonderRoomTimer = 0; // infinite + else + gFieldTimers.wonderRoomTimer = 5; + effect = 1; + } + break; + case STARTING_STATUS_TAILWIND_PLAYER: + if (!(gSideStatuses[B_SIDE_PLAYER] & SIDE_STATUS_TAILWIND)) + { + gBattlerAttacker = B_POSITION_PLAYER_LEFT; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_TAILWIND_PLAYER; + gSideStatuses[B_SIDE_PLAYER] |= SIDE_STATUS_TAILWIND; + gBattleScripting.animArg1 = B_ANIM_TAILWIND; + if (timerVal == 0) + gSideTimers[B_SIDE_PLAYER].tailwindTimer = 0; // infinite + else + gSideTimers[B_SIDE_PLAYER].tailwindTimer = 5; + effect = 1; + } + break; + case STARTING_STATUS_TAILWIND_OPPONENT: + if (!(gSideStatuses[B_SIDE_OPPONENT] & SIDE_STATUS_TAILWIND)) + { + gBattlerAttacker = B_POSITION_OPPONENT_LEFT; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_TAILWIND_OPPONENT; + gSideStatuses[B_SIDE_OPPONENT] |= SIDE_STATUS_TAILWIND; + gBattleScripting.animArg1 = B_ANIM_TAILWIND; + if (timerVal == 0) + gSideTimers[B_SIDE_OPPONENT].tailwindTimer = 0; // infinite + else + gSideTimers[B_SIDE_OPPONENT].tailwindTimer = 5; + effect = 1; + } + break; } - else if (B_THUNDERSTORM_TERRAIN == TRUE - && GetCurrentWeather() == WEATHER_RAIN_THUNDERSTORM - && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)) - { - // overworld weather started rain, so just do electric terrain anim - if (varTerrainTimer == 0) - { - gFieldStatuses = (STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); - } - else - { - gFieldStatuses |= STATUS_FIELD_ELECTRIC_TERRAIN; - gFieldTimers.terrainTimer = varTerrainTimer; - } - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; + + if (effect == 1) + BattleScriptPushCursorAndCallback(BattleScript_OverworldStatusStarts); + else if (effect == 2) BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); - effect++; - } - else if (B_FOG_TERRAIN == TRUE - && (GetCurrentWeather() == WEATHER_FOG_HORIZONTAL || GetCurrentWeather() == WEATHER_FOG_DIAGONAL) - && !(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) - { - if (varTerrainTimer == 0) - { - gFieldStatuses = (STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); - } - else - { - gFieldStatuses |= STATUS_FIELD_ELECTRIC_TERRAIN; - gFieldTimers.terrainTimer = varTerrainTimer; - } - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; - BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); - effect++; - } + } + break; + case ABILITYEFFECT_SWITCH_IN_TERRAIN: // terrain starting from overworld weather + if (B_THUNDERSTORM_TERRAIN == TRUE + && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + && GetCurrentWeather() == WEATHER_RAIN_THUNDERSTORM) + { + // overworld weather started rain, so just do electric terrain anim + gFieldStatuses = (STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; + BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); + effect++; + } + else if (B_FOG_TERRAIN == TRUE + && (GetCurrentWeather() == WEATHER_FOG_HORIZONTAL || GetCurrentWeather() == WEATHER_FOG_DIAGONAL) + && !(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + { + gFieldStatuses = (STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; + BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); + effect++; } break; case ABILITYEFFECT_SWITCH_IN_WEATHER: diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 0aa18ceeea..310eef23af 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -1052,12 +1052,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleTvScore = 1, }, - [EFFECT_TEETER_DANCE] = - { - .battleScript = BattleScript_EffectTeeterDance, - .battleTvScore = 6, - }, - [EFFECT_HIT_ESCAPE] = { .battleScript = BattleScript_EffectHitEscape, @@ -2200,12 +2194,14 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = { .battleScript = BattleScript_EffectShedTail, .battleTvScore = 0, // TODO: Assign points + .encourageEncore = TRUE, }, [EFFECT_UPPER_HAND] = { .battleScript = BattleScript_EffectUpperHand, .battleTvScore = 0, // TODO: Assign points + .encourageEncore = TRUE, }, [EFFECT_DRAGON_CHEER] = @@ -2220,4 +2216,11 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleScript = BattleScript_EffectHit, .battleTvScore = 0, // TODO: Assign points }, + + [EFFECT_TIDY_UP] = + { + .battleScript = BattleScript_EffectTidyUp, + .battleTvScore = 0, // TODO: Assign points + .encourageEncore = TRUE, + }, }; diff --git a/src/data/battle_partners.h b/src/data/battle_partners.h index 806a50bdf6..39bb91132f 100644 --- a/src/data/battle_partners.h +++ b/src/data/battle_partners.h @@ -17,5 +17,4 @@ const struct Trainer gBattlePartners[] = { .trainerPic = TRAINER_BACK_PIC_STEVEN, .trainerName = _("STEVEN"), }, - }; diff --git a/src/data/graphics/pokemon.h b/src/data/graphics/pokemon.h index 718d417ba1..296bd1ae90 100644 --- a/src/data/graphics/pokemon.h +++ b/src/data/graphics/pokemon.h @@ -15778,9 +15778,9 @@ const u32 gObjectEventPic_Substitute[] = INCBIN_COMP("graphics/pokemon/question_ // const u8 gMonFootprint_Squawkabilly[] = INCBIN_U8("graphics/pokemon/squawkabilly/footprint.1bpp"); #endif //P_FOOTPRINTS - const u32 gMonPalette_SquawkabillyGreenPlumage[] = INCBIN_U32("graphics/pokemon/squawkabilly/green_plumage/normal.gbapal.lz"); - const u32 gMonShinyPalette_SquawkabillyGreenPlumage[] = INCBIN_U32("graphics/pokemon/squawkabilly/green_plumage/shiny.gbapal.lz"); - const u8 gMonIcon_SquawkabillyGreenPlumage[] = INCBIN_U8("graphics/pokemon/squawkabilly/green_plumage/icon.4bpp"); + const u32 gMonPalette_SquawkabillyGreenPlumage[] = INCBIN_U32("graphics/pokemon/squawkabilly/normal.gbapal.lz"); + const u32 gMonShinyPalette_SquawkabillyGreenPlumage[] = INCBIN_U32("graphics/pokemon/squawkabilly/shiny.gbapal.lz"); + const u8 gMonIcon_SquawkabillyGreenPlumage[] = INCBIN_U8("graphics/pokemon/squawkabilly/icon.4bpp"); #if OW_FOLLOWERS_ENABLED // const u32 gObjectEventPic_SquawkabillyGreenPlumage[] = INCBIN_COMP("graphics/pokemon/squawkabilly/green_plumage/follower.4bpp"); #endif //OW_FOLLOWERS_ENABLED @@ -16419,11 +16419,11 @@ const u32 gObjectEventPic_Substitute[] = INCBIN_COMP("graphics/pokemon/question_ #endif //P_FAMILY_DONDOZO #if P_FAMILY_TATSUGIRI - const u32 gMonFrontPic_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/curly/front.4bpp.lz"); - const u32 gMonPalette_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/curly/normal.gbapal.lz"); - const u32 gMonBackPic_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/curly/back.4bpp.lz"); - const u32 gMonShinyPalette_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/curly/shiny.gbapal.lz"); - const u8 gMonIcon_TatsugiriCurly[] = INCBIN_U8("graphics/pokemon/tatsugiri/curly/icon.4bpp"); + const u32 gMonFrontPic_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/front.4bpp.lz"); + const u32 gMonPalette_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/normal.gbapal.lz"); + const u32 gMonBackPic_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/back.4bpp.lz"); + const u32 gMonShinyPalette_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/shiny.gbapal.lz"); + const u8 gMonIcon_TatsugiriCurly[] = INCBIN_U8("graphics/pokemon/tatsugiri/icon.4bpp"); #if P_FOOTPRINTS // const u8 gMonFootprint_Tatsugiri[] = INCBIN_U8("graphics/pokemon/tatsugiri/footprint.1bpp"); #endif //P_FOOTPRINTS diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 280e38d2ad..6ecb89e24b 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -1847,6 +1847,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, + .zMove = { .powerOverride = 120 }, .ignoresKingsRock = (B_UPDATED_MOVE_FLAGS == GEN_3 || B_UPDATED_MOVE_FLAGS == GEN_4), .healingMove = B_HEAL_BLOCKING >= GEN_6, .contestEffect = CONTEST_EFFECT_BADLY_STARTLE_FRONT_MON, @@ -5638,7 +5639,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .category = DAMAGE_CATEGORY_PHYSICAL, .makesContact = TRUE, .additionalEffects = ADDITIONAL_EFFECTS({ - .moveEffect = MOVE_EFFECT_RAPIDSPIN, + .moveEffect = MOVE_EFFECT_RAPID_SPIN, .self = TRUE, } #if B_SPEED_BUFFING_RAPID_SPIN >= GEN_8 @@ -7313,7 +7314,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Confuses all Pokémon on\n" "the scene."), - .effect = EFFECT_TEETER_DANCE, + .effect = EFFECT_CONFUSE, .power = 0, .type = TYPE_NORMAL, .accuracy = 100, @@ -7649,6 +7650,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, + .zMove = { .powerOverride = 160 }, .ballisticMove = TRUE, .contestEffect = CONTEST_EFFECT_HIGHLY_APPEALING, .contestCategory = CONTEST_CATEGORY_SMART, @@ -12247,6 +12249,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, + .zMove = { .powerOverride = 160 }, .argument = STATUS1_ANY, .contestEffect = CONTEST_EFFECT_BETTER_IF_SAME_TYPE, .contestCategory = CONTEST_CATEGORY_SMART, @@ -13114,6 +13117,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, + .zMove = { .powerOverride = 180 }, .makesContact = TRUE, .strikeCount = 2, .contestEffect = CONTEST_EFFECT_REPETITION_NOT_BORING, @@ -13449,6 +13453,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, + .zMove = { .powerOverride = 220 }, .makesContact = TRUE, .metronomeBanned = TRUE, .additionalEffects = ADDITIONAL_EFFECTS({ @@ -13516,6 +13521,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, + .zMove = { .powerOverride = 170 }, .argument = TYPE_FLYING, .makesContact = TRUE, .minimizeDoubleDamage = TRUE, @@ -15021,6 +15027,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 2, .category = DAMAGE_CATEGORY_PHYSICAL, + .argument = MOVE_FIRST_IMPRESSION, .makesContact = TRUE, .contestEffect = CONTEST_EFFECT_BETTER_IF_FIRST, .contestCategory = CONTEST_CATEGORY_COOL, @@ -18927,7 +18934,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sheerForceBoost = TRUE, .makesContact = TRUE, .additionalEffects = ADDITIONAL_EFFECTS({ - .moveEffect = MOVE_EFFECT_RAPIDSPIN, + .moveEffect = MOVE_EFFECT_RAPID_SPIN, .self = TRUE, }, { @@ -19200,7 +19207,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "User tidies up hazards and\n" "raises its Attack and Speed."), - .effect = EFFECT_PLACEHOLDER, // EFFECT_TIDY_UP + .effect = EFFECT_TIDY_UP, .power = 0, .type = TYPE_NORMAL, .accuracy = 0, diff --git a/src/data/partner_parties.h b/src/data/partner_parties.h index fec80c6ca1..1b071ec28e 100644 --- a/src/data/partner_parties.h +++ b/src/data/partner_parties.h @@ -2,7 +2,7 @@ static const struct TrainerMon sParty_StevenPartner[] = { { .species = SPECIES_METANG, .lvl = 42, - .nature = TRAINER_PARTY_NATURE(NATURE_BRAVE), + .nature = NATURE_BRAVE, .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), .ev = TRAINER_PARTY_EVS(0, 252, 252, 0, 6, 0), .moves = {MOVE_LIGHT_SCREEN, MOVE_PSYCHIC, MOVE_REFLECT, MOVE_METAL_CLAW}, @@ -10,7 +10,7 @@ static const struct TrainerMon sParty_StevenPartner[] = { { .species = SPECIES_SKARMORY, .lvl = 43, - .nature = TRAINER_PARTY_NATURE(NATURE_IMPISH), + .nature = NATURE_IMPISH, .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), .ev = TRAINER_PARTY_EVS(252, 0, 0, 0, 6, 252), .moves = {MOVE_TOXIC, MOVE_AERIAL_ACE, MOVE_PROTECT, MOVE_STEEL_WING}, @@ -18,7 +18,7 @@ static const struct TrainerMon sParty_StevenPartner[] = { { .species = SPECIES_AGGRON, .lvl = 44, - .nature = TRAINER_PARTY_NATURE(NATURE_ADAMANT), + .nature = NATURE_ADAMANT, .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), .ev = TRAINER_PARTY_EVS(0, 252, 0, 0, 252, 6), .moves = {MOVE_THUNDER, MOVE_PROTECT, MOVE_SOLAR_BEAM, MOVE_DRAGON_CLAW}, diff --git a/src/data/pokemon/species_info/gen_5.h b/src/data/pokemon/species_info/gen_5.h index cacc857ea1..bdcd04142f 100644 --- a/src/data/pokemon/species_info/gen_5.h +++ b/src/data/pokemon/species_info/gen_5.h @@ -3125,7 +3125,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .frontAnimFrames = sAnims_Basculegion, \ .frontPicYOffset = 0, \ .enemyMonElevation = 5, \ - .backPicYOffset = 0, \ + .backPicYOffset = 8, \ LEARNSETS(Basculegion), \ .formSpeciesIdTable = sBasculegionFormSpeciesIdTable //.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, diff --git a/src/data/pokemon/species_info/gen_6.h b/src/data/pokemon/species_info/gen_6.h index 483699f6ba..7c9581e73f 100644 --- a/src/data/pokemon/species_info/gen_6.h +++ b/src/data/pokemon/species_info/gen_6.h @@ -458,8 +458,8 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .genderRatio = MON_MALE, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_BATTLE_BOND, ABILITY_NONE, ABILITY_NONE }, - .frontAnimId = ANIM_FLICKER_INCREASING, - .backAnimId = BACK_ANIM_V_STRETCH, + .frontAnimId = ANIM_V_STRETCH, + .backAnimId = BACK_ANIM_JOLT_RIGHT, .formChangeTable = sGreninjaBattleBondFormChangeTable, }, @@ -488,7 +488,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .backPicYOffset = 11, PALETTES(GreninjaAsh), ICON(GreninjaAsh, 0), - .frontAnimId = ANIM_FLICKER_INCREASING, + .frontAnimId = ANIM_V_STRETCH, .backAnimId = BACK_ANIM_SHAKE_GLOW_BLUE, .formChangeTable = sGreninjaBattleBondFormChangeTable, }, diff --git a/src/overworld.c b/src/overworld.c index 25ba2b6a73..6e0338da0f 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -402,12 +402,12 @@ void Overworld_ResetStateAfterDigEscRope(void) #if B_RESET_FLAGS_VARS_AFTER_WHITEOUT == TRUE void Overworld_ResetBattleFlagsAndVars(void) { - #if B_VAR_TERRAIN != 0 - VarSet(B_VAR_TERRAIN, 0); + #if B_VAR_STARTING_STATUS != 0 + VarSet(B_VAR_STARTING_STATUS, 0); #endif - #if B_VAR_TERRAIN_TIMER != 0 - VarSet(B_VAR_TERRAIN_TIMER, 0); + #if B_VAR_STARTING_STATUS_TIMER != 0 + VarSet(B_VAR_STARTING_STATUS_TIMER, 0); #endif #if B_VAR_WILD_AI_FLAGS != 0 diff --git a/src/pokedex.c b/src/pokedex.c index a7aa98c424..74a95aa1d7 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -274,8 +274,19 @@ static void Task_HandleCaughtMonPageInput(u8); static void Task_ExitCaughtMonPage(u8); static void SpriteCB_SlideCaughtMonToCenter(struct Sprite *sprite); static void PrintMonInfo(u32 num, u32, u32 owned, u32 newEntry); -static void PrintMonHeight(u16 height, u8 left, u8 top); -static void PrintMonWeight(u16 weight, u8 left, u8 top); +static u32 GetMeasurementTextPositions(u32 textElement); +static void PrintUnknownMonMeasurements(void); +static u8* GetUnknownMonHeightString(void); +static u8* GetUnknownMonWeightString(void); +static u8* ReplaceDecimalSeparator(const u8* originalString); +static void PrintOwnedMonMeasurements(u16 species); +static void PrintOwnedMonHeight(u16 species); +static void PrintOwnedMonWeight(u16 species); +static u8* ConvertMonHeightToImperialString(u32 height); +static u8* ConvertMonHeightToMetricString(u32 height); +static u8* ConvertMonWeightToImperialString(u32 weight); +static u8* ConvertMonWeightToMetricString(u32 weight); +static u8* ConvertMeasurementToMetricString(u32 num, u32* index); static void ResetOtherVideoRegisters(u16); static u8 PrintCryScreenSpeciesName(u8, u16, u8, u8); static void PrintDecimalNum(u8 windowId, u16 num, u8 left, u8 top); @@ -4141,7 +4152,7 @@ static void PrintMonInfo(u32 num, u32 value, u32 owned, u32 newEntry) const u8 *name; const u8 *category; const u8 *description; - u8 digitCount = (NATIONAL_DEX_COUNT > 999 && IsNationalPokedexEnabled()) ? 4 : 3; + u8 digitCount = (NATIONAL_DEX_COUNT > 999 && IsNationalPokedexEnabled()) ? 4 : 3; if (newEntry) PrintInfoScreenText(gText_PokedexRegistration, GetStringCenterAlignXOffset(FONT_NORMAL, gText_PokedexRegistration, DISPLAY_WIDTH), 0); @@ -4169,18 +4180,7 @@ static void PrintMonInfo(u32 num, u32 value, u32 owned, u32 newEntry) category = gText_5MarksPokemon; } PrintInfoScreenText(category, 0x64, 0x29); - PrintInfoScreenText(gText_HTHeight, 0x60, 0x39); - PrintInfoScreenText(gText_WTWeight, 0x60, 0x49); - if (owned) - { - PrintMonHeight(GetSpeciesHeight(species), 0x81, 0x39); - PrintMonWeight(GetSpeciesWeight(species), 0x81, 0x49); - } - else - { - PrintInfoScreenText(gText_UnkHeight, 0x81, 0x39); - PrintInfoScreenText(gText_UnkWeight, 0x81, 0x49); - } + PrintMonMeasurements(species,owned); if (owned) description = GetSpeciesPokedexDescription(species); else @@ -4188,95 +4188,289 @@ static void PrintMonInfo(u32 num, u32 value, u32 owned, u32 newEntry) PrintInfoScreenText(description, GetStringCenterAlignXOffset(FONT_NORMAL, description, DISPLAY_WIDTH), 95); } -static void PrintMonHeight(u16 height, u8 left, u8 top) +void PrintMonMeasurements(u16 species, u32 owned) { - u8 buffer[16]; - u32 inches, feet; - u8 i = 0; + u32 x = GetMeasurementTextPositions(DEX_HEADER_X); + u32 yTop = GetMeasurementTextPositions(DEX_Y_TOP); + u32 yBottom = GetMeasurementTextPositions(DEX_Y_BOTTOM); - inches = (height * 10000) / 254; + PrintInfoScreenText(gText_HTHeight, x, yTop); + PrintInfoScreenText(gText_WTWeight, x, yBottom); + + if (owned) + PrintOwnedMonMeasurements(species); + else + PrintUnknownMonMeasurements(); +} + +static u32 GetMeasurementTextPositions(u32 textElement) +{ + if (!POKEDEX_PLUS_HGSS) + return textElement; + + switch(textElement) + { + case DEX_HEADER_X: + return (DEX_HEADER_X + DEX_HGSS_HEADER_X_PADDING); + case DEX_Y_TOP: + return (DEX_Y_TOP + DEX_HGSS_Y_TOP_PADDING); + case DEX_Y_BOTTOM: + return (DEX_Y_BOTTOM + DEX_HGSS_Y_BOTTOM_PADDING); + default: + case DEX_MEASUREMENT_X: + return (DEX_MEASUREMENT_X + DEX_HGSS_MEASUREMENT_X_PADDING); + } +} + +static void PrintUnknownMonMeasurements(void) +{ + u8* heightString = GetUnknownMonHeightString(); + u8* weightString = GetUnknownMonWeightString(); + + u32 x = GetMeasurementTextPositions(DEX_MEASUREMENT_X); + u32 yTop = GetMeasurementTextPositions(DEX_Y_TOP); + u32 yBottom = GetMeasurementTextPositions(DEX_Y_BOTTOM); + + PrintInfoScreenText(heightString, x, yTop); + PrintInfoScreenText(weightString, x, yBottom); + + Free(heightString); + Free(weightString); +} + +static u8* GetUnknownMonHeightString(void) +{ + if (UNITS == UNITS_IMPERIAL) + return ReplaceDecimalSeparator(gText_UnkHeight); + else + return ReplaceDecimalSeparator(gText_UnkHeightMetric); +} + +static u8* GetUnknownMonWeightString(void) +{ + if (UNITS == UNITS_IMPERIAL) + return ReplaceDecimalSeparator(gText_UnkWeight); + else + return ReplaceDecimalSeparator(gText_UnkWeightMetric); +} + +static u8* ReplaceDecimalSeparator(const u8* originalString) +{ + bool32 replaced = FALSE; + u32 length = StringLength(originalString), i; + u8* modifiedString = Alloc(WEIGHT_HEIGHT_STR_MEM); + + for (i = 0; i < length; i++) + { + if ((originalString[i] != CHAR_PERIOD) || replaced) + { + modifiedString[i] = originalString[i]; + continue; + } + + modifiedString[i] = CHAR_DEC_SEPARATOR; + replaced = TRUE; + } + modifiedString[length] = EOS; + return modifiedString; +} + +static void PrintOwnedMonMeasurements(u16 species) +{ + PrintOwnedMonHeight(species); + PrintOwnedMonWeight(species); +} + +static void PrintOwnedMonHeight(u16 species) +{ + u32 height = GetSpeciesHeight(species); + u8* heightString; + + u32 x = GetMeasurementTextPositions(DEX_MEASUREMENT_X); + u32 yTop = GetMeasurementTextPositions(DEX_Y_TOP); + + heightString = ConvertMonHeightToString(height); + + PrintInfoScreenText(heightString, x, yTop); + Free(heightString); +} + +u8* ConvertMonHeightToString(u32 height) +{ + if (UNITS == UNITS_IMPERIAL) + return ConvertMonHeightToImperialString(height); + else + return ConvertMonHeightToMetricString(height); +} + +static void PrintOwnedMonWeight(u16 species) +{ + u32 weight = GetSpeciesWeight(species); + u8* weightString; + u32 x = GetMeasurementTextPositions(DEX_MEASUREMENT_X); + u32 yBottom = GetMeasurementTextPositions(DEX_Y_BOTTOM); + + weightString = ConvertMonWeightToString(weight); + + PrintInfoScreenText(weightString, x, yBottom); + Free(weightString); +} + +u8* ConvertMonWeightToString(u32 weight) +{ + if (UNITS == UNITS_IMPERIAL) + return ConvertMonWeightToImperialString(weight); + else + return ConvertMonWeightToMetricString(weight); +} + +static u8* ConvertMonHeightToImperialString(u32 height) +{ + u8* heightString = Alloc(WEIGHT_HEIGHT_STR_MEM); + u32 inches, feet, index = 0; + + inches = (height * 10000) / CM_PER_INCH_FACTOR; if (inches % 10 >= 5) inches += 10; - feet = inches / 120; - inches = (inches - (feet * 120)) / 10; + feet = inches / INCHES_IN_FOOT_FACTOR; + inches = (inches - (feet * INCHES_IN_FOOT_FACTOR)) / 10; - buffer[i++] = EXT_CTRL_CODE_BEGIN; - buffer[i++] = EXT_CTRL_CODE_CLEAR_TO; + heightString[index++] = EXT_CTRL_CODE_BEGIN; + heightString[index++] = EXT_CTRL_CODE_CLEAR_TO; if (feet / 10 == 0) { - buffer[i++] = 18; - buffer[i++] = feet + CHAR_0; + heightString[index++] = INCHES_IN_ONE_AND_HALF_FOOT; + heightString[index++] = feet + CHAR_0; } else { - buffer[i++] = 12; - buffer[i++] = feet / 10 + CHAR_0; - buffer[i++] = (feet % 10) + CHAR_0; + heightString[index++] = INCHES_IN_FOOT; + heightString[index++] = feet / 10 + CHAR_0; + heightString[index++] = (feet % 10) + CHAR_0; } - buffer[i++] = CHAR_SGL_QUOTE_RIGHT; - buffer[i++] = (inches / 10) + CHAR_0; - buffer[i++] = (inches % 10) + CHAR_0; - buffer[i++] = CHAR_DBL_QUOTE_RIGHT; - buffer[i++] = EOS; - PrintInfoScreenText(buffer, left, top); + heightString[index++] = CHAR_SGL_QUOTE_RIGHT; + heightString[index++] = (inches / 10) + CHAR_0; + heightString[index++] = (inches % 10) + CHAR_0; + heightString[index++] = CHAR_DBL_QUOTE_RIGHT; + heightString[index++] = EOS; + + return heightString; } -static void PrintMonWeight(u16 weight, u8 left, u8 top) +static u8* ConvertMonHeightToMetricString(u32 height) { - u8 buffer[16]; - bool8 output; - u8 i; - u32 lbs = (weight * 100000) / 4536; + u32 index = 0; + u8* heightString = ConvertMeasurementToMetricString(height, &index); + + heightString[index++] = CHAR_m; + heightString[index++] = EOS; + return heightString; +} + +static u8* ConvertMonWeightToImperialString(u32 weight) +{ + u8* weightString = Alloc(WEIGHT_HEIGHT_STR_MEM); + bool32 output = FALSE; + u32 index = 0, lbs = (weight * 100000) / DECAGRAMS_IN_POUND; if (lbs % 10u >= 5) lbs += 10; - i = 0; - output = FALSE; - if ((buffer[i] = (lbs / 100000) + CHAR_0) == CHAR_0 && !output) + if ((weightString[index] = (lbs / 100000) + CHAR_0) == CHAR_0 && !output) { - buffer[i++] = CHAR_SPACER; + weightString[index++] = CHAR_SPACER; } else { output = TRUE; - i++; + index++; } lbs %= 100000; - if ((buffer[i] = (lbs / 10000) + CHAR_0) == CHAR_0 && !output) + if ((weightString[index] = (lbs / 10000) + CHAR_0) == CHAR_0 && !output) { - buffer[i++] = CHAR_SPACER; + weightString[index++] = CHAR_SPACER; } else { output = TRUE; - i++; + index++; } lbs %= 10000; - if ((buffer[i] = (lbs / 1000) + CHAR_0) == CHAR_0 && !output) + if ((weightString[index] = (lbs / 1000) + CHAR_0) == CHAR_0 && !output) { - buffer[i++] = CHAR_SPACER; + weightString[index++] = CHAR_SPACER; } else { output = TRUE; - i++; + index++; } lbs %= 1000; - buffer[i++] = (lbs / 100) + CHAR_0; + weightString[index++] = (lbs / 100) + CHAR_0; lbs %= 100; - buffer[i++] = CHAR_PERIOD; - buffer[i++] = (lbs / 10) + CHAR_0; - buffer[i++] = CHAR_SPACE; - buffer[i++] = CHAR_l; - buffer[i++] = CHAR_b; - buffer[i++] = CHAR_s; - buffer[i++] = CHAR_PERIOD; - buffer[i++] = EOS; - PrintInfoScreenText(buffer, left, top); + weightString[index++] = CHAR_DEC_SEPARATOR; + weightString[index++] = (lbs / 10) + CHAR_0; + weightString[index++] = CHAR_SPACE; + weightString[index++] = CHAR_l; + weightString[index++] = CHAR_b; + weightString[index++] = CHAR_s; + weightString[index++] = CHAR_PERIOD; + weightString[index++] = EOS; + + return weightString; +} + +static u8* ConvertMonWeightToMetricString(u32 weight) +{ + u32 index = 0; + u8* weightString = ConvertMeasurementToMetricString(weight, &index); + + weightString[index++] = CHAR_k; + weightString[index++] = CHAR_g; + weightString[index++] = CHAR_PERIOD; + weightString[index++] = EOS; + return weightString; +} + +static u8* ConvertMeasurementToMetricString(u32 num, u32* index) +{ + u8* string = Alloc(WEIGHT_HEIGHT_STR_MEM); + bool32 outputted = FALSE; + u32 result; + + result = num / 1000; + if (result == 0) + { + string[(*index)++] = CHAR_SPACER; + outputted = FALSE; + } + else + { + string[(*index)++] = CHAR_0 + result; + outputted = TRUE; + } + + result = (num % 1000) / 100; + if (result == 0 && !outputted) + { + string[(*index)++] = CHAR_SPACER; + outputted = FALSE; + } + else + { + string[(*index)++] = CHAR_0 + result; + outputted = TRUE; + } + + string[(*index)++] = CHAR_0 + ((num % 1000) % 100) / 10; + string[(*index)++] = CHAR_DEC_SEPARATOR; + string[(*index)++] = CHAR_0 + ((num % 1000) % 100) % 10; + string[(*index)++] = CHAR_SPACE; + + return string; } s8 GetSetPokedexFlag(u16 nationalDexNo, u8 caseID) diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index 0e3817d4ac..4910fe6e66 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -536,8 +536,6 @@ static void Task_HandleCaughtMonPageInput(u8); static void Task_ExitCaughtMonPage(u8); static void SpriteCB_SlideCaughtMonToCenter(struct Sprite *sprite); static void PrintMonInfo(u32 num, u32, u32 owned, u32 newEntry); -static void PrintMonHeight(u16 height, u8 left, u8 top); -static void PrintMonWeight(u16 weight, u8 left, u8 top); static void ResetOtherVideoRegisters(u16); static u8 PrintCryScreenSpeciesName(u8, u16, u8, u8); static void PrintDecimalNum(u8 windowId, u16 num, u8 left, u8 top); @@ -4487,18 +4485,7 @@ static void PrintMonInfo(u32 num, u32 value, u32 owned, u32 newEntry) category = gText_5MarksPokemon; } PrintInfoScreenText(category, 123, 31); - PrintInfoScreenText(gText_HTHeight, 155, 64); - PrintInfoScreenText(gText_WTWeight, 155, 77); - if (owned) - { - PrintMonHeight(GetSpeciesHeight(species), 180, 64); - PrintMonWeight(GetSpeciesWeight(species), 180, 77); - } - else - { - PrintInfoScreenText(gText_UnkHeight, 180, 64); - PrintInfoScreenText(gText_UnkWeight, 180, 77); - } + PrintMonMeasurements(species,owned); if (owned) description = GetSpeciesPokedexDescription(species); else @@ -4510,97 +4497,6 @@ static void PrintMonInfo(u32 num, u32 value, u32 owned, u32 newEntry) PrintCurrentSpeciesTypeInfo(newEntry, species); } -static void PrintMonHeight(u16 height, u8 left, u8 top) -{ - u8 buffer[16]; - u32 inches, feet; - u8 i = 0; - - inches = (height * 10000) / 254; - if (inches % 10 >= 5) - inches += 10; - feet = inches / 120; - inches = (inches - (feet * 120)) / 10; - - buffer[i++] = EXT_CTRL_CODE_BEGIN; - buffer[i++] = EXT_CTRL_CODE_CLEAR_TO; - if (feet / 10 == 0) - { - buffer[i++] = 18; - buffer[i++] = feet + CHAR_0; - } - else - { - buffer[i++] = 12; - buffer[i++] = feet / 10 + CHAR_0; - buffer[i++] = (feet % 10) + CHAR_0; - } - buffer[i++] = CHAR_SGL_QUOTE_RIGHT; - buffer[i++] = (inches / 10) + CHAR_0; - buffer[i++] = (inches % 10) + CHAR_0; - buffer[i++] = CHAR_DBL_QUOTE_RIGHT; - buffer[i++] = EOS; - PrintInfoScreenText(buffer, left, top); -} - -static void PrintMonWeight(u16 weight, u8 left, u8 top) -{ - u8 buffer[16]; - bool8 output; - u8 i; - u32 lbs = (weight * 100000) / 4536; - - if (lbs % 10u >= 5) - lbs += 10; - i = 0; - output = FALSE; - - if ((buffer[i] = (lbs / 100000) + CHAR_0) == CHAR_0 && !output) - { - buffer[i++] = CHAR_SPACER; - } - else - { - output = TRUE; - i++; - } - - lbs %= 100000; - if ((buffer[i] = (lbs / 10000) + CHAR_0) == CHAR_0 && !output) - { - buffer[i++] = CHAR_SPACER; - } - else - { - output = TRUE; - i++; - } - - lbs %= 10000; - if ((buffer[i] = (lbs / 1000) + CHAR_0) == CHAR_0 && !output) - { - buffer[i++] = CHAR_SPACER; - } - else - { - output = TRUE; - i++; - } - - lbs %= 1000; - buffer[i++] = (lbs / 100) + CHAR_0; - lbs %= 100; - buffer[i++] = CHAR_PERIOD; - buffer[i++] = (lbs / 10) + CHAR_0; - buffer[i++] = CHAR_SPACE; - buffer[i++] = CHAR_l; - buffer[i++] = CHAR_b; - buffer[i++] = CHAR_s; - buffer[i++] = CHAR_PERIOD; - buffer[i++] = EOS; - PrintInfoScreenText(buffer, left, top); -} - // Unused in the English version, used to print height/weight in versions which use metric system. static void UNUSED PrintDecimalNum(u8 windowId, u16 num, u8 left, u8 top) { @@ -5294,7 +5190,7 @@ static bool8 CalculateMoves(void) if (j >= NUM_TECHNICAL_MACHINES + NUM_HIDDEN_MACHINES) { numTutorMoves++; - + sStatsMoves[movesTotal] = move; movesTotal++; } diff --git a/src/pokemon_animation.c b/src/pokemon_animation.c index 95557e431f..69f6003a4e 100644 --- a/src/pokemon_animation.c +++ b/src/pokemon_animation.c @@ -425,7 +425,7 @@ static const u8 sBackAnimationIds[] = [(BACK_ANIM_SHAKE_FLASH_YELLOW - 1) * 3] = ANIM_SHAKE_FLASH_YELLOW_FAST, ANIM_SHAKE_FLASH_YELLOW, ANIM_SHAKE_FLASH_YELLOW_SLOW, [(BACK_ANIM_SHAKE_GLOW_RED - 1) * 3] = ANIM_SHAKE_GLOW_RED_FAST, ANIM_SHAKE_GLOW_RED, ANIM_SHAKE_GLOW_RED_SLOW, [(BACK_ANIM_SHAKE_GLOW_GREEN - 1) * 3] = ANIM_SHAKE_GLOW_GREEN_FAST, ANIM_SHAKE_GLOW_GREEN, ANIM_SHAKE_GLOW_GREEN_SLOW, - [(BACK_ANIM_SHAKE_GLOW_BLUE - 1) * 3] = ANIM_SHAKE_GLOW_BLUE_FAST, ANIM_SHAKE_GLOW_BLUE, ANIM_SHAKE_GLOW_BLUE_SLOW, + [(BACK_ANIM_SHAKE_GLOW_BLUE - 1) * 3] = ANIM_SHAKE_GLOW_BLUE_FAST, ANIM_SHAKE_GLOW_BLUE, ANIM_SHAKE_GLOW_BLUE_SLOW, }; static const u8 sBackAnimNatureModTable[NUM_NATURES] = diff --git a/src/pokemon_size_record.c b/src/pokemon_size_record.c index 3f98e65ba6..f30604ef19 100644 --- a/src/pokemon_size_record.c +++ b/src/pokemon_size_record.c @@ -8,6 +8,7 @@ #include "text.h" #define DEFAULT_MAX_SIZE 0x8000 // was 0x8100 in Ruby/Sapphire +static u8* ReturnHeightStringNoWhitespace(u32 size); struct UnknownStruct { @@ -47,8 +48,6 @@ static const u8 sGiftRibbonsMonDataIds[GIFT_RIBBONS_COUNT - 4] = extern const u8 gText_DecimalPoint[]; extern const u8 gText_Marco[]; -#define CM_PER_INCH 2.54 - static u32 GetMonSizeHash(struct Pokemon *pkmn) { u16 personality = GetMonData(pkmn, MON_DATA_PERSONALITY); @@ -84,7 +83,7 @@ static u32 GetMonSize(u16 species, u16 b) u32 height; u32 var; - height = GetSpeciesWeight(species); + height = GetSpeciesHeight(species); var = TranslateBigMonSizeTableIndex(b); unk0 = sBigMonSizeTable[var].unk0; unk2 = sBigMonSizeTable[var].unk2; @@ -95,14 +94,24 @@ static u32 GetMonSize(u16 species, u16 b) static void FormatMonSizeRecord(u8 *string, u32 size) { -#ifdef UNITS_IMPERIAL - //Convert size from centimeters to inches - size = (f64)(size * 10) / (CM_PER_INCH * 10); -#endif + size = (f64)(size / 100); + StringCopy(string,ReturnHeightStringNoWhitespace(size)); +} - string = ConvertIntToDecimalStringN(string, size / 10, STR_CONV_MODE_LEFT_ALIGN, 8); - string = StringAppend(string, gText_DecimalPoint); - ConvertIntToDecimalStringN(string, size % 10, STR_CONV_MODE_LEFT_ALIGN, 1); +static u8* ReturnHeightStringNoWhitespace(u32 size) +{ + u8* heightStr = ConvertMonHeightToString(size); + u32 length = StringLength(heightStr); + u32 i = 0, j = 0; + + while (i < length && !(heightStr[i] >= CHAR_0 && heightStr[i] <= CHAR_9)) + i++; + + while (i < length) + heightStr[j++] = heightStr[i++]; + + heightStr[j] = EOS; + return heightStr; } static u8 CompareMonSize(u16 species, u16 *sizeRecord) diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index f37a70a146..358e02e5d9 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -345,7 +345,11 @@ u32 ScriptGiveMonParameterized(u16 species, u8 level, u16 item, u8 ball, u8 natu } // ability - if (abilityNum >= NUM_ABILITY_SLOTS || GetAbilityBySpecies(species, abilityNum) == ABILITY_NONE) + if (abilityNum == NUM_ABILITY_PERSONALITY) + { + abilityNum = GetMonData(&mon, MON_DATA_PERSONALITY) & 1; + } + else if (abilityNum > NUM_NORMAL_ABILITY_SLOTS || GetAbilityBySpecies(species, abilityNum) == ABILITY_NONE) { do { abilityNum = Random() % NUM_ABILITY_SLOTS; // includes hidden abilities @@ -410,7 +414,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(species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_SLOTS, MON_GENDERLESS, evs, ivs, moves, FALSE, FALSE, NUMBER_OF_MON_TYPES); + return ScriptGiveMonParameterized(species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_PERSONALITY, MON_GENDERLESS, evs, ivs, moves, FALSE, FALSE, NUMBER_OF_MON_TYPES); } #define PARSE_FLAG(n, default_) (flags & (1 << (n))) ? VarGet(ScriptReadHalfword(ctx)) : (default_) @@ -424,7 +428,7 @@ void ScrCmd_givemon(struct ScriptContext *ctx) u16 item = PARSE_FLAG(0, ITEM_NONE); u8 ball = PARSE_FLAG(1, ITEM_POKE_BALL); u8 nature = PARSE_FLAG(2, NUM_NATURES); - u8 abilityNum = PARSE_FLAG(3, NUM_ABILITY_SLOTS); + u8 abilityNum = PARSE_FLAG(3, NUM_ABILITY_PERSONALITY); u8 gender = PARSE_FLAG(4, MON_GENDERLESS); // TODO: Find a better way to assign a random gender. u8 hpEv = PARSE_FLAG(5, 0); u8 atkEv = PARSE_FLAG(6, 0); diff --git a/src/strings.c b/src/strings.c index 35f610017a..c98ff2519e 100644 --- a/src/strings.c +++ b/src/strings.c @@ -101,7 +101,9 @@ const u8 gText_DefaultNameHalie[] = _("HALIE"); const u8 gText_ThisIsAPokemon[] = _("This is what we call a “POKéMON.”{PAUSE 96}\p"); const u8 gText_5MarksPokemon[] = _("????? POKéMON"); const u8 gText_UnkHeight[] = _("{CLEAR_TO 0x0C}??'??”"); +const u8 gText_UnkHeightMetric[] = _("???.? m"); const u8 gText_UnkWeight[] = _("????.? lbs."); +const u8 gText_UnkWeightMetric[] = _("???.? kg."); const u8 gText_EmptyPkmnCategory[] = _(" POKéMON"); // Unused const u8 gText_EmptyHeight[] = _("{CLEAR_TO 0x0C} ' ”"); // Unused const u8 gText_EmptyWeight[] = _(" . lbs."); // Unused diff --git a/test/battle/ability/dancer.c b/test/battle/ability/dancer.c new file mode 100644 index 0000000000..97435a2ea7 --- /dev/null +++ b/test/battle/ability/dancer.c @@ -0,0 +1,35 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Dancer can copy a dance move immediately after it was used and allow the user of Dancer to still use its move") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_QUIVER_DANCE].danceMove == TRUE); + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_ORICORIO) { Ability(ABILITY_DANCER); } + } WHEN { + TURN { MOVE(player, MOVE_QUIVER_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUIVER_DANCE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + ABILITY_POPUP(opponent, ABILITY_DANCER); + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUIVER_DANCE, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); // Same turn + } +} + +SINGLE_BATTLE_TEST("Dancer can copy Teeter Dance") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_TEETER_DANCE].danceMove == TRUE); + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_ORICORIO) { Ability(ABILITY_DANCER); Item(ITEM_LUM_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_TEETER_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TEETER_DANCE, player); + ABILITY_POPUP(opponent, ABILITY_DANCER); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TEETER_DANCE, opponent); + } +} diff --git a/test/battle/ability/shield_dust.c b/test/battle/ability/shield_dust.c index 4cd95de619..cac3dfac4c 100644 --- a/test/battle/ability/shield_dust.c +++ b/test/battle/ability/shield_dust.c @@ -94,7 +94,7 @@ SINGLE_BATTLE_TEST("Shield Dust does not block self-targeting effects, primary o GIVEN { ASSUME(MoveHasMoveEffectSelf(MOVE_POWER_UP_PUNCH, MOVE_EFFECT_ATK_PLUS_1) == TRUE); - ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPIDSPIN) == TRUE); + ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPID_SPIN) == TRUE); ASSUME(MoveHasMoveEffectSelf(MOVE_LEAF_STORM, MOVE_EFFECT_SP_ATK_TWO_DOWN) == TRUE); ASSUME(MoveHasMoveEffectSelf(MOVE_METEOR_ASSAULT, MOVE_EFFECT_RECHARGE) == TRUE); PLAYER(SPECIES_WOBBUFFET); diff --git a/test/battle/ai_check_viability.c b/test/battle/ai_check_viability.c index 69794804c7..a2cd33ed2c 100644 --- a/test/battle/ai_check_viability.c +++ b/test/battle/ai_check_viability.c @@ -179,7 +179,6 @@ AI_SINGLE_BATTLE_TEST("AI chooses moves with secondary effect that have a 100% c PARAMETRIZE { ability = ABILITY_SERENE_GRACE; } GIVEN { - AI_LOG; ASSUME(MoveHasMoveEffectWithChance(MOVE_SHADOW_BALL, MOVE_EFFECT_SP_DEF_MINUS_1, 20)); ASSUME(MoveHasMoveEffectWithChance(MOVE_OCTAZOOKA, MOVE_EFFECT_ACC_MINUS_1, 50)); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); diff --git a/test/battle/ai_powerful_status.c b/test/battle/ai_powerful_status.c new file mode 100644 index 0000000000..b625b1126f --- /dev/null +++ b/test/battle/ai_powerful_status.c @@ -0,0 +1,54 @@ +#include "global.h" +#include "test/battle.h" +#include "battle_ai_util.h" + +AI_SINGLE_BATTLE_TEST("AI prefers to set up a powerful Status over fainting a target") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_TRICK_ROOM].effect == EFFECT_TRICK_ROOM); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_POWERFUL_STATUS); + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_DUSCLOPS) { Moves(MOVE_TRICK_ROOM, MOVE_TACKLE); } + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_TRICK_ROOM); } + TURN { EXPECT_MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("Wobbuffet fainted!"); + } +} + +AI_SINGLE_BATTLE_TEST("AI will try to do damage on target instead of setting up hazards if target has a way to remove them") +{ + GIVEN { + ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPID_SPIN) == TRUE); + ASSUME(gMovesInfo[MOVE_STEALTH_ROCK].effect == EFFECT_STEALTH_ROCK); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_POWERFUL_STATUS | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { HP(1); Moves(MOVE_RAPID_SPIN, MOVE_DEFOG, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GLIGAR) { Moves(MOVE_STEALTH_ROCK, MOVE_TACKLE); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("Wobbuffet fainted!"); + } +} + +AI_SINGLE_BATTLE_TEST("AI will not set up Rain if it is already raining") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_RAIN_DANCE].effect == EFFECT_RAIN_DANCE); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_POWERFUL_STATUS); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_RAIN_DANCE, MOVE_TACKLE); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_RAIN_DANCE); } + TURN { EXPECT_MOVE(opponent, MOVE_TACKLE); } + } +} diff --git a/test/battle/move_effect/confuse.c b/test/battle/move_effect/confuse.c new file mode 100644 index 0000000000..dcd843b2c6 --- /dev/null +++ b/test/battle/move_effect/confuse.c @@ -0,0 +1,58 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_TEETER_DANCE].effect == EFFECT_CONFUSE); +} + +SINGLE_BATTLE_TEST("Teeter Dance confuses target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TEETER_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TEETER_DANCE, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponent); + MESSAGE("Foe Wobbuffet became confused!"); + } +} + +SINGLE_BATTLE_TEST("Teeter Dance confusion is blocked by Own Tempo") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); } + } WHEN { + TURN { MOVE(player, MOVE_TEETER_DANCE); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TEETER_DANCE, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponent); + MESSAGE("Foe Wobbuffet became confused!"); + } + } +} + +DOUBLE_BATTLE_TEST("Teeter Dance can confuse foes and allies") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_TEETER_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TEETER_DANCE, playerLeft); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponentLeft); + MESSAGE("Foe Wobbuffet became confused!"); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, playerRight); + MESSAGE("Wynaut became confused!"); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponentRight); + MESSAGE("Foe Wynaut became confused!"); + } +} diff --git a/test/battle/move_effect/rapid_spin.c b/test/battle/move_effect/rapid_spin.c index 2874aa4527..1d078ef71e 100644 --- a/test/battle/move_effect/rapid_spin.c +++ b/test/battle/move_effect/rapid_spin.c @@ -3,11 +3,11 @@ ASSUMPTIONS { - ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPIDSPIN) == TRUE); + ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPID_SPIN) == TRUE); #if B_SPEED_BUFFING_RAPID_SPIN >= GEN_8 ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_SPD_PLUS_1) == TRUE); #endif - ASSUME(MoveHasMoveEffectSelf(MOVE_MORTAL_SPIN, MOVE_EFFECT_RAPIDSPIN) == TRUE); + ASSUME(MoveHasMoveEffectSelf(MOVE_MORTAL_SPIN, MOVE_EFFECT_RAPID_SPIN) == TRUE); ASSUME(MoveHasMoveEffect(MOVE_MORTAL_SPIN, MOVE_EFFECT_POISON) == TRUE); } diff --git a/test/battle/move_effect/tidy_up.c b/test/battle/move_effect/tidy_up.c new file mode 100644 index 0000000000..463fb65af8 --- /dev/null +++ b/test/battle/move_effect/tidy_up.c @@ -0,0 +1,99 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_TIDY_UP].effect == EFFECT_TIDY_UP); +} + +SINGLE_BATTLE_TEST("Tidy Up raises Attack and Speed by one") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TIDY_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TIDY_UP, player); + NOT MESSAGE("Tidying up complete!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Attack rose!"); + MESSAGE("Wobbuffet's Speed rose!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("Tidy Up removes hazards and raises Stats") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SPIKES); } + TURN { MOVE(opponent, MOVE_STEALTH_ROCK); } + TURN { MOVE(opponent, MOVE_TOXIC_SPIKES); } + TURN { MOVE(opponent, MOVE_STICKY_WEB); MOVE(player, MOVE_TIDY_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC_SPIKES, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponent); + MESSAGE("Wobbuffet used Tidy Up!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TIDY_UP, player); + MESSAGE("The spikes disappeared from the ground around your team!"); + MESSAGE("The pointed stones disappeared from around your team!"); + MESSAGE("The poison spikes disappeared from the ground around your team!"); + MESSAGE("The sticky web has disappeared from the ground around your team!"); + MESSAGE("Tidying up complete!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Attack rose!"); + MESSAGE("Wobbuffet's Speed rose!"); + } +} + +SINGLE_BATTLE_TEST("Tidy Up removes Substitute") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_TIDY_UP); } + } SCENE { + MESSAGE("Foe Wobbuffet used Substitute!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent); + MESSAGE("Foe Wobbuffet made a SUBSTITUTE!"); + MESSAGE("Wobbuffet used Tidy Up!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TIDY_UP, player); + MESSAGE("Foe Wobbuffet's SUBSTITUTE faded!"); + MESSAGE("Tidying up complete!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Attack rose!"); + MESSAGE("Wobbuffet's Speed rose!"); + } +} + +AI_SINGLE_BATTLE_TEST("AI prefers to keep it's substitute over removing hazards if target is slower") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { Speed(50); Status1(STATUS1_PARALYSIS); Moves(MOVE_SLEEP_POWDER, MOVE_STEALTH_ROCK, MOVE_CELEBRATE); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(100); Moves(MOVE_BITE, MOVE_TACKLE, MOVE_SUBSTITUTE, MOVE_TIDY_UP); } + } WHEN { + TURN { MOVE(player, MOVE_STEALTH_ROCK); EXPECT_MOVE(opponent, MOVE_SUBSTITUTE); } + TURN { EXPECT_MOVE(opponent, MOVE_BITE); } + } +} + +AI_SINGLE_BATTLE_TEST("AI will try to remove hazards if slower then target even with a Substitute because it expects the Sub to be broken") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { Speed(100); Status1(STATUS1_BURN); Moves(MOVE_SLEEP_POWDER, MOVE_STEALTH_ROCK, MOVE_CELEBRATE); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_BITE, MOVE_TACKLE, MOVE_SUBSTITUTE, MOVE_TIDY_UP); } + } WHEN { + TURN { MOVE(player, MOVE_STEALTH_ROCK); EXPECT_MOVE(opponent, MOVE_SUBSTITUTE); } + TURN { EXPECT_MOVE(opponent, MOVE_TIDY_UP); } + } +} diff --git a/test/battle/move_effect/uproar.c b/test/battle/move_effect/uproar.c new file mode 100644 index 0000000000..fe6a4c9931 --- /dev/null +++ b/test/battle/move_effect/uproar.c @@ -0,0 +1,28 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_UPROAR].effect == EFFECT_UPROAR); +} + +DOUBLE_BATTLE_TEST("Uproar status causes sleeping pokemon to wake up during an attack") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); } + OPPONENT(SPECIES_VOLTORB) { Ability(ABILITY_SOUNDPROOF); Status1(STATUS1_SLEEP); } + OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_UPROAR); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_UPROAR, playerLeft); + HP_BAR(opponentRight); + MESSAGE("Wobbuffet woke up in the UPROAR!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + MESSAGE("Foe Voltorb woke up in the UPROAR!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + MESSAGE("Foe Wobbuffet woke up in the UPROAR!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + } +} diff --git a/test/battle/trainer_control.c b/test/battle/trainer_control.c index e3e071745d..9415360948 100644 --- a/test/battle/trainer_control.c +++ b/test/battle/trainer_control.c @@ -26,7 +26,7 @@ static const struct TrainerMon sTestParty1[] = .ev = TRAINER_PARTY_EVS(252, 0, 0, 252, 4, 0), .lvl = 67, .moves = {MOVE_AIR_SLASH, MOVE_BARRIER, MOVE_SOLAR_BEAM, MOVE_EXPLOSION}, - .nature = TRAINER_PARTY_NATURE(NATURE_HASTY), + .nature = NATURE_HASTY, .nickname = COMPOUND_STRING("Bubbles"), .dynamaxLevel = 5, }, @@ -35,6 +35,10 @@ static const struct TrainerMon sTestParty1[] = .ability = ABILITY_SHADOW_TAG, .lvl = 5, }, + { + .species = SPECIES_WYNAUT, + .lvl = 5, + }, }; static const struct Trainer sTestTrainer1 = @@ -59,6 +63,7 @@ TEST("CreateNPCTrainerPartyForTrainer generates customized Pokémon") EXPECT(GetMonAbility(&testParty[0]) == ABILITY_TELEPATHY); EXPECT(GetMonAbility(&testParty[1]) == ABILITY_SHADOW_TAG); + EXPECT(GetMonAbility(&testParty[2]) == ABILITY_SHADOW_TAG); EXPECT(GetMonData(&testParty[0], MON_DATA_FRIENDSHIP, 0) == 42); EXPECT(GetMonData(&testParty[1], MON_DATA_FRIENDSHIP, 0) == 0); @@ -110,6 +115,7 @@ TEST("CreateNPCTrainerPartyForTrainer generates customized Pokémon") EXPECT(GetMonGender(&testParty[0]) == MON_FEMALE); EXPECT(GetNature(&testParty[0]) == NATURE_HASTY); + EXPECT(GetNature(&testParty[1]) == NATURE_HARDY); EXPECT_EQ(GetMonData(&testParty[0], MON_DATA_DYNAMAX_LEVEL), 5); EXPECT_EQ(GetMonData(&testParty[1], MON_DATA_DYNAMAX_LEVEL), 0); diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 3adebd78ab..68a83321fe 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1533,7 +1533,7 @@ void SetFlagForTest(u32 sourceLine, u16 flagId) void ClearFlagAfterTest(void) { - if (DATA.flagId != 0) + if (DATA.flagId != 0) { FlagClear(DATA.flagId); DATA.flagId = 0;