From d74cbca0b915d91db9d573a93cd10ed690a51753 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Sun, 6 Aug 2023 21:02:16 -0400 Subject: [PATCH 01/16] LF shenanigans --- .../ISSUE_TEMPLATE/01_battle_engine_bugs.yaml | 100 +++++++++--------- .../ISSUE_TEMPLATE/02_battle_ai_issues.yaml | 100 +++++++++--------- .../ISSUE_TEMPLATE/03_feature_requests.yaml | 54 +++++----- .github/ISSUE_TEMPLATE/04_other_errors.yaml | 100 +++++++++--------- 4 files changed, 177 insertions(+), 177 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml index 55d4fada4..c4404a039 100644 --- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml +++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml @@ -1,50 +1,50 @@ -name: ⚔️ Battle Engine mechanical bugs 🐛 -description: File a bug report related to battle mechanic, be it moves, abilities and/or items. -labels: ["bug", "status: unconfirmed", "category: battle-mechanic"] -body: - - type: markdown - attributes: - value: | - Please fill in all required fields with as many details as possible. - - type: textarea - id: description - attributes: - label: Description - description: | - Describe the issue you are experiencing. - Attach images/videos if possible. - placeholder: | - Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video - validations: - required: true - - type: dropdown - id: version - attributes: - label: Version - description: What version of pokeemerald-expansion are you using as a base? - options: - - 1.5.1 (Default) - - upcoming (Edge) - - 1.5.0 - - 1.4.3 - - 1.4.2 - - 1.4.1 - - 1.4.0 - - pre-1.4.0 - validations: - required: true - - type: input - id: upcomingversion - attributes: - label: Upcoming Version - description: If you're using the upcoming branch, please specify what was the commit hash you pulled from. - validations: - required: false - - type: input - id: contact - attributes: - label: Discord contact info - description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)). - placeholder: ex. Lunos#4026 - validations: - required: false +name: ⚔️ Battle Engine mechanical bugs 🐛 +description: File a bug report related to battle mechanic, be it moves, abilities and/or items. +labels: ["bug", "status: unconfirmed", "category: battle-mechanic"] +body: + - type: markdown + attributes: + value: | + Please fill in all required fields with as many details as possible. + - type: textarea + id: description + attributes: + label: Description + description: | + Describe the issue you are experiencing. + Attach images/videos if possible. + placeholder: | + Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video + validations: + required: true + - type: dropdown + id: version + attributes: + label: Version + description: What version of pokeemerald-expansion are you using as a base? + options: + - 1.5.1 (Default) + - upcoming (Edge) + - 1.5.0 + - 1.4.3 + - 1.4.2 + - 1.4.1 + - 1.4.0 + - pre-1.4.0 + validations: + required: true + - type: input + id: upcomingversion + attributes: + label: Upcoming Version + description: If you're using the upcoming branch, please specify what was the commit hash you pulled from. + validations: + required: false + - type: input + id: contact + attributes: + label: Discord contact info + description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)). + placeholder: ex. Lunos#4026 + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml index 8c28b3942..a24757fc7 100644 --- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml +++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml @@ -1,50 +1,50 @@ -name: 🧠 Battle AI bugs 🐛 -description: File a bug report related to battle AI. -labels: ["bug", "status: unconfirmed", "category: battle-ai"] -body: - - type: markdown - attributes: - value: | - Please fill in all required fields with as many details as possible. - - type: textarea - id: description - attributes: - label: Description - description: | - Describe the issue you are experiencing. - Attach images/videos if possible. - placeholder: | - Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video - validations: - required: true - - type: dropdown - id: version - attributes: - label: Version - description: What version of pokeemerald-expansion are you using as a base? - options: - - 1.5.1 (Default) - - upcoming (Edge) - - 1.5.0 - - 1.4.3 - - 1.4.2 - - 1.4.1 - - 1.4.0 - - pre-1.4.0 - validations: - required: true - - type: input - id: upcomingversion - attributes: - label: Upcoming Version - description: If you're using the upcoming branch, please specify what was the commit hash you pulled from. - validations: - required: false - - type: input - id: contact - attributes: - label: Discord contact info - description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)). - placeholder: ex. Lunos#4026 - validations: - required: false +name: 🧠 Battle AI bugs 🐛 +description: File a bug report related to battle AI. +labels: ["bug", "status: unconfirmed", "category: battle-ai"] +body: + - type: markdown + attributes: + value: | + Please fill in all required fields with as many details as possible. + - type: textarea + id: description + attributes: + label: Description + description: | + Describe the issue you are experiencing. + Attach images/videos if possible. + placeholder: | + Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video + validations: + required: true + - type: dropdown + id: version + attributes: + label: Version + description: What version of pokeemerald-expansion are you using as a base? + options: + - 1.5.1 (Default) + - upcoming (Edge) + - 1.5.0 + - 1.4.3 + - 1.4.2 + - 1.4.1 + - 1.4.0 + - pre-1.4.0 + validations: + required: true + - type: input + id: upcomingversion + attributes: + label: Upcoming Version + description: If you're using the upcoming branch, please specify what was the commit hash you pulled from. + validations: + required: false + - type: input + id: contact + attributes: + label: Discord contact info + description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)). + placeholder: ex. Lunos#4026 + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/03_feature_requests.yaml b/.github/ISSUE_TEMPLATE/03_feature_requests.yaml index 79e85709f..682f0dc09 100644 --- a/.github/ISSUE_TEMPLATE/03_feature_requests.yaml +++ b/.github/ISSUE_TEMPLATE/03_feature_requests.yaml @@ -1,27 +1,27 @@ -name: 🙏 Feature Request 🙏 -description: Do you want a feature to be added to the Expansion? Let us know! -labels: ["feature-request"] -body: - - type: markdown - attributes: - value: | - Please fill in all required fields with as many details as possible. - - type: textarea - id: description - attributes: - label: Description - description: | - Describe the issue you are experiencing. - Attach images/videos if possible. - placeholder: | - Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video - validations: - required: true - - type: input - id: contact - attributes: - label: Discord contact info - description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)). - placeholder: ex. Lunos#4026 - validations: - required: false +name: 🙏 Feature Request 🙏 +description: Do you want a feature to be added to the Expansion? Let us know! +labels: ["feature-request"] +body: + - type: markdown + attributes: + value: | + Please fill in all required fields with as many details as possible. + - type: textarea + id: description + attributes: + label: Description + description: | + Describe the issue you are experiencing. + Attach images/videos if possible. + placeholder: | + Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video + validations: + required: true + - type: input + id: contact + attributes: + label: Discord contact info + description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)). + placeholder: ex. Lunos#4026 + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml index 6f11d5b9a..e25695c0b 100644 --- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml +++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml @@ -1,50 +1,50 @@ -name: 💾 Other errors 🖥️ -description: Everything else that doesn't fit in the above categories. -labels: ["bug", "status: unconfirmed"] -body: - - type: markdown - attributes: - value: | - Please fill in all required fields with as many details as possible. - - type: textarea - id: description - attributes: - label: Description - description: | - Describe the issue you are experiencing. - Attach images/videos if possible. - placeholder: | - Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video - validations: - required: true - - type: dropdown - id: version - attributes: - label: Version - description: What version of pokeemerald-expansion are you using as a base? - options: - - 1.5.1 (Default) - - upcoming (Edge) - - 1.5.0 - - 1.4.3 - - 1.4.2 - - 1.4.1 - - 1.4.0 - - pre-1.4.0 - validations: - required: true - - type: input - id: upcomingversion - attributes: - label: Upcoming Version - description: If you're using the upcoming branch, please specify what was the commit hash you pulled from. - validations: - required: false - - type: input - id: contact - attributes: - label: Discord contact info - description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)). - placeholder: ex. Lunos#4026 - validations: - required: false +name: 💾 Other errors 🖥️ +description: Everything else that doesn't fit in the above categories. +labels: ["bug", "status: unconfirmed"] +body: + - type: markdown + attributes: + value: | + Please fill in all required fields with as many details as possible. + - type: textarea + id: description + attributes: + label: Description + description: | + Describe the issue you are experiencing. + Attach images/videos if possible. + placeholder: | + Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video + validations: + required: true + - type: dropdown + id: version + attributes: + label: Version + description: What version of pokeemerald-expansion are you using as a base? + options: + - 1.5.1 (Default) + - upcoming (Edge) + - 1.5.0 + - 1.4.3 + - 1.4.2 + - 1.4.1 + - 1.4.0 + - pre-1.4.0 + validations: + required: true + - type: input + id: upcomingversion + attributes: + label: Upcoming Version + description: If you're using the upcoming branch, please specify what was the commit hash you pulled from. + validations: + required: false + - type: input + id: contact + attributes: + label: Discord contact info + description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)). + placeholder: ex. Lunos#4026 + validations: + required: false From 1fb42e44972ef77422a4efe9776d2d129417846e Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Mon, 7 Aug 2023 16:27:57 +0200 Subject: [PATCH 02/16] Sticky web / Court Change / Mirror Armor tests + small fixes (#3160) Co-authored-by: Eduardo Quezada D'Ottone Co-authored-by: Martin Griffin --- include/battle.h | 2 +- src/battle_main.c | 21 ++- src/battle_message.c | 4 +- src/battle_script_commands.c | 15 +- test/ability_mirror_armor.c | 202 +++++++++++++++++++++++++++ test/hold_effect_berserk_gene.c | 2 +- test/move_effect_court_change.c | 153 +++++++++++++++++++++ test/move_effect_defog.c | 2 +- test/move_effect_sticky_web.c | 237 ++++++++++++++++++++++++++++++++ 9 files changed, 620 insertions(+), 18 deletions(-) create mode 100644 test/ability_mirror_armor.c create mode 100644 test/move_effect_court_change.c create mode 100644 test/move_effect_sticky_web.c diff --git a/include/battle.h b/include/battle.h index b27062c12..e3f2e90d0 100644 --- a/include/battle.h +++ b/include/battle.h @@ -212,6 +212,7 @@ struct SideTimer u8 toxicSpikesAmount; u8 stealthRockAmount; u8 stickyWebAmount; + u8 stickyWebBattlerId; u8 stickyWebBattlerSide; // Used for Court Change u8 auroraVeilTimer; u8 auroraVeilBattlerId; @@ -644,7 +645,6 @@ struct BattleStruct u8 forcedSwitch:4; // For each battler u8 switchInAbilityPostponed:4; // To not activate against an empty field, each bit for battler u8 ballSpriteIds[2]; // item gfx, window gfx - u8 stickyWebUser; u8 appearedInBattle; // Bitfield to track which Pokemon appeared in battle. Used for Burmy's form change u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle. // When using a move which hits multiple opponents which is then bounced by a target, we need to make sure, the move hits both opponents, the one with bounce, and the one without. diff --git a/src/battle_main.c b/src/battle_main.c index b75c44b77..855e77017 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3170,7 +3170,10 @@ static void BattleStartClearSetData(void) gBattleStruct->mega.triggerSpriteId = 0xFF; - gBattleStruct->stickyWebUser = 0xFF; + for (i = 0; i < ARRAY_COUNT(gSideTimers); i++) + { + gSideTimers[i].stickyWebBattlerId = 0xFF; + } gBattleStruct->appearedInBattle = 0; gBattleStruct->beatUpSlot = 0; @@ -3276,8 +3279,12 @@ void SwitchInClearSetData(void) gBattleStruct->lastMoveFailed &= ~(gBitTable[gActiveBattler]); gBattleStruct->palaceFlags &= ~(gBitTable[gActiveBattler]); - if (gActiveBattler == gBattleStruct->stickyWebUser) - gBattleStruct->stickyWebUser = 0xFF; // Switched into sticky web user slot so reset it + for (i = 0; i < ARRAY_COUNT(gSideTimers); i++) + { + // Switched into sticky web user slot, so reset stored battler ID + if (gSideTimers[i].stickyWebBattlerId == gActiveBattler) + gSideTimers[i].stickyWebBattlerId = 0xFF; + } for (i = 0; i < gBattlersCount; i++) { @@ -3390,8 +3397,12 @@ void FaintClearSetData(void) gBattleStruct->palaceFlags &= ~(gBitTable[gActiveBattler]); - if (gActiveBattler == gBattleStruct->stickyWebUser) - gBattleStruct->stickyWebUser = 0xFF; // User of sticky web fainted, so reset the stored battler ID + for (i = 0; i < ARRAY_COUNT(gSideTimers); i++) + { + // User of sticky web fainted, so reset the stored battler ID + if (gSideTimers[i].stickyWebBattlerId == gActiveBattler) + gSideTimers[i].stickyWebBattlerId = 0xFF; + } for (i = 0; i < gBattlersCount; i++) { diff --git a/src/battle_message.c b/src/battle_message.c index 32c4d7b04..307b539a5 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -142,8 +142,8 @@ static const u8 sText_PkmnRaisedSpDefALittle[] = _("{B_ATK_PREFIX2}'s {B_CURRENT static const u8 sText_PkmnRaisedDef[] = _("{B_ATK_PREFIX2}'s {B_CURRENT_MOVE}\nraised DEFENSE!"); static const u8 sText_PkmnRaisedDefALittle[] = _("{B_ATK_PREFIX2}'s {B_CURRENT_MOVE}\nraised DEFENSE a little!"); static const u8 sText_PkmnCoveredByVeil[] = _("{B_ATK_PREFIX2}'s party is covered\nby a veil!"); -static const u8 sText_PkmnUsedSafeguard[] = _("{B_DEF_NAME_WITH_PREFIX}'s party is protected\nby SAFEGUARD!"); -static const u8 sText_PkmnSafeguardExpired[] = _("{B_ATK_PREFIX3}'s party is no longer\nprotected by SAFEGUARD!"); +static const u8 sText_PkmnUsedSafeguard[] = _("{B_DEF_NAME_WITH_PREFIX}'s party is protected\nby Safeguard!"); +static const u8 sText_PkmnSafeguardExpired[] = _("{B_ATK_PREFIX3}'s party is no longer\nprotected by Safeguard!"); static const u8 sText_PkmnWentToSleep[] = _("{B_ATK_NAME_WITH_PREFIX} went\nto sleep!"); static const u8 sText_PkmnSleptHealthy[] = _("{B_ATK_NAME_WITH_PREFIX} slept and\nbecame healthy!"); static const u8 sText_PkmnWhippedWhirlwind[] = _("{B_ATK_NAME_WITH_PREFIX} whipped\nup a whirlwind!"); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 8b69e36fb..f1bb07281 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8760,8 +8760,9 @@ static bool32 IsTeatimeAffected(u32 battlerId) #define UPDATE_COURTCHANGED_BATTLER(structField)\ { \ - sideTimerPlayer->structField ^= BIT_SIDE; \ - sideTimerOpp->structField ^= BIT_SIDE; \ + temp = sideTimerPlayer->structField; \ + sideTimerPlayer->structField = BATTLE_OPPOSITE(sideTimerOpp->structField); \ + sideTimerOpp->structField = BATTLE_OPPOSITE(temp); \ } \ static bool32 CourtChangeSwapSideStatuses(void) @@ -8796,9 +8797,7 @@ static bool32 CourtChangeSwapSideStatuses(void) UPDATE_COURTCHANGED_BATTLER(auroraVeilBattlerId); UPDATE_COURTCHANGED_BATTLER(tailwindBattlerId); UPDATE_COURTCHANGED_BATTLER(luckyChantBattlerId); - - // For Mirror Armor only - gBattleStruct->stickyWebUser = gBattlerAttacker; + UPDATE_COURTCHANGED_BATTLER(stickyWebBattlerId); // Track which side originally set the Sticky Web SWAP(sideTimerPlayer->stickyWebBattlerSide, sideTimerOpp->stickyWebBattlerSide, temp); @@ -10830,8 +10829,8 @@ static void Cmd_various(void) // If Pokémon which set up Sticky Web is not on the field, no Pokémon have their Speed lowered." gBattlerAttacker = gBattlerTarget; // Initialize 'fail' condition SET_STATCHANGER(STAT_SPEED, 1, TRUE); - if (gBattleStruct->stickyWebUser != 0xFF) - gBattlerAttacker = gBattleStruct->stickyWebUser; + if (gSideTimers[GetBattlerSide(gActiveBattler)].stickyWebBattlerId != 0xFF) + gBattlerAttacker = gSideTimers[GetBattlerSide(gActiveBattler)].stickyWebBattlerId; break; } case VARIOUS_CUT_1_3_HP_RAISE_STATS: @@ -14092,9 +14091,9 @@ static void Cmd_setstickyweb(void) else { gSideStatuses[targetSide] |= SIDE_STATUS_STICKY_WEB; + gSideTimers[targetSide].stickyWebBattlerId = gBattlerAttacker; // For Mirror Armor gSideTimers[targetSide].stickyWebBattlerSide = GetBattlerSide(gBattlerAttacker); // For Court Change/Defiant - set this to the user's side gSideTimers[targetSide].stickyWebAmount = 1; - gBattleStruct->stickyWebUser = gBattlerAttacker; // For Mirror Armor gBattlescriptCurrInstr = cmd->nextInstr; } } diff --git a/test/ability_mirror_armor.c b/test/ability_mirror_armor.c new file mode 100644 index 000000000..7e15e029b --- /dev/null +++ b/test/ability_mirror_armor.c @@ -0,0 +1,202 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(P_GEN_8_POKEMON == TRUE); +} + +SINGLE_BATTLE_TEST("Mirror Armor lowers a stat of the attacking pokemon") +{ + u16 move, statId; + + PARAMETRIZE { move = MOVE_LEER; statId = STAT_DEF; } + PARAMETRIZE { move = MOVE_GROWL; statId = STAT_ATK; } + PARAMETRIZE { move = MOVE_SWEET_SCENT; statId = STAT_EVASION; } + PARAMETRIZE { move = MOVE_SAND_ATTACK; statId = STAT_ACC; } + PARAMETRIZE { move = MOVE_CONFIDE; statId = STAT_SPATK; } + PARAMETRIZE { move = MOVE_FAKE_TEARS; statId = STAT_SPDEF; } + + GIVEN { + PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR);} + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, move); } + } SCENE { + ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + switch (statId) + { + case STAT_DEF: + MESSAGE("Foe Wynaut's Defense fell!"); + break; + case STAT_ATK: + MESSAGE("Foe Wynaut's Attack fell!"); + break; + case STAT_EVASION: + MESSAGE("Foe Wynaut's evasiveness harshly fell!"); + break; + case STAT_ACC: + MESSAGE("Foe Wynaut's accuracy fell!"); + break; + case STAT_SPATK: + MESSAGE("Foe Wynaut's Sp. Atk fell!"); + break; + case STAT_SPDEF: + MESSAGE("Foe Wynaut's Sp. Def harshly fell!"); + break; + } + } THEN { + EXPECT_EQ(player->statStages[statId], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[statId], (statId == STAT_SPDEF || statId == STAT_EVASION) ? DEFAULT_STAT_STAGE - 2 : DEFAULT_STAT_STAGE - 1); + } +} + +SINGLE_BATTLE_TEST("Mirror Armor triggers even if the attacking Pokemon also has Mirror Armor ability") +{ + GIVEN { + PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); } + OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); } + } WHEN { + TURN { MOVE(opponent, MOVE_LEER); } + } SCENE { + MESSAGE("Foe Corviknigh used Leer!"); + ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Corviknigh's Defense fell!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); + } +} + +SINGLE_BATTLE_TEST("Mirror Armor doesn't lower the stats of an attacking Pokemon with the Clear Body ability") +{ + GIVEN { + PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); } + OPPONENT(SPECIES_WYNAUT) { Ability(ABILITY_CLEAR_BODY); } + } WHEN { + TURN { MOVE(opponent, MOVE_LEER); } + } SCENE { + MESSAGE("Foe Wynaut used Leer!"); + ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR); + ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + MESSAGE("Foe Wynaut's Clear Body prevents stat loss!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + } +} + +SINGLE_BATTLE_TEST("Mirror Armor lowers the Attack of Pokemon with Intimidate") +{ + GIVEN { + PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); } + OPPONENT(SPECIES_GYARADOS) { Ability(ABILITY_INTIMIDATE); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Gyarados's Attack fell!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + } +} + +// Unsure whether this should or should not fail, as Showdown has conflicting information. Needs testing in gen8 games. +SINGLE_BATTLE_TEST("Mirror Armor doesn't lower the stats of an attacking Pokemon behind Substitute") +{ + KNOWN_FAILING; + GIVEN { + PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); } + TURN { MOVE(opponent, MOVE_LEER); } + } SCENE { + MESSAGE("Foe Wynaut used Substitute!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent); + MESSAGE("Foe Wynaut used Leer!"); + ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + } +} + +SINGLE_BATTLE_TEST("Mirror Armor raises the stat of an attacking Pokemon with Contrary") +{ + GIVEN { + PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR);} + OPPONENT(SPECIES_SHUCKLE) {Ability(ABILITY_CONTRARY);} + } WHEN { + TURN { MOVE(opponent, MOVE_LEER); } + } SCENE { + MESSAGE("Foe Shuckle used Leer!"); + ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Shuckle's Defense rose!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("Mirror Armor doesn't lower the stat of the attacking Pokemon if it is already at -6") +{ + GIVEN { + PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR);} + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_SCREECH); } + TURN { MOVE(player, MOVE_SCREECH); } + TURN { MOVE(player, MOVE_SCREECH); } + TURN { MOVE(opponent, MOVE_LEER); } + } SCENE { + MESSAGE("Corviknigh used Screech!"); + MESSAGE("Corviknigh used Screech!"); + MESSAGE("Corviknigh used Screech!"); + MESSAGE("Foe Wynaut used Leer!"); + ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wynaut's Defense won't go lower!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_DEF], MIN_STAT_STAGE); + } +} + +// This behaviour needs to be verified in the actual games. Currently it's written to follow Showdown's logic. +DOUBLE_BATTLE_TEST("Mirror Armor lowers Speed of the partner Pokemon after Court Change was used by the opponent after it set up Sticky Web") +{ + KNOWN_FAILING; + GIVEN { + ASSUME(gBattleMoves[MOVE_STICKY_WEB].effect == EFFECT_STICKY_WEB); + ASSUME(gBattleMoves[MOVE_COURT_CHANGE].effect == EFFECT_COURT_CHANGE); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR); Item(ITEM_IRON_BALL); } + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_STICKY_WEB); } + TURN { MOVE(opponentLeft, MOVE_COURT_CHANGE); } + TURN { SWITCH(playerRight, 2);} + TURN { } + } SCENE { + MESSAGE("Wobbuffet used Sticky Web!"); + MESSAGE("Foe Wynaut used Court Change!"); + MESSAGE("Foe Wynaut swapped the battle effects affecting each side!"); + MESSAGE("Go! Corviknigh!"); + MESSAGE("Corviknigh was caught in a Sticky Web!"); + ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Wobbuffet's Speed fell!"); + } +} diff --git a/test/hold_effect_berserk_gene.c b/test/hold_effect_berserk_gene.c index 8a473e8da..981881e74 100644 --- a/test/hold_effect_berserk_gene.c +++ b/test/hold_effect_berserk_gene.c @@ -112,7 +112,7 @@ SINGLE_BATTLE_TEST("Berserk Gene does not confuse when Safeguard is active") } SCENE { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); MESSAGE("Using Berserk Gene, the Attack of Wobbuffet sharply rose!"); - MESSAGE("Wobbuffet's party is protected by SAFEGUARD!"); + MESSAGE("Wobbuffet's party is protected by Safeguard!"); NOT MESSAGE("Wobbuffet became confused!"); } } diff --git a/test/move_effect_court_change.c b/test/move_effect_court_change.c new file mode 100644 index 000000000..9d03efddf --- /dev/null +++ b/test/move_effect_court_change.c @@ -0,0 +1,153 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_COURT_CHANGE].effect == EFFECT_COURT_CHANGE); +} + +DOUBLE_BATTLE_TEST("Court Change swaps entry hazards used by the opponent") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_STICKY_WEB); MOVE(opponentRight, MOVE_STEALTH_ROCK); } + TURN { MOVE(opponentLeft, MOVE_SPIKES); MOVE(opponentRight, MOVE_TOXIC_SPIKES); } + TURN { MOVE(playerLeft, MOVE_COURT_CHANGE); } + TURN { SWITCH(playerLeft, 2); SWITCH(opponentLeft, 2); } + } SCENE { + MESSAGE("Foe Wobbuffet used Sticky Web!"); + MESSAGE("Foe Wobbuffet used Stealth Rock!"); + MESSAGE("Foe Wobbuffet used Spikes!"); + MESSAGE("Foe Wobbuffet used Toxic Spikes!"); + MESSAGE("Wynaut used Court Change!"); + MESSAGE("Wynaut swapped the battle effects affecting each side!"); + MESSAGE("Go! Wynaut!"); + NONE_OF { + MESSAGE("Wynaut is hurt by spikes!"); + MESSAGE("Pointed stones dug into Wynaut!"); + MESSAGE("Wynaut was poisoned!"); + MESSAGE("Wynaut was caught in a Sticky Web!"); + } + MESSAGE("2 sent out Wobbuffet!"); + MESSAGE("Foe Wobbuffet is hurt by spikes!"); + MESSAGE("Pointed stones dug into Foe Wobbuffet!"); + MESSAGE("Foe Wobbuffet was poisoned!"); + MESSAGE("Foe Wobbuffet was caught in a Sticky Web!"); + } +} + +DOUBLE_BATTLE_TEST("Court Change swaps entry hazards used by the player") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_STICKY_WEB); MOVE(playerRight, MOVE_STEALTH_ROCK); } + TURN { MOVE(playerLeft, MOVE_SPIKES); MOVE(playerRight, MOVE_TOXIC_SPIKES); } + TURN { MOVE(opponentLeft, MOVE_COURT_CHANGE); } + TURN { SWITCH(opponentLeft, 2); SWITCH(playerLeft, 2); } + } SCENE { + MESSAGE("Wobbuffet used Sticky Web!"); + MESSAGE("Wobbuffet used Stealth Rock!"); + MESSAGE("Wobbuffet used Spikes!"); + MESSAGE("Wobbuffet used Toxic Spikes!"); + MESSAGE("Foe Wynaut used Court Change!"); + MESSAGE("Foe Wynaut swapped the battle effects affecting each side!"); + MESSAGE("Go! Wobbuffet!"); + MESSAGE("Wobbuffet is hurt by spikes!"); + MESSAGE("Pointed stones dug into Wobbuffet!"); + MESSAGE("Wobbuffet was poisoned!"); + MESSAGE("Wobbuffet was caught in a Sticky Web!"); + MESSAGE("2 sent out Wynaut!"); + NONE_OF { + MESSAGE("Foe Wynaut is hurt by spikes!"); + MESSAGE("Pointed stones dug into Foe Wynaut!"); + MESSAGE("Foe Wynaut was poisoned!"); + MESSAGE("Foe Wynaut was caught in a Sticky Web!"); + } + } +} + +DOUBLE_BATTLE_TEST("Court Change used by the player swaps Mist, Safeguard, Lucky Chant, Reflect, Light Screen, Tailwind") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_MIST); MOVE(opponentRight, MOVE_SAFEGUARD); } + TURN { MOVE(opponentLeft, MOVE_LUCKY_CHANT); MOVE(opponentRight, MOVE_REFLECT); } + TURN { MOVE(opponentLeft, MOVE_LIGHT_SCREEN); MOVE(opponentRight, MOVE_TAILWIND); } + TURN { MOVE(playerLeft, MOVE_COURT_CHANGE); } + TURN { } + TURN { } + TURN { } + TURN { } + } SCENE { + MESSAGE("Foe Wobbuffet used Mist!"); + MESSAGE("Foe Wobbuffet used Safeguard!"); + MESSAGE("Foe Wobbuffet used Lucky Chant!"); + MESSAGE("Foe Wobbuffet used Reflect!"); + MESSAGE("Foe Wobbuffet used Light Screen!"); + MESSAGE("Foe Wobbuffet used Tailwind!"); + MESSAGE("Wynaut used Court Change!"); + MESSAGE("Wynaut swapped the battle effects affecting each side!"); + // The effects now end for the player side. + MESSAGE("Ally's Mist wore off!"); + MESSAGE("Ally's party is no longer protected by Safeguard!"); + MESSAGE("Ally's Reflect wore off!"); + MESSAGE("Your team's Lucky Chant wore off!"); + MESSAGE("Your team's tailwind petered out!"); + MESSAGE("Ally's Light Screen wore off!"); + } +} + +DOUBLE_BATTLE_TEST("Court Change used by the opponent swaps Mist, Safeguard, Lucky Chant, Reflect, Light Screen, Tailwind") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_MIST); MOVE(playerRight, MOVE_SAFEGUARD); } + TURN { MOVE(playerLeft, MOVE_LUCKY_CHANT); MOVE(playerRight, MOVE_REFLECT); } + TURN { MOVE(playerLeft, MOVE_LIGHT_SCREEN); MOVE(playerRight, MOVE_TAILWIND); } + TURN { MOVE(opponentLeft, MOVE_COURT_CHANGE); } + TURN { } + TURN { } + TURN { } + TURN { } + } SCENE { + MESSAGE("Wobbuffet used Mist!"); + MESSAGE("Wobbuffet used Safeguard!"); + MESSAGE("Wobbuffet used Lucky Chant!"); + MESSAGE("Wobbuffet used Reflect!"); + MESSAGE("Wobbuffet used Light Screen!"); + MESSAGE("Wobbuffet used Tailwind!"); + MESSAGE("Foe Wynaut used Court Change!"); + MESSAGE("Foe Wynaut swapped the battle effects affecting each side!"); + // The effects now end for the player side. + MESSAGE("Foe's Mist wore off!"); + MESSAGE("Foe's party is no longer protected by Safeguard!"); + MESSAGE("Foe's Reflect wore off!"); + MESSAGE("The opposing team's Lucky Chant wore off!"); + MESSAGE("The opposing team's tailwind petered out!"); + MESSAGE("Foe's Light Screen wore off!"); + } +} diff --git a/test/move_effect_defog.c b/test/move_effect_defog.c index b55d5eab6..055f3f61a 100644 --- a/test/move_effect_defog.c +++ b/test/move_effect_defog.c @@ -125,7 +125,7 @@ DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Mist and Safeguard STATUS_ICON(opponentRight, badPoison: TRUE); } else { - MESSAGE("Foe Wobbuffet's party is protected by SAFEGUARD!"); + MESSAGE("Foe Wobbuffet's party is protected by Safeguard!"); NOT STATUS_ICON(opponentRight, badPoison: TRUE); } } diff --git a/test/move_effect_sticky_web.c b/test/move_effect_sticky_web.c new file mode 100644 index 000000000..f0a6eb8d8 --- /dev/null +++ b/test/move_effect_sticky_web.c @@ -0,0 +1,237 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_STICKY_WEB].effect == EFFECT_STICKY_WEB); +} + +SINGLE_BATTLE_TEST("Sticky Web lowers Speed by 1 on switch-in") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_STICKY_WEB); } + TURN { SWITCH(opponent, 1); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, player); + MESSAGE("A sticky web spreads out on the ground around the opposing team!"); + MESSAGE("2 sent out Wynaut!"); + MESSAGE("Foe Wynaut was caught in a Sticky Web!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wynaut's Speed fell!"); + } +} + +SINGLE_BATTLE_TEST("Sticky Web can only be set up 1 time") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_STICKY_WEB); } + TURN { MOVE(player, MOVE_STICKY_WEB); } + } SCENE { + MESSAGE("Wobbuffet used Sticky Web!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, player); + MESSAGE("A sticky web spreads out on the ground around the opposing team!"); + + MESSAGE("Wobbuffet used Sticky Web!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, player); + MESSAGE("But it failed!"); + } +} + + +DOUBLE_BATTLE_TEST("Sticky Web lowers Speed by 1 in a double battle after Explosion fainting both mons") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_WOBBUFFET) {Speed(5);} + PLAYER(SPECIES_WOBBUFFET) {HP(1500); Speed(10);} + PLAYER(SPECIES_WOBBUFFET) {Speed(10);} + OPPONENT(SPECIES_WOBBUFFET) {HP(1); Speed(1);} + OPPONENT(SPECIES_WOBBUFFET) {HP(1); Speed(1);} + OPPONENT(SPECIES_WYNAUT) {Speed(10);} + OPPONENT(SPECIES_WYNAUT) {Speed(10);} + } WHEN { + TURN { MOVE(playerRight, MOVE_STICKY_WEB); MOVE(playerLeft, MOVE_EXPLOSION); SEND_OUT(playerLeft, 2); SEND_OUT(opponentLeft, 2); SEND_OUT(opponentRight, 3); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, playerRight); + MESSAGE("A sticky web spreads out on the ground around the opposing team!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, playerLeft); + MESSAGE("2 sent out Wynaut!"); + MESSAGE("Foe Wynaut was caught in a Sticky Web!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("Foe Wynaut's Speed fell!"); + MESSAGE("2 sent out Wynaut!"); + MESSAGE("Foe Wynaut was caught in a Sticky Web!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("Foe Wynaut's Speed fell!"); + } +} + +SINGLE_BATTLE_TEST("Sticky Web raises Speed by 1 for a Pokemon with Contrary") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SHUCKLE) { Ability(ABILITY_CONTRARY); } + } WHEN { + TURN { MOVE(player, MOVE_STICKY_WEB); } + TURN { SWITCH(opponent, 1); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, player); + MESSAGE("A sticky web spreads out on the ground around the opposing team!"); + MESSAGE("2 sent out Shuckle!"); + MESSAGE("Foe Shuckle was caught in a Sticky Web!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Shuckle's Speed rose!"); + } +} + +#define BATTLER_OPPONENT (opponentSetUpper == 0 ? opponentLeft : opponentRight) +#define BATTLER_PLAYER (playerSetUpper == 0 ? playerLeft : playerRight) + +DOUBLE_BATTLE_TEST("Sticky Web has correct interactions with Mirror Armor - the battler which set up Sticky Web has its Speed lowered instead") +{ + u8 playerSetUpper, opponentSetUpper; // 0 left, 1 right + + PARAMETRIZE {playerSetUpper = 0; opponentSetUpper = 0; } + PARAMETRIZE {playerSetUpper = 0; opponentSetUpper = 1; } + PARAMETRIZE {playerSetUpper = 1; opponentSetUpper = 0; } + PARAMETRIZE {playerSetUpper = 1; opponentSetUpper = 1; } + + GIVEN { + ASSUME(P_GEN_8_POKEMON == TRUE); + PLAYER(SPECIES_SQUIRTLE); + PLAYER(SPECIES_CHARMANDER); + PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); Item(ITEM_IRON_BALL); } // Iron Ball, so that flying type Corviknight is affected by Sticky Web. + OPPONENT(SPECIES_CATERPIE); + OPPONENT(SPECIES_WEEDLE); + } WHEN { + TURN { MOVE(BATTLER_OPPONENT, MOVE_STICKY_WEB); } + TURN { MOVE(BATTLER_PLAYER, MOVE_STICKY_WEB); } + TURN { SWITCH(playerRight, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, BATTLER_OPPONENT); + MESSAGE("A sticky web spreads out on the ground around your team!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, BATTLER_PLAYER); + MESSAGE("A sticky web spreads out on the ground around the opposing team!"); + + MESSAGE("Go! Corviknigh!"); + MESSAGE("Corviknigh was caught in a Sticky Web!"); + ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, BATTLER_OPPONENT); + if (opponentSetUpper == 0) { + MESSAGE("Foe Caterpie's Speed fell!"); + } else { + MESSAGE("Foe Weedle's Speed fell!"); + } + } +} + +#undef BATTLER_OPPONENT +#undef BATTLER_PLAYER + +DOUBLE_BATTLE_TEST("Sticky Web has correct interactions with Mirror Armor - no one has their Speed lowered if the set upper switched") +{ + u16 speedPlayer, speedOpponent; + + // We need to make sure Sticky Web user saves for both sides, so it doesn't matter who sets it first. + PARAMETRIZE { speedPlayer = 5; speedOpponent = 10; } + PARAMETRIZE { speedPlayer = 10; speedOpponent = 5; } + + GIVEN { + ASSUME(P_GEN_8_POKEMON == TRUE); + PLAYER(SPECIES_SQUIRTLE) { Speed(speedPlayer); } + PLAYER(SPECIES_CHARMANDER) { Speed(speedPlayer); } + PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); Item(ITEM_IRON_BALL); Speed(speedOpponent); } // Iron Ball, so that flying type Corviknight is affected by Sticky Web. + OPPONENT(SPECIES_CATERPIE) { Speed(speedOpponent); } + OPPONENT(SPECIES_WEEDLE) { Speed(speedOpponent); } + OPPONENT(SPECIES_PIDGEY) { Speed(speedOpponent); } // Flying type,so not affected by Sticky Web. + } WHEN { + TURN { MOVE(opponentLeft, MOVE_STICKY_WEB); MOVE(playerRight, MOVE_STICKY_WEB); } + TURN { SWITCH(opponentLeft, 2); } + TURN { SWITCH(playerRight, 2); } + } SCENE { + if (speedPlayer > speedOpponent) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, playerRight); + MESSAGE("A sticky web spreads out on the ground around the opposing team!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponentLeft); + MESSAGE("A sticky web spreads out on the ground around your team!"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponentLeft); + MESSAGE("A sticky web spreads out on the ground around your team!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, playerRight); + MESSAGE("A sticky web spreads out on the ground around the opposing team!"); + } + + MESSAGE("Go! Corviknigh!"); + MESSAGE("Corviknigh was caught in a Sticky Web!"); + ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + EXPECT_EQ(playerRight->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentLeft->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentRight->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + } +} + +DOUBLE_BATTLE_TEST("Sticky Web has correct interactions with Mirror Armor - no one has their Speed lowered if the set upper fainted") +{ + bool8 hasReplacement; + + // We need to make sure Sticky Web user saves for both sides, so it doesn't matter who sets it first. + PARAMETRIZE {hasReplacement = TRUE;} + PARAMETRIZE {hasReplacement = FALSE;} + + GIVEN { + ASSUME(P_GEN_8_POKEMON == TRUE); + ASSUME(gBattleMoves[MOVE_MEMENTO].effect == EFFECT_MEMENTO); + PLAYER(SPECIES_SQUIRTLE) {Speed(5); } + PLAYER(SPECIES_CHARMANDER) {Speed(5); } + PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR); Item(ITEM_IRON_BALL); Speed(5); } // Iron Ball, so that flying type Corviknight is affected by Sticky Web. + OPPONENT(SPECIES_CATERPIE) {Speed(7); } + OPPONENT(SPECIES_WEEDLE) {Speed(7); } + if (hasReplacement) { + OPPONENT(SPECIES_PIDGEY) {Speed(7); } + } + + } WHEN { + TURN { MOVE(opponentLeft, MOVE_STICKY_WEB); } + if (hasReplacement) { + TURN { MOVE(opponentLeft, MOVE_MEMENTO, target:playerLeft); SEND_OUT(opponentLeft, 2); } + } else { + TURN { MOVE(opponentLeft, MOVE_MEMENTO, target:playerLeft);} + } + TURN { SWITCH(playerRight, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponentLeft); + MESSAGE("A sticky web spreads out on the ground around your team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_MEMENTO, opponentLeft); + MESSAGE("Foe Caterpie fainted!"); + if (hasReplacement) { + MESSAGE("2 sent out Pidgey!"); + } + + MESSAGE("Go! Corviknigh!"); + MESSAGE("Corviknigh was caught in a Sticky Web!"); + ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + } THEN { + if (hasReplacement) { + EXPECT_EQ(opponentLeft->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + } + EXPECT_EQ(playerLeft->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + EXPECT_EQ(playerRight->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentRight->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + } +} From 42992ca5ce4b6da00ef98a2952a40547ef8ba71e Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 7 Aug 2023 17:23:12 +0200 Subject: [PATCH 03/16] Fix Teleport ending trainer battles (#3166) --- asm/macros/battle_script.inc | 17 +++++----- data/battle_scripts_1.s | 34 +++----------------- src/battle_script_commands.c | 34 -------------------- test/move_effect_teleport.c | 61 ++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 72 deletions(-) create mode 100644 test/move_effect_teleport.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 9ba68ef05..93c747810 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1318,7 +1318,7 @@ .2byte \holdEffect .4byte \jumpInstr .endm - + .macro dostockpilestatchangeswearoff, battler:req, statChangeInstr:req callnative BS_DoStockpileStatChangesWearOff .byte \battler @@ -1354,7 +1354,7 @@ .macro setsnow callnative BS_SetSnow .endm - + .macro setzeffect callnative BS_SetZEffect .endm @@ -1364,12 +1364,6 @@ callnative BS_TrySymbiosis .endm - @ returns TRUE or FALSE to gBattleCommunication[0] - .macro canteleport battler:req - callnative BS_CanTeleport - .byte \battler - .endm - @ returns B_SIDE_x to gBattleCommunication[0] .macro getbattlerside battler:req callnative BS_GetBattlerSide @@ -2077,7 +2071,7 @@ .macro swapsidestatuses various BS_ATTACKER, VARIOUS_SWAP_SIDE_STATUSES .endm - + .macro swapstats stat:req various BS_ATTACKER, VARIOUS_SWAP_STATS .byte \stat @@ -2178,6 +2172,11 @@ jumpifbyte CMP_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_NO_EFFECT, \jumpInstr .endm + .macro jumpifside battler:req, side:req, equalJumpInstr:req + getbattlerside \battler + jumpifbyte CMP_EQUAL, gBattleCommunication, \side, \equalJumpInstr + .endm + .macro jumpifbattletype flags:req, jumpInstr:req jumpifword CMP_COMMON_BITS, gBattleTypeFlags, \flags, \jumpInstr .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index c707ef75f..0375c1325 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -5401,15 +5401,14 @@ BattleScript_EffectHurricane: BattleScript_EffectTeleport: attackcanceler attackstring - ppreduce .if B_TELEPORT_BEHAVIOR >= GEN_7 - canteleport BS_ATTACKER - jumpifbyte CMP_EQUAL, gBattleCommunication, TRUE, BattleScript_EffectTeleportNew - goto BattleScript_ButItFailed + jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_EffectBatonPass + jumpifside BS_ATTACKER, B_SIDE_PLAYER, BattleScript_EffectBatonPass .else jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_ButItFailed .endif BattleScript_EffectTeleportTryToRunAway: + ppreduce getifcantrunfrombattle BS_ATTACKER jumpifbyte CMP_EQUAL, gBattleCommunication, BATTLE_RUN_FORBIDDEN, BattleScript_ButItFailed jumpifbyte CMP_EQUAL, gBattleCommunication, BATTLE_RUN_FAILURE, BattleScript_PrintAbilityMadeIneffective @@ -5420,29 +5419,6 @@ BattleScript_EffectTeleportTryToRunAway: setoutcomeonteleport BS_ATTACKER goto BattleScript_MoveEnd -BattleScript_EffectTeleportNew: - getbattlerside BS_ATTACKER - jumpifbyte CMP_EQUAL, gBattleCommunication, B_SIDE_OPPONENT, BattleScript_EffectTeleportTryToRunAway - attackanimation - waitanimation - openpartyscreen BS_ATTACKER, BattleScript_EffectTeleportNewEnd - switchoutabilities BS_ATTACKER - waitstate - switchhandleorder BS_ATTACKER, 2 - returntoball BS_ATTACKER - getswitchedmondata BS_ATTACKER - switchindataupdate BS_ATTACKER - hpthresholds BS_ATTACKER - trytoclearprimalweather - printstring STRINGID_EMPTYSTRING3 - waitmessage 1 - printstring STRINGID_SWITCHINMON - switchinanim BS_ATTACKER, TRUE - waitstate - switchineffects BS_ATTACKER -BattleScript_EffectTeleportNewEnd: - goto BattleScript_MoveEnd - BattleScript_EffectBeatUp:: attackcanceler accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE @@ -7897,7 +7873,7 @@ BattleScript_WishMegaEvolution:: BattleScript_PrimalReversion:: call BattleScript_PrimalReversionRet end2 - + BattleScript_PrimalReversionRestoreAttacker:: call BattleScript_PrimalReversionRet copybyte gBattlerAttacker, sSAVED_BATTLER @@ -8944,7 +8920,7 @@ BattleScript_BadDreams_ShowPopUp: goto BattleScript_BadDreams_DmgAfterPopUp BattleScript_BadDreams_HidePopUp: destroyabilitypopup - tryfaintmon BS_TARGET + tryfaintmon BS_TARGET goto BattleScript_BadDreamsIncrement BattleScript_TookAttack:: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index f1bb07281..af222e6ba 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8837,33 +8837,6 @@ static void HandleScriptMegaPrimal(u32 caseId, u32 battlerId, bool32 isMega) } } -static bool32 CanTeleport(u8 battlerId) -{ - struct Pokemon *party = GetBattlerParty(battlerId); - u32 species, count, i; - - for (i = 0; i < PARTY_SIZE; i++) - { - species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); - if (species != SPECIES_NONE && species != SPECIES_EGG && GetMonData(&party[i], MON_DATA_HP) != 0) - count++; - } - - switch (GetBattlerSide(battlerId)) - { - case B_SIDE_OPPONENT: - if (count == 1 || gBattleTypeFlags & BATTLE_TYPE_DOUBLE) - return FALSE; - break; - case B_SIDE_PLAYER: - if (count == 1 || (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && count <= 2)) - return FALSE; - break; - } - - return TRUE; -} - // Return True if the order was changed, and false if the order was not changed(for example because the target would move after the attacker anyway). static bool32 ChangeOrderTargetAfterAttacker(void) { @@ -16327,13 +16300,6 @@ void BS_GetBattlerSide(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_CanTeleport(void) -{ - NATIVE_ARGS(u8 battler); - gBattleCommunication[0] = CanTeleport(cmd->battler); - gBattlescriptCurrInstr = cmd->nextInstr; -} - void BS_TrySymbiosis(void) { NATIVE_ARGS(); diff --git a/test/move_effect_teleport.c b/test/move_effect_teleport.c new file mode 100644 index 000000000..9c8a16d4b --- /dev/null +++ b/test/move_effect_teleport.c @@ -0,0 +1,61 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_TELEPORT].effect == EFFECT_TELEPORT); +} + +SINGLE_BATTLE_TEST("Teleport fails when there is no pokemon to switch in") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TELEPORT); } + } SCENE { + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Teleport fails when there no alive pokemon left") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT) { HP(0); } + } WHEN { + TURN { MOVE(opponent, MOVE_TELEPORT); } + } SCENE { + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Teleport forces the pokemon to switch out") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, MOVE_TELEPORT); SEND_OUT(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TELEPORT, opponent); + MESSAGE("2 sent out Wynaut!"); + } +} + +SINGLE_BATTLE_TEST("Teleport does not fail if the user is trapped") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_FIRE_SPIN); MOVE(opponent, MOVE_TELEPORT); SEND_OUT(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FIRE_SPIN, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TELEPORT, opponent); + MESSAGE("2 sent out Wynaut!"); + } +} From f6e6a1f3839b82e547bc54c5163132927b3a3f15 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Wed, 9 Aug 2023 15:06:03 +0100 Subject: [PATCH 04/16] Initialize save to empty in test runner (#3215) --- test/test_runner.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/test_runner.c b/test/test_runner.c index d91ad02d1..3f1352e5a 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -2,6 +2,7 @@ #include "global.h" #include "characters.h" #include "gpu_regs.h" +#include "load_save.h" #include "main.h" #include "malloc.h" #include "random.h" @@ -114,6 +115,10 @@ void CB2_TestRunner(void) return; } + MoveSaveBlocks_ResetHeap(); + ClearSav1(); + ClearSav2(); + gIntrTable[7] = Intr_Timer2; // The current test restarted the ROM (e.g. by jumping to NULL). From bd7960ef211c0f3383785a40d7464e6c92cc518b Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Fri, 11 Aug 2023 12:04:03 -0400 Subject: [PATCH 05/16] Updated versions for issue templates (#3220) --- .github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml | 3 ++- .github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml | 3 ++- .github/ISSUE_TEMPLATE/04_other_errors.yaml | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml index c4404a039..558f249e4 100644 --- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml +++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml @@ -23,8 +23,9 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.5.1 (Default) + - 1.5.2 (Default) - upcoming (Edge) + - 1.5.1 - 1.5.0 - 1.4.3 - 1.4.2 diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml index a24757fc7..157177dce 100644 --- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml +++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml @@ -23,8 +23,9 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.5.1 (Default) + - 1.5.2 (Default) - upcoming (Edge) + - 1.5.1 - 1.5.0 - 1.4.3 - 1.4.2 diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml index e25695c0b..a73096bab 100644 --- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml +++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml @@ -23,8 +23,9 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.5.1 (Default) + - 1.5.2 (Default) - upcoming (Edge) + - 1.5.1 - 1.5.0 - 1.4.3 - 1.4.2 From 59da940283cbce98de9819b6abfa403ae2991b89 Mon Sep 17 00:00:00 2001 From: Philipp AUER Date: Fri, 11 Aug 2023 22:28:38 +0200 Subject: [PATCH 06/16] Refactor damage formula to match Gen5+ (#3196) * [battle, damage] refactor damage formula to match gen5+ * [test] use exact values for dry skin, swarm tests * fixup: assume stats for dry-skin, swarm tests --------- Co-authored-by: sbird --- include/battle_util.h | 7 - include/fpmath.h | 20 + src/battle_script_commands.c | 2 +- src/battle_util.c | 709 ++++++++++++++++++++--------------- test/ability_contrary.c | 2 +- test/ability_dry_skin.c | 15 +- test/ability_fluffy.c | 66 ++++ test/ability_swarm.c | 14 +- test/damage_formula.c | 78 ++++ test/status3.c | 90 +++++ test/weather_snow.c | 1 - 11 files changed, 680 insertions(+), 324 deletions(-) create mode 100644 test/ability_fluffy.c create mode 100644 test/damage_formula.c create mode 100644 test/status3.c diff --git a/include/battle_util.h b/include/battle_util.h index 9de5d6ded..87ef161a2 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -209,14 +209,12 @@ void BufferStatChange(u8 battlerId, u8 statId, u8 stringId); bool32 BlocksPrankster(u16 move, u8 battlerPrankster, u8 battlerDef, bool32 checkTarget); u16 GetUsedHeldItem(u8 battler); bool32 IsBattlerWeatherAffected(u8 battlerId, u32 weatherFlags); -u32 ApplyWeatherDamageMultiplier(u8 battlerAtk, u16 move, u8 moveType, u32 dmg, u16 holdEffectAtk, u16 holdEffectDef); u32 GetBattlerMoveTargetType(u8 battlerId, u16 move); bool32 CanTargetBattler(u8 battlerAtk, u8 battlerDef, u16 move); bool8 IsMoveAffectedByParentalBond(u16 move, u8 battlerId); void CopyMonLevelAndBaseStatsToBattleMon(u32 battler, struct Pokemon *mon); void CopyMonAbilityAndTypesToBattleMon(u32 battler, struct Pokemon *mon); void RecalcBattlerStats(u32 battler, struct Pokemon *mon); -void MulModifier(u16 *modifier, u16 val); bool32 IsAlly(u32 battlerAtk, u32 battlerDef); // Ability checks @@ -245,9 +243,4 @@ u8 GetBattlerGender(u8 battlerId); bool8 AreBattlersOfOppositeGender(u8 battler1, u8 battler2); u32 CalcSecondaryEffectChance(u8 battlerId, u8 secondaryEffectChance); -static inline u32 ApplyModifier(uq4_12_t modifier, u32 val) -{ - return UQ_4_12_TO_INT((modifier * val) + UQ_4_12_ROUND); -} - #endif // GUARD_BATTLE_UTIL_H diff --git a/include/fpmath.h b/include/fpmath.h index 987c59d5a..6e3edd64e 100644 --- a/include/fpmath.h +++ b/include/fpmath.h @@ -52,10 +52,30 @@ static inline uq4_12_t uq4_12_multiply(uq4_12_t a, uq4_12_t b) return (product + UQ_4_12_ROUND) >> UQ_4_12_SHIFT; } +static inline uq4_12_t uq4_12_multiply_half_down(uq4_12_t a, uq4_12_t b) +{ + u32 product = (u32) a * b; + return (product + UQ_4_12_ROUND - 1) >> UQ_4_12_SHIFT; +} + static inline uq4_12_t uq4_12_divide(uq4_12_t dividend, uq4_12_t divisor) { if (divisor == UQ_4_12(0.0)) return UQ_4_12(0); return (dividend << UQ_4_12_SHIFT) / divisor; } +// Multiplies value by the UQ_4_12 number modifier. +// Returns an integer, rounded to nearest (rounding down on n.5) +static inline u32 uq4_12_multiply_by_int_half_down(uq4_12_t modifier, u32 value) +{ + return UQ_4_12_TO_INT((modifier * value) + UQ_4_12_ROUND - 1); +} + +// Multiplies value by the UQ_4_12 number modifier. +// Returns an integer, rounded to nearest (rounding up on n.5) +static inline u32 uq4_12_multiply_by_int_half_up(uq4_12_t modifier, u32 value) +{ + return UQ_4_12_TO_INT((modifier * value) + UQ_4_12_ROUND); +} + #endif // FPMATH_H_ diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index aa5276805..d95209e5e 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1414,7 +1414,7 @@ static void Cmd_attackcanceler(void) return; } - // Z-moves and Max Moves bypass protection, but deal reduced damage (factored in CalcFinalDmg) + // Z-moves and Max Moves bypass protection, but deal reduced damage (factored in AccumulateOtherModifiers) if (gBattleStruct->zmove.active && IS_BATTLER_PROTECTED(gBattlerTarget)) { BattleScriptPush(cmd->nextInstr); diff --git a/src/battle_util.c b/src/battle_util.c index 2f200026b..8950917a5 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8694,6 +8694,85 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe u16 atkAbility = GetBattlerAbility(battlerAtk); u16 defAbility = GetBattlerAbility(battlerDef); + // move effect + switch (gBattleMoves[move].effect) + { + case EFFECT_FACADE: + if (gBattleMons[battlerAtk].status1 & (STATUS1_BURN | STATUS1_PSN_ANY | STATUS1_PARALYSIS | STATUS1_FROSTBITE)) + modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + break; + case EFFECT_BRINE: + if (gBattleMons[battlerDef].hp <= (gBattleMons[battlerDef].maxHP / 2)) + modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + break; + case EFFECT_BARB_BARRAGE: + case EFFECT_VENOSHOCK: + if (gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) + modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + break; + case EFFECT_RETALIATE: + if (gSideTimers[atkSide].retaliateTimer == 1) + modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + break; + case EFFECT_SOLAR_BEAM: + if (IsBattlerWeatherAffected(battlerAtk, (B_WEATHER_HAIL | B_WEATHER_SANDSTORM | B_WEATHER_RAIN | B_WEATHER_SNOW))) + modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); + break; + case EFFECT_STOMPING_TANTRUM: + if (gBattleStruct->lastMoveFailed & gBitTable[battlerAtk]) + modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + break; + case EFFECT_BULLDOZE: + case EFFECT_MAGNITUDE: + case EFFECT_EARTHQUAKE: + if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && !(gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE)) + modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); + break; + case EFFECT_KNOCK_OFF: + #if B_KNOCK_OFF_DMG >= GEN_6 + if (gBattleMons[battlerDef].item != ITEM_NONE + && CanBattlerGetOrLoseItem(battlerDef, gBattleMons[battlerDef].item)) + modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + #endif + break; + } + +#if B_TERRAIN_TYPE_BOOST >= GEN_8 + #define TERRAIN_TYPE_BOOST UQ_4_12(1.3) +#else + #define TERRAIN_TYPE_BOOST UQ_4_12(1.5) +#endif + + // various effects + if (gProtectStructs[battlerAtk].helpingHand) + modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + if (gSpecialStatuses[battlerAtk].gemBoost) + modifier = uq4_12_multiply(modifier, UQ_4_12(1.0) + sPercentToModifier[gSpecialStatuses[battlerAtk].gemParam]); + if (gStatuses3[battlerAtk] & STATUS3_CHARGED_UP && moveType == TYPE_ELECTRIC) + modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + if (gStatuses3[battlerAtk] & STATUS3_ME_FIRST) + modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + if (IsBattlerTerrainAffected(battlerAtk, STATUS_FIELD_GRASSY_TERRAIN) && moveType == TYPE_GRASS) + modifier = uq4_12_multiply(modifier, TERRAIN_TYPE_BOOST); + if (IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN) && moveType == TYPE_DRAGON) + modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); + if (IsBattlerTerrainAffected(battlerAtk, STATUS_FIELD_ELECTRIC_TERRAIN) && moveType == TYPE_ELECTRIC) + modifier = uq4_12_multiply(modifier, TERRAIN_TYPE_BOOST); + if (IsBattlerTerrainAffected(battlerAtk, STATUS_FIELD_PSYCHIC_TERRAIN) && moveType == TYPE_PSYCHIC) + modifier = uq4_12_multiply(modifier, TERRAIN_TYPE_BOOST); + #if B_SPORT_TURNS >= GEN_6 + if ((moveType == TYPE_ELECTRIC && gFieldStatuses & STATUS_FIELD_MUDSPORT) + || (moveType == TYPE_FIRE && gFieldStatuses & STATUS_FIELD_WATERSPORT)) + #else + if ((moveType == TYPE_ELECTRIC && AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, 0, ABILITYEFFECT_MUD_SPORT, 0)) + || (moveType == TYPE_FIRE && AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, 0, ABILITYEFFECT_WATER_SPORT, 0))) + #endif + #if B_SPORT_DMG_REDUCTION >= GEN_5 + modifier = uq4_12_multiply(modifier, UQ_4_12(0.23)); + #else + modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); + #endif + // attacker's abilities switch (atkAbility) { @@ -8890,16 +8969,6 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe if (moveType == TYPE_FIRE) modifier = uq4_12_multiply(modifier, UQ_4_12(1.25)); break; - case ABILITY_FLUFFY: - if (IsMoveMakingContact(move, battlerAtk)) - { - modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); - if (updateFlags) - RecordAbilityBattle(battlerDef, defAbility); - } - if (moveType == TYPE_FIRE) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); - break; case ABILITY_PROTOSYNTHESIS: { u8 defHighestStat = GetHighestStatId(battlerDef); @@ -8993,86 +9062,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe modifier = uq4_12_multiply(modifier, UQ_4_12(1.1)); break; } - - // move effect - switch (gBattleMoves[move].effect) - { - case EFFECT_FACADE: - if (gBattleMons[battlerAtk].status1 & (STATUS1_BURN | STATUS1_PSN_ANY | STATUS1_PARALYSIS | STATUS1_FROSTBITE)) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); - break; - case EFFECT_BRINE: - if (gBattleMons[battlerDef].hp <= (gBattleMons[battlerDef].maxHP / 2)) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); - break; - case EFFECT_BARB_BARRAGE: - case EFFECT_VENOSHOCK: - if (gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); - break; - case EFFECT_RETALIATE: - if (gSideTimers[atkSide].retaliateTimer == 1) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); - break; - case EFFECT_SOLAR_BEAM: - if (IsBattlerWeatherAffected(battlerAtk, (B_WEATHER_HAIL | B_WEATHER_SANDSTORM | B_WEATHER_RAIN | B_WEATHER_SNOW))) - modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); - break; - case EFFECT_STOMPING_TANTRUM: - if (gBattleStruct->lastMoveFailed & gBitTable[battlerAtk]) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); - break; - case EFFECT_BULLDOZE: - case EFFECT_MAGNITUDE: - case EFFECT_EARTHQUAKE: - if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && !(gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE)) - modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); - break; - case EFFECT_KNOCK_OFF: - #if B_KNOCK_OFF_DMG >= GEN_6 - if (gBattleMons[battlerDef].item != ITEM_NONE - && CanBattlerGetOrLoseItem(battlerDef, gBattleMons[battlerDef].item)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); - #endif - break; - } - -#if B_TERRAIN_TYPE_BOOST >= GEN_8 - #define TERRAIN_TYPE_BOOST UQ_4_12(1.3) -#else - #define TERRAIN_TYPE_BOOST UQ_4_12(1.5) -#endif - - // various effects - if (gProtectStructs[battlerAtk].helpingHand) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); - if (gSpecialStatuses[battlerAtk].gemBoost) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.0) + sPercentToModifier[gSpecialStatuses[battlerAtk].gemParam]); - if (gStatuses3[battlerAtk] & STATUS3_CHARGED_UP && moveType == TYPE_ELECTRIC) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); - if (gStatuses3[battlerAtk] & STATUS3_ME_FIRST) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); - if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && moveType == TYPE_GRASS && IsBattlerGrounded(battlerAtk) && !(gStatuses3[battlerAtk] & STATUS3_SEMI_INVULNERABLE)) - modifier = uq4_12_multiply(modifier, TERRAIN_TYPE_BOOST); - if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN && moveType == TYPE_DRAGON && IsBattlerGrounded(battlerDef) && !(gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE)) - modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); - if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && moveType == TYPE_ELECTRIC && IsBattlerGrounded(battlerAtk) && !(gStatuses3[battlerAtk] & STATUS3_SEMI_INVULNERABLE)) - modifier = uq4_12_multiply(modifier, TERRAIN_TYPE_BOOST); - if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN && moveType == TYPE_PSYCHIC && IsBattlerGrounded(battlerAtk) && !(gStatuses3[battlerAtk] & STATUS3_SEMI_INVULNERABLE)) - modifier = uq4_12_multiply(modifier, TERRAIN_TYPE_BOOST); - #if B_SPORT_TURNS >= GEN_6 - if ((moveType == TYPE_ELECTRIC && gFieldStatuses & STATUS_FIELD_MUDSPORT) - || (moveType == TYPE_FIRE && gFieldStatuses & STATUS_FIELD_WATERSPORT)) - #else - if ((moveType == TYPE_ELECTRIC && AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, 0, ABILITYEFFECT_MUD_SPORT, 0)) - || (moveType == TYPE_FIRE && AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, 0, ABILITYEFFECT_WATER_SPORT, 0))) - #endif - #if B_SPORT_DMG_REDUCTION >= GEN_5 - modifier = uq4_12_multiply(modifier, UQ_4_12(0.23)); - #else - modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); - #endif - return ApplyModifier(modifier, basePower); + return uq4_12_multiply_by_int_half_down(modifier, basePower); } #undef TERRAIN_TYPE_BOOST @@ -9136,39 +9126,39 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b case ABILITY_HUGE_POWER: case ABILITY_PURE_POWER: if (IS_MOVE_PHYSICAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; case ABILITY_SLOW_START: if (gDisableStructs[battlerAtk].slowStartTimer != 0) - modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(0.5)); break; case ABILITY_SOLAR_POWER: if (IS_MOVE_SPECIAL(move) && IsBattlerWeatherAffected(battlerAtk, B_WEATHER_SUN)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; case ABILITY_DEFEATIST: if (gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 2)) - modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(0.5)); break; case ABILITY_FLASH_FIRE: if (moveType == TYPE_FIRE && gBattleResources->flags->flags[battlerAtk] & RESOURCE_FLAG_FLASH_FIRE) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; case ABILITY_SWARM: if (moveType == TYPE_BUG && gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 3)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; case ABILITY_TORRENT: if (moveType == TYPE_WATER && gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 3)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; case ABILITY_BLAZE: if (moveType == TYPE_FIRE && gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 3)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; case ABILITY_OVERGROW: if (moveType == TYPE_GRASS && gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 3)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; #if B_PLUS_MINUS_INTERACTION >= GEN_5 case ABILITY_PLUS: @@ -9177,34 +9167,34 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b { u32 partnerAbility = GetBattlerAbility(BATTLE_PARTNER(battlerAtk)); if (partnerAbility == ABILITY_PLUS || partnerAbility == ABILITY_MINUS) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); } break; #else case ABILITY_PLUS: if (IS_MOVE_SPECIAL(move) && IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_MINUS) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; case ABILITY_MINUS: if (IS_MOVE_SPECIAL(move) && IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_PLUS) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; #endif case ABILITY_FLOWER_GIFT: if (gBattleMons[battlerAtk].species == SPECIES_CHERRIM_SUNSHINE && IsBattlerWeatherAffected(battlerAtk, B_WEATHER_SUN) && IS_MOVE_PHYSICAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; case ABILITY_HUSTLE: if (IS_MOVE_PHYSICAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; case ABILITY_STAKEOUT: if (gDisableStructs[battlerDef].isFirstTurn == 2) // just switched in - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; case ABILITY_GUTS: if (gBattleMons[battlerAtk].status1 & STATUS1_ANY && IS_MOVE_PHYSICAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; } @@ -9214,15 +9204,11 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b case ABILITY_THICK_FAT: if (moveType == TYPE_FIRE || moveType == TYPE_ICE) { - modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(0.5)); if (updateFlags) RecordAbilityBattle(battlerDef, ABILITY_THICK_FAT); } break; - case ABILITY_ICE_SCALES: - if (IS_MOVE_SPECIAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); - break; } // ally's abilities @@ -9232,7 +9218,7 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b { case ABILITY_FLOWER_GIFT: if (gBattleMons[BATTLE_PARTNER(battlerAtk)].species == SPECIES_CHERRIM_SUNSHINE && IsBattlerWeatherAffected(BATTLE_PARTNER(battlerAtk), B_WEATHER_SUN) && IS_MOVE_PHYSICAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; } } @@ -9242,34 +9228,34 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b { case HOLD_EFFECT_THICK_CLUB: if ((atkBaseSpeciesId == SPECIES_CUBONE || atkBaseSpeciesId == SPECIES_MAROWAK) && IS_MOVE_PHYSICAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_DEEP_SEA_TOOTH: if (gBattleMons[battlerAtk].species == SPECIES_CLAMPERL && IS_MOVE_SPECIAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_LIGHT_BALL: if (atkBaseSpeciesId == SPECIES_PIKACHU) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_CHOICE_BAND: if (IS_MOVE_PHYSICAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; case HOLD_EFFECT_CHOICE_SPECS: if (IS_MOVE_SPECIAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; } // The offensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the 1st badge and 7th badges. // Having the 1st badge boosts physical attack while having the 7th badge boosts special attack. if (ShouldGetStatBadgeBoost(FLAG_BADGE01_GET, battlerAtk) && IS_MOVE_PHYSICAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.1)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.1)); if (ShouldGetStatBadgeBoost(FLAG_BADGE07_GET, battlerAtk) && IS_MOVE_SPECIAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.1)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.1)); - return ApplyModifier(modifier, atkStat); + return uq4_12_multiply_by_int_half_down(modifier, atkStat); } static bool32 CanEvolve(u32 species) @@ -9343,7 +9329,7 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, case ABILITY_MARVEL_SCALE: if (gBattleMons[battlerDef].status1 & STATUS1_ANY && usesDefStat) { - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); if (updateFlags) RecordAbilityBattle(battlerDef, ABILITY_MARVEL_SCALE); } @@ -9351,7 +9337,7 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, case ABILITY_FUR_COAT: if (usesDefStat) { - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); if (updateFlags) RecordAbilityBattle(battlerDef, ABILITY_FUR_COAT); } @@ -9359,22 +9345,18 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, case ABILITY_GRASS_PELT: if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && usesDefStat) { - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); if (updateFlags) RecordAbilityBattle(battlerDef, ABILITY_GRASS_PELT); } break; case ABILITY_FLOWER_GIFT: if (gBattleMons[battlerDef].species == SPECIES_CHERRIM_SUNSHINE && IsBattlerWeatherAffected(battlerDef, B_WEATHER_SUN) && !usesDefStat) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); - break; - case ABILITY_PUNK_ROCK: - if (gBattleMoves[move].soundMove) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; case ABILITY_PURIFYING_SALT: if (gBattleMoves[move].type == TYPE_GHOST) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; } @@ -9385,7 +9367,7 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, { case ABILITY_FLOWER_GIFT: if (gBattleMons[BATTLE_PARTNER(battlerDef)].species == SPECIES_CHERRIM_SUNSHINE && IsBattlerWeatherAffected(BATTLE_PARTNER(battlerDef), B_WEATHER_SUN) && !usesDefStat) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; } } @@ -9395,240 +9377,374 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, { case HOLD_EFFECT_DEEP_SEA_SCALE: if (gBattleMons[battlerDef].species == SPECIES_CLAMPERL && !usesDefStat) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_METAL_POWDER: if (gBattleMons[battlerDef].species == SPECIES_DITTO && usesDefStat && !(gBattleMons[battlerDef].status2 & STATUS2_TRANSFORMED)) - modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_EVIOLITE: if (CanEvolve(gBattleMons[battlerDef].species)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; case HOLD_EFFECT_ASSAULT_VEST: if (!usesDefStat) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; #if B_SOUL_DEW_BOOST <= GEN_6 case HOLD_EFFECT_SOUL_DEW: if ((gBattleMons[battlerDef].species == SPECIES_LATIAS || gBattleMons[battlerDef].species == SPECIES_LATIOS) && !(gBattleTypeFlags & BATTLE_TYPE_FRONTIER) && !usesDefStat) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; #endif } // sandstorm sp.def boost for rock types if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ROCK) && gBattleWeather & B_WEATHER_SANDSTORM && WEATHER_HAS_EFFECT && !usesDefStat) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); // snow def boost for ice types if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && gBattleWeather & B_WEATHER_SNOW && WEATHER_HAS_EFFECT && usesDefStat) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); // The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the 5th badge and 7th badges. // Having the 5th badge boosts physical defense while having the 7th badge boosts special defense. if (ShouldGetStatBadgeBoost(FLAG_BADGE05_GET, battlerDef) && IS_MOVE_PHYSICAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.1)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.1)); if (ShouldGetStatBadgeBoost(FLAG_BADGE07_GET, battlerDef) && IS_MOVE_SPECIAL(move)) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.1)); + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.1)); - return ApplyModifier(modifier, defStat); + return uq4_12_multiply_by_int_half_down(modifier, defStat); } -static u32 CalcFinalDmg(u32 dmg, u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, uq4_12_t typeEffectivenessModifier, bool32 isCrit, bool32 updateFlags) +// base damage formula before adding any modifiers +static inline s32 CalculateBaseDamage(u32 power, u32 userFinalAttack, u32 level, u32 targetFinalDefense) { - u32 percentBoost; - u32 abilityAtk = GetBattlerAbility(battlerAtk); - u32 abilityDef = GetBattlerAbility(battlerDef); - u32 defSide = GET_BATTLER_SIDE(battlerDef); - uq4_12_t finalModifier = UQ_4_12(1.0); - u16 itemDef = gBattleMons[battlerDef].item; - u16 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); - u16 holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE); + return power * userFinalAttack * (2 * level / 5 + 2) / targetFinalDefense / 50 + 2; +} - // check multiple targets in double battle +#if B_MULTIPLE_TARGETS_DMG >= GEN_4 + #define V_MULTIPLE_TARGETS_DMG UQ_4_12(0.75) +#else + #define V_MULTIPLE_TARGETS_DMG UQ_4_12(0.5) +#endif + +#if B_CRIT_MULTIPLIER >= GEN_6 + #define V_CRIT_MULTIPLIER UQ_4_12(1.5) +#else + #define V_CRIT_MULTIPLIER UQ_4_12(2.0) +#endif + +#if B_BURN_FACADE_DMG >= GEN_6 + #define FACADE_PREVENTS_BURN_MALUS(move) (gBattleMoves[move].effect == EFFECT_FACADE) +#else + #define FACADE_PREVENTS_BURN_MALUS(move) (FALSE) +#endif + +#if B_PARENTAL_BOND_DMG < GEN_7 + #define V_PARENTAL_BOND_DMG UQ_4_12(0.5) +#else + #define V_PARENTAL_BOND_DMG UQ_4_12(0.25) +#endif + +static inline uq4_12_t GetTargetDamageModifier(u32 move, u32 battlerAtk, u32 battlerDef) +{ if (GetMoveTargetCount(move, battlerAtk, battlerDef) >= 2) - #if B_MULTIPLE_TARGETS_DMG >= GEN_4 - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(0.75)); - #else - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(0.5)); - #endif + return V_MULTIPLE_TARGETS_DMG; + return UQ_4_12(1.0); +} - // take type effectiveness - finalModifier = uq4_12_multiply(finalModifier, typeEffectivenessModifier); +static inline uq4_12_t GetParentalBondModifier(u32 battlerAtk) +{ + if (gSpecialStatuses[battlerAtk].parentalBondState != PARENTAL_BOND_2ND_HIT) + return UQ_4_12(1.0); + return V_PARENTAL_BOND_DMG; +} - // check crit - if (isCrit) - #if B_CRIT_MULTIPLIER >= GEN_6 - dmg = ApplyModifier(UQ_4_12(1.5), dmg); - #else - dmg = ApplyModifier(UQ_4_12(2.0), dmg); - #endif +static inline uq4_12_t GetSameTypeAttackBonusModifier(u32 battlerAtk, u32 moveType, u32 move, u32 abilityAtk) +{ + if (!IS_BATTLER_OF_TYPE(battlerAtk, moveType) || move == MOVE_STRUGGLE || move == MOVE_NONE) + return UQ_4_12(1.0); + return (abilityAtk == ABILITY_ADAPTABILITY) ? UQ_4_12(2.0) : UQ_4_12(1.5); +} - // check burn - if (gBattleMons[battlerAtk].status1 & STATUS1_BURN && IS_MOVE_PHYSICAL(move) - #if B_BURN_FACADE_DMG >= GEN_6 - && gBattleMoves[move].effect != EFFECT_FACADE - #endif +// Utility Umbrella holders take normal damage from what would be rain- and sun-weakened attacks. +static uq4_12_t GetWeatherDamageModifier(u32 battlerAtk, u32 move, u32 moveType, u32 holdEffectAtk, u32 holdEffectDef) +{ + if (!WEATHER_HAS_EFFECT) + return UQ_4_12(1.0); + if (gBattleMoves[move].effect == EFFECT_HYDRO_STEAM && (gBattleWeather & B_WEATHER_SUN) && holdEffectAtk != HOLD_EFFECT_UTILITY_UMBRELLA) + return UQ_4_12(1.5); + if (holdEffectDef == HOLD_EFFECT_UTILITY_UMBRELLA) + return UQ_4_12(1.0); + + if (gBattleWeather & B_WEATHER_RAIN) + { + if (moveType != TYPE_FIRE && moveType != TYPE_WATER) + return UQ_4_12(1.0); + return (moveType == TYPE_FIRE) ? UQ_4_12(0.5) : UQ_4_12(1.5); + } + if (gBattleWeather & B_WEATHER_SUN) + { + if (moveType != TYPE_FIRE && moveType != TYPE_WATER) + return UQ_4_12(1.0); + return (moveType == TYPE_WATER) ? UQ_4_12(0.5) : UQ_4_12(1.5); + } + return UQ_4_12(1.0); +} + +static inline uq4_12_t GetBurnOrFrostBiteModifier(u32 battlerAtk, u32 move, u32 abilityAtk) +{ + if (gBattleMons[battlerAtk].status1 & STATUS1_BURN + && IS_MOVE_PHYSICAL(move) + && !FACADE_PREVENTS_BURN_MALUS(move) && abilityAtk != ABILITY_GUTS) - dmg = ApplyModifier(UQ_4_12(0.5), dmg); - - // check frostbite - if (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE && IS_MOVE_SPECIAL(move) - #if B_BURN_FACADE_DMG >= GEN_6 - && gBattleMoves[move].effect != EFFECT_FACADE - #endif + return UQ_4_12(0.5); + if (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE + && IS_MOVE_SPECIAL(move) + && !FACADE_PREVENTS_BURN_MALUS(move) && abilityAtk != ABILITY_GUTS) - dmg = ApplyModifier(UQ_4_12(0.5), dmg); + return UQ_4_12(0.5); + return UQ_4_12(1.0); +} - // check weather - dmg = ApplyWeatherDamageMultiplier(battlerAtk, move, moveType, dmg, holdEffectAtk, holdEffectDef); +static inline uq4_12_t GetCriticalModifier(bool32 isCrit) +{ + return isCrit ? V_CRIT_MULTIPLIER : UQ_4_12(1.0); +} - // check stab - if (IS_BATTLER_OF_TYPE(battlerAtk, moveType) && move != MOVE_STRUGGLE && move != MOVE_NONE) - { - if (abilityAtk == ABILITY_ADAPTABILITY) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(2.0)); - else - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(1.5)); - } - - // Collision Course, Electro Drift - if (gBattleMoves[move].effect == EFFECT_COLLISION_COURSE && typeEffectivenessModifier >= UQ_4_12(2.0)) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(1.3333)); - - // reflect, light screen, aurora veil - if (((gSideStatuses[defSide] & SIDE_STATUS_REFLECT && IS_MOVE_PHYSICAL(move)) - || (gSideStatuses[defSide] & SIDE_STATUS_LIGHTSCREEN && IS_MOVE_SPECIAL(move)) - || (gSideStatuses[defSide] & SIDE_STATUS_AURORA_VEIL)) - && abilityAtk != ABILITY_INFILTRATOR - && !(isCrit) - && !gProtectStructs[battlerAtk].confusionSelfDmg) - { - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(0.66)); - else - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(0.5)); - } - - // Parental Bond Second Strike - if (gSpecialStatuses[battlerAtk].parentalBondState == PARENTAL_BOND_2ND_HIT) - { - if (B_PARENTAL_BOND_DMG < GEN_7) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(0.5)); - else - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(0.25)); - } - - // Z-Moves and Max Moves bypass Protect and do 25% of their original damage +static inline uq4_12_t GetZMoveAgainstProtectionModifier(u32 battlerDef) +{ if (gBattleStruct->zmove.active && IS_BATTLER_PROTECTED(battlerDef)) - { - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(0.25)); - } + return UQ_4_12(0.25); + return UQ_4_12(1.0); +} - // attacker's abilities +static inline uq4_12_t GetMinimizeModifier(u32 move, u32 battlerDef) +{ + if (gBattleMoves[move].minimizeDoubleDamage && gStatuses3[battlerDef] & STATUS3_MINIMIZED) + return UQ_4_12(2.0); + return UQ_4_12(1.0); +} + +static inline uq4_12_t GetUndergroundModifier(u32 move, u32 battlerDef) +{ + if (gBattleMoves[move].damagesUnderground && gStatuses3[battlerDef] & STATUS3_UNDERGROUND) + return UQ_4_12(2.0); + return UQ_4_12(1.0); +} + +static inline uq4_12_t GetDiveModifier(u32 move, u32 battlerDef) +{ + if (gBattleMoves[move].damagesUnderwater && gStatuses3[battlerDef] & STATUS3_UNDERWATER) + return UQ_4_12(2.0); + return UQ_4_12(1.0); +} + +static inline uq4_12_t GetAirborneModifier(u32 move, u32 battlerDef) +{ + if (gBattleMoves[move].damagesAirborneDoubleDamage && gStatuses3[battlerDef] & STATUS3_ON_AIR) + return UQ_4_12(2.0); + return UQ_4_12(1.0); +} + +static inline uq4_12_t GetScreensModifier(u32 move, u32 battlerAtk, u32 battlerDef, bool32 isCrit) +{ + u32 sideStatus = gSideStatuses[GET_BATTLER_SIDE(battlerDef)]; + bool32 lightScreen = (sideStatus & SIDE_STATUS_LIGHTSCREEN) && IS_MOVE_SPECIAL(move); + bool32 reflect = (sideStatus & SIDE_STATUS_REFLECT) && IS_MOVE_PHYSICAL(move); + bool32 auroraVeil = sideStatus & SIDE_STATUS_AURORA_VEIL; + u32 abilityAtk = GetBattlerAbility(battlerAtk); + + if (isCrit || abilityAtk == ABILITY_INFILTRATOR || gProtectStructs[battlerAtk].confusionSelfDmg) + return UQ_4_12(1.0); + if (reflect || lightScreen || auroraVeil) + return (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) ? UQ_4_12(0.667) : UQ_4_12(0.5); + return UQ_4_12(1.0); +} + +static inline uq4_12_t GetCollisionCourseElectroDriftModifier(u32 move, uq4_12_t typeEffectivenessModifier) +{ + if (gBattleMoves[move].effect == EFFECT_COLLISION_COURSE && typeEffectivenessModifier >= UQ_4_12(2.0)) + return UQ_4_12(1.3333); + return UQ_4_12(1.0); +} + +static inline uq4_12_t GetAttackerAbilitiesModifier(u32 battlerAtk, uq4_12_t typeEffectivenessModifier, bool32 isCrit) +{ + u32 abilityAtk = GetBattlerAbility(battlerAtk); switch (abilityAtk) { - case ABILITY_TINTED_LENS: - if (typeEffectivenessModifier <= UQ_4_12(0.5)) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(2.0)); + case ABILITY_NEUROFORCE: + if (typeEffectivenessModifier >= UQ_4_12(2.0)) + return UQ_4_12(1.25); break; case ABILITY_SNIPER: if (isCrit) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(1.5)); + return UQ_4_12(1.5); break; - case ABILITY_NEUROFORCE: - if (typeEffectivenessModifier >= UQ_4_12(2.0)) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(1.25)); + case ABILITY_TINTED_LENS: + if (typeEffectivenessModifier <= UQ_4_12(0.5)) + return UQ_4_12(2.0); break; } + return UQ_4_12(1.0); +} - // target's abilities +static inline uq4_12_t GetDefenderAbilitiesModifier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, uq4_12_t typeEffectivenessModifier) +{ + u32 abilityDef = GetBattlerAbility(battlerDef); switch (abilityDef) { case ABILITY_MULTISCALE: case ABILITY_SHADOW_SHIELD: if (BATTLER_MAX_HP(battlerDef)) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(0.5)); + return UQ_4_12(0.5); break; case ABILITY_FILTER: case ABILITY_SOLID_ROCK: case ABILITY_PRISM_ARMOR: if (typeEffectivenessModifier >= UQ_4_12(2.0)) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(0.75)); + return UQ_4_12(0.75); + break; + case ABILITY_FLUFFY: + if (!IsMoveMakingContact(move, battlerAtk) && moveType == TYPE_FIRE) + return UQ_4_12(2.0); + if (IsMoveMakingContact(move, battlerAtk) && moveType != TYPE_FIRE) + return UQ_4_12(0.5); + break; + case ABILITY_PUNK_ROCK: + if (gBattleMoves[move].soundMove) + return UQ_4_12(0.5); + break; + case ABILITY_ICE_SCALES: + if (IS_MOVE_SPECIAL(move)) + return UQ_4_12(0.5); break; } + return UQ_4_12(1.0); +} - // target's ally's abilities - if (IsBattlerAlive(BATTLE_PARTNER(battlerDef))) +static inline uq4_12_t GetDefenderPartnerAbilitiesModifier(u32 battlerPartnerDef) +{ + if (!IsBattlerAlive(battlerPartnerDef)) + return UQ_4_12(1.0); + + switch (GetBattlerAbility(battlerPartnerDef)) { - switch (GetBattlerAbility(BATTLE_PARTNER(battlerDef))) - { - case ABILITY_FRIEND_GUARD: - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(0.75)); - break; - } + case ABILITY_FRIEND_GUARD: + return UQ_4_12(0.75); + break; } + return UQ_4_12(1.0); +} - // attacker's hold effect +static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEffectivenessModifier) +{ + u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); + u32 percentBoost; switch (holdEffectAtk) { case HOLD_EFFECT_METRONOME: percentBoost = min((gBattleStruct->sameMoveTurns[battlerAtk] * GetBattlerHoldEffectParam(battlerAtk)), 100); - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(1.0) + sPercentToModifier[percentBoost]); + return sPercentToModifier[percentBoost]; break; case HOLD_EFFECT_EXPERT_BELT: if (typeEffectivenessModifier >= UQ_4_12(2.0)) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(1.2)); + return UQ_4_12(1.2); break; case HOLD_EFFECT_LIFE_ORB: - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(1.3)); + return UQ_4_12(1.3); break; } + return UQ_4_12(1.0); +} + +static inline uq4_12_t GetDefenderItemsModifier(u32 moveType, u32 battlerDef, uq4_12_t typeEffectivenessModifier, bool32 updateFlags) +{ + u32 holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE); + u32 holdEffectDefParam = GetBattlerHoldEffectParam(battlerDef); + u32 itemDef = gBattleMons[battlerDef].item; + u32 abilityDef = GetBattlerAbility(battlerDef); - // target's hold effect switch (holdEffectDef) { - // berries reducing dmg case HOLD_EFFECT_RESIST_BERRY: - if (moveType == GetBattlerHoldEffectParam(battlerDef) - && (moveType == TYPE_NORMAL || typeEffectivenessModifier >= UQ_4_12(2.0)) - && !UnnerveOn(battlerDef, itemDef)) + if (UnnerveOn(battlerDef, itemDef)) + return UQ_4_12(1.0); + if (moveType == holdEffectDefParam && (moveType == TYPE_NORMAL || typeEffectivenessModifier >= UQ_4_12(2.0))) { - if (abilityDef == ABILITY_RIPEN) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(0.25)); - else - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(0.5)); if (updateFlags) gSpecialStatuses[battlerDef].berryReduced = TRUE; + return (abilityDef == ABILITY_RIPEN) ? UQ_4_12(0.25) : UQ_4_12(0.5); } break; } - - if (gBattleMoves[move].minimizeDoubleDamage && gStatuses3[battlerDef] & STATUS3_MINIMIZED) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(2.0)); - if (gBattleMoves[move].damagesUnderground && gStatuses3[battlerDef] & STATUS3_UNDERGROUND) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(2.0)); - if (gBattleMoves[move].damagesUnderwater && gStatuses3[battlerDef] & STATUS3_UNDERWATER) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(2.0)); - if (gBattleMoves[move].damagesAirborneDoubleDamage && gStatuses3[battlerDef] & STATUS3_ON_AIR) - finalModifier = uq4_12_multiply(finalModifier, UQ_4_12(2.0)); - - dmg = ApplyModifier(finalModifier, dmg); - if (dmg == 0) - dmg = 1; - - return dmg; + return UQ_4_12(1.0); } -static s32 DoMoveDamageCalc(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, +#define DAMAGE_MULTIPLY_MODIFIER(modifier) do { \ + finalModifier = uq4_12_multiply_half_down(modifier, finalModifier); \ +} while (0) + +// Calculates the "other" modifier which accounts for held items, abilities, +// or very specific interactions of moves that are not handled in the basic +// damage calculation. It is implemented as described by bulbapedia: +// https://bulbapedia.bulbagarden.net/wiki/Damage#Generation_V_onward +// Please Note: Fixed Point Multiplication is not associative. +// The order of operations is relevant. +static uq4_12_t GetOtherModifiers(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, bool32 isCrit, uq4_12_t typeEffectivenessModifier, bool32 updateFlags) +{ + u32 abilityAtk = GetBattlerAbility(battlerAtk); + uq4_12_t finalModifier = UQ_4_12(1.0); + u32 battlerDefPartner = BATTLE_PARTNER(battlerDef); + u32 unmodifiedAttackerSpeed = gBattleMons[battlerAtk].speed; + u32 unmodifiedDefenderSpeed = gBattleMons[battlerDef].speed; + //TODO: Behemoth Blade, Behemoth Bash, Dynamax Cannon (Dynamax) + DAMAGE_MULTIPLY_MODIFIER(GetMinimizeModifier(move, battlerDef)); + DAMAGE_MULTIPLY_MODIFIER(GetUndergroundModifier(move, battlerDef)); + DAMAGE_MULTIPLY_MODIFIER(GetDiveModifier(move, battlerDef)); + DAMAGE_MULTIPLY_MODIFIER(GetAirborneModifier(move, battlerDef)); + DAMAGE_MULTIPLY_MODIFIER(GetScreensModifier(move, battlerAtk, battlerDef, isCrit)); + DAMAGE_MULTIPLY_MODIFIER(GetCollisionCourseElectroDriftModifier(move, typeEffectivenessModifier)); + + if (unmodifiedAttackerSpeed >= unmodifiedDefenderSpeed) + { + DAMAGE_MULTIPLY_MODIFIER(GetAttackerAbilitiesModifier(battlerAtk, typeEffectivenessModifier, isCrit)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderAbilitiesModifier(move, moveType, battlerAtk, battlerDef, typeEffectivenessModifier)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderPartnerAbilitiesModifier(battlerDefPartner)); + DAMAGE_MULTIPLY_MODIFIER(GetAttackerItemsModifier(battlerAtk, typeEffectivenessModifier)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(moveType, battlerDef, typeEffectivenessModifier, updateFlags)); + } + else + { + DAMAGE_MULTIPLY_MODIFIER(GetDefenderAbilitiesModifier(move, moveType, battlerAtk, battlerDef, typeEffectivenessModifier)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderPartnerAbilitiesModifier(battlerDefPartner)); + DAMAGE_MULTIPLY_MODIFIER(GetAttackerAbilitiesModifier(battlerAtk, typeEffectivenessModifier, isCrit)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(moveType, battlerDef, typeEffectivenessModifier, updateFlags)); + DAMAGE_MULTIPLY_MODIFIER(GetAttackerItemsModifier(battlerAtk, typeEffectivenessModifier)); + } + return finalModifier; +} + +#undef DAMAGE_ACCUMULATE_MULTIPLIER + +#define DAMAGE_APPLY_MODIFIER(modifier) do { \ + dmg = uq4_12_multiply_by_int_half_down(modifier, dmg); \ +} while (0) + +static s32 DoMoveDamageCalc(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags, uq4_12_t typeEffectivenessModifier) { s32 dmg; + u32 userFinalAttack; + u32 targetFinalDefense; + u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); + u32 holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE); + u32 abilityAtk = GetBattlerAbility(battlerAtk); - // Don't calculate damage if the move has no effect on target. - if (typeEffectivenessModifier == UQ_4_12(0)) + if (typeEffectivenessModifier == UQ_4_12(0.0)) return 0; if (fixedBasePower) @@ -9636,29 +9752,34 @@ static s32 DoMoveDamageCalc(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, else gBattleMovePower = CalcMoveBasePowerAfterModifiers(move, battlerAtk, battlerDef, moveType, updateFlags); - // long dmg basic formula - dmg = ((gBattleMons[battlerAtk].level * 2) / 5) + 2; - dmg *= gBattleMovePower; - dmg *= CalcAttackStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags); - dmg /= CalcDefenseStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags); - dmg = (dmg / 50) + 2; + userFinalAttack = CalcAttackStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags); + targetFinalDefense = CalcDefenseStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags); - // Calculate final modifiers. - dmg = CalcFinalDmg(dmg, move, battlerAtk, battlerDef, moveType, typeEffectivenessModifier, isCrit, updateFlags); - - // Add a random factor. + dmg = CalculateBaseDamage(gBattleMovePower, userFinalAttack, gBattleMons[battlerAtk].level, targetFinalDefense); + DAMAGE_APPLY_MODIFIER(GetTargetDamageModifier(move, battlerAtk, battlerDef)); + DAMAGE_APPLY_MODIFIER(GetParentalBondModifier(battlerAtk)); + DAMAGE_APPLY_MODIFIER(GetWeatherDamageModifier(battlerAtk, move, moveType, holdEffectAtk, holdEffectDef)); + DAMAGE_APPLY_MODIFIER(GetCriticalModifier(isCrit)); + // TODO: Glaive Rush (Gen IX effect) if (randomFactor) { dmg *= 100 - RandomUniform(RNG_DAMAGE_MODIFIER, 0, 15); dmg /= 100; } + DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(battlerAtk, moveType, move, abilityAtk)); + DAMAGE_APPLY_MODIFIER(typeEffectivenessModifier); + DAMAGE_APPLY_MODIFIER(GetBurnOrFrostBiteModifier(battlerAtk, move, abilityAtk)); + DAMAGE_APPLY_MODIFIER(GetZMoveAgainstProtectionModifier(battlerDef)); + DAMAGE_APPLY_MODIFIER(GetOtherModifiers(move, moveType, battlerAtk, battlerDef, isCrit, typeEffectivenessModifier, updateFlags)); + if (dmg == 0) dmg = 1; - return dmg; } +#undef DAMAGE_APPLY_MODIFIER + s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags) { return DoMoveDamageCalc(move, battlerAtk, battlerDef, moveType, fixedBasePower, isCrit, randomFactor, @@ -10775,34 +10896,6 @@ bool32 IsBattlerWeatherAffected(u8 battlerId, u32 weatherFlags) return FALSE; } -// Utility Umbrella holders take normal damage from what would be rain- and sun-weakened attacks. -u32 ApplyWeatherDamageMultiplier(u8 battlerAtk, u16 move, u8 moveType, u32 dmg, u16 holdEffectAtk, u16 holdEffectDef) -{ - if (WEATHER_HAS_EFFECT) - { - if (gBattleMoves[move].effect == EFFECT_HYDRO_STEAM && (gBattleWeather & B_WEATHER_SUN) && holdEffectAtk != HOLD_EFFECT_UTILITY_UMBRELLA) - dmg = ApplyModifier(UQ_4_12(1.5), dmg); - else if (holdEffectDef != HOLD_EFFECT_UTILITY_UMBRELLA) - { - if (gBattleWeather & B_WEATHER_RAIN) - { - if (moveType == TYPE_FIRE) - dmg = ApplyModifier(UQ_4_12(0.5), dmg); - else if (moveType == TYPE_WATER) - dmg = ApplyModifier(UQ_4_12(1.5), dmg); - } - else if (gBattleWeather & B_WEATHER_SUN) - { - if (moveType == TYPE_FIRE) - dmg = ApplyModifier(UQ_4_12(1.5), dmg); - else if (moveType == TYPE_WATER) - dmg = ApplyModifier(UQ_4_12(0.5), dmg); - } - } - } - return dmg; -} - // Gets move target before redirection effects etc. are applied // Possible return values are defined in battle.h following MOVE_TARGET_SELECTED u32 GetBattlerMoveTargetType(u8 battlerId, u16 move) diff --git a/test/ability_contrary.c b/test/ability_contrary.c index 52347f379..799cb1116 100644 --- a/test/ability_contrary.c +++ b/test/ability_contrary.c @@ -26,7 +26,7 @@ SINGLE_BATTLE_TEST("Contrary raises Attack when Intimidated", s16 damage) HP_BAR(player, captureDamage: &results[i].damage); } FINALLY { - EXPECT_MUL_EQ(results[1].damage, Q_4_12(2.125), results[0].damage); + EXPECT_MUL_EQ(results[1].damage, Q_4_12(2.25), results[0].damage); } } diff --git a/test/ability_dry_skin.c b/test/ability_dry_skin.c index 028076d5a..59f99760c 100644 --- a/test/ability_dry_skin.c +++ b/test/ability_dry_skin.c @@ -36,15 +36,24 @@ SINGLE_BATTLE_TEST("Dry Skin increases damage taken from Fire-type moves by 25%" PARAMETRIZE { ability = ABILITY_DRY_SKIN; } GIVEN { ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_PARASECT) { Ability(ability); } + ASSUME(gBattleMoves[MOVE_EMBER].power == 40); + ASSUME(gSpeciesInfo[SPECIES_PARASECT].types[0] == TYPE_BUG); + ASSUME(gSpeciesInfo[SPECIES_PARASECT].types[1] == TYPE_GRASS); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC); + PLAYER(SPECIES_WOBBUFFET) { SpAttack(71); } + OPPONENT(SPECIES_PARASECT) { Ability(ability); SpDefense(165); } } WHEN { TURN { MOVE(player, MOVE_EMBER); } } SCENE { MESSAGE("Wobbuffet used Ember!"); HP_BAR(opponent, captureDamage: &results[i].damage); } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.25), results[1].damage); + // Due to numerics related to rounding on each applied multiplier, + // the ability effect doesn't manifest as a 25% damage increase, but as a ~31% damage increase in this case. + // Values obtained from https://calc.pokemonshowdown.com (Neutral nature and 0 IVs on both sides) + EXPECT_EQ(results[0].damage, 52); + EXPECT_EQ(results[1].damage, 68); } } diff --git a/test/ability_fluffy.c b/test/ability_fluffy.c new file mode 100644 index 000000000..238045e25 --- /dev/null +++ b/test/ability_fluffy.c @@ -0,0 +1,66 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_TACKLE].makesContact); + ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); + ASSUME(gBattleMoves[MOVE_TACKLE].makesContact); + ASSUME(gBattleMoves[MOVE_FIRE_PUNCH].makesContact); + ASSUME(gBattleMoves[MOVE_FIRE_PUNCH].type == TYPE_FIRE); + ASSUME(P_GEN_7_POKEMON == TRUE); +} + +SINGLE_BATTLE_TEST("Fluffy halves damage taken from moves that make direct contact", s16 damage) +{ + u32 ability; + PARAMETRIZE { ability = ABILITY_KLUTZ; } + PARAMETRIZE { ability = ABILITY_FLUFFY; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_STUFFUL) { Ability(ability); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + MESSAGE("Wobbuffet used Tackle!"); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Fluffy doubles damage taken from fire type moves", s16 damage) +{ + u32 ability; + PARAMETRIZE { ability = ABILITY_KLUTZ; } + PARAMETRIZE { ability = ABILITY_FLUFFY; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_STUFFUL) { Ability(ability); } + } WHEN { + TURN { MOVE(player, MOVE_EMBER); } + } SCENE { + MESSAGE("Wobbuffet used Ember!"); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Fluffy does not alter damage of fire-type moves that make direct contact", s16 damage) +{ + u32 ability; + PARAMETRIZE { ability = ABILITY_KLUTZ; } + PARAMETRIZE { ability = ABILITY_FLUFFY; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_STUFFUL) { Ability(ability); } + } WHEN { + TURN { MOVE(player, MOVE_FIRE_PUNCH); } + } SCENE { + MESSAGE("Wobbuffet used Fire Punch!"); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} diff --git a/test/ability_swarm.c b/test/ability_swarm.c index 7709e976e..7e4211119 100644 --- a/test/ability_swarm.c +++ b/test/ability_swarm.c @@ -8,13 +8,21 @@ SINGLE_BATTLE_TEST("Swarm boosts Bug-type moves in a pinch", s16 damage) PARAMETRIZE { hp = 33; } GIVEN { ASSUME(gBattleMoves[MOVE_BUG_BITE].type == TYPE_BUG); - PLAYER(SPECIES_LEDYBA) { Ability(ABILITY_SWARM); MaxHP(99); HP(hp); } - OPPONENT(SPECIES_WOBBUFFET); + ASSUME(gBattleMoves[MOVE_BUG_BITE].power == 60); + ASSUME(gSpeciesInfo[SPECIES_LEDYBA].types[0] == TYPE_BUG); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC); + PLAYER(SPECIES_LEDYBA) { Ability(ABILITY_SWARM); MaxHP(99); HP(hp); Attack(45); } + OPPONENT(SPECIES_WOBBUFFET) { Defense(121); } } WHEN { TURN { MOVE(player, MOVE_BUG_BITE); } } SCENE { HP_BAR(opponent, captureDamage: &results[i].damage); } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + // Due to numerics related to rounding on each applied multiplier, + // the 50% move power increase doesn't manifest as a 50% damage increase, but as a 44% damage increase in this case. + // Values obtained from https://calc.pokemonshowdown.com (Neutral nature and 0 IVs on both sides) + EXPECT_EQ(results[0].damage, 50); + EXPECT_EQ(results[1].damage, 72); } } diff --git a/test/damage_formula.c b/test/damage_formula.c new file mode 100644 index 000000000..73d919330 --- /dev/null +++ b/test/damage_formula.c @@ -0,0 +1,78 @@ +#include "global.h" +#include "test_battle.h" + +// From https://bulbapedia.bulbagarden.net/wiki/Damage#Example + +SINGLE_BATTLE_TEST("Damage calculation matches Gen5+") +{ + s16 dmg; + s16 expectedDamage; + PARAMETRIZE { expectedDamage = 196; } + PARAMETRIZE { expectedDamage = 192; } + PARAMETRIZE { expectedDamage = 192; } + PARAMETRIZE { expectedDamage = 192; } + PARAMETRIZE { expectedDamage = 184; } + PARAMETRIZE { expectedDamage = 184; } + PARAMETRIZE { expectedDamage = 184; } + PARAMETRIZE { expectedDamage = 180; } + PARAMETRIZE { expectedDamage = 180; } + PARAMETRIZE { expectedDamage = 180; } + PARAMETRIZE { expectedDamage = 172; } + PARAMETRIZE { expectedDamage = 172; } + PARAMETRIZE { expectedDamage = 172; } + PARAMETRIZE { expectedDamage = 168; } + PARAMETRIZE { expectedDamage = 168; } + PARAMETRIZE { expectedDamage = 168; } + GIVEN { + PLAYER(SPECIES_GLACEON) { Level(75); Attack(123); } + OPPONENT(SPECIES_GARCHOMP) { Defense(163); } + } WHEN { + TURN { + MOVE(player, MOVE_ICE_FANG, WITH_RNG(RNG_DAMAGE_MODIFIER, i)); + } + } + SCENE{ + MESSAGE("Glaceon used Ice Fang!"); + HP_BAR(opponent, captureDamage: &dmg); + } + THEN{ + EXPECT_EQ(expectedDamage, dmg); + } +} + +SINGLE_BATTLE_TEST("Damage calculation matches Gen5+ (Muscle Band, crit)") +{ + s16 dmg; + s16 expectedDamage; + PARAMETRIZE { expectedDamage = 324; } + PARAMETRIZE { expectedDamage = 316; } + PARAMETRIZE { expectedDamage = 312; } + PARAMETRIZE { expectedDamage = 312; } + PARAMETRIZE { expectedDamage = 304; } + PARAMETRIZE { expectedDamage = 304; } + PARAMETRIZE { expectedDamage = 300; } + PARAMETRIZE { expectedDamage = 300; } + PARAMETRIZE { expectedDamage = 292; } + PARAMETRIZE { expectedDamage = 292; } + PARAMETRIZE { expectedDamage = 288; } + PARAMETRIZE { expectedDamage = 288; } + PARAMETRIZE { expectedDamage = 280; } + PARAMETRIZE { expectedDamage = 276; } + PARAMETRIZE { expectedDamage = 276; } + PARAMETRIZE { expectedDamage = 268; } + GIVEN { + PLAYER(SPECIES_GLACEON) { Level(75); Attack(123); Item(ITEM_MUSCLE_BAND); } + OPPONENT(SPECIES_GARCHOMP) { Defense(163); } + } WHEN { + TURN { + MOVE(player, MOVE_ICE_FANG, WITH_RNG(RNG_DAMAGE_MODIFIER, i), criticalHit: TRUE); + } + } + SCENE{ + MESSAGE("Glaceon used Ice Fang!"); + HP_BAR(opponent, captureDamage: &dmg); + } + THEN{ + EXPECT_EQ(expectedDamage, dmg); + } +} diff --git a/test/status3.c b/test/status3.c new file mode 100644 index 000000000..0331883bd --- /dev/null +++ b/test/status3.c @@ -0,0 +1,90 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS { + ASSUME(gBattleMoves[MOVE_MINIMIZE].effect == EFFECT_MINIMIZE); + ASSUME(gBattleMoves[MOVE_STEAMROLLER].minimizeDoubleDamage); + ASSUME(gBattleMoves[MOVE_EARTHQUAKE].damagesUnderground); + ASSUME(gBattleMoves[MOVE_SURF].damagesUnderwater); + ASSUME(gBattleMoves[MOVE_TWISTER].damagesAirborneDoubleDamage); +} + +SINGLE_BATTLE_TEST("Minimize causes the target to take double damage from certain moves", s16 damage) +{ + bool32 useMinimize; + PARAMETRIZE { useMinimize = FALSE; } + PARAMETRIZE { useMinimize = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } + } WHEN { + if (useMinimize) + TURN { MOVE(opponent, MOVE_MINIMIZE); MOVE(player, MOVE_STEAMROLLER); } + else + TURN { MOVE(player, MOVE_STEAMROLLER); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Being underground causes the target to take double damage from certain moves", s16 damage) +{ + bool32 useDig; + PARAMETRIZE { useDig = FALSE; } + PARAMETRIZE { useDig = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } + } WHEN { + if (useDig) + TURN { MOVE(opponent, MOVE_DIG); MOVE(player, MOVE_EARTHQUAKE); } + else + TURN { MOVE(player, MOVE_EARTHQUAKE); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Being underwater causes the target to take double damage from certain moves", s16 damage) +{ + bool32 useDive; + PARAMETRIZE { useDive = FALSE; } + PARAMETRIZE { useDive = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } + } WHEN { + if (useDive) + TURN { MOVE(opponent, MOVE_DIVE); MOVE(player, MOVE_SURF); } + else + TURN { MOVE(player, MOVE_SURF); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Being airborne causes the target to take double damage from certain moves", s16 damage) +{ + bool32 useDive; + PARAMETRIZE { useDive = FALSE; } + PARAMETRIZE { useDive = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } + } WHEN { + if (useDive) + TURN { MOVE(opponent, MOVE_FLY); MOVE(player, MOVE_TWISTER); } + else + TURN { MOVE(player, MOVE_TWISTER); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); + } +} diff --git a/test/weather_snow.c b/test/weather_snow.c index 93deb1432..bee4759eb 100644 --- a/test/weather_snow.c +++ b/test/weather_snow.c @@ -81,7 +81,6 @@ SINGLE_BATTLE_TEST("Snow halves the power of Solar Beam", s16 damage) SINGLE_BATTLE_TEST("Snow halves the power of Solar Blade", s16 damage) { u16 move; - KNOWN_FAILING; // fails bc the bp of solar blade gets rounded up which leads to slightly incorrect calcs down the line PARAMETRIZE{ move = MOVE_CELEBRATE; } PARAMETRIZE{ move = MOVE_SNOWSCAPE; } GIVEN { From 1ba70a46f250672b0bdfd577cafde9c2c61a81f6 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Fri, 11 Aug 2023 17:26:43 -0400 Subject: [PATCH 07/16] Fixed Jump Kick's recoil not happening after Spiky Shield's damage (#3208) Co-authored-by: DizzyEggg --- data/battle_scripts_1.s | 2 ++ test/move_effect_recoil_if_miss.c | 47 +++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 0375c1325..5f38029a5 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -4101,6 +4101,8 @@ BattleScript_MoveMissedDoDamage:: .if B_CRASH_IF_TARGET_IMMUNE < GEN_4 jumpifhalfword CMP_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE, BattleScript_MoveEnd .endif + moveendcase MOVEEND_PROTECT_LIKE_EFFECT @ Spiky Shield's damage happens before recoil. + jumpifhasnohp BS_ATTACKER, BattleScript_MoveEnd printstring STRINGID_PKMNCRASHED waitmessage B_WAIT_TIME_LONG damagecalc diff --git a/test/move_effect_recoil_if_miss.c b/test/move_effect_recoil_if_miss.c index 8a80309d7..51b5fe5e9 100644 --- a/test/move_effect_recoil_if_miss.c +++ b/test/move_effect_recoil_if_miss.c @@ -8,7 +8,6 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on miss") { - s16 recoil; GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -25,7 +24,6 @@ SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on miss") SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on protect") { - s16 recoil; GIVEN { ASSUME(gBattleMoves[MOVE_JUMP_KICK].flags & FLAG_PROTECT_AFFECTED); PLAYER(SPECIES_WOBBUFFET); @@ -55,3 +53,48 @@ SINGLE_BATTLE_TEST("Jump Kick has no recoil if no target") NOT HP_BAR(player, damage: maxHP / 2); } } + +SINGLE_BATTLE_TEST("Jump Kick's recoil happens after Spiky Shield damage and Pokemon can faint from either of these") +{ + s16 hp, maxHp = 256; + bool32 faintOnSpiky = FALSE, faintOnJumpKick = FALSE; + + PARAMETRIZE { hp = maxHp; } + PARAMETRIZE { hp = maxHp / 2; faintOnJumpKick = TRUE; } // Faints after Jump Kick's recoil + PARAMETRIZE { hp = maxHp / 8; faintOnSpiky = TRUE; } // Faints after Spiky Shield's recoil + + GIVEN { + ASSUME(gBattleMoves[MOVE_SPIKY_SHIELD].effect == EFFECT_PROTECT); + PLAYER(SPECIES_WOBBUFFET) { HP(hp); MaxHP(maxHp); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (!faintOnJumpKick && !faintOnSpiky) { + TURN { MOVE(opponent, MOVE_SPIKY_SHIELD); MOVE(player, MOVE_JUMP_KICK, hit: FALSE); } + } else { + TURN { MOVE(opponent, MOVE_SPIKY_SHIELD); MOVE(player, MOVE_JUMP_KICK, hit: FALSE); SEND_OUT(player, 1); } + } + TURN { ; } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKY_SHIELD, opponent); + MESSAGE("Wobbuffet used Jump Kick!"); + MESSAGE("Foe Wobbuffet protected itself!"); + HP_BAR(player, damage: maxHp / 8); + MESSAGE("Wobbuffet was hurt by Foe Wobbuffet's Spiky Shield!"); + if (faintOnSpiky){ + MESSAGE("Wobbuffet fainted!"); + MESSAGE("Go! Wynaut!"); + NONE_OF { + MESSAGE("Wobbuffet kept going and crashed!"); + HP_BAR(player); + } + } else { + MESSAGE("Wobbuffet kept going and crashed!"); + HP_BAR(player); + if (faintOnJumpKick) { + MESSAGE("Wobbuffet fainted!"); + MESSAGE("Go! Wynaut!"); + } + } + } +} From ada29313de9959334a5aea75292538c769568890 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Fri, 11 Aug 2023 17:29:10 -0400 Subject: [PATCH 08/16] Fix for wild double battles with an in-game partner (#3210) Co-authored-by: DizzyEggg --- src/battle_controllers.c | 2 +- src/battle_main.c | 6 +++- src/battle_message.c | 2 +- src/battle_util.c | 66 +++++++++++++++++++++++---------------- src/script_pokemon_util.c | 0 5 files changed, 46 insertions(+), 30 deletions(-) mode change 100755 => 100644 src/script_pokemon_util.c diff --git a/src/battle_controllers.c b/src/battle_controllers.c index d5d3e479e..6424bee41 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -152,7 +152,7 @@ static void InitSinglePlayerBtlControllers(void) gBattlerPartyIndexes[0] = 0; gBattlerPartyIndexes[1] = 0; - if (BATTLE_TWO_VS_ONE_OPPONENT) + if (BATTLE_TWO_VS_ONE_OPPONENT || WILD_DOUBLE_BATTLE) { gBattlerPartyIndexes[2] = 3; gBattlerPartyIndexes[3] = 1; diff --git a/src/battle_main.c b/src/battle_main.c index 855e77017..25736731c 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4595,7 +4595,11 @@ static void HandleTurnActionSelectionState(void) { // if we choose to throw a ball with our second mon, skip the action of the first // (if we have chosen throw ball with first, second's is already skipped) - gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)] = B_ACTION_NOTHING_FAINTED; + // if throwing a ball in a wild battle with an in-game partner, skip partner's turn when throwing a ball + if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) + gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)] = B_ACTION_NOTHING_FAINTED; + else + gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)] = B_ACTION_NOTHING_FAINTED; } gBattleMainFunc = SetActionsAndBattlersTurnOrder; diff --git a/src/battle_message.c b/src/battle_message.c index 307b539a5..e0ebf9f1f 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -2698,7 +2698,7 @@ void BufferStringBattle(u16 stringID) { if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY) stringPtr = sText_LegendaryPkmnAppeared; - else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(gActiveBattler)]])) // interesting, looks like they had something planned for wild double battles + else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT)]])) stringPtr = sText_TwoWildPkmnAppeared; else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL) stringPtr = sText_WildPkmnAppearedPause; diff --git a/src/battle_util.c b/src/battle_util.c index 8f981fe3f..b5fb8732d 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3877,14 +3877,15 @@ u8 AtkCanceller_UnableToUseMove2(void) bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) { - u8 playerId, flankId; + u32 i, side, playerId, flankId; struct Pokemon *party; - s32 i; if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) return FALSE; - if (BATTLE_TWO_VS_ONE_OPPONENT && GetBattlerSide(battler) == B_SIDE_OPPONENT) + side = GetBattlerSide(battler); + + if (BATTLE_TWO_VS_ONE_OPPONENT && side == B_SIDE_OPPONENT) { flankId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); playerId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); @@ -3897,9 +3898,7 @@ bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) for (i = 0; i < PARTY_SIZE; i++) { - if (GetMonData(&party[i], MON_DATA_HP) != 0 - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG + if (IsValidForBattle(&party[i]) && i != partyIdBattlerOn1 && i != partyIdBattlerOn2 && i != *(gBattleStruct->monToSwitchIntoId + flankId) && i != playerId[gBattleStruct->monToSwitchIntoId]) break; @@ -3909,22 +3908,41 @@ bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) else if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { party = GetBattlerParty(battler); - - playerId = ((battler & BIT_FLANK) / 2); - for (i = playerId * MULTI_PARTY_SIZE; i < playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE; i++) + if (side == B_SIDE_OPPONENT && WILD_DOUBLE_BATTLE) { - if (GetMonData(&party[i], MON_DATA_HP) != 0 - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG) - break; + flankId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + playerId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + + if (partyIdBattlerOn1 == PARTY_SIZE) + partyIdBattlerOn1 = gBattlerPartyIndexes[flankId]; + if (partyIdBattlerOn2 == PARTY_SIZE) + partyIdBattlerOn2 = gBattlerPartyIndexes[playerId]; + + for (i = 0; i < PARTY_SIZE; i++) + { + if (IsValidForBattle(&party[i]) + && i != partyIdBattlerOn1 && i != partyIdBattlerOn2 + && i != *(gBattleStruct->monToSwitchIntoId + flankId) && i != playerId[gBattleStruct->monToSwitchIntoId]) + break; + } + return (i == PARTY_SIZE); + } + else + { + playerId = ((battler & BIT_FLANK) / 2); + for (i = playerId * MULTI_PARTY_SIZE; i < playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE; i++) + { + if (IsValidForBattle(&party[i])) + break; + } + return (i == playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE); } - return (i == playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE); } else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { if (gBattleTypeFlags & BATTLE_TYPE_TOWER_LINK_MULTI) { - if (GetBattlerSide(battler) == B_SIDE_PLAYER) + if (side == B_SIDE_PLAYER) { party = gPlayerParty; flankId = GetBattlerMultiplayerId(battler); @@ -3948,14 +3966,12 @@ bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) for (i = playerId * MULTI_PARTY_SIZE; i < playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE; i++) { - if (GetMonData(&party[i], MON_DATA_HP) != 0 - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG) + if (IsValidForBattle(&party[i])) break; } return (i == playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE); } - else if ((gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && GetBattlerSide(battler) == B_SIDE_OPPONENT) + else if ((gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && side == B_SIDE_OPPONENT) { party = gEnemyParty; @@ -3966,16 +3982,14 @@ bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) for (i = playerId; i < playerId + MULTI_PARTY_SIZE; i++) { - if (GetMonData(&party[i], MON_DATA_HP) != 0 - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG) + if (IsValidForBattle(&party[i])) break; } return (i == playerId + 3); } else { - if (GetBattlerSide(battler) == B_SIDE_OPPONENT) + if (side == B_SIDE_OPPONENT) { flankId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); playerId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); @@ -3995,9 +4009,7 @@ bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) for (i = 0; i < PARTY_SIZE; i++) { - if (GetMonData(&party[i], MON_DATA_HP) != 0 - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG + if (IsValidForBattle(&party[i]) && i != partyIdBattlerOn1 && i != partyIdBattlerOn2 && i != *(gBattleStruct->monToSwitchIntoId + flankId) && i != playerId[gBattleStruct->monToSwitchIntoId]) break; @@ -8000,7 +8012,7 @@ u8 IsMonDisobedient(void) if (IsBattlerModernFatefulEncounter(gBattlerAttacker)) // only false if illegal Mew or Deoxys { - if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gBattlerAttacker) == 2) + if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gBattlerAttacker) == B_POSITION_PLAYER_RIGHT) return 0; if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) return 0; diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c old mode 100755 new mode 100644 From d62c0d6d51942afac048532f92f57d5e0746a72a Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Sat, 12 Aug 2023 15:00:15 -0400 Subject: [PATCH 09/16] Organized tests into subfolders (#2822) --- Makefile | 2 +- test/test_battle.h => include/test/battle.h | 3 +- {test => include/test}/test.h | 0 .../ability/bad_dreams.c} | 2 +- .../ability/beads_of_ruin.c} | 2 +- .../ability/blaze.c} | 2 +- .../ability/clear_body.c} | 2 +- .../ability/cloud_nine.c} | 2 +- .../ability/compound_eyes.c} | 2 +- .../ability/contrary.c} | 2 +- .../ability/cute_charm.c} | 2 +- .../{ability_damp.c => battle/ability/damp.c} | 2 +- .../ability/desolate_land.c} | 62 +----- .../ability/download.c} | 2 +- .../ability/drizzle.c} | 2 +- .../ability/dry_skin.c} | 2 +- .../ability/electromorphosis.c} | 2 +- .../ability/flame_body.c} | 2 +- .../ability/flower_gift.c} | 2 +- .../ability/forecast.c} | 2 +- .../ability/full_metal_body.c} | 2 +- .../ability/hunger_switch.c} | 2 +- .../ability/hydration.c} | 2 +- .../ability/hyper_cutter.c} | 2 +- .../ability/ice_body.c} | 2 +- .../ability/immunity.c} | 2 +- .../ability/inner_focus.c} | 2 +- .../ability/insomnia.c} | 2 +- .../ability/intimidate.c} | 2 +- .../ability/leaf_guard.c} | 2 +- .../ability/limber.c} | 2 +- .../ability/magic_bounce.c} | 2 +- .../ability/mirror_armor.c} | 2 +- .../ability/oblivious.c} | 2 +- test/battle/ability/overcoat.c | 21 ++ .../ability/overgrow.c} | 2 +- .../ability/own_tempo.c} | 2 +- .../ability/pastel_veil.c} | 2 +- .../ability/poison_point.c} | 2 +- test/battle/ability/primordial_sea.c | 66 ++++++ .../ability/protosynthesis.c} | 2 +- .../ability/quark_drive.c} | 2 +- .../ability/rain_dish.c} | 2 +- .../ability/rattled.c} | 2 +- .../ability/sand_veil.c} | 2 +- .../ability/schooling.c} | 2 +- .../ability/scrappy.c} | 2 +- .../ability/snow_cloak.c} | 2 +- .../ability/snow_warning.c} | 2 +- .../ability/speed_boost.c} | 2 +- .../ability/stamina.c} | 2 +- .../ability/static.c} | 2 +- .../ability/stench.c} | 2 +- .../ability/sturdy.c} | 2 +- .../ability/swarm.c} | 2 +- .../ability/sword_of_ruin.c} | 2 +- .../ability/tablets_of_ruin.c} | 2 +- .../ability/torrent.c} | 2 +- .../ability/vessel_of_ruin.c} | 2 +- .../ability/volt_absorb.c} | 2 +- .../ability/water_absorb.c} | 2 +- .../ability/white_smoke.c} | 2 +- .../ability/wind_power.c} | 2 +- .../ability/zen_mode.c} | 2 +- test/battle/form_change/battle_switch.c | 23 +++ .../form_change/begin_battle.c} | 40 +--- test/battle/form_change/faint.c | 19 ++ .../{ => battle/form_change}/mega_evolution.c | 2 +- .../form_change}/primal_reversion.c | 2 +- .../hold_effect/air_balloon.c} | 2 +- .../hold_effect/berserk_gene.c} | 2 +- .../hold_effect/clear_amulet.c} | 2 +- .../hold_effect/eject_button.c} | 2 +- .../hold_effect/gems.c} | 2 +- .../hold_effect/leftovers.c} | 2 +- .../hold_effect/mirror_herb.c} | 2 +- .../hold_effect/red_card.c} | 2 +- test/battle/hold_effect/safety_goggles.c | 26 +++ .../hold_effect/utility_umbrella.c} | 2 +- .../item_effect/cure_status.c} | 2 +- .../item_effect/heal_and_cure_status.c} | 2 +- .../item_effect/increase_stat.c} | 2 +- .../item_effect/restore_hp.c} | 2 +- .../item_effect/restore_pp.c} | 2 +- .../item_effect/revive.c} | 2 +- .../item_effect/set_focus_energy.c} | 2 +- .../item_effect/set_mist.c} | 2 +- test/{ => battle}/move.c | 2 +- .../move_effect/absorb.c} | 2 +- .../move_effect/accuracy_down.c} | 2 +- .../move_effect/after_you.c} | 2 +- .../move_effect/assist.c} | 2 +- .../move_effect/attack_down.c} | 2 +- .../move_effect/attack_up.c} | 2 +- .../move_effect/attack_up_user_ally.c} | 2 +- .../move_effect/barb_barrage.c} | 2 +- .../move_effect/beak_blast.c} | 2 +- .../move_effect/bide.c} | 2 +- .../move_effect/burn_hit.c} | 2 +- .../move_effect/burn_up.c} | 2 +- .../move_effect/court_change.c} | 2 +- .../move_effect/defense_down.c} | 2 +- .../move_effect/defense_up.c} | 2 +- .../move_effect/defog.c} | 2 +- .../move_effect/dire_claw.c} | 2 +- .../move_effect/double_shock.c} | 2 +- .../move_effect/dream_eater.c} | 2 +- .../move_effect/encore.c} | 2 +- .../move_effect/evasion_up.c} | 2 +- .../move_effect/explosion.c} | 2 +- .../move_effect/focus_punch.c} | 2 +- .../move_effect/freeze_hit.c} | 2 +- .../move_effect/haze.c} | 2 +- .../move_effect/healing_wish.c} | 2 +- .../move_effect/hex.c} | 2 +- .../move_effect/hit_escape.c} | 2 +- .../move_effect/hit_set_entry_hazardss.c} | 2 +- .../move_effect/hit_switch_target.c} | 2 +- .../move_effect/hydro_steam.c} | 2 +- .../move_effect/metronome.c} | 2 +- .../move_effect/mind_blown.c} | 2 +- .../move_effect/mirror_move.c} | 2 +- .../move_effect/paralyze_hit.c} | 2 +- .../move_effect/poison_hit.c} | 2 +- .../move_effect/rampage.c} | 2 +- .../move_effect/recoil_if_miss.c} | 2 +- .../move_effect/reflect.c} | 2 +- .../move_effect/revival_blessing.c} | 2 +- .../move_effect/roar.c} | 2 +- .../move_effect/shell_trap.c} | 2 +- .../move_effect/sleep.c} | 2 +- .../move_effect/special_attack_down.c} | 2 +- .../move_effect/special_attack_up_3.c} | 2 +- .../move_effect/spikes.c} | 2 +- .../move_effect/sticky_web.c} | 2 +- .../move_effect/stockpile.c} | 2 +- .../move_effect/tailwind.c} | 2 +- .../move_effect/teleport.c} | 2 +- .../move_effect/torment.c} | 2 +- .../move_effect/toxic.c} | 2 +- .../move_effect/toxic_spikes.c} | 2 +- .../move_effect/tri_attack.c} | 2 +- .../move_effect/triple_kick.c} | 2 +- .../move_effect/venoshock.c} | 2 +- .../move_flags/three_strikes.c} | 2 +- test/battle/status1/bad_poison.c | 39 ++++ test/battle/status1/burn.c | 35 ++++ test/battle/status1/freeze.c | 45 ++++ .../status1/frostbite.c} | 2 +- test/battle/status1/paralysis.c | 44 ++++ test/battle/status1/poison.c | 17 ++ test/battle/status1/sleep.c | 23 +++ .../terrain/electric.c} | 2 +- .../terrain/grassy.c} | 2 +- .../terrain/misty.c} | 2 +- .../terrain/psychic.c} | 2 +- test/{ => battle}/trainer_control.c | 2 +- test/battle/type/grass.c | 17 ++ .../{weather_rain.c => battle/weather/rain.c} | 2 +- .../{weather_snow.c => battle/weather/snow.c} | 2 +- .../weather/sunlight.c} | 2 +- test/powder_moves.c | 47 ----- test/random.c | 2 +- test/status1.c | 193 ------------------ test/test_runner.c | 2 +- test/test_runner_battle.c | 2 +- 166 files changed, 527 insertions(+), 489 deletions(-) rename test/test_battle.h => include/test/battle.h (99%) rename {test => include/test}/test.h (100%) rename test/{ability_bad_dreams.c => battle/ability/bad_dreams.c} (99%) rename test/{ability_beads_of_ruin.c => battle/ability/beads_of_ruin.c} (98%) rename test/{ability_blaze.c => battle/ability/blaze.c} (95%) rename test/{ability_clear_body.c => battle/ability/clear_body.c} (98%) rename test/{ability_cloud_nine.c => battle/ability/cloud_nine.c} (94%) rename test/{ability_compound_eyes.c => battle/ability/compound_eyes.c} (97%) rename test/{ability_contrary.c => battle/ability/contrary.c} (99%) rename test/{ability_cute_charm.c => battle/ability/cute_charm.c} (98%) rename test/{ability_damp.c => battle/ability/damp.c} (98%) rename test/{primal_weather.c => battle/ability/desolate_land.c} (51%) rename test/{ability_download.c => battle/ability/download.c} (99%) rename test/{ability_drizzle.c => battle/ability/drizzle.c} (96%) rename test/{ability_dry_skin.c => battle/ability/dry_skin.c} (99%) rename test/{ability_electromorphosis.c => battle/ability/electromorphosis.c} (98%) rename test/{ability_flame_body.c => battle/ability/flame_body.c} (97%) rename test/{ability_flower_gift.c => battle/ability/flower_gift.c} (99%) rename test/{ability_forecast.c => battle/ability/forecast.c} (99%) rename test/{ability_full_metal_body.c => battle/ability/full_metal_body.c} (98%) rename test/{ability_hunger_switch.c => battle/ability/hunger_switch.c} (96%) rename test/{ability_hydration.c => battle/ability/hydration.c} (95%) rename test/{ability_hyper_cutter.c => battle/ability/hyper_cutter.c} (98%) rename test/{ability_ice_body.c => battle/ability/ice_body.c} (96%) rename test/{ability_immunity.c => battle/ability/immunity.c} (98%) rename test/{ability_inner_focus.c => battle/ability/inner_focus.c} (98%) rename test/{ability_insomnia.c => battle/ability/insomnia.c} (98%) rename test/{ability_intimidate.c => battle/ability/intimidate.c} (99%) rename test/{ability_leaf_guard.c => battle/ability/leaf_guard.c} (99%) rename test/{ability_limber.c => battle/ability/limber.c} (94%) rename test/{ability_magic_bounce.c => battle/ability/magic_bounce.c} (99%) rename test/{ability_mirror_armor.c => battle/ability/mirror_armor.c} (99%) rename test/{ability_oblivious.c => battle/ability/oblivious.c} (98%) create mode 100644 test/battle/ability/overcoat.c rename test/{ability_overgrow.c => battle/ability/overgrow.c} (96%) rename test/{ability_own_tempo.c => battle/ability/own_tempo.c} (99%) rename test/{ability_pastel_veil.c => battle/ability/pastel_veil.c} (99%) rename test/{ability_poison_point.c => battle/ability/poison_point.c} (97%) create mode 100644 test/battle/ability/primordial_sea.c rename test/{ability_protosynthesis.c => battle/ability/protosynthesis.c} (99%) rename test/{ability_quark_drive.c => battle/ability/quark_drive.c} (99%) rename test/{ability_rain_dish.c => battle/ability/rain_dish.c} (94%) rename test/{ability_rattled.c => battle/ability/rattled.c} (99%) rename test/{ability_sand_veil.c => battle/ability/sand_veil.c} (96%) rename test/{ability_schooling.c => battle/ability/schooling.c} (99%) rename test/{ability_scrappy.c => battle/ability/scrappy.c} (98%) rename test/{ability_snow_cloak.c => battle/ability/snow_cloak.c} (96%) rename test/{ability_snow_warning.c => battle/ability/snow_warning.c} (96%) rename test/{ability_speed_boost.c => battle/ability/speed_boost.c} (96%) rename test/{ability_stamina.c => battle/ability/stamina.c} (99%) rename test/{ability_static.c => battle/ability/static.c} (97%) rename test/{ability_stench.c => battle/ability/stench.c} (97%) rename test/{ability_sturdy.c => battle/ability/sturdy.c} (98%) rename test/{ability_swarm.c => battle/ability/swarm.c} (95%) rename test/{ability_sword_of_ruin.c => battle/ability/sword_of_ruin.c} (98%) rename test/{ability_tablets_of_ruin.c => battle/ability/tablets_of_ruin.c} (98%) rename test/{ability_torrent.c => battle/ability/torrent.c} (95%) rename test/{ability_vessel_of_ruin.c => battle/ability/vessel_of_ruin.c} (98%) rename test/{ability_volt_absorb.c => battle/ability/volt_absorb.c} (99%) rename test/{ability_water_absorb.c => battle/ability/water_absorb.c} (99%) rename test/{ability_white_smoke.c => battle/ability/white_smoke.c} (98%) rename test/{ability_wind_power.c => battle/ability/wind_power.c} (99%) rename test/{ability_zen_mode.c => battle/ability/zen_mode.c} (99%) create mode 100644 test/battle/form_change/battle_switch.c rename test/{form_change.c => battle/form_change/begin_battle.c} (68%) create mode 100644 test/battle/form_change/faint.c rename test/{ => battle/form_change}/mega_evolution.c (99%) rename test/{ => battle/form_change}/primal_reversion.c (99%) rename test/{hold_effect_air_balloon.c => battle/hold_effect/air_balloon.c} (99%) rename test/{hold_effect_berserk_gene.c => battle/hold_effect/berserk_gene.c} (99%) rename test/{hold_effect_clear_amulet.c => battle/hold_effect/clear_amulet.c} (99%) rename test/{hold_effect_eject_button.c => battle/hold_effect/eject_button.c} (99%) rename test/{hold_effect_gems.c => battle/hold_effect/gems.c} (99%) rename test/{hold_effect_leftovers.c => battle/hold_effect/leftovers.c} (98%) rename test/{hold_effect_mirror_herb.c => battle/hold_effect/mirror_herb.c} (98%) rename test/{hold_effect_red_card.c => battle/hold_effect/red_card.c} (99%) create mode 100644 test/battle/hold_effect/safety_goggles.c rename test/{hold_effect_utility_umbrella.c => battle/hold_effect/utility_umbrella.c} (99%) rename test/{item_effect_cure_status.c => battle/item_effect/cure_status.c} (99%) rename test/{item_effect_heal_and_cure_status.c => battle/item_effect/heal_and_cure_status.c} (98%) rename test/{item_effect_increase_stat.c => battle/item_effect/increase_stat.c} (99%) rename test/{item_effect_restore_hp.c => battle/item_effect/restore_hp.c} (98%) rename test/{item_effect_restore_pp.c => battle/item_effect/restore_pp.c} (98%) rename test/{item_effect_revive.c => battle/item_effect/revive.c} (98%) rename test/{item_effect_set_focus_energy.c => battle/item_effect/set_focus_energy.c} (96%) rename test/{item_effect_set_mist.c => battle/item_effect/set_mist.c} (96%) rename test/{ => battle}/move.c (99%) rename test/{move_effect_absorb.c => battle/move_effect/absorb.c} (97%) rename test/{move_effect_accuracy_down.c => battle/move_effect/accuracy_down.c} (96%) rename test/{move_effect_after_you.c => battle/move_effect/after_you.c} (98%) rename test/{move_effect_assist.c => battle/move_effect/assist.c} (96%) rename test/{move_effect_attack_down.c => battle/move_effect/attack_down.c} (97%) rename test/{move_effect_attack_up.c => battle/move_effect/attack_up.c} (97%) rename test/{move_effect_attack_up_user_ally.c => battle/move_effect/attack_up_user_ally.c} (99%) rename test/{move_effect_barb_barrage.c => battle/move_effect/barb_barrage.c} (98%) rename test/{move_effect_beak_blast.c => battle/move_effect/beak_blast.c} (99%) rename test/{move_effect_bide.c => battle/move_effect/bide.c} (97%) rename test/{move_effect_burn_hit.c => battle/move_effect/burn_hit.c} (97%) rename test/{move_effect_burn_up.c => battle/move_effect/burn_up.c} (98%) rename test/{move_effect_court_change.c => battle/move_effect/court_change.c} (99%) rename test/{move_effect_defense_down.c => battle/move_effect/defense_down.c} (97%) rename test/{move_effect_defense_up.c => battle/move_effect/defense_up.c} (97%) rename test/{move_effect_defog.c => battle/move_effect/defog.c} (99%) rename test/{move_effect_dire_claw.c => battle/move_effect/dire_claw.c} (99%) rename test/{move_effect_double_shock.c => battle/move_effect/double_shock.c} (98%) rename test/{move_effect_dream_eater.c => battle/move_effect/dream_eater.c} (98%) rename test/{move_effect_encore.c => battle/move_effect/encore.c} (98%) rename test/{move_effect_evasion_up.c => battle/move_effect/evasion_up.c} (96%) rename test/{move_effect_explosion.c => battle/move_effect/explosion.c} (99%) rename test/{move_effect_focus_punch.c => battle/move_effect/focus_punch.c} (99%) rename test/{move_effect_freeze_hit.c => battle/move_effect/freeze_hit.c} (97%) rename test/{move_effect_haze.c => battle/move_effect/haze.c} (97%) rename test/{move_effect_healing_wish.c => battle/move_effect/healing_wish.c} (99%) rename test/{move_effect_hex.c => battle/move_effect/hex.c} (97%) rename test/{move_effect_hit_escape.c => battle/move_effect/hit_escape.c} (99%) rename test/{move_effect_hit_set_entry_hazardss.c => battle/move_effect/hit_set_entry_hazardss.c} (99%) rename test/{move_effect_hit_switch_target.c => battle/move_effect/hit_switch_target.c} (98%) rename test/{move_effect_hydro_steam.c => battle/move_effect/hydro_steam.c} (98%) rename test/{move_effect_metronome.c => battle/move_effect/metronome.c} (98%) rename test/{move_effect_mind_blown.c => battle/move_effect/mind_blown.c} (99%) rename test/{move_effect_mirror_move.c => battle/move_effect/mirror_move.c} (99%) rename test/{move_effect_paralyze_hit.c => battle/move_effect/paralyze_hit.c} (97%) rename test/{move_effect_poison_hit.c => battle/move_effect/poison_hit.c} (97%) rename test/{move_effect_rampage.c => battle/move_effect/rampage.c} (99%) rename test/{move_effect_recoil_if_miss.c => battle/move_effect/recoil_if_miss.c} (99%) rename test/{move_effect_reflect.c => battle/move_effect/reflect.c} (99%) rename test/{move_effect_revival_blessing.c => battle/move_effect/revival_blessing.c} (99%) rename test/{move_effect_roar.c => battle/move_effect/roar.c} (98%) rename test/{move_effect_shell_trap.c => battle/move_effect/shell_trap.c} (99%) rename test/{move_effect_sleep.c => battle/move_effect/sleep.c} (97%) rename test/{move_effect_special_attack_down.c => battle/move_effect/special_attack_down.c} (97%) rename test/{move_effect_special_attack_up_3.c => battle/move_effect/special_attack_up_3.c} (97%) rename test/{move_effect_spikes.c => battle/move_effect/spikes.c} (99%) rename test/{move_effect_sticky_web.c => battle/move_effect/sticky_web.c} (99%) rename test/{move_effect_stockpile.c => battle/move_effect/stockpile.c} (99%) rename test/{move_effect_tailwind.c => battle/move_effect/tailwind.c} (98%) rename test/{move_effect_teleport.c => battle/move_effect/teleport.c} (98%) rename test/{move_effect_torment.c => battle/move_effect/torment.c} (98%) rename test/{move_effect_toxic.c => battle/move_effect/toxic.c} (98%) rename test/{move_effect_toxic_spikes.c => battle/move_effect/toxic_spikes.c} (99%) rename test/{move_effect_tri_attack.c => battle/move_effect/tri_attack.c} (99%) rename test/{move_effect_triple_kick.c => battle/move_effect/triple_kick.c} (97%) rename test/{move_effect_venoshock.c => battle/move_effect/venoshock.c} (97%) rename test/{move_flag_three_strikes.c => battle/move_flags/three_strikes.c} (98%) create mode 100644 test/battle/status1/bad_poison.c create mode 100644 test/battle/status1/burn.c create mode 100644 test/battle/status1/freeze.c rename test/{status_frostbite.c => battle/status1/frostbite.c} (99%) create mode 100644 test/battle/status1/paralysis.c create mode 100644 test/battle/status1/poison.c create mode 100644 test/battle/status1/sleep.c rename test/{terrain_electric.c => battle/terrain/electric.c} (99%) rename test/{terrain_grassy.c => battle/terrain/grassy.c} (99%) rename test/{terrain_misty.c => battle/terrain/misty.c} (99%) rename test/{terrain_psychic.c => battle/terrain/psychic.c} (99%) rename test/{ => battle}/trainer_control.c (99%) create mode 100644 test/battle/type/grass.c rename test/{weather_rain.c => battle/weather/rain.c} (98%) rename test/{weather_snow.c => battle/weather/snow.c} (99%) rename test/{weather_sunlight.c => battle/weather/sunlight.c} (98%) delete mode 100644 test/powder_moves.c delete mode 100644 test/status1.c diff --git a/Makefile b/Makefile index c4964e42e..79ed8f160 100644 --- a/Makefile +++ b/Makefile @@ -450,7 +450,7 @@ $(OBJ_DIR)/sym_ewram.ld: sym_ewram.txt # NOTE: Based on C_DEP above, but without NODEP and KEEP_TEMPS handling. define TEST_DEP -$1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib -I test $2) +$1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib $2) @echo "$$(CC1) -o $$@ $$<" @$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) $$< charmap.txt -i | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ - endef diff --git a/test/test_battle.h b/include/test/battle.h similarity index 99% rename from test/test_battle.h rename to include/test/battle.h index b4f38ed91..dc40a8a20 100644 --- a/test/test_battle.h +++ b/include/test/battle.h @@ -447,13 +447,13 @@ #ifndef GUARD_TEST_BATTLE_H #define GUARD_TEST_BATTLE_H +#include "global.h" #include "battle.h" #include "battle_anim.h" #include "data.h" #include "item.h" #include "random.h" #include "recorded_battle.h" -#include "test.h" #include "util.h" #include "constants/abilities.h" #include "constants/battle_anim.h" @@ -462,6 +462,7 @@ #include "constants/items.h" #include "constants/moves.h" #include "constants/species.h" +#include "test/test.h" // NOTE: If the stack is too small the test runner will probably crash // or loop. diff --git a/test/test.h b/include/test/test.h similarity index 100% rename from test/test.h rename to include/test/test.h diff --git a/test/ability_bad_dreams.c b/test/battle/ability/bad_dreams.c similarity index 99% rename from test/ability_bad_dreams.c rename to test/battle/ability/bad_dreams.c index 9bf800534..42ac3f04a 100644 --- a/test/ability_bad_dreams.c +++ b/test/battle/ability/bad_dreams.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_beads_of_ruin.c b/test/battle/ability/beads_of_ruin.c similarity index 98% rename from test/ability_beads_of_ruin.c rename to test/battle/ability/beads_of_ruin.c index 28b39a430..1c6f740e8 100644 --- a/test/ability_beads_of_ruin.c +++ b/test/battle/ability/beads_of_ruin.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_blaze.c b/test/battle/ability/blaze.c similarity index 95% rename from test/ability_blaze.c rename to test/battle/ability/blaze.c index 1e12c6b29..12ccced93 100644 --- a/test/ability_blaze.c +++ b/test/battle/ability/blaze.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Blaze boosts Fire-type moves in a pinch", s16 damage) { diff --git a/test/ability_clear_body.c b/test/battle/ability/clear_body.c similarity index 98% rename from test/ability_clear_body.c rename to test/battle/ability/clear_body.c index 11789aaf6..7f167c9fe 100644 --- a/test/ability_clear_body.c +++ b/test/battle/ability/clear_body.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Clear Body prevents intimidate") { diff --git a/test/ability_cloud_nine.c b/test/battle/ability/cloud_nine.c similarity index 94% rename from test/ability_cloud_nine.c rename to test/battle/ability/cloud_nine.c index 02da15ee1..e8de0fd8a 100644 --- a/test/ability_cloud_nine.c +++ b/test/battle/ability/cloud_nine.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Cloud Nine prevents weather effects") { diff --git a/test/ability_compound_eyes.c b/test/battle/ability/compound_eyes.c similarity index 97% rename from test/ability_compound_eyes.c rename to test/battle/ability/compound_eyes.c index 6bdb15fa7..cc005943b 100644 --- a/test/ability_compound_eyes.c +++ b/test/battle/ability/compound_eyes.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Compound Eyes raises accuracy") { diff --git a/test/ability_contrary.c b/test/battle/ability/contrary.c similarity index 99% rename from test/ability_contrary.c rename to test/battle/ability/contrary.c index 52347f379..de4093716 100644 --- a/test/ability_contrary.c +++ b/test/battle/ability/contrary.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_cute_charm.c b/test/battle/ability/cute_charm.c similarity index 98% rename from test/ability_cute_charm.c rename to test/battle/ability/cute_charm.c index ccf5c490b..1a55fc359 100644 --- a/test/ability_cute_charm.c +++ b/test/battle/ability/cute_charm.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Cute Charm inflicts infatuation on contact") { diff --git a/test/ability_damp.c b/test/battle/ability/damp.c similarity index 98% rename from test/ability_damp.c rename to test/battle/ability/damp.c index 19a4ac5ad..ce53db112 100644 --- a/test/ability_damp.c +++ b/test/battle/ability/damp.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Damp prevents explosion-like moves from enemies") { diff --git a/test/primal_weather.c b/test/battle/ability/desolate_land.c similarity index 51% rename from test/primal_weather.c rename to test/battle/ability/desolate_land.c index 650a79921..ca52c9cd6 100644 --- a/test/primal_weather.c +++ b/test/battle/ability/desolate_land.c @@ -1,72 +1,12 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { - ASSUME(gBattleMoves[MOVE_EMBER].power != 0); - ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); ASSUME(gBattleMoves[MOVE_WATER_GUN].power != 0); ASSUME(gBattleMoves[MOVE_WATER_GUN].type == TYPE_WATER); } -SINGLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves") -{ - GIVEN { - PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);} - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, MOVE_EMBER); } - TURN { MOVE(opponent, MOVE_EMBER); } - } SCENE { - MESSAGE("Foe Wobbuffet used Ember!"); - NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent); - MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); - NOT HP_BAR(player); - MESSAGE("Foe Wobbuffet used Ember!"); - NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent); - MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); - NOT HP_BAR(player); - } THEN { - EXPECT_EQ(player->hp, player->maxHP); - } -} - -DOUBLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves and prints the message only once with moves hitting multiple targets") -{ - GIVEN { - ASSUME(gBattleMoves[MOVE_ERUPTION].power != 0); - ASSUME(gBattleMoves[MOVE_ERUPTION].type == TYPE_FIRE); - ASSUME(gBattleMoves[MOVE_ERUPTION].target == MOVE_TARGET_BOTH); - PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB); {Speed(5);}} - PLAYER(SPECIES_WOBBUFFET) {Speed(5);} - OPPONENT(SPECIES_WOBBUFFET) {Speed(10);} - OPPONENT(SPECIES_WOBBUFFET) {Speed(8);} - } WHEN { - TURN { MOVE(opponentLeft, MOVE_ERUPTION); } - } SCENE { - MESSAGE("Foe Wobbuffet used Eruption!"); - NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ERUPTION, opponentLeft); - MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); - NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); - } THEN { - EXPECT_EQ(playerLeft->hp, playerLeft->maxHP); - EXPECT_EQ(playerRight->hp, playerRight->maxHP); - } -} - -SINGLE_BATTLE_TEST("Primordial Sea does not block a move if pokemon is asleep and uses a Fire-type move") // Sleep/confusion/paralysis all happen before the check for primal weather -{ - GIVEN { - PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);} - OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);} - } WHEN { - TURN { MOVE(opponent, MOVE_EMBER); } - } SCENE { - NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); - MESSAGE("Foe Wobbuffet is fast asleep."); - } -} - SINGLE_BATTLE_TEST("Desolate Land blocks damaging Water-type moves") { GIVEN { diff --git a/test/ability_download.c b/test/battle/ability/download.c similarity index 99% rename from test/ability_download.c rename to test/battle/ability/download.c index ee03f6911..5b59e37f0 100644 --- a/test/ability_download.c +++ b/test/battle/ability/download.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_drizzle.c b/test/battle/ability/drizzle.c similarity index 96% rename from test/ability_drizzle.c rename to test/battle/ability/drizzle.c index fd0d42616..ce0fc9514 100644 --- a/test/ability_drizzle.c +++ b/test/battle/ability/drizzle.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Drizzle summons rain", s16 damage) { diff --git a/test/ability_dry_skin.c b/test/battle/ability/dry_skin.c similarity index 99% rename from test/ability_dry_skin.c rename to test/battle/ability/dry_skin.c index 028076d5a..832b7baaf 100644 --- a/test/ability_dry_skin.c +++ b/test/battle/ability/dry_skin.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Dry Skin causes 1/8th Max HP damage in Sun") { diff --git a/test/ability_electromorphosis.c b/test/battle/ability/electromorphosis.c similarity index 98% rename from test/ability_electromorphosis.c rename to test/battle/ability/electromorphosis.c index 25d33aac0..9111fa328 100644 --- a/test/ability_electromorphosis.c +++ b/test/battle/ability/electromorphosis.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Electromorphosis sets up Charge when hit by any move") { diff --git a/test/ability_flame_body.c b/test/battle/ability/flame_body.c similarity index 97% rename from test/ability_flame_body.c rename to test/battle/ability/flame_body.c index 303337693..ae9138e12 100644 --- a/test/ability_flame_body.c +++ b/test/battle/ability/flame_body.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Flame Body inflicts burn on contact") { diff --git a/test/ability_flower_gift.c b/test/battle/ability/flower_gift.c similarity index 99% rename from test/ability_flower_gift.c rename to test/battle/ability/flower_gift.c index 560706af4..fdd191f55 100644 --- a/test/ability_flower_gift.c +++ b/test/battle/ability/flower_gift.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Flower Gift transforms Cherrim in harsh sunlight") { diff --git a/test/ability_forecast.c b/test/battle/ability/forecast.c similarity index 99% rename from test/ability_forecast.c rename to test/battle/ability/forecast.c index 696c0383a..26ad789da 100644 --- a/test/ability_forecast.c +++ b/test/battle/ability/forecast.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Forecast transforms Castform in weather from an opponent's move") { diff --git a/test/ability_full_metal_body.c b/test/battle/ability/full_metal_body.c similarity index 98% rename from test/ability_full_metal_body.c rename to test/battle/ability/full_metal_body.c index c47ce9d4d..d00714d52 100644 --- a/test/ability_full_metal_body.c +++ b/test/battle/ability/full_metal_body.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Full Metal Body prevents intimidate") { diff --git a/test/ability_hunger_switch.c b/test/battle/ability/hunger_switch.c similarity index 96% rename from test/ability_hunger_switch.c rename to test/battle/ability/hunger_switch.c index 336c2c160..c0802d664 100644 --- a/test/ability_hunger_switch.c +++ b/test/battle/ability/hunger_switch.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Hunger Switch switches Morpeko's forms at the end of the turn") { diff --git a/test/ability_hydration.c b/test/battle/ability/hydration.c similarity index 95% rename from test/ability_hydration.c rename to test/battle/ability/hydration.c index 45689b212..2dca490c0 100644 --- a/test/ability_hydration.c +++ b/test/battle/ability/hydration.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Hydration cures non-volatile Status conditions if it is raining") { diff --git a/test/ability_hyper_cutter.c b/test/battle/ability/hyper_cutter.c similarity index 98% rename from test/ability_hyper_cutter.c rename to test/battle/ability/hyper_cutter.c index 67dac4c66..ac942039b 100644 --- a/test/ability_hyper_cutter.c +++ b/test/battle/ability/hyper_cutter.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Hyper Cutter prevents intimidate") { diff --git a/test/ability_ice_body.c b/test/battle/ability/ice_body.c similarity index 96% rename from test/ability_ice_body.c rename to test/battle/ability/ice_body.c index 0658ace81..aca84532a 100644 --- a/test/ability_ice_body.c +++ b/test/battle/ability/ice_body.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Ice Body prevents damage from hail") { diff --git a/test/ability_immunity.c b/test/battle/ability/immunity.c similarity index 98% rename from test/ability_immunity.c rename to test/battle/ability/immunity.c index fea2eb522..3c6c4afa6 100644 --- a/test/ability_immunity.c +++ b/test/battle/ability/immunity.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Immunity prevents Poison Sting poison") { diff --git a/test/ability_inner_focus.c b/test/battle/ability/inner_focus.c similarity index 98% rename from test/ability_inner_focus.c rename to test/battle/ability/inner_focus.c index 5b713475d..574c860a7 100644 --- a/test/ability_inner_focus.c +++ b/test/battle/ability/inner_focus.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Inner Focus prevents intimidate") { diff --git a/test/ability_insomnia.c b/test/battle/ability/insomnia.c similarity index 98% rename from test/ability_insomnia.c rename to test/battle/ability/insomnia.c index 2fcb6a97e..cb22be925 100644 --- a/test/ability_insomnia.c +++ b/test/battle/ability/insomnia.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Insomnia prevents sleep") { diff --git a/test/ability_intimidate.c b/test/battle/ability/intimidate.c similarity index 99% rename from test/ability_intimidate.c rename to test/battle/ability/intimidate.c index f9681493b..a0a23cc13 100644 --- a/test/ability_intimidate.c +++ b/test/battle/ability/intimidate.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_leaf_guard.c b/test/battle/ability/leaf_guard.c similarity index 99% rename from test/ability_leaf_guard.c rename to test/battle/ability/leaf_guard.c index 92727175a..2c052b5a6 100644 --- a/test/ability_leaf_guard.c +++ b/test/battle/ability/leaf_guard.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Leaf Guard prevents non-volatile status conditions in sun") { diff --git a/test/ability_limber.c b/test/battle/ability/limber.c similarity index 94% rename from test/ability_limber.c rename to test/battle/ability/limber.c index ff701b663..87d4177ff 100644 --- a/test/ability_limber.c +++ b/test/battle/ability/limber.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Limber prevents paralysis") { diff --git a/test/ability_magic_bounce.c b/test/battle/ability/magic_bounce.c similarity index 99% rename from test/ability_magic_bounce.c rename to test/battle/ability/magic_bounce.c index ab51369ef..348b80f0a 100644 --- a/test/ability_magic_bounce.c +++ b/test/battle/ability/magic_bounce.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Magic Bounce bounces back status moves") diff --git a/test/ability_mirror_armor.c b/test/battle/ability/mirror_armor.c similarity index 99% rename from test/ability_mirror_armor.c rename to test/battle/ability/mirror_armor.c index 7e15e029b..2b0a59044 100644 --- a/test/ability_mirror_armor.c +++ b/test/battle/ability/mirror_armor.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_oblivious.c b/test/battle/ability/oblivious.c similarity index 98% rename from test/ability_oblivious.c rename to test/battle/ability/oblivious.c index 9d9aeeb29..51d27bb16 100644 --- a/test/ability_oblivious.c +++ b/test/battle/ability/oblivious.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Oblivious prevents Infatuation") { diff --git a/test/battle/ability/overcoat.c b/test/battle/ability/overcoat.c new file mode 100644 index 000000000..5f2405167 --- /dev/null +++ b/test/battle/ability/overcoat.c @@ -0,0 +1,21 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Overcoat blocks powder and spore moves") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_PINECO) { Ability(ABILITY_OVERCOAT); } + } WHEN { + TURN { MOVE(player, MOVE_STUN_SPORE); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_OVERCOAT); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); + MESSAGE("It doesn't affect Foe Pineco…"); + } +} + +TO_DO_BATTLE_TEST("Overcoat blocks damage from hail"); +TO_DO_BATTLE_TEST("Overcoat blocks damage from sandstorm"); +TO_DO_BATTLE_TEST("Overcoat blocks Effect Spore's effect"); diff --git a/test/ability_overgrow.c b/test/battle/ability/overgrow.c similarity index 96% rename from test/ability_overgrow.c rename to test/battle/ability/overgrow.c index 82d8dd467..548dc9bb3 100644 --- a/test/ability_overgrow.c +++ b/test/battle/ability/overgrow.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Overgrow boosts Grass-type moves in a pinch", s16 damage) { diff --git a/test/ability_own_tempo.c b/test/battle/ability/own_tempo.c similarity index 99% rename from test/ability_own_tempo.c rename to test/battle/ability/own_tempo.c index 2440ac779..4c6a3167f 100644 --- a/test/ability_own_tempo.c +++ b/test/battle/ability/own_tempo.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Own Tempo prevents intimidate") { diff --git a/test/ability_pastel_veil.c b/test/battle/ability/pastel_veil.c similarity index 99% rename from test/ability_pastel_veil.c rename to test/battle/ability/pastel_veil.c index 363c018c1..74d764fc3 100644 --- a/test/ability_pastel_veil.c +++ b/test/battle/ability/pastel_veil.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Pastel Veil prevents Poison Sting poison") { diff --git a/test/ability_poison_point.c b/test/battle/ability/poison_point.c similarity index 97% rename from test/ability_poison_point.c rename to test/battle/ability/poison_point.c index a85edd85b..63690d84d 100644 --- a/test/ability_poison_point.c +++ b/test/battle/ability/poison_point.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Poison Point inflicts poison on contact") { diff --git a/test/battle/ability/primordial_sea.c b/test/battle/ability/primordial_sea.c new file mode 100644 index 000000000..37fbb1b23 --- /dev/null +++ b/test/battle/ability/primordial_sea.c @@ -0,0 +1,66 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_EMBER].power != 0); + ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); +} + +SINGLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves") +{ + GIVEN { + PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);} + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_EMBER); } + TURN { MOVE(opponent, MOVE_EMBER); } + } SCENE { + MESSAGE("Foe Wobbuffet used Ember!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent); + MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + NOT HP_BAR(player); + MESSAGE("Foe Wobbuffet used Ember!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent); + MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + NOT HP_BAR(player); + } THEN { + EXPECT_EQ(player->hp, player->maxHP); + } +} + +DOUBLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves and prints the message only once with moves hitting multiple targets") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_ERUPTION].power != 0); + ASSUME(gBattleMoves[MOVE_ERUPTION].type == TYPE_FIRE); + ASSUME(gBattleMoves[MOVE_ERUPTION].target == MOVE_TARGET_BOTH); + PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB); {Speed(5);}} + PLAYER(SPECIES_WOBBUFFET) {Speed(5);} + OPPONENT(SPECIES_WOBBUFFET) {Speed(10);} + OPPONENT(SPECIES_WOBBUFFET) {Speed(8);} + } WHEN { + TURN { MOVE(opponentLeft, MOVE_ERUPTION); } + } SCENE { + MESSAGE("Foe Wobbuffet used Eruption!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ERUPTION, opponentLeft); + MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + } THEN { + EXPECT_EQ(playerLeft->hp, playerLeft->maxHP); + EXPECT_EQ(playerRight->hp, playerRight->maxHP); + } +} + +SINGLE_BATTLE_TEST("Primordial Sea does not block a move if pokemon is asleep and uses a Fire-type move") // Sleep/confusion/paralysis all happen before the check for primal weather +{ + GIVEN { + PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);} + OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);} + } WHEN { + TURN { MOVE(opponent, MOVE_EMBER); } + } SCENE { + NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + MESSAGE("Foe Wobbuffet is fast asleep."); + } +} \ No newline at end of file diff --git a/test/ability_protosynthesis.c b/test/battle/ability/protosynthesis.c similarity index 99% rename from test/ability_protosynthesis.c rename to test/battle/ability/protosynthesis.c index 9f794e00a..0ed74d4f6 100644 --- a/test/ability_protosynthesis.c +++ b/test/battle/ability/protosynthesis.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_quark_drive.c b/test/battle/ability/quark_drive.c similarity index 99% rename from test/ability_quark_drive.c rename to test/battle/ability/quark_drive.c index b004c760c..bce6795ca 100644 --- a/test/ability_quark_drive.c +++ b/test/battle/ability/quark_drive.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_rain_dish.c b/test/battle/ability/rain_dish.c similarity index 94% rename from test/ability_rain_dish.c rename to test/battle/ability/rain_dish.c index 04009507c..dd647f0eb 100644 --- a/test/ability_rain_dish.c +++ b/test/battle/ability/rain_dish.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Rain Dish recovers 1/16th of Max HP in Rain") { diff --git a/test/ability_rattled.c b/test/battle/ability/rattled.c similarity index 99% rename from test/ability_rattled.c rename to test/battle/ability/rattled.c index 493e3cfca..e936ed7de 100644 --- a/test/ability_rattled.c +++ b/test/battle/ability/rattled.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_sand_veil.c b/test/battle/ability/sand_veil.c similarity index 96% rename from test/ability_sand_veil.c rename to test/battle/ability/sand_veil.c index 50abad51c..c2f6440c3 100644 --- a/test/ability_sand_veil.c +++ b/test/battle/ability/sand_veil.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Sand Veil prevents damage from sandstorm") { diff --git a/test/ability_schooling.c b/test/battle/ability/schooling.c similarity index 99% rename from test/ability_schooling.c rename to test/battle/ability/schooling.c index 87f69ea31..448e37261 100644 --- a/test/ability_schooling.c +++ b/test/battle/ability/schooling.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Schooling switches Level 20+ Wishiwashi's form when HP is 25-percent or less at the end of the turn") { diff --git a/test/ability_scrappy.c b/test/battle/ability/scrappy.c similarity index 98% rename from test/ability_scrappy.c rename to test/battle/ability/scrappy.c index b4f83afcf..df4ededa4 100644 --- a/test/ability_scrappy.c +++ b/test/battle/ability/scrappy.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Scrappy prevents intimidate") { diff --git a/test/ability_snow_cloak.c b/test/battle/ability/snow_cloak.c similarity index 96% rename from test/ability_snow_cloak.c rename to test/battle/ability/snow_cloak.c index 8a95a8ea0..88fe6e509 100644 --- a/test/ability_snow_cloak.c +++ b/test/battle/ability/snow_cloak.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Snow Cloak prevents damage from hail") { diff --git a/test/ability_snow_warning.c b/test/battle/ability/snow_warning.c similarity index 96% rename from test/ability_snow_warning.c rename to test/battle/ability/snow_warning.c index 5e8b13d54..17f18814b 100644 --- a/test/ability_snow_warning.c +++ b/test/battle/ability/snow_warning.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" #if B_SNOW_WARNING < GEN_9 SINGLE_BATTLE_TEST("Snow Warning summons hail") diff --git a/test/ability_speed_boost.c b/test/battle/ability/speed_boost.c similarity index 96% rename from test/ability_speed_boost.c rename to test/battle/ability/speed_boost.c index 78aa876e6..0cc82e806 100644 --- a/test/ability_speed_boost.c +++ b/test/battle/ability/speed_boost.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Speed Boost gradually boosts Speed") { diff --git a/test/ability_stamina.c b/test/battle/ability/stamina.c similarity index 99% rename from test/ability_stamina.c rename to test/battle/ability/stamina.c index 70cf3cdc0..3470cce51 100644 --- a/test/ability_stamina.c +++ b/test/battle/ability/stamina.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" #define STAMINA_STAT_RAISE(target, msg) \ { \ diff --git a/test/ability_static.c b/test/battle/ability/static.c similarity index 97% rename from test/ability_static.c rename to test/battle/ability/static.c index 022efa5bb..eb52a838b 100644 --- a/test/ability_static.c +++ b/test/battle/ability/static.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Static inflicts paralysis on contact") { diff --git a/test/ability_stench.c b/test/battle/ability/stench.c similarity index 97% rename from test/ability_stench.c rename to test/battle/ability/stench.c index dd1d027e9..a91c861fc 100644 --- a/test/ability_stench.c +++ b/test/battle/ability/stench.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Stench has a 10% chance to flinch") { diff --git a/test/ability_sturdy.c b/test/battle/ability/sturdy.c similarity index 98% rename from test/ability_sturdy.c rename to test/battle/ability/sturdy.c index 42cdab11f..7a7fdf39f 100644 --- a/test/ability_sturdy.c +++ b/test/battle/ability/sturdy.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Sturdy prevents OHKO moves") { diff --git a/test/ability_swarm.c b/test/battle/ability/swarm.c similarity index 95% rename from test/ability_swarm.c rename to test/battle/ability/swarm.c index 7709e976e..c6479412a 100644 --- a/test/ability_swarm.c +++ b/test/battle/ability/swarm.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Swarm boosts Bug-type moves in a pinch", s16 damage) { diff --git a/test/ability_sword_of_ruin.c b/test/battle/ability/sword_of_ruin.c similarity index 98% rename from test/ability_sword_of_ruin.c rename to test/battle/ability/sword_of_ruin.c index 6ec4f8fe8..2abc0c04f 100644 --- a/test/ability_sword_of_ruin.c +++ b/test/battle/ability/sword_of_ruin.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_tablets_of_ruin.c b/test/battle/ability/tablets_of_ruin.c similarity index 98% rename from test/ability_tablets_of_ruin.c rename to test/battle/ability/tablets_of_ruin.c index 38dcfc856..ca12b6813 100644 --- a/test/ability_tablets_of_ruin.c +++ b/test/battle/ability/tablets_of_ruin.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_torrent.c b/test/battle/ability/torrent.c similarity index 95% rename from test/ability_torrent.c rename to test/battle/ability/torrent.c index e5c701775..673e21b79 100644 --- a/test/ability_torrent.c +++ b/test/battle/ability/torrent.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Torrent boosts Water-type moves in a pinch", s16 damage) { diff --git a/test/ability_vessel_of_ruin.c b/test/battle/ability/vessel_of_ruin.c similarity index 98% rename from test/ability_vessel_of_ruin.c rename to test/battle/ability/vessel_of_ruin.c index 6c7b2664f..4f8408795 100644 --- a/test/ability_vessel_of_ruin.c +++ b/test/battle/ability/vessel_of_ruin.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_volt_absorb.c b/test/battle/ability/volt_absorb.c similarity index 99% rename from test/ability_volt_absorb.c rename to test/battle/ability/volt_absorb.c index f9af7e17b..2f69a287b 100644 --- a/test/ability_volt_absorb.c +++ b/test/battle/ability/volt_absorb.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Volt Absorb heals 25% when hit by electric type moves") { diff --git a/test/ability_water_absorb.c b/test/battle/ability/water_absorb.c similarity index 99% rename from test/ability_water_absorb.c rename to test/battle/ability/water_absorb.c index 50547d13d..cda319603 100644 --- a/test/ability_water_absorb.c +++ b/test/battle/ability/water_absorb.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Water Absorb heals 25% when hit by water type moves") { diff --git a/test/ability_white_smoke.c b/test/battle/ability/white_smoke.c similarity index 98% rename from test/ability_white_smoke.c rename to test/battle/ability/white_smoke.c index 9689aee6c..7bedbca24 100644 --- a/test/ability_white_smoke.c +++ b/test/battle/ability/white_smoke.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("White Smoke prevents intimidate") { diff --git a/test/ability_wind_power.c b/test/battle/ability/wind_power.c similarity index 99% rename from test/ability_wind_power.c rename to test/battle/ability/wind_power.c index 8488db269..996910640 100644 --- a/test/ability_wind_power.c +++ b/test/battle/ability/wind_power.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/ability_zen_mode.c b/test/battle/ability/zen_mode.c similarity index 99% rename from test/ability_zen_mode.c rename to test/battle/ability/zen_mode.c index e0e72471f..37bb4160f 100644 --- a/test/ability_zen_mode.c +++ b/test/battle/ability/zen_mode.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Zen Mode switches Darmanitan's form when HP is half or less at the end of the turn") { diff --git a/test/battle/form_change/battle_switch.c b/test/battle/form_change/battle_switch.c new file mode 100644 index 000000000..bd18e0ccf --- /dev/null +++ b/test/battle/form_change/battle_switch.c @@ -0,0 +1,23 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon switching out") +{ + GIVEN { + ASSUME(P_GEN_6_POKEMON == TRUE); + PLAYER(SPECIES_AEGISLASH); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + TURN { SWITCH(player, 1); } + TURN { SWITCH(player, 0); } + } SCENE { + ABILITY_POPUP(player, ABILITY_STANCE_CHANGE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + MESSAGE("Aegislash used Tackle!"); + MESSAGE("Foe Wobbuffet used Celebrate!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_AEGISLASH); + } +} diff --git a/test/form_change.c b/test/battle/form_change/begin_battle.c similarity index 68% rename from test/form_change.c rename to test/battle/form_change/begin_battle.c index 43497101c..328db756d 100644 --- a/test/form_change.c +++ b/test/battle/form_change/begin_battle.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Xerneas changes into Active Form upon battle start") { @@ -79,41 +79,3 @@ SINGLE_BATTLE_TEST("Zamazenta's Iron Head becomes Behemoth Bash upon form change EXPECT_EQ(player->moves[0], MOVE_BEHEMOTH_BASH); } } - -SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon switching out") -{ - GIVEN { - ASSUME(P_GEN_6_POKEMON == TRUE); - PLAYER(SPECIES_AEGISLASH); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_TACKLE); } - TURN { SWITCH(player, 1); } - TURN { SWITCH(player, 0); } - } SCENE { - ABILITY_POPUP(player, ABILITY_STANCE_CHANGE); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); - MESSAGE("Aegislash used Tackle!"); - MESSAGE("Foe Wobbuffet used Celebrate!"); - } THEN { - EXPECT_EQ(player->species, SPECIES_AEGISLASH); - } -} - -SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting") -{ - GIVEN { - ASSUME(P_GEN_6_POKEMON == TRUE); - PLAYER(SPECIES_AEGISLASH) { HP(1); } - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, MOVE_GUST); SEND_OUT(player, 1); } - } SCENE { - MESSAGE("Foe Wobbuffet used Gust!"); - MESSAGE("Aegislash fainted!"); - } THEN { - EXPECT_EQ(GetMonData(&PLAYER_PARTY[0], MON_DATA_SPECIES), SPECIES_AEGISLASH); - } -} diff --git a/test/battle/form_change/faint.c b/test/battle/form_change/faint.c new file mode 100644 index 000000000..f4baa2894 --- /dev/null +++ b/test/battle/form_change/faint.c @@ -0,0 +1,19 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting") +{ + GIVEN { + ASSUME(P_GEN_6_POKEMON == TRUE); + PLAYER(SPECIES_AEGISLASH) { HP(1); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_GUST); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("Foe Wobbuffet used Gust!"); + MESSAGE("Aegislash fainted!"); + } THEN { + EXPECT_EQ(GetMonData(&PLAYER_PARTY[0], MON_DATA_SPECIES), SPECIES_AEGISLASH); + } +} diff --git a/test/mega_evolution.c b/test/battle/form_change/mega_evolution.c similarity index 99% rename from test/mega_evolution.c rename to test/battle/form_change/mega_evolution.c index 5597f5396..32cfb2c59 100644 --- a/test/mega_evolution.c +++ b/test/battle/form_change/mega_evolution.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Venusaur can Mega Evolve holding Venusaurite") { diff --git a/test/primal_reversion.c b/test/battle/form_change/primal_reversion.c similarity index 99% rename from test/primal_reversion.c rename to test/battle/form_change/primal_reversion.c index f888d17f7..3e271f67c 100644 --- a/test/primal_reversion.c +++ b/test/battle/form_change/primal_reversion.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Primal reversion happens for Groudon only when holding Red Orb") { diff --git a/test/hold_effect_air_balloon.c b/test/battle/hold_effect/air_balloon.c similarity index 99% rename from test/hold_effect_air_balloon.c rename to test/battle/hold_effect/air_balloon.c index ba740948a..1ed8a89e4 100644 --- a/test/hold_effect_air_balloon.c +++ b/test/battle/hold_effect/air_balloon.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/hold_effect_berserk_gene.c b/test/battle/hold_effect/berserk_gene.c similarity index 99% rename from test/hold_effect_berserk_gene.c rename to test/battle/hold_effect/berserk_gene.c index 981881e74..6fd0f8a85 100644 --- a/test/hold_effect_berserk_gene.c +++ b/test/battle/hold_effect/berserk_gene.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/hold_effect_clear_amulet.c b/test/battle/hold_effect/clear_amulet.c similarity index 99% rename from test/hold_effect_clear_amulet.c rename to test/battle/hold_effect/clear_amulet.c index 83c887b2d..39e6c22a6 100644 --- a/test/hold_effect_clear_amulet.c +++ b/test/battle/hold_effect/clear_amulet.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/hold_effect_eject_button.c b/test/battle/hold_effect/eject_button.c similarity index 99% rename from test/hold_effect_eject_button.c rename to test/battle/hold_effect/eject_button.c index 7e6312656..4409f5d5b 100644 --- a/test/hold_effect_eject_button.c +++ b/test/battle/hold_effect/eject_button.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/hold_effect_gems.c b/test/battle/hold_effect/gems.c similarity index 99% rename from test/hold_effect_gems.c rename to test/battle/hold_effect/gems.c index 9a90b81f9..c98718313 100644 --- a/test/hold_effect_gems.c +++ b/test/battle/hold_effect/gems.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/hold_effect_leftovers.c b/test/battle/hold_effect/leftovers.c similarity index 98% rename from test/hold_effect_leftovers.c rename to test/battle/hold_effect/leftovers.c index 01b0655a5..7e51a3bd7 100644 --- a/test/hold_effect_leftovers.c +++ b/test/battle/hold_effect/leftovers.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/hold_effect_mirror_herb.c b/test/battle/hold_effect/mirror_herb.c similarity index 98% rename from test/hold_effect_mirror_herb.c rename to test/battle/hold_effect/mirror_herb.c index 22f564b85..1e9058b08 100644 --- a/test/hold_effect_mirror_herb.c +++ b/test/battle/hold_effect/mirror_herb.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/hold_effect_red_card.c b/test/battle/hold_effect/red_card.c similarity index 99% rename from test/hold_effect_red_card.c rename to test/battle/hold_effect/red_card.c index 0a8bb3460..aad7fea64 100644 --- a/test/hold_effect_red_card.c +++ b/test/battle/hold_effect/red_card.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/battle/hold_effect/safety_goggles.c b/test/battle/hold_effect/safety_goggles.c new file mode 100644 index 000000000..bec703443 --- /dev/null +++ b/test/battle/hold_effect/safety_goggles.c @@ -0,0 +1,26 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + gItems[ITEM_SAFETY_GOGGLES].holdEffect == HOLD_EFFECT_SAFETY_GOGGLES; +}; + +SINGLE_BATTLE_TEST("Safety Goggles block powder and spore moves") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER); + ASSUME(gItems[ITEM_SAFETY_GOGGLES].holdEffect == HOLD_EFFECT_SAFETY_GOGGLES); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ABRA) { Item(ITEM_SAFETY_GOGGLES); } + } WHEN { + TURN { MOVE(player, MOVE_STUN_SPORE); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); + MESSAGE("Foe Abra is not affected thanks to its SafetyGoggles!"); + } +} + +TO_DO_BATTLE_TEST("Safety Goggles blocks damage from hail"); +TO_DO_BATTLE_TEST("Safety Goggles blocks damage from sandstorm"); +TO_DO_BATTLE_TEST("Safety Goggles blocks Effect Spore's effect"); diff --git a/test/hold_effect_utility_umbrella.c b/test/battle/hold_effect/utility_umbrella.c similarity index 99% rename from test/hold_effect_utility_umbrella.c rename to test/battle/hold_effect/utility_umbrella.c index 6716ee72f..837bacc90 100644 --- a/test/hold_effect_utility_umbrella.c +++ b/test/battle/hold_effect/utility_umbrella.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" // Please add Utility Umbrella interactions with move, item and ability effects on their respective files. ASSUMPTIONS diff --git a/test/item_effect_cure_status.c b/test/battle/item_effect/cure_status.c similarity index 99% rename from test/item_effect_cure_status.c rename to test/battle/item_effect/cure_status.c index 1f7c9d7a2..c8b16dfdb 100644 --- a/test/item_effect_cure_status.c +++ b/test/battle/item_effect/cure_status.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Paralyze Heal heals a battler from being paralyzed") { diff --git a/test/item_effect_heal_and_cure_status.c b/test/battle/item_effect/heal_and_cure_status.c similarity index 98% rename from test/item_effect_heal_and_cure_status.c rename to test/battle/item_effect/heal_and_cure_status.c index 1f81ad922..de6a459a1 100644 --- a/test/item_effect_heal_and_cure_status.c +++ b/test/battle/item_effect/heal_and_cure_status.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary status") { diff --git a/test/item_effect_increase_stat.c b/test/battle/item_effect/increase_stat.c similarity index 99% rename from test/item_effect_increase_stat.c rename to test/battle/item_effect/increase_stat.c index ab4113064..3aeb8d525 100644 --- a/test/item_effect_increase_stat.c +++ b/test/battle/item_effect/increase_stat.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("X Attack sharply raises battler's Attack stat", s16 damage) { diff --git a/test/item_effect_restore_hp.c b/test/battle/item_effect/restore_hp.c similarity index 98% rename from test/item_effect_restore_hp.c rename to test/battle/item_effect/restore_hp.c index a469b21ff..b9e4be034 100644 --- a/test/item_effect_restore_hp.c +++ b/test/battle/item_effect/restore_hp.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Items can restore a battler's HP by a fixed amount") { diff --git a/test/item_effect_restore_pp.c b/test/battle/item_effect/restore_pp.c similarity index 98% rename from test/item_effect_restore_pp.c rename to test/battle/item_effect/restore_pp.c index 7194efbbb..e998ef3d9 100644 --- a/test/item_effect_restore_pp.c +++ b/test/battle/item_effect/restore_pp.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Ether restores the PP of one of a battler's moves by 10 ") { diff --git a/test/item_effect_revive.c b/test/battle/item_effect/revive.c similarity index 98% rename from test/item_effect_revive.c rename to test/battle/item_effect/revive.c index c2a22f80a..2be2ac4a6 100644 --- a/test/item_effect_revive.c +++ b/test/battle/item_effect/revive.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Revive restores a fainted battler's HP to half") { diff --git a/test/item_effect_set_focus_energy.c b/test/battle/item_effect/set_focus_energy.c similarity index 96% rename from test/item_effect_set_focus_energy.c rename to test/battle/item_effect/set_focus_energy.c index 95648465e..da326bbe3 100644 --- a/test/item_effect_set_focus_energy.c +++ b/test/battle/item_effect/set_focus_energy.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Dire Hit increases a battler's critical hit chance by 2 stages") { diff --git a/test/item_effect_set_mist.c b/test/battle/item_effect/set_mist.c similarity index 96% rename from test/item_effect_set_mist.c rename to test/battle/item_effect/set_mist.c index e01775ec4..a369e70d9 100644 --- a/test/item_effect_set_mist.c +++ b/test/battle/item_effect/set_mist.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Guard Spec. sets Mist effect on the battlers side") { diff --git a/test/move.c b/test/battle/move.c similarity index 99% rename from test/move.c rename to test/battle/move.c index 9e23c2bd0..e89c2ad35 100644 --- a/test/move.c +++ b/test/battle/move.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Accuracy controls the proportion of misses") { diff --git a/test/move_effect_absorb.c b/test/battle/move_effect/absorb.c similarity index 97% rename from test/move_effect_absorb.c rename to test/battle/move_effect/absorb.c index 2ea49ef9e..d46dbd9c5 100644 --- a/test/move_effect_absorb.c +++ b/test/battle/move_effect/absorb.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_accuracy_down.c b/test/battle/move_effect/accuracy_down.c similarity index 96% rename from test/move_effect_accuracy_down.c rename to test/battle/move_effect/accuracy_down.c index a6a79d8db..a30430a28 100644 --- a/test/move_effect_accuracy_down.c +++ b/test/battle/move_effect/accuracy_down.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_after_you.c b/test/battle/move_effect/after_you.c similarity index 98% rename from test/move_effect_after_you.c rename to test/battle/move_effect/after_you.c index a488d7a8c..576267cb4 100644 --- a/test/move_effect_after_you.c +++ b/test/battle/move_effect/after_you.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_assist.c b/test/battle/move_effect/assist.c similarity index 96% rename from test/move_effect_assist.c rename to test/battle/move_effect/assist.c index 28ae78572..ed2f43dc1 100644 --- a/test/move_effect_assist.c +++ b/test/battle/move_effect/assist.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_attack_down.c b/test/battle/move_effect/attack_down.c similarity index 97% rename from test/move_effect_attack_down.c rename to test/battle/move_effect/attack_down.c index 0df14f701..2149624c8 100644 --- a/test/move_effect_attack_down.c +++ b/test/battle/move_effect/attack_down.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_attack_up.c b/test/battle/move_effect/attack_up.c similarity index 97% rename from test/move_effect_attack_up.c rename to test/battle/move_effect/attack_up.c index 4978d03f9..7cd8f953c 100644 --- a/test/move_effect_attack_up.c +++ b/test/battle/move_effect/attack_up.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_attack_up_user_ally.c b/test/battle/move_effect/attack_up_user_ally.c similarity index 99% rename from test/move_effect_attack_up_user_ally.c rename to test/battle/move_effect/attack_up_user_ally.c index 3d2ac6bb8..6691b49ed 100644 --- a/test/move_effect_attack_up_user_ally.c +++ b/test/battle/move_effect/attack_up_user_ally.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_barb_barrage.c b/test/battle/move_effect/barb_barrage.c similarity index 98% rename from test/move_effect_barb_barrage.c rename to test/battle/move_effect/barb_barrage.c index ad9f1ec2e..89062b3de 100644 --- a/test/move_effect_barb_barrage.c +++ b/test/battle/move_effect/barb_barrage.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_beak_blast.c b/test/battle/move_effect/beak_blast.c similarity index 99% rename from test/move_effect_beak_blast.c rename to test/battle/move_effect/beak_blast.c index a2d66b405..50bff2411 100644 --- a/test/move_effect_beak_blast.c +++ b/test/battle/move_effect/beak_blast.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_bide.c b/test/battle/move_effect/bide.c similarity index 97% rename from test/move_effect_bide.c rename to test/battle/move_effect/bide.c index 0b0e18408..4dfdc3430 100644 --- a/test/move_effect_bide.c +++ b/test/battle/move_effect/bide.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_burn_hit.c b/test/battle/move_effect/burn_hit.c similarity index 97% rename from test/move_effect_burn_hit.c rename to test/battle/move_effect/burn_hit.c index 88fef17cb..3f257dfcb 100644 --- a/test/move_effect_burn_hit.c +++ b/test/battle/move_effect/burn_hit.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_burn_up.c b/test/battle/move_effect/burn_up.c similarity index 98% rename from test/move_effect_burn_up.c rename to test/battle/move_effect/burn_up.c index a53b1afa8..d24e47992 100644 --- a/test/move_effect_burn_up.c +++ b/test/battle/move_effect/burn_up.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_court_change.c b/test/battle/move_effect/court_change.c similarity index 99% rename from test/move_effect_court_change.c rename to test/battle/move_effect/court_change.c index 9d03efddf..4aa2e9d36 100644 --- a/test/move_effect_court_change.c +++ b/test/battle/move_effect/court_change.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_defense_down.c b/test/battle/move_effect/defense_down.c similarity index 97% rename from test/move_effect_defense_down.c rename to test/battle/move_effect/defense_down.c index 9ecd1c25f..8a8c69ec3 100644 --- a/test/move_effect_defense_down.c +++ b/test/battle/move_effect/defense_down.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_defense_up.c b/test/battle/move_effect/defense_up.c similarity index 97% rename from test/move_effect_defense_up.c rename to test/battle/move_effect/defense_up.c index 492ce7bc9..b4c7cb4db 100644 --- a/test/move_effect_defense_up.c +++ b/test/battle/move_effect/defense_up.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_defog.c b/test/battle/move_effect/defog.c similarity index 99% rename from test/move_effect_defog.c rename to test/battle/move_effect/defog.c index 055f3f61a..1cd78fb50 100644 --- a/test/move_effect_defog.c +++ b/test/battle/move_effect/defog.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_dire_claw.c b/test/battle/move_effect/dire_claw.c similarity index 99% rename from test/move_effect_dire_claw.c rename to test/battle/move_effect/dire_claw.c index 712b6ea25..421ba201d 100644 --- a/test/move_effect_dire_claw.c +++ b/test/battle/move_effect/dire_claw.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_double_shock.c b/test/battle/move_effect/double_shock.c similarity index 98% rename from test/move_effect_double_shock.c rename to test/battle/move_effect/double_shock.c index c3651c403..89f758681 100644 --- a/test/move_effect_double_shock.c +++ b/test/battle/move_effect/double_shock.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_dream_eater.c b/test/battle/move_effect/dream_eater.c similarity index 98% rename from test/move_effect_dream_eater.c rename to test/battle/move_effect/dream_eater.c index fa17b94a6..047fe7898 100644 --- a/test/move_effect_dream_eater.c +++ b/test/battle/move_effect/dream_eater.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_encore.c b/test/battle/move_effect/encore.c similarity index 98% rename from test/move_effect_encore.c rename to test/battle/move_effect/encore.c index f8a178512..eff6c33b2 100644 --- a/test/move_effect_encore.c +++ b/test/battle/move_effect/encore.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_evasion_up.c b/test/battle/move_effect/evasion_up.c similarity index 96% rename from test/move_effect_evasion_up.c rename to test/battle/move_effect/evasion_up.c index 4a4e99db7..ecb6429cd 100644 --- a/test/move_effect_evasion_up.c +++ b/test/battle/move_effect/evasion_up.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_explosion.c b/test/battle/move_effect/explosion.c similarity index 99% rename from test/move_effect_explosion.c rename to test/battle/move_effect/explosion.c index 41e74044b..ef6dba6c0 100644 --- a/test/move_effect_explosion.c +++ b/test/battle/move_effect/explosion.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_focus_punch.c b/test/battle/move_effect/focus_punch.c similarity index 99% rename from test/move_effect_focus_punch.c rename to test/battle/move_effect/focus_punch.c index 2bd1e52e5..e97deef43 100644 --- a/test/move_effect_focus_punch.c +++ b/test/battle/move_effect/focus_punch.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_freeze_hit.c b/test/battle/move_effect/freeze_hit.c similarity index 97% rename from test/move_effect_freeze_hit.c rename to test/battle/move_effect/freeze_hit.c index bb0878d0d..5d99311ce 100644 --- a/test/move_effect_freeze_hit.c +++ b/test/battle/move_effect/freeze_hit.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_haze.c b/test/battle/move_effect/haze.c similarity index 97% rename from test/move_effect_haze.c rename to test/battle/move_effect/haze.c index bd43c6947..f1e4456aa 100644 --- a/test/move_effect_haze.c +++ b/test/battle/move_effect/haze.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_healing_wish.c b/test/battle/move_effect/healing_wish.c similarity index 99% rename from test/move_effect_healing_wish.c rename to test/battle/move_effect/healing_wish.c index 00ee76561..fd25b8489 100644 --- a/test/move_effect_healing_wish.c +++ b/test/battle/move_effect/healing_wish.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_hex.c b/test/battle/move_effect/hex.c similarity index 97% rename from test/move_effect_hex.c rename to test/battle/move_effect/hex.c index 2847aeb17..e0a924906 100644 --- a/test/move_effect_hex.c +++ b/test/battle/move_effect/hex.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_hit_escape.c b/test/battle/move_effect/hit_escape.c similarity index 99% rename from test/move_effect_hit_escape.c rename to test/battle/move_effect/hit_escape.c index fb5ff2dd5..f2d513095 100644 --- a/test/move_effect_hit_escape.c +++ b/test/battle/move_effect/hit_escape.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_hit_set_entry_hazardss.c b/test/battle/move_effect/hit_set_entry_hazardss.c similarity index 99% rename from test/move_effect_hit_set_entry_hazardss.c rename to test/battle/move_effect/hit_set_entry_hazardss.c index 23f0f0b28..caf75ed49 100644 --- a/test/move_effect_hit_set_entry_hazardss.c +++ b/test/battle/move_effect/hit_set_entry_hazardss.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_hit_switch_target.c b/test/battle/move_effect/hit_switch_target.c similarity index 98% rename from test/move_effect_hit_switch_target.c rename to test/battle/move_effect/hit_switch_target.c index 5af3062a4..683b005fd 100644 --- a/test/move_effect_hit_switch_target.c +++ b/test/battle/move_effect/hit_switch_target.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_hydro_steam.c b/test/battle/move_effect/hydro_steam.c similarity index 98% rename from test/move_effect_hydro_steam.c rename to test/battle/move_effect/hydro_steam.c index 487449159..f86da07ac 100644 --- a/test/move_effect_hydro_steam.c +++ b/test/battle/move_effect/hydro_steam.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_metronome.c b/test/battle/move_effect/metronome.c similarity index 98% rename from test/move_effect_metronome.c rename to test/battle/move_effect/metronome.c index 1e68603e6..c6611ffa5 100644 --- a/test/move_effect_metronome.c +++ b/test/battle/move_effect/metronome.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_mind_blown.c b/test/battle/move_effect/mind_blown.c similarity index 99% rename from test/move_effect_mind_blown.c rename to test/battle/move_effect/mind_blown.c index 19179f990..485f2abd6 100644 --- a/test/move_effect_mind_blown.c +++ b/test/battle/move_effect/mind_blown.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_mirror_move.c b/test/battle/move_effect/mirror_move.c similarity index 99% rename from test/move_effect_mirror_move.c rename to test/battle/move_effect/mirror_move.c index ff21c0efa..b709ffce7 100644 --- a/test/move_effect_mirror_move.c +++ b/test/battle/move_effect/mirror_move.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_paralyze_hit.c b/test/battle/move_effect/paralyze_hit.c similarity index 97% rename from test/move_effect_paralyze_hit.c rename to test/battle/move_effect/paralyze_hit.c index 8e7d259f7..4efe6746c 100644 --- a/test/move_effect_paralyze_hit.c +++ b/test/battle/move_effect/paralyze_hit.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_poison_hit.c b/test/battle/move_effect/poison_hit.c similarity index 97% rename from test/move_effect_poison_hit.c rename to test/battle/move_effect/poison_hit.c index b1a154810..91d700466 100644 --- a/test/move_effect_poison_hit.c +++ b/test/battle/move_effect/poison_hit.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_rampage.c b/test/battle/move_effect/rampage.c similarity index 99% rename from test/move_effect_rampage.c rename to test/battle/move_effect/rampage.c index aa4a002fb..a1a271b37 100644 --- a/test/move_effect_rampage.c +++ b/test/battle/move_effect/rampage.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_recoil_if_miss.c b/test/battle/move_effect/recoil_if_miss.c similarity index 99% rename from test/move_effect_recoil_if_miss.c rename to test/battle/move_effect/recoil_if_miss.c index 51b5fe5e9..c72fd2648 100644 --- a/test/move_effect_recoil_if_miss.c +++ b/test/battle/move_effect/recoil_if_miss.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_reflect.c b/test/battle/move_effect/reflect.c similarity index 99% rename from test/move_effect_reflect.c rename to test/battle/move_effect/reflect.c index 4ea875f35..dd8738f72 100644 --- a/test/move_effect_reflect.c +++ b/test/battle/move_effect/reflect.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_revival_blessing.c b/test/battle/move_effect/revival_blessing.c similarity index 99% rename from test/move_effect_revival_blessing.c rename to test/battle/move_effect/revival_blessing.c index 3cb4d4ca4..a5dd32c19 100644 --- a/test/move_effect_revival_blessing.c +++ b/test/battle/move_effect/revival_blessing.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" // Note: Since these tests are recorded battle, they don't test the right battle controller // behaviors. These have been tested in-game, in double, in multi, and in link battles. AI will always diff --git a/test/move_effect_roar.c b/test/battle/move_effect/roar.c similarity index 98% rename from test/move_effect_roar.c rename to test/battle/move_effect/roar.c index 99256b298..20f934808 100644 --- a/test/move_effect_roar.c +++ b/test/battle/move_effect/roar.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_shell_trap.c b/test/battle/move_effect/shell_trap.c similarity index 99% rename from test/move_effect_shell_trap.c rename to test/battle/move_effect/shell_trap.c index 2498e12f2..7fb80210e 100644 --- a/test/move_effect_shell_trap.c +++ b/test/battle/move_effect/shell_trap.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_sleep.c b/test/battle/move_effect/sleep.c similarity index 97% rename from test/move_effect_sleep.c rename to test/battle/move_effect/sleep.c index c34e1248e..d2cac0af9 100644 --- a/test/move_effect_sleep.c +++ b/test/battle/move_effect/sleep.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_special_attack_down.c b/test/battle/move_effect/special_attack_down.c similarity index 97% rename from test/move_effect_special_attack_down.c rename to test/battle/move_effect/special_attack_down.c index 0511692d4..6e7a84f0a 100644 --- a/test/move_effect_special_attack_down.c +++ b/test/battle/move_effect/special_attack_down.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_special_attack_up_3.c b/test/battle/move_effect/special_attack_up_3.c similarity index 97% rename from test/move_effect_special_attack_up_3.c rename to test/battle/move_effect/special_attack_up_3.c index ad53bedb9..340c59360 100644 --- a/test/move_effect_special_attack_up_3.c +++ b/test/battle/move_effect/special_attack_up_3.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_spikes.c b/test/battle/move_effect/spikes.c similarity index 99% rename from test/move_effect_spikes.c rename to test/battle/move_effect/spikes.c index 0c84a5fb1..bfc0f39b5 100644 --- a/test/move_effect_spikes.c +++ b/test/battle/move_effect/spikes.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_sticky_web.c b/test/battle/move_effect/sticky_web.c similarity index 99% rename from test/move_effect_sticky_web.c rename to test/battle/move_effect/sticky_web.c index f0a6eb8d8..e12cc8090 100644 --- a/test/move_effect_sticky_web.c +++ b/test/battle/move_effect/sticky_web.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_stockpile.c b/test/battle/move_effect/stockpile.c similarity index 99% rename from test/move_effect_stockpile.c rename to test/battle/move_effect/stockpile.c index b16408d49..0e540bb5d 100644 --- a/test/move_effect_stockpile.c +++ b/test/battle/move_effect/stockpile.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" // These tests cover all 3 effects: Stockpile, Spit up and Swallow. ASSUMPTIONS diff --git a/test/move_effect_tailwind.c b/test/battle/move_effect/tailwind.c similarity index 98% rename from test/move_effect_tailwind.c rename to test/battle/move_effect/tailwind.c index 7dfffdbe0..2e9ee5ade 100644 --- a/test/move_effect_tailwind.c +++ b/test/battle/move_effect/tailwind.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_teleport.c b/test/battle/move_effect/teleport.c similarity index 98% rename from test/move_effect_teleport.c rename to test/battle/move_effect/teleport.c index 9c8a16d4b..7544ae8a4 100644 --- a/test/move_effect_teleport.c +++ b/test/battle/move_effect/teleport.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_torment.c b/test/battle/move_effect/torment.c similarity index 98% rename from test/move_effect_torment.c rename to test/battle/move_effect/torment.c index 43f05e29f..dbef1d983 100644 --- a/test/move_effect_torment.c +++ b/test/battle/move_effect/torment.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_toxic.c b/test/battle/move_effect/toxic.c similarity index 98% rename from test/move_effect_toxic.c rename to test/battle/move_effect/toxic.c index f71d2a972..78d978b26 100644 --- a/test/move_effect_toxic.c +++ b/test/battle/move_effect/toxic.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_toxic_spikes.c b/test/battle/move_effect/toxic_spikes.c similarity index 99% rename from test/move_effect_toxic_spikes.c rename to test/battle/move_effect/toxic_spikes.c index 44780da21..3be2d1627 100644 --- a/test/move_effect_toxic_spikes.c +++ b/test/battle/move_effect/toxic_spikes.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_tri_attack.c b/test/battle/move_effect/tri_attack.c similarity index 99% rename from test/move_effect_tri_attack.c rename to test/battle/move_effect/tri_attack.c index 91b465067..aec711aab 100644 --- a/test/move_effect_tri_attack.c +++ b/test/battle/move_effect/tri_attack.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_triple_kick.c b/test/battle/move_effect/triple_kick.c similarity index 97% rename from test/move_effect_triple_kick.c rename to test/battle/move_effect/triple_kick.c index e0a91b011..5ff47c840 100644 --- a/test/move_effect_triple_kick.c +++ b/test/battle/move_effect/triple_kick.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_effect_venoshock.c b/test/battle/move_effect/venoshock.c similarity index 97% rename from test/move_effect_venoshock.c rename to test/battle/move_effect/venoshock.c index 134982a60..b2b8fcda2 100644 --- a/test/move_effect_venoshock.c +++ b/test/battle/move_effect/venoshock.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/move_flag_three_strikes.c b/test/battle/move_flags/three_strikes.c similarity index 98% rename from test/move_flag_three_strikes.c rename to test/battle/move_flags/three_strikes.c index b597b4efe..794df10d3 100644 --- a/test/move_flag_three_strikes.c +++ b/test/battle/move_flags/three_strikes.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Three-strike flag turns a move into a 3-hit move") { diff --git a/test/battle/status1/bad_poison.c b/test/battle/status1/bad_poison.c new file mode 100644 index 000000000..7a430086b --- /dev/null +++ b/test/battle/status1/bad_poison.c @@ -0,0 +1,39 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Bad poison deals 1/16th cumulative damage per turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + for (i = 0; i < 4; i++) + TURN {} + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + for (i = 0; i < 4; i++) + HP_BAR(player, damage: maxHP / 16 * (i + 1)); + } +} + +SINGLE_BATTLE_TEST("Bad poison cumulative damage resets on switch") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN {} + TURN {} + TURN { SWITCH(player, 1); } + TURN { SWITCH(player, 0); } + TURN {} + TURN {} + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + for (i = 0; i < 2; i++) + HP_BAR(player, damage: maxHP / 16 * (i + 1)); + for (i = 0; i < 2; i++) + HP_BAR(player, damage: maxHP / 16 * (i + 1)); + } +} diff --git a/test/battle/status1/burn.c b/test/battle/status1/burn.c new file mode 100644 index 000000000..84439ec9e --- /dev/null +++ b/test/battle/status1/burn.c @@ -0,0 +1,35 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Burn deals 1/16th damage per turn") +{ + GIVEN { + ASSUME(B_BURN_DAMAGE >= GEN_LATEST); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_BURN); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + for (i = 0; i < 4; i++) + TURN {} + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + for (i = 0; i < 4; i++) + HP_BAR(player, damage: maxHP / 16); + } +} + +SINGLE_BATTLE_TEST("Burn reduces Attack by 50%", s16 damage) +{ + bool32 burned; + PARAMETRIZE { burned = FALSE; } + PARAMETRIZE { burned = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { if (burned) Status1(STATUS1_BURN); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} diff --git a/test/battle/status1/freeze.c b/test/battle/status1/freeze.c new file mode 100644 index 000000000..e3c53f790 --- /dev/null +++ b/test/battle/status1/freeze.c @@ -0,0 +1,45 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Freeze has a 20% chance of being thawed") +{ + PASSES_RANDOMLY(20, 100, RNG_FROZEN); + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); } + } SCENE { + STATUS_ICON(player, none: TRUE); + } +} + +SINGLE_BATTLE_TEST("Freeze is thawed by opponent's Fire-type attacks") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_EMBER); MOVE(player, MOVE_CELEBRATE); } + } SCENE { + MESSAGE("Foe Wobbuffet used Ember!"); + MESSAGE("Wobbuffet was defrosted!"); + STATUS_ICON(player, none: TRUE); + } +} + +SINGLE_BATTLE_TEST("Freeze is thawed by user's Flame Wheel") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_FLAME_WHEEL].flags & FLAG_THAW_USER); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FLAME_WHEEL); } + } SCENE { + MESSAGE("Wobbuffet was defrosted by Flame Wheel!"); + STATUS_ICON(player, none: TRUE); + MESSAGE("Wobbuffet used Flame Wheel!"); + } +} diff --git a/test/status_frostbite.c b/test/battle/status1/frostbite.c similarity index 99% rename from test/status_frostbite.c rename to test/battle/status1/frostbite.c index d2bfa6bba..2b61c4d65 100644 --- a/test/status_frostbite.c +++ b/test/battle/status1/frostbite.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Frostbite reduces the special attack by 50 percent") { diff --git a/test/battle/status1/paralysis.c b/test/battle/status1/paralysis.c new file mode 100644 index 000000000..b6edaf263 --- /dev/null +++ b/test/battle/status1/paralysis.c @@ -0,0 +1,44 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Paralysis reduces Speed by 50%") +{ + u16 playerSpeed; + bool32 playerFirst; + PARAMETRIZE { playerSpeed = 98; playerFirst = FALSE; } + PARAMETRIZE { playerSpeed = 102; playerFirst = TRUE; } + GIVEN { + ASSUME(B_PARALYSIS_SPEED >= GEN_7); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); Speed(playerSpeed); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(50); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + if (playerFirst) { + ONE_OF { + MESSAGE("Wobbuffet used Celebrate!"); + MESSAGE("Wobbuffet is paralyzed! It can't move!"); + } + MESSAGE("Foe Wobbuffet used Celebrate!"); + } else { + MESSAGE("Foe Wobbuffet used Celebrate!"); + ONE_OF { + MESSAGE("Wobbuffet used Celebrate!"); + MESSAGE("Wobbuffet is paralyzed! It can't move!"); + } + } + } +} + +SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn") +{ + PASSES_RANDOMLY(25, 100, RNG_PARALYSIS); + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); } + } SCENE { + MESSAGE("Wobbuffet is paralyzed! It can't move!"); + } +} diff --git a/test/battle/status1/poison.c b/test/battle/status1/poison.c new file mode 100644 index 000000000..771d0d2ec --- /dev/null +++ b/test/battle/status1/poison.c @@ -0,0 +1,17 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Poison deals 1/8th damage per turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + for (i = 0; i < 4; i++) + TURN {} + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + for (i = 0; i < 4; i++) + HP_BAR(player, damage: maxHP / 8); + } +} diff --git a/test/battle/status1/sleep.c b/test/battle/status1/sleep.c new file mode 100644 index 000000000..8ef6137d3 --- /dev/null +++ b/test/battle/status1/sleep.c @@ -0,0 +1,23 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move") +{ + u32 turns; + PARAMETRIZE { turns = 1; } + PARAMETRIZE { turns = 2; } + PARAMETRIZE { turns = 3; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP_TURN(turns)); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + for (i = 0; i < turns; i++) + TURN { MOVE(player, MOVE_CELEBRATE); } + } SCENE { + for (i = 0; i < turns - 1; i++) + MESSAGE("Wobbuffet is fast asleep."); + MESSAGE("Wobbuffet woke up!"); + STATUS_ICON(player, none: TRUE); + MESSAGE("Wobbuffet used Celebrate!"); + } +} diff --git a/test/terrain_electric.c b/test/battle/terrain/electric.c similarity index 99% rename from test/terrain_electric.c rename to test/battle/terrain/electric.c index 78f490c42..cebb0cb75 100644 --- a/test/terrain_electric.c +++ b/test/battle/terrain/electric.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Electric Terrain protects grounded battlers from falling asleep") { diff --git a/test/terrain_grassy.c b/test/battle/terrain/grassy.c similarity index 99% rename from test/terrain_grassy.c rename to test/battle/terrain/grassy.c index 21d64f61e..893a30528 100644 --- a/test/terrain_grassy.c +++ b/test/battle/terrain/grassy.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Grassy Terrain recovers 1/16th HP at end of turn") { diff --git a/test/terrain_misty.c b/test/battle/terrain/misty.c similarity index 99% rename from test/terrain_misty.c rename to test/battle/terrain/misty.c index f60122075..dd6489af6 100644 --- a/test/terrain_misty.c +++ b/test/battle/terrain/misty.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Misty Terrain protects grounded battlers from non-volatile status conditions") { diff --git a/test/terrain_psychic.c b/test/battle/terrain/psychic.c similarity index 99% rename from test/terrain_psychic.c rename to test/battle/terrain/psychic.c index f9f95a47d..c810af340 100644 --- a/test/terrain_psychic.c +++ b/test/battle/terrain/psychic.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" SINGLE_BATTLE_TEST("Psychic Terrain protects grounded battlers from priority moves") { diff --git a/test/trainer_control.c b/test/battle/trainer_control.c similarity index 99% rename from test/trainer_control.c rename to test/battle/trainer_control.c index 2a21dcdf6..f5ca68e36 100644 --- a/test/trainer_control.c +++ b/test/battle/trainer_control.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test.h" +#include "test/test.h" #include "battle.h" #include "battle_main.h" #include "data.h" diff --git a/test/battle/type/grass.c b/test/battle/type/grass.c new file mode 100644 index 000000000..b36813ac7 --- /dev/null +++ b/test/battle/type/grass.c @@ -0,0 +1,17 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Grass-type Pokémon block powder and spore moves") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER); + ASSUME(gSpeciesInfo[SPECIES_ODDISH].types[0] == TYPE_GRASS); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ODDISH); + } WHEN { + TURN { MOVE(player, MOVE_STUN_SPORE); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); + MESSAGE("It doesn't affect Foe Oddish…"); + } +} diff --git a/test/weather_rain.c b/test/battle/weather/rain.c similarity index 98% rename from test/weather_rain.c rename to test/battle/weather/rain.c index b99681495..00ac26304 100644 --- a/test/weather_rain.c +++ b/test/battle/weather/rain.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" // Please add Rain interactions with move, item and ability effects on their respective files. ASSUMPTIONS diff --git a/test/weather_snow.c b/test/battle/weather/snow.c similarity index 99% rename from test/weather_snow.c rename to test/battle/weather/snow.c index 93deb1432..1e8cb62cb 100644 --- a/test/weather_snow.c +++ b/test/battle/weather/snow.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" ASSUMPTIONS { diff --git a/test/weather_sunlight.c b/test/battle/weather/sunlight.c similarity index 98% rename from test/weather_sunlight.c rename to test/battle/weather/sunlight.c index beba0e9b6..9d471332a 100644 --- a/test/weather_sunlight.c +++ b/test/battle/weather/sunlight.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test_battle.h" +#include "test/battle.h" // Please add Sunlight interactions with move, item and ability effects on their respective files. ASSUMPTIONS diff --git a/test/powder_moves.c b/test/powder_moves.c deleted file mode 100644 index a0185ad37..000000000 --- a/test/powder_moves.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "global.h" -#include "test_battle.h" - -SINGLE_BATTLE_TEST("Powder Moves are blocked by Grass Types") -{ - GIVEN { - ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER); - ASSUME(gSpeciesInfo[SPECIES_ODDISH].types[0] == TYPE_GRASS); - PLAYER(SPECIES_WYNAUT); - OPPONENT(SPECIES_ODDISH); - } WHEN { - TURN { MOVE(player, MOVE_STUN_SPORE); } - } SCENE { - NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); - MESSAGE("It doesn't affect Foe Oddish…"); - } -} - -SINGLE_BATTLE_TEST("Powder Moves are blocked by Ability Overcoat") -{ - GIVEN { - ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER); - PLAYER(SPECIES_WYNAUT); - OPPONENT(SPECIES_PINECO) { Ability(ABILITY_OVERCOAT); } - } WHEN { - TURN { MOVE(player, MOVE_STUN_SPORE); } - } SCENE { - ABILITY_POPUP(opponent, ABILITY_OVERCOAT); - NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); - MESSAGE("It doesn't affect Foe Pineco…"); - } -} - -SINGLE_BATTLE_TEST("Powder Moves are blocked by Item Safety Goggles") -{ - GIVEN { - ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER); - ASSUME(gItems[ITEM_SAFETY_GOGGLES].holdEffect == HOLD_EFFECT_SAFETY_GOGGLES); - PLAYER(SPECIES_WYNAUT); - OPPONENT(SPECIES_ABRA) { Item(ITEM_SAFETY_GOGGLES); } - } WHEN { - TURN { MOVE(player, MOVE_STUN_SPORE); } - } SCENE { - NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); - MESSAGE("Foe Abra is not affected thanks to its SafetyGoggles!"); - } -} diff --git a/test/random.c b/test/random.c index a56e91a10..202933beb 100644 --- a/test/random.c +++ b/test/random.c @@ -1,5 +1,5 @@ #include "global.h" -#include "test.h" +#include "test/test.h" #include "random.h" TEST("RandomUniform generates lo..hi") diff --git a/test/status1.c b/test/status1.c deleted file mode 100644 index c244b9a60..000000000 --- a/test/status1.c +++ /dev/null @@ -1,193 +0,0 @@ -#include "global.h" -#include "test_battle.h" - -SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move") -{ - u32 turns; - PARAMETRIZE { turns = 1; } - PARAMETRIZE { turns = 2; } - PARAMETRIZE { turns = 3; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP_TURN(turns)); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - for (i = 0; i < turns; i++) - TURN { MOVE(player, MOVE_CELEBRATE); } - } SCENE { - for (i = 0; i < turns - 1; i++) - MESSAGE("Wobbuffet is fast asleep."); - MESSAGE("Wobbuffet woke up!"); - STATUS_ICON(player, none: TRUE); - MESSAGE("Wobbuffet used Celebrate!"); - } -} - -SINGLE_BATTLE_TEST("Poison deals 1/8th damage per turn") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - for (i = 0; i < 4; i++) - TURN {} - } SCENE { - s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); - for (i = 0; i < 4; i++) - HP_BAR(player, damage: maxHP / 8); - } -} - -SINGLE_BATTLE_TEST("Burn deals 1/16th damage per turn") -{ - GIVEN { - ASSUME(B_BURN_DAMAGE >= GEN_LATEST); - PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_BURN); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - for (i = 0; i < 4; i++) - TURN {} - } SCENE { - s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); - for (i = 0; i < 4; i++) - HP_BAR(player, damage: maxHP / 16); - } -} - -SINGLE_BATTLE_TEST("Burn reduces Attack by 50%", s16 damage) -{ - bool32 burned; - PARAMETRIZE { burned = FALSE; } - PARAMETRIZE { burned = TRUE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { if (burned) Status1(STATUS1_BURN); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_TACKLE); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Freeze has a 20% chance of being thawed") -{ - PASSES_RANDOMLY(20, 100, RNG_FROZEN); - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_CELEBRATE); } - } SCENE { - STATUS_ICON(player, none: TRUE); - } -} - -SINGLE_BATTLE_TEST("Freeze is thawed by opponent's Fire-type attacks") -{ - GIVEN { - ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); - PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, MOVE_EMBER); MOVE(player, MOVE_CELEBRATE); } - } SCENE { - MESSAGE("Foe Wobbuffet used Ember!"); - MESSAGE("Wobbuffet was defrosted!"); - STATUS_ICON(player, none: TRUE); - } -} - -SINGLE_BATTLE_TEST("Freeze is thawed by user's Flame Wheel") -{ - GIVEN { - ASSUME(gBattleMoves[MOVE_FLAME_WHEEL].flags & FLAG_THAW_USER); - PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_FLAME_WHEEL); } - } SCENE { - MESSAGE("Wobbuffet was defrosted by Flame Wheel!"); - STATUS_ICON(player, none: TRUE); - MESSAGE("Wobbuffet used Flame Wheel!"); - } -} - -SINGLE_BATTLE_TEST("Paralysis reduces Speed by 50%") -{ - u16 playerSpeed; - bool32 playerFirst; - PARAMETRIZE { playerSpeed = 98; playerFirst = FALSE; } - PARAMETRIZE { playerSpeed = 102; playerFirst = TRUE; } - GIVEN { - ASSUME(B_PARALYSIS_SPEED >= GEN_7); - PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); Speed(playerSpeed); } - OPPONENT(SPECIES_WOBBUFFET) { Speed(50); } - } WHEN { - TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_CELEBRATE); } - } SCENE { - if (playerFirst) { - ONE_OF { - MESSAGE("Wobbuffet used Celebrate!"); - MESSAGE("Wobbuffet is paralyzed! It can't move!"); - } - MESSAGE("Foe Wobbuffet used Celebrate!"); - } else { - MESSAGE("Foe Wobbuffet used Celebrate!"); - ONE_OF { - MESSAGE("Wobbuffet used Celebrate!"); - MESSAGE("Wobbuffet is paralyzed! It can't move!"); - } - } - } -} - -SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn") -{ - PASSES_RANDOMLY(25, 100, RNG_PARALYSIS); - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_CELEBRATE); } - } SCENE { - MESSAGE("Wobbuffet is paralyzed! It can't move!"); - } -} - -SINGLE_BATTLE_TEST("Bad poison deals 1/16th cumulative damage per turn") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - for (i = 0; i < 4; i++) - TURN {} - } SCENE { - s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); - for (i = 0; i < 4; i++) - HP_BAR(player, damage: maxHP / 16 * (i + 1)); - } -} - -SINGLE_BATTLE_TEST("Bad poison cumulative damage resets on switch") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); } - PLAYER(SPECIES_WYNAUT); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN {} - TURN {} - TURN { SWITCH(player, 1); } - TURN { SWITCH(player, 0); } - TURN {} - TURN {} - } SCENE { - s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); - for (i = 0; i < 2; i++) - HP_BAR(player, damage: maxHP / 16 * (i + 1)); - for (i = 0; i < 2; i++) - HP_BAR(player, damage: maxHP / 16 * (i + 1)); - } -} diff --git a/test/test_runner.c b/test/test_runner.c index 3f1352e5a..637ff0ed8 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -6,8 +6,8 @@ #include "main.h" #include "malloc.h" #include "random.h" -#include "test.h" #include "test_runner.h" +#include "test/test.h" #define TIMEOUT_SECONDS 55 diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 4de144c38..e65066a84 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -7,7 +7,7 @@ #include "main.h" #include "malloc.h" #include "random.h" -#include "test_battle.h" +#include "test/battle.h" #include "window.h" #if defined(__INTELLISENSE__) From f493ae8c692b0efb1cda87ab33b5ba10f56e74c7 Mon Sep 17 00:00:00 2001 From: gruxor <95389790+gruxor@users.noreply.github.com> Date: Sat, 12 Aug 2023 18:13:33 -0400 Subject: [PATCH 10/16] Fixed item sprites with palette errors (#3222) --- graphics/items/icons/berserk_gene.png | Bin 356 -> 290 bytes graphics/items/icons/black_augurite.png | Bin 385 -> 315 bytes graphics/items/icons/clear_amulet.png | Bin 434 -> 356 bytes graphics/items/icons/linking_cord.png | Bin 380 -> 325 bytes graphics/items/icons/peat_block.png | Bin 403 -> 326 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/graphics/items/icons/berserk_gene.png b/graphics/items/icons/berserk_gene.png index faac0f67690b8861aa2794e781924c47b26ada27..54007c804e9a993bbe3f0d31c675f69d946d911b 100644 GIT binary patch delta 233 zcmaFDw1{bfgbD{U0|UeLm1j=@DVB6cUq=RpjeRx011AId1_3@Hu3NTjnb@XV|K-4@ zPN0xLNswPK!+%t8AfVeBDA(xe;uxYaap|OsTn!35u6upY#{B*Ne%qukU2Kl*H8&r= znqyVo+@{>H)RAkGK@jhr<-5M?Sk2;aoOjp7>48g6wxXL?MEH~kZ4+O}trU*F7JD`9 zT3kfp{tJ8ds=8Svh87zarn61FCp_6Wz~$|#UX}$cd#9e|vfw(f{^hI%N0x6qzwbP! Y=u7s-_6%ctplcXBUHx3vIVCg!0HT{|Qvd(} delta 316 zcmZ3)^n_`GL_G^L0|SFlq{3++#a`m+>&pI|g^gd0-`{y5A5ciV#5JNMI6tkVJh3R1 z!QICtz|UQwEVW3%SkI)eb;(1Z5{{ArKX;&vF_6vR)u1U4q*&4&eH|GXHuiJ>Nn{1` z4FY^ZT(3;>HZ(MBu5M0>O7e2_S`u~d*_MgEGW7wy2ET!77?Zr+UH&g+|8x|{>F{)M z45^rtoRE+pz{1GLW*`v4#OP|!f05%%pb<-P%z+$HcMf(Yg*gGq3tBd1D!8bv-Lq$p z&QZZ6sfc3&N!wU%$Z=|Ngy6kO3QQ2F|;YLXv}-fq~)r%Co0{6id3JuOkD)#=e^0fs-dz+t&+}1o;Is{6_@`0=k`n zif4JcIEH9UY@OuDcUXakB{HVq&8Pq0%NkuA9A3#4%}DHR6*^>npv|g3o$;p(E6Ym- z20Gs_<8tbw=*8T0OIW?+#-AUMZ2M7jsHy?bNQyc{Ae$TSbnZ3D!FOdeMYy hQ@-zSP=6R-*L|4f=>l=N8lW>7JYD@<);T3K0RR|vZJ+=E delta 310 zcmdnZ)W|$RBAJDmfq_9NQsFd^VlVOZb!C6fBq?NOq4#5o6i`UK#5JNMI6tkVJh3R1 z!QICtz|UQwEVW3%SkI)eb;(1Z5{{ArKX;&vF_6vR)u1U4q*&4&eH|GXHuiJ>No1X9 zXkX8mRlyOWJv}nJ0T$>1avzC z6(9C=aSYLzm^(30sKtQ8+51hDSM=Whx#lm{B*ogv)FwI|m{O;zv*FTW4)OJK?D<~Z1;P`X%l4qWMb-YX#llF8yHsv^J>=jxVS8{H(npSot_tlm>ksU!X zrR!&gUSIOD@^*%4a)9yY?cNO&7VvZ3n(<^C)7${AHHket7RLBVAA0p@pG@9bPGOIT zxnfB&Dt2p+Elyx(+iIwBly~Fn1q*oo{b{`s6EH8^isOX!dDCL9z58$TJO005eBEc? YvJ5uet--Tnf&O9eboFyt=akR{0C?(st^fc4 delta 359 zcmaFDw266wL^2CA0|SFlq{3++#a`m+>&pI|O^R8Fe{PMyEufHiiEBhjaDG}zd16s2 zgS(GQfSyn2+B^)IMe(pdSV<4Nst3gv9NU@|l`Z_W&Z0zU$lgK*J z(7v8A$=luK|5El(M}e}}JY5_^D&{07Ffh3}Dyp)waR`W*nU!%iZIJAhZJjdZszT5q zKP@gEpH~TdizjvoF*64XyEvWfoY?5ed?>IiaDvFCjOKzFQzX(kN|%^B_NKj=B%s}1 zoW|*=lP}V8LvwG|GpB=dxeq>)-7IFhHp-CuOl9JvsHMf3q1z`ZMa+Dp_jS?LUFHgU z6JnMw-1=%F+YDvH?60e?N{9Qg6we6L){9Y9*(#JY@sfJ1=1uP)0eyqNJG8IfSjbYy zaJhT8P;Y=4bMSlFPS*_z39l>_S(1$n4qkEYX*OhVD>sQ$xvm`!^df_&tDnm{r-UW| DF}-}x diff --git a/graphics/items/icons/linking_cord.png b/graphics/items/icons/linking_cord.png index a3f92da04b753298958b4a21333b3732d5ecabb4..079f1d8db3d5eefd52f2da50749185219780e905 100644 GIT binary patch delta 268 zcmeyvbd+g=gbD{U0|UeLm1j=@DVB6cUq=RpjeRx011AId1_3@Hu0VQXn{IuMp4S5) zQ=lZsFPPy!DmW0(?F^J#;_2cTqA{^_VxZ6w1rFz>LPh_j^H*G5IrZyd$yx`dEoo_M zt;%A)laF9N{LSYA5+Kc;xpnS0DQ@{o(s%SK9X6TetaIc8|}` zIr|oE*IaUW$LSTbYpq^cN86Mi+oIZ(d{TK=)JCtj7wuQqv#ej2ajh5VU&pI|Ns3)rv9wNPH&95t#5JNMI6tkVJh3R1 z!QICtz|UQwEVW3%SkI)eb;(1Z5{{ArKX;&vF_6vR)u1U4q*&4&eH|GXHuiJ>Nn{1` z4FY^ZTpJFo2oH~#J#S8FQ3+5?MODkrX`-)8J+r^cEg+vU$=luK|5El(M}eFLo-U3d z6?1&g?-goR;9+?1h3{3=ZSxn>Uw9=<*6^9F<5O9)H~Z*yagilDcc$BO#6+!l{%=pH zO;oU>(Vxe`jp|A3^-_OLePy)FM2vf%;nL~f{XTvSii=S?yYgbWh=j>M7Rwa`e&*Y~$RNJIeuaXOm6r6*z82$I2~~FG h{U3MjdR0`z%=}k_#p7lASD<4VJYD@<);T3K0RS3%dw2i< diff --git a/graphics/items/icons/peat_block.png b/graphics/items/icons/peat_block.png index 9c106598756eeef0a1e141e71fd46d1569c72001..25a3c3bc0568c58fed78723a6a16c3f1c8c0e85b 100644 GIT binary patch delta 251 zcmbQte2i&=gd_(u0|UeLm1j=@DVB6cUq=RpjeRx011C?cwyzf`3GxeO_>T$>1avzC z6)*L4aSYLzxOC!ezGed+XU(JS$Kb@Uye`%MxD`ji) zdw6B#PAcAWyK+kK%9{?^DuE?R7m6ln@iBzHmHox`gJEB8vteFZT(;27a`(y0-tw)_ z4WH(+VzU#w?pyHiya@BRL+ sgJ+&?Uf=OJU;5!MCi#!~yZ;92d+uXXag}+V3v@Aqr>mdKI;Vst0DE6>ZU6uP delta 328 zcmX@cG?{sVL^2CA0|SFlq{3++#a`m+>&pI|Ns2>6TycJyHc&{s#5JNMI6tkVJh3R1 z!QICtz|UQwEVW3%SkI)eb;(1Z5{{ArKX;&vF_6vR)u1U4q*&4&eH|GXHuiJ>No1X9 zXkX8ma6b+9W%wVB- XIF@Kq+j;4t0LZ7Fu6{1-oD!M<%_(<# From b8a22eb1192a0d449ec50c63c8362dd53f9129cf Mon Sep 17 00:00:00 2001 From: PCG <75729017+PCG06@users.noreply.github.com> Date: Sun, 13 Aug 2023 10:23:22 +0530 Subject: [PATCH 11/16] Remove soundMove from Steam Eruption and replace it with sheerForceBoost (#3229) Co-authored-by: PCG06 --- src/data/battle_moves.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index c8c19573f..0902ea1ff 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -9942,7 +9942,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = .priority = 0, .split = SPLIT_SPECIAL, .zMoveEffect = Z_EFFECT_NONE, - .soundMove = TRUE, + .sheerForceBoost = TRUE, .thawsUser = TRUE, .metronomeBanned = TRUE, }, From 32c3c333769403ea14f0679a874551761dda0c3f Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Sun, 13 Aug 2023 19:01:03 -0400 Subject: [PATCH 12/16] Missing master merge organization My bad ^^; --- .../ability/fluffy.c} | 0 test/{ => battle}/damage_formula.c | 0 .../move_effect/axe_kick.c} | 0 .../move_effect/collision_course.c} | 0 .../move_effect/corrosive_gas.c} | 0 .../move_effect/infernal_parade.c} | 0 .../move_effect/make_it_rain.c} | 0 .../move_effect/mortal_spin.c} | 0 .../move_effect/multi_hit.c} | 0 .../move_effect/population_bomb.c} | 0 .../move_effect/spin_out.c} | 0 .../move_effect/take_heart.c} | 0 .../move_effect/teatime.c} | 0 .../move_effect/triple_arrows.c} | 0 .../damages_airborne_double_damage.c | 23 +++++ test/battle/move_flags/damages_underground.c | 23 +++++ test/battle/move_flags/damages_underwater.c | 23 +++++ .../move_flags/minimize_double_damage.c | 27 ++++++ test/status3.c | 90 ------------------- 19 files changed, 96 insertions(+), 90 deletions(-) rename test/{ability_fluffy.c => battle/ability/fluffy.c} (100%) rename test/{ => battle}/damage_formula.c (100%) rename test/{move_effect_axe_kick.c => battle/move_effect/axe_kick.c} (100%) rename test/{move_effect_collision_course.c => battle/move_effect/collision_course.c} (100%) rename test/{move_effect_corrosive_gas.c => battle/move_effect/corrosive_gas.c} (100%) rename test/{move_effect_infernal_parade.c => battle/move_effect/infernal_parade.c} (100%) rename test/{move_effect_make_it_rain.c => battle/move_effect/make_it_rain.c} (100%) rename test/{move_effect_mortal_spin.c => battle/move_effect/mortal_spin.c} (100%) rename test/{move_effect_multi_hit.c => battle/move_effect/multi_hit.c} (100%) rename test/{move_effect_population_bomb.c => battle/move_effect/population_bomb.c} (100%) rename test/{move_effect_spin_out.c => battle/move_effect/spin_out.c} (100%) rename test/{move_effect_take_heart.c => battle/move_effect/take_heart.c} (100%) rename test/{move_effect_teatime.c => battle/move_effect/teatime.c} (100%) rename test/{move_effect_triple_arrows.c => battle/move_effect/triple_arrows.c} (100%) create mode 100644 test/battle/move_flags/damages_airborne_double_damage.c create mode 100644 test/battle/move_flags/damages_underground.c create mode 100644 test/battle/move_flags/damages_underwater.c create mode 100644 test/battle/move_flags/minimize_double_damage.c delete mode 100644 test/status3.c diff --git a/test/ability_fluffy.c b/test/battle/ability/fluffy.c similarity index 100% rename from test/ability_fluffy.c rename to test/battle/ability/fluffy.c diff --git a/test/damage_formula.c b/test/battle/damage_formula.c similarity index 100% rename from test/damage_formula.c rename to test/battle/damage_formula.c diff --git a/test/move_effect_axe_kick.c b/test/battle/move_effect/axe_kick.c similarity index 100% rename from test/move_effect_axe_kick.c rename to test/battle/move_effect/axe_kick.c diff --git a/test/move_effect_collision_course.c b/test/battle/move_effect/collision_course.c similarity index 100% rename from test/move_effect_collision_course.c rename to test/battle/move_effect/collision_course.c diff --git a/test/move_effect_corrosive_gas.c b/test/battle/move_effect/corrosive_gas.c similarity index 100% rename from test/move_effect_corrosive_gas.c rename to test/battle/move_effect/corrosive_gas.c diff --git a/test/move_effect_infernal_parade.c b/test/battle/move_effect/infernal_parade.c similarity index 100% rename from test/move_effect_infernal_parade.c rename to test/battle/move_effect/infernal_parade.c diff --git a/test/move_effect_make_it_rain.c b/test/battle/move_effect/make_it_rain.c similarity index 100% rename from test/move_effect_make_it_rain.c rename to test/battle/move_effect/make_it_rain.c diff --git a/test/move_effect_mortal_spin.c b/test/battle/move_effect/mortal_spin.c similarity index 100% rename from test/move_effect_mortal_spin.c rename to test/battle/move_effect/mortal_spin.c diff --git a/test/move_effect_multi_hit.c b/test/battle/move_effect/multi_hit.c similarity index 100% rename from test/move_effect_multi_hit.c rename to test/battle/move_effect/multi_hit.c diff --git a/test/move_effect_population_bomb.c b/test/battle/move_effect/population_bomb.c similarity index 100% rename from test/move_effect_population_bomb.c rename to test/battle/move_effect/population_bomb.c diff --git a/test/move_effect_spin_out.c b/test/battle/move_effect/spin_out.c similarity index 100% rename from test/move_effect_spin_out.c rename to test/battle/move_effect/spin_out.c diff --git a/test/move_effect_take_heart.c b/test/battle/move_effect/take_heart.c similarity index 100% rename from test/move_effect_take_heart.c rename to test/battle/move_effect/take_heart.c diff --git a/test/move_effect_teatime.c b/test/battle/move_effect/teatime.c similarity index 100% rename from test/move_effect_teatime.c rename to test/battle/move_effect/teatime.c diff --git a/test/move_effect_triple_arrows.c b/test/battle/move_effect/triple_arrows.c similarity index 100% rename from test/move_effect_triple_arrows.c rename to test/battle/move_effect/triple_arrows.c diff --git a/test/battle/move_flags/damages_airborne_double_damage.c b/test/battle/move_flags/damages_airborne_double_damage.c new file mode 100644 index 000000000..5906e3b6b --- /dev/null +++ b/test/battle/move_flags/damages_airborne_double_damage.c @@ -0,0 +1,23 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Being airborne causes the target to take double damage from certain moves", s16 damage) +{ + bool32 useDive; + PARAMETRIZE { useDive = FALSE; } + PARAMETRIZE { useDive = TRUE; } + GIVEN { + ASSUME(gBattleMoves[MOVE_TWISTER].damagesAirborneDoubleDamage); + PLAYER(SPECIES_WOBBUFFET) { Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } + } WHEN { + if (useDive) + TURN { MOVE(opponent, MOVE_FLY); MOVE(player, MOVE_TWISTER); } + else + TURN { MOVE(player, MOVE_TWISTER); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); + } +} diff --git a/test/battle/move_flags/damages_underground.c b/test/battle/move_flags/damages_underground.c new file mode 100644 index 000000000..54af8b829 --- /dev/null +++ b/test/battle/move_flags/damages_underground.c @@ -0,0 +1,23 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Being underground causes the target to take double damage from certain moves", s16 damage) +{ + bool32 useDig; + PARAMETRIZE { useDig = FALSE; } + PARAMETRIZE { useDig = TRUE; } + GIVEN { + ASSUME(gBattleMoves[MOVE_EARTHQUAKE].damagesUnderground); + PLAYER(SPECIES_WOBBUFFET) { Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } + } WHEN { + if (useDig) + TURN { MOVE(opponent, MOVE_DIG); MOVE(player, MOVE_EARTHQUAKE); } + else + TURN { MOVE(player, MOVE_EARTHQUAKE); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); + } +} diff --git a/test/battle/move_flags/damages_underwater.c b/test/battle/move_flags/damages_underwater.c new file mode 100644 index 000000000..e8f45e8de --- /dev/null +++ b/test/battle/move_flags/damages_underwater.c @@ -0,0 +1,23 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Being underwater causes the target to take double damage from certain moves", s16 damage) +{ + bool32 useDive; + PARAMETRIZE { useDive = FALSE; } + PARAMETRIZE { useDive = TRUE; } + GIVEN { + ASSUME(gBattleMoves[MOVE_SURF].damagesUnderwater); + PLAYER(SPECIES_WOBBUFFET) { Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } + } WHEN { + if (useDive) + TURN { MOVE(opponent, MOVE_DIVE); MOVE(player, MOVE_SURF); } + else + TURN { MOVE(player, MOVE_SURF); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); + } +} diff --git a/test/battle/move_flags/minimize_double_damage.c b/test/battle/move_flags/minimize_double_damage.c new file mode 100644 index 000000000..6b631c9a3 --- /dev/null +++ b/test/battle/move_flags/minimize_double_damage.c @@ -0,0 +1,27 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("MinimizeDoubleDamage flag makes moves cause double damage to Minimized targets", s16 damage) +{ + bool32 useMinimize; + PARAMETRIZE { useMinimize = FALSE; } + PARAMETRIZE { useMinimize = TRUE; } + GIVEN { + ASSUME(gBattleMoves[MOVE_MINIMIZE].effect == EFFECT_MINIMIZE); + ASSUME(gBattleMoves[MOVE_STEAMROLLER].minimizeDoubleDamage); + PLAYER(SPECIES_WOBBUFFET) { Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } + } WHEN { + if (useMinimize) + TURN { MOVE(opponent, MOVE_MINIMIZE); MOVE(player, MOVE_STEAMROLLER); } + else + TURN { MOVE(player, MOVE_STEAMROLLER); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); + } +} + +// Remember to add ASSUME(B_MINIMIZE_DMG_ACC >= GEN_6) +TO_DO_BATTLE_TEST("MinimizeDoubleDamage flag allows moves to skip accuracy checks towards Minimized targets") diff --git a/test/status3.c b/test/status3.c deleted file mode 100644 index c8a438451..000000000 --- a/test/status3.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -ASSUMPTIONS { - ASSUME(gBattleMoves[MOVE_MINIMIZE].effect == EFFECT_MINIMIZE); - ASSUME(gBattleMoves[MOVE_STEAMROLLER].minimizeDoubleDamage); - ASSUME(gBattleMoves[MOVE_EARTHQUAKE].damagesUnderground); - ASSUME(gBattleMoves[MOVE_SURF].damagesUnderwater); - ASSUME(gBattleMoves[MOVE_TWISTER].damagesAirborneDoubleDamage); -} - -SINGLE_BATTLE_TEST("Minimize causes the target to take double damage from certain moves", s16 damage) -{ - bool32 useMinimize; - PARAMETRIZE { useMinimize = FALSE; } - PARAMETRIZE { useMinimize = TRUE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Speed(1); } - OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } - } WHEN { - if (useMinimize) - TURN { MOVE(opponent, MOVE_MINIMIZE); MOVE(player, MOVE_STEAMROLLER); } - else - TURN { MOVE(player, MOVE_STEAMROLLER); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Being underground causes the target to take double damage from certain moves", s16 damage) -{ - bool32 useDig; - PARAMETRIZE { useDig = FALSE; } - PARAMETRIZE { useDig = TRUE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Speed(1); } - OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } - } WHEN { - if (useDig) - TURN { MOVE(opponent, MOVE_DIG); MOVE(player, MOVE_EARTHQUAKE); } - else - TURN { MOVE(player, MOVE_EARTHQUAKE); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Being underwater causes the target to take double damage from certain moves", s16 damage) -{ - bool32 useDive; - PARAMETRIZE { useDive = FALSE; } - PARAMETRIZE { useDive = TRUE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Speed(1); } - OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } - } WHEN { - if (useDive) - TURN { MOVE(opponent, MOVE_DIVE); MOVE(player, MOVE_SURF); } - else - TURN { MOVE(player, MOVE_SURF); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Being airborne causes the target to take double damage from certain moves", s16 damage) -{ - bool32 useDive; - PARAMETRIZE { useDive = FALSE; } - PARAMETRIZE { useDive = TRUE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Speed(1); } - OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } - } WHEN { - if (useDive) - TURN { MOVE(opponent, MOVE_FLY); MOVE(player, MOVE_TWISTER); } - else - TURN { MOVE(player, MOVE_TWISTER); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); - } -} From c414a0103c2cafc7175317c07d73402a5bf66af9 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Mon, 14 Aug 2023 09:45:37 -0300 Subject: [PATCH 13/16] Made Cmd_jumpifnopursuitswitchdmg and Cmd_pursuitdoubles use Pursuit's effect ID instead of its move ID (#3231) * Made jumpifnopursuitswitchdmg use Pursuit's effect ID instead of its move ID * Made Cmd_pursuitdoubles use Pursuit's effect ID too, even though it's unused --- src/battle_script_commands.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index e78b81414..373a7004b 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -13643,7 +13643,7 @@ static void Cmd_jumpifnopursuitswitchdmg(void) && !(gBattleMons[gBattlerTarget].status1 & (STATUS1_SLEEP | STATUS1_FREEZE)) && gBattleMons[gBattlerAttacker].hp && !gDisableStructs[gBattlerTarget].truantCounter - && gChosenMoveByBattler[gBattlerTarget] == MOVE_PURSUIT) + && gBattleMoves[gChosenMoveByBattler[gBattlerTarget]].effect == EFFECT_PURSUIT) { s32 i; @@ -13653,7 +13653,7 @@ static void Cmd_jumpifnopursuitswitchdmg(void) gActionsByTurnOrder[i] = B_ACTION_TRY_FINISH; } - gCurrentMove = MOVE_PURSUIT; + gCurrentMove = gChosenMoveByBattler[gBattlerTarget]; gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerTarget); gBattlescriptCurrInstr = cmd->nextInstr; gBattleScripting.animTurn = 1; @@ -15073,10 +15073,10 @@ static void Cmd_pursuitdoubles(void) if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !(gAbsentBattlerFlags & gBitTable[gActiveBattler]) && gChosenActionByBattler[gActiveBattler] == B_ACTION_USE_MOVE - && gChosenMoveByBattler[gActiveBattler] == MOVE_PURSUIT) + && gBattleMoves[gChosenMoveByBattler[gActiveBattler]].effect == EFFECT_PURSUIT) { gActionsByTurnOrder[gActiveBattler] = B_ACTION_TRY_FINISH; - gCurrentMove = MOVE_PURSUIT; + gCurrentMove = gChosenMoveByBattler[gActiveBattler]; gBattlescriptCurrInstr = cmd->nextInstr; gBattleScripting.animTurn = 1; gBattleScripting.savedBattler = gBattlerAttacker; From 2d45b9b8fe44425ba958004dfcb40fb96ae61bb6 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Mon, 14 Aug 2023 17:14:12 -0300 Subject: [PATCH 14/16] Corrected conditionals used for B_SPEED_BUFFING_RAPID_SPIN and I_GEM_BOOST_POWER (#3232) --- data/battle_scripts_1.s | 2 +- src/data/items.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 5f38029a5..55e4cc351 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -5128,7 +5128,7 @@ BattleScript_EffectBatonPass:: goto BattleScript_MoveEnd BattleScript_EffectRapidSpin:: -.if B_SPEED_BUFFING_RAPID_SPIN == GEN_8 +.if B_SPEED_BUFFING_RAPID_SPIN >= GEN_8 attackcanceler accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE attackstring diff --git a/src/data/items.h b/src/data/items.h index 802cd325d..5e15b2734 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -6,7 +6,7 @@ #define EVO_HELD_ITEM_FIELD_FUNC ItemUseOutOfBattle_CannotUse #endif -#if I_GEM_BOOST_POWER >= GEN_5 +#if I_GEM_BOOST_POWER >= GEN_6 #define GEM_BOOST_PARAM 30 #else #define GEM_BOOST_PARAM 50 From dba28cf93f623064e986267de745c196f637efc7 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Mon, 14 Aug 2023 19:10:15 -0400 Subject: [PATCH 15/16] Allow Z-Moves to be selected after Mega Evolution (#3233) * Allow Z-Moves to be selected after Mega Evolution * You cannot do Wish Mega Evolution at all if you hold a z crystal * Rebase to master --- src/battle_util.c | 26 +++++++++++++++----------- src/battle_z_move.c | 10 ---------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index b5fb8732d..91df461f0 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10076,19 +10076,19 @@ bool32 CanMegaEvolve(u8 battlerId) species = GetMonData(mon, MON_DATA_SPECIES); itemId = GetMonData(mon, MON_DATA_HELD_ITEM); +#if DEBUG_BATTLE_MENU == TRUE + if (gBattleStruct->debugHoldEffects[battlerId]) + holdEffect = gBattleStruct->debugHoldEffects[battlerId]; + else +#endif + if (itemId == ITEM_ENIGMA_BERRY_E_READER) + holdEffect = gEnigmaBerries[battlerId].holdEffect; + else + holdEffect = ItemId_GetHoldEffect(itemId); + // Check if there is an entry in the evolution table for regular Mega Evolution. if (GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM) != SPECIES_NONE) { - #if DEBUG_BATTLE_MENU == TRUE - if (gBattleStruct->debugHoldEffects[battlerId]) - holdEffect = gBattleStruct->debugHoldEffects[battlerId]; - else - #endif - if (itemId == ITEM_ENIGMA_BERRY_E_READER) - holdEffect = gEnigmaBerries[battlerId].holdEffect; - else - holdEffect = ItemId_GetHoldEffect(itemId); - // Can Mega Evolve via Mega Stone. if (holdEffect == HOLD_EFFECT_MEGA_STONE) return TRUE; @@ -10096,7 +10096,11 @@ bool32 CanMegaEvolve(u8 battlerId) // Check if there is an entry in the evolution table for Wish Mega Evolution. if (GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != SPECIES_NONE) - return TRUE; + { + // Can't Wish Mega Evolve if holding a Z Crystal. + if (holdEffect != HOLD_EFFECT_Z_CRYSTAL) + return TRUE; + } // No checks passed, the mon CAN'T mega evolve. return FALSE; diff --git a/src/battle_z_move.c b/src/battle_z_move.c index dec3701c0..a24ad8f38 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -159,7 +159,6 @@ void QueueZMove(u8 battlerId, u16 baseMove) bool32 IsViableZMove(u8 battlerId, u16 move) { struct Pokemon *mon; - struct MegaEvolutionData *mega = &(((struct ChooseMoveStruct *)(&gBattleResources->bufferA[gActiveBattler][4]))->mega); u8 battlerPosition = GetBattlerPosition(battlerId); u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battlerId)); u32 item; @@ -185,15 +184,6 @@ bool32 IsViableZMove(u8 battlerId, u16 move) if ((GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battlerId) == B_POSITION_PLAYER_RIGHT)) && !CheckBagHasItem(ITEM_Z_POWER_RING, 1)) return FALSE; - if (mega->alreadyEvolved[battlerPosition]) - return FALSE; // Trainer has mega evolved - - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) - { - if (IsPartnerMonFromSameTrainer(battlerId) && (mega->alreadyEvolved[partnerPosition] || (mega->toEvolve & gBitTable[BATTLE_PARTNER(battlerId)]))) - return FALSE; // Partner has mega evolved or is about to mega evolve - } - #if DEBUG_BATTLE_MENU == TRUE if (gBattleStruct->debugHoldEffects[battlerId]) holdEffect = gBattleStruct->debugHoldEffects[battlerId]; From cd90fdec5d33e9e17849f95a9647722bb3ff230a Mon Sep 17 00:00:00 2001 From: Philipp AUER Date: Thu, 17 Aug 2023 02:21:16 +0200 Subject: [PATCH 16/16] [Critical] fix memory corruption when handling trigger sprites (#3238) Co-authored-by: sbird --- src/battle_interface.c | 9 ++++----- src/battle_z_move.c | 7 +++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/battle_interface.c b/src/battle_interface.c index 3e6029ab7..196e6050f 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -1409,11 +1409,10 @@ bool32 IsMegaTriggerSpriteActive(void) void HideMegaTriggerSprite(void) { - if (gBattleStruct->mega.triggerSpriteId != 0xFF) - { - ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, 0); - gSprites[gBattleStruct->mega.triggerSpriteId].tHide = TRUE; - } + if (gBattleStruct->mega.triggerSpriteId >= MAX_SPRITES) + return; + ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, 0); + gSprites[gBattleStruct->mega.triggerSpriteId].tHide = TRUE; } void HideTriggerSprites(void) diff --git a/src/battle_z_move.c b/src/battle_z_move.c index a24ad8f38..f17de921d 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -345,9 +345,12 @@ bool32 IsZMoveTriggerSpriteActive(void) void HideZMoveTriggerSprite(void) { - struct Sprite *sprite = &gSprites[gBattleStruct->zmove.triggerSpriteId]; - sprite->tHide = TRUE; + struct Sprite *sprite; gBattleStruct->zmove.viable = FALSE; + if (gBattleStruct->zmove.triggerSpriteId >= MAX_SPRITES) + return; + sprite = &gSprites[gBattleStruct->zmove.triggerSpriteId]; + sprite->tHide = TRUE; } static void ShowZMoveTriggerSprite(u8 battlerId)