From b6d3bdf6225f0a61c3adfb41062ab090a03f3dc6 Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Tue, 18 Jun 2024 21:07:40 +0300 Subject: [PATCH 01/26] Confusion, cursed body and poison touch trigger chance fixes and tests (#4831) * accurate confusion chance and a test * Accurate Poison Touch chance and tests * Accurate cursed body chance * Create cursed_body.c --- include/random.h | 2 + src/battle_util.c | 6 +-- test/battle/ability/cursed_body.c | 17 +++++++ test/battle/ability/poison_touch.c | 77 ++++++++++++++++++++++++++++++ test/battle/status2/confusion.c | 28 +++++++++++ 5 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 test/battle/ability/cursed_body.c create mode 100644 test/battle/ability/poison_touch.c create mode 100644 test/battle/status2/confusion.c diff --git a/include/random.h b/include/random.h index 3f47dc1e16..1503814c0e 100644 --- a/include/random.h +++ b/include/random.h @@ -158,6 +158,7 @@ enum RandomTag RNG_ACCURACY, RNG_CONFUSION, RNG_CRITICAL_HIT, + RNG_CURSED_BODY, RNG_CUTE_CHARM, RNG_DAMAGE_MODIFIER, RNG_DIRE_CLAW, @@ -175,6 +176,7 @@ enum RandomTag RNG_METRONOME, RNG_PARALYSIS, RNG_POISON_POINT, + RNG_POISON_TOUCH, RNG_RAMPAGE_TURNS, RNG_SECONDARY_EFFECT, RNG_SECONDARY_EFFECT_2, diff --git a/src/battle_util.c b/src/battle_util.c index 394e34d42e..00a8950c3b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3371,7 +3371,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType) if (gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION) { // confusion dmg - if (RandomWeighted(RNG_CONFUSION, (B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 ? 2 : 1), 1)) + if (RandomPercentage(RNG_CONFUSION, (B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 ? 33 : 50))) { gBattleCommunication[MULTISTRING_CHOOSER] = TRUE; gBattlerTarget = gBattlerAttacker; @@ -5295,7 +5295,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && !IsAbilityOnSide(gBattlerAttacker, ABILITY_AROMA_VEIL) && gBattleMons[gBattlerAttacker].pp[gChosenMovePos] != 0 && !IsDynamaxed(gBattlerAttacker) // TODO: Max Moves don't make contact, useless? - && (Random() % 3) == 0) + && RandomPercentage(RNG_CURSED_BODY, 30)) { gDisableStructs[gBattlerAttacker].disabledMove = gChosenMove; gDisableStructs[gBattlerAttacker].disableTimer = 4; @@ -5749,7 +5749,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker) && TARGET_TURN_DAMAGED // Need to actually hit the target - && (Random() % 3) == 0) + && RandomPercentage(RNG_POISON_TOUCH, 30)) { gBattleScripting.moveEffect = MOVE_EFFECT_POISON; PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); diff --git a/test/battle/ability/cursed_body.c b/test/battle/ability/cursed_body.c new file mode 100644 index 0000000000..20fe659d21 --- /dev/null +++ b/test/battle/ability/cursed_body.c @@ -0,0 +1,17 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Cursed Body triggers 30% of the time") +{ + PASSES_RANDOMLY(3, 10, RNG_CURSED_BODY); + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_AQUA_JET); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_JET, player); + ABILITY_POPUP(opponent, ABILITY_CURSED_BODY); + MESSAGE("Wobbuffet's Aqua Jet was disabled by Foe Frillish's Cursed Body!"); + } +} diff --git a/test/battle/ability/poison_touch.c b/test/battle/ability/poison_touch.c new file mode 100644 index 0000000000..b69fa20444 --- /dev/null +++ b/test/battle/ability/poison_touch.c @@ -0,0 +1,77 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Poison Touch has a 30% chance to poison when attacking with contact moves") +{ + PASSES_RANDOMLY(3, 10, RNG_POISON_TOUCH); + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); + ASSUME(gMovesInfo[MOVE_TACKLE].makesContact); + PLAYER(SPECIES_GRIMER) { Ability(ABILITY_POISON_TOUCH); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + ABILITY_POPUP(player, ABILITY_POISON_TOUCH); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!"); + STATUS_ICON(opponent, poison: TRUE); + } +} + +SINGLE_BATTLE_TEST("Poison Touch only applies when using contact moves") +{ + u32 move; + + PARAMETRIZE { move = MOVE_TACKLE; } + PARAMETRIZE { move = MOVE_SWIFT; } + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].makesContact); + ASSUME(!gMovesInfo[MOVE_SWIFT].makesContact); + PLAYER(SPECIES_GRIMER) { Ability(ABILITY_POISON_TOUCH); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + if (gMovesInfo[move].makesContact) { + ABILITY_POPUP(player, ABILITY_POISON_TOUCH); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!"); + STATUS_ICON(opponent, poison: TRUE); + } else { + NONE_OF { + ABILITY_POPUP(player, ABILITY_POISON_TOUCH); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!"); + STATUS_ICON(opponent, poison: TRUE); + } + } + } +} + +SINGLE_BATTLE_TEST("Poison Touch applies between multi-hit move hits") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_ARM_THRUST].effect == EFFECT_MULTI_HIT); + ASSUME(gMovesInfo[MOVE_ARM_THRUST].makesContact); + ASSUME(gItemsInfo[ITEM_PECHA_BERRY].holdEffect == HOLD_EFFECT_CURE_PSN); + PLAYER(SPECIES_GRIMER) { Ability(ABILITY_POISON_TOUCH); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_PECHA_BERRY); }; + } WHEN { + TURN { MOVE(player, MOVE_ARM_THRUST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ARM_THRUST, player); + ABILITY_POPUP(player, ABILITY_POISON_TOUCH); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!"); + STATUS_ICON(opponent, poison: TRUE); + MESSAGE("Foe Wobbuffet's Pecha Berry cured poison!"); + STATUS_ICON(opponent, poison: FALSE); + ABILITY_POPUP(player, ABILITY_POISON_TOUCH); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!"); + STATUS_ICON(opponent, poison: TRUE); + } +} diff --git a/test/battle/status2/confusion.c b/test/battle/status2/confusion.c new file mode 100644 index 0000000000..3c86e5d555 --- /dev/null +++ b/test/battle/status2/confusion.c @@ -0,0 +1,28 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Confusion adds a 50/33% chance to hit self with 40 power") +{ + s16 damage[2]; + + ASSUME(gMovesInfo[MOVE_TACKLE].power == 40); + + PASSES_RANDOMLY(B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 ? 33 : 50, 100, RNG_CONFUSION); + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(1); }; + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); }; + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE, WITH_RNG(RNG_DAMAGE_MODIFIER, 0)); MOVE(player, MOVE_CONFUSE_RAY); } + TURN; + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + HP_BAR(player, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, player); + MESSAGE("Foe Wobbuffet became confused!"); + MESSAGE("Foe Wobbuffet is confused!"); + MESSAGE("It hurt itself in its confusion!"); + HP_BAR(opponent, captureDamage: &damage[1]); + } THEN { + EXPECT_EQ(damage[0], damage[1]); + } +} From 74f53a7e189480af1dc057a9735a7aee2112ee93 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Tue, 18 Jun 2024 13:31:03 -0500 Subject: [PATCH 02/26] Fix caught wild pokemon not restoring their used held item (#4803) * Fix caught wild pokemon not restoring their used held item * Actually we can use the same struct --- include/battle.h | 2 +- src/battle_main.c | 3 ++- src/battle_script_commands.c | 9 ++++++++- src/battle_util.c | 8 ++++---- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/include/battle.h b/include/battle.h index ef4611869e..13359d0f4f 100644 --- a/include/battle.h +++ b/include/battle.h @@ -733,7 +733,7 @@ struct BattleStruct u16 moveEffect2; // For Knock Off u16 changedSpecies[NUM_BATTLE_SIDES][PARTY_SIZE]; // For forms when multiple mons can change into the same pokemon. u8 quickClawBattlerId; - struct LostItem itemLost[PARTY_SIZE]; // Player's team that had items consumed or stolen (two bytes per party member) + struct LostItem itemLost[NUM_BATTLE_SIDES][PARTY_SIZE]; // Pokemon that had items consumed or stolen (two bytes per party member per side) u8 forcedSwitch:4; // For each battler u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects u8 blunderPolicy:1; // should blunder policy activate diff --git a/src/battle_main.c b/src/battle_main.c index fe36bde552..f5d6487740 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3370,7 +3370,8 @@ static void BattleStartClearSetData(void) { gBattleStruct->usedHeldItems[i][B_SIDE_PLAYER] = 0; gBattleStruct->usedHeldItems[i][B_SIDE_OPPONENT] = 0; - gBattleStruct->itemLost[i].originalItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); + gBattleStruct->itemLost[B_SIDE_PLAYER][i].originalItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); + gBattleStruct->itemLost[B_SIDE_OPPONENT][i].originalItem = GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM); gPartyCriticalHits[i] = 0; gBattleStruct->allowedToChangeFormInWeather[i][B_SIDE_PLAYER] = FALSE; gBattleStruct->allowedToChangeFormInWeather[i][B_SIDE_OPPONENT] = FALSE; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index d60e4b5524..8bf7aa64ca 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7968,7 +7968,7 @@ static void BestowItem(u32 battlerAtk, u32 battlerDef) // Called by Cmd_removeitem. itemId represents the item that was removed, not being given. static bool32 TrySymbiosis(u32 battler, u32 itemId) { - if (!gBattleStruct->itemLost[gBattlerPartyIndexes[battler]].stolen + if (!gBattleStruct->itemLost[B_SIDE_PLAYER][gBattlerPartyIndexes[battler]].stolen && gBattleStruct->changedItems[battler] == ITEM_NONE && GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_EJECT_BUTTON && GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_EJECT_PACK @@ -15259,6 +15259,13 @@ static void Cmd_givecaughtmon(void) { CMD_ARGS(); + if (B_RESTORE_HELD_BATTLE_ITEMS >= GEN_9) + { + u16 lostItem = gBattleStruct->itemLost[B_SIDE_OPPONENT][gBattlerPartyIndexes[GetCatchingBattler()]].originalItem; + if (lostItem != ITEM_NONE && ItemId_GetPocket(lostItem) != POCKET_BERRIES) + SetMonData(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]], MON_DATA_HELD_ITEM, &lostItem); // Restore non-berry items + } + if (GiveMonToPlayer(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]]) != MON_GIVEN_TO_PARTY) { if (!ShouldShowBoxWasFullMessage()) diff --git a/src/battle_util.c b/src/battle_util.c index 00a8950c3b..300cad8630 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10890,9 +10890,9 @@ void TryRestoreHeldItems(void) for (i = 0; i < PARTY_SIZE; i++) { - if (B_RESTORE_HELD_BATTLE_ITEMS >= GEN_9 || gBattleStruct->itemLost[i].stolen) + if (B_RESTORE_HELD_BATTLE_ITEMS >= GEN_9 || gBattleStruct->itemLost[B_SIDE_PLAYER][i].stolen) { - lostItem = gBattleStruct->itemLost[i].originalItem; + lostItem = gBattleStruct->itemLost[B_SIDE_PLAYER][i].originalItem; if (lostItem != ITEM_NONE && ItemId_GetPocket(lostItem) != POCKET_BERRIES) SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &lostItem); // Restore stolen non-berry items } @@ -10947,8 +10947,8 @@ void TrySaveExchangedItem(u32 battler, u16 stolenItem) if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & BATTLE_TYPE_FRONTIER) && GetBattlerSide(battler) == B_SIDE_PLAYER - && stolenItem == gBattleStruct->itemLost[gBattlerPartyIndexes[battler]].originalItem) - gBattleStruct->itemLost[gBattlerPartyIndexes[battler]].stolen = TRUE; + && stolenItem == gBattleStruct->itemLost[B_SIDE_PLAYER][gBattlerPartyIndexes[battler]].originalItem) + gBattleStruct->itemLost[B_SIDE_PLAYER][gBattlerPartyIndexes[battler]].stolen = TRUE; } bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes) From c3b1701c1606af01c982a6900f376ee347c2a73b Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Wed, 19 Jun 2024 13:40:46 +0200 Subject: [PATCH 03/26] Contrary stat drop anim with Spectral Thief (#4824) * Contrary stat drop anim with Spectral Thief * Are you happy now agbcc? I hope I w --- src/battle_script_commands.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 8bf7aa64ca..7bf1dfe7fd 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3584,6 +3584,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) case MOVE_EFFECT_SPECTRAL_THIEF: if (!NoAliveMonsForEitherParty()) { + bool32 contrary = (GetBattlerAbility(gBattlerAttacker) == ABILITY_CONTRARY); gBattleStruct->stolenStats[0] = 0; // Stats to steal. gBattleScripting.animArg1 = 0; for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++) @@ -3605,16 +3606,16 @@ void SetMoveEffect(bool32 primary, bool32 certain) if (gBattleScripting.animArg1 == 0) { if (byTwo) - gBattleScripting.animArg1 = STAT_ANIM_PLUS2 + i; + gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS2 : STAT_ANIM_PLUS2) + i; else - gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + i; + gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS1 : STAT_ANIM_PLUS1) + i; } else { if (byTwo) - gBattleScripting.animArg1 = STAT_ANIM_MULTIPLE_PLUS2; + gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS2 : STAT_ANIM_MULTIPLE_PLUS2); else - gBattleScripting.animArg1 = STAT_ANIM_MULTIPLE_PLUS1; + gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS1 : STAT_ANIM_MULTIPLE_PLUS1); } } } From 3c3142172b0f7c322f6d64d9b384df0adeb1b056 Mon Sep 17 00:00:00 2001 From: Pawkkie <61265402+Pawkkie@users.noreply.github.com> Date: Wed, 19 Jun 2024 18:38:56 -0400 Subject: [PATCH 04/26] Tests point to clear_body.c (#4837) --- test/battle/ability/full_metal_body.c | 4 ++++ test/battle/ability/white_smoke.c | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 test/battle/ability/full_metal_body.c create mode 100644 test/battle/ability/white_smoke.c diff --git a/test/battle/ability/full_metal_body.c b/test/battle/ability/full_metal_body.c new file mode 100644 index 0000000000..3b36f2d1f2 --- /dev/null +++ b/test/battle/ability/full_metal_body.c @@ -0,0 +1,4 @@ +#include "global.h" +#include "test/battle.h" + +// Tests for Full Metal Body are handled in test/battle/ability/clear_body.c diff --git a/test/battle/ability/white_smoke.c b/test/battle/ability/white_smoke.c new file mode 100644 index 0000000000..4cb1687141 --- /dev/null +++ b/test/battle/ability/white_smoke.c @@ -0,0 +1,4 @@ +#include "global.h" +#include "test/battle.h" + +// Tests for White Smoke are handled in test/battle/ability/clear_body.c From 7ae88d84555ee8bb50878132df85e571f1c2a9ca Mon Sep 17 00:00:00 2001 From: Pawkkie <61265402+Pawkkie@users.noreply.github.com> Date: Thu, 20 Jun 2024 07:46:01 -0400 Subject: [PATCH 05/26] Tests for Corrosion, Acrobatics, Hone Claws, Hurricane (#4708) * Tests for Corrosion, Acrobatics, Hone Claws, Hurricane * First review feedback * Re-review feedback * Update acrobatics.c * Review feedback, Sky Drop? * Separate Sky Drop test --- test/battle/ability/corrosion.c | 111 +++++++++++++++- test/battle/ability/minds_eye.c | 4 +- test/battle/ai.c | 30 ++--- test/battle/hold_effect/cure_status.c | 12 +- test/battle/item_effect/cure_status.c | 120 +++++++++--------- .../battle/item_effect/heal_and_cure_status.c | 52 ++++---- test/battle/move_effect/acrobatics.c | 47 ++++++- test/battle/move_effect/attack_accuracy_up.c | 16 ++- test/battle/move_effect/aura_wheel.c | 4 +- test/battle/move_effect/embargo.c | 12 +- test/battle/move_effect/roost.c | 72 +++++------ test/battle/move_effect/strength_sap.c | 10 +- test/battle/move_effect/two_turns_attack.c | 32 ++--- test/battle/move_effect/weather_ball.c | 18 +-- test/battle/move_effects_combined/hurricane.c | 40 +++++- test/battle/weather/sandstorm.c | 4 +- test/battle/weather/snow.c | 4 +- 17 files changed, 391 insertions(+), 197 deletions(-) diff --git a/test/battle/ability/corrosion.c b/test/battle/ability/corrosion.c index 87477ddbcc..8addbd90fa 100644 --- a/test/battle/ability/corrosion.c +++ b/test/battle/ability/corrosion.c @@ -106,23 +106,122 @@ SINGLE_BATTLE_TEST("If a Poison- or Steel-type Pokémon with Corrosion holds a T SINGLE_BATTLE_TEST("If a Poison- or Steel-type Pokémon with Corrosion poisons a target with Synchronize, Synchronize will not poison Poison- or Steel-type Pokémon") { + u16 move; + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISON_POWDER; } GIVEN { ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON); PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); } OPPONENT(SPECIES_ABRA) { Ability(ABILITY_SYNCHRONIZE); } } WHEN { - TURN { MOVE(player, MOVE_TOXIC); } + TURN { MOVE(player, move); } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + ANIMATION(ANIM_TYPE_MOVE, move, player); ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); - STATUS_ICON(opponent, badPoison: TRUE); + if (move == MOVE_TOXIC) + STATUS_ICON(opponent, badPoison: TRUE); + else + STATUS_ICON(opponent, poison: TRUE); NONE_OF { ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, player); STATUS_ICON(player, badPoison: TRUE); + STATUS_ICON(player, poison: TRUE); } } } -TO_DO_BATTLE_TEST("Corrosion cannot bypass moves or Abilities that prevent poisoning, such as Safeguard or Immunity"); -TO_DO_BATTLE_TEST("If the Pokémon with this Ability uses Magic Coat to reflect a status move that inflicts poison, the reflected move will be able to poison Poison- or Steel-type Pokémon."); -TO_DO_BATTLE_TEST("Moves used by a Pokémon with Corrosion that are reflected by Magic Coat or Magic Bounce do not retain the ability to poison Poison- or Steel-type Pokémon.") +SINGLE_BATTLE_TEST("Corrosion cannot bypass moves that prevent poisoning such as Safeguard") +{ + u16 move; + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISON_POWDER; } + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON); + PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SAFEGUARD); MOVE(player, move); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, badPoison: TRUE); + STATUS_ICON(opponent, poison: TRUE); + } + } +} + +SINGLE_BATTLE_TEST("Corrosion cannot bypass abilities that prevent poisoning such as Immunity") +{ + u16 move; + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISON_POWDER; } + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON); + PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); } + OPPONENT(SPECIES_SNORLAX) { Ability(ABILITY_IMMUNITY); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, badPoison: TRUE); + STATUS_ICON(opponent, poison: TRUE); + } + } +} + +SINGLE_BATTLE_TEST("Corrosion allows the Pokémon with the ability to poison a Steel or Poison-type opponent by using Magic Coat") +{ + u16 move; + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISON_POWDER; } + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON); + ASSUME(gMovesInfo[MOVE_MAGIC_COAT].effect == EFFECT_MAGIC_COAT); + PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); } + OPPONENT(SPECIES_BELDUM); + } WHEN { + TURN { MOVE(player, MOVE_MAGIC_COAT); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_COAT, player); + ANIMATION(ANIM_TYPE_MOVE, move, player); // Bounced by Magic Coat + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + if (move == MOVE_TOXIC) + STATUS_ICON(opponent, badPoison: TRUE); + else + STATUS_ICON(opponent, poison: TRUE); + } +} + +SINGLE_BATTLE_TEST("Corrosion's effect is lost if the move used by the Pokémon with the ability is reflected by Magic Coat") +{ + u16 move; + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISON_POWDER; } + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON); + ASSUME(gMovesInfo[MOVE_MAGIC_COAT].effect == EFFECT_MAGIC_COAT); + PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_MAGIC_COAT); MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_COAT, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, move, player); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, player); + if (move == MOVE_TOXIC) + STATUS_ICON(opponent, badPoison: TRUE); + else + STATUS_ICON(opponent, poison: TRUE); + } + } +} diff --git a/test/battle/ability/minds_eye.c b/test/battle/ability/minds_eye.c index 6498f9f098..7799735ee2 100644 --- a/test/battle/ability/minds_eye.c +++ b/test/battle/ability/minds_eye.c @@ -49,8 +49,8 @@ AI_SINGLE_BATTLE_TEST("AI doesn't use accuracy-lowering moves if it knows that t for (j = MOVE_NONE + 1; j < MOVES_COUNT; j++) { if (gMovesInfo[j].effect == EFFECT_ACCURACY_DOWN || gMovesInfo[j].effect == EFFECT_ACCURACY_DOWN_2) { - PARAMETRIZE{ moveAI = j; abilityAI = ABILITY_SWIFT_SWIM; } - PARAMETRIZE{ moveAI = j; abilityAI = ABILITY_MOLD_BREAKER; } + PARAMETRIZE { moveAI = j; abilityAI = ABILITY_SWIFT_SWIM; } + PARAMETRIZE { moveAI = j; abilityAI = ABILITY_MOLD_BREAKER; } } } diff --git a/test/battle/ai.c b/test/battle/ai.c index e5c5a5e76d..31a354b52c 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -422,9 +422,9 @@ AI_DOUBLE_BATTLE_TEST("AI will not use Helping Hand if partner does not have any { u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE; - PARAMETRIZE{ move1 = MOVE_LEER; move2 = MOVE_TOXIC; } - PARAMETRIZE{ move1 = MOVE_HELPING_HAND; move2 = MOVE_PROTECT; } - PARAMETRIZE{ move1 = MOVE_ACUPRESSURE; move2 = MOVE_DOUBLE_TEAM; move3 = MOVE_TOXIC; move4 = MOVE_PROTECT; } + PARAMETRIZE { move1 = MOVE_LEER; move2 = MOVE_TOXIC; } + PARAMETRIZE { move1 = MOVE_HELPING_HAND; move2 = MOVE_PROTECT; } + PARAMETRIZE { move1 = MOVE_ACUPRESSURE; move2 = MOVE_DOUBLE_TEAM; move3 = MOVE_TOXIC; move4 = MOVE_PROTECT; } GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); @@ -451,7 +451,7 @@ AI_DOUBLE_BATTLE_TEST("AI will not use a status move if partner already chose He for (j = MOVE_NONE + 1; j < MOVES_COUNT; j++) { if (gMovesInfo[j].category == DAMAGE_CATEGORY_STATUS) { - PARAMETRIZE{ statusMove = j; } + PARAMETRIZE { statusMove = j; } } } @@ -585,9 +585,9 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will not switch in a Pokemo u32 speedAlakazm; u32 aiSmartSwitchFlags = 0; - PARAMETRIZE{ speedAlakazm = 200; alakazamFirst = TRUE; } // AI will always send out Alakazan as it sees a KO with Focus Blast, even if Alakazam dies before it can get it off - PARAMETRIZE{ speedAlakazm = 200; alakazamFirst = FALSE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES lets AI see that Alakazam would be KO'd before it can KO, and won't switch it in - PARAMETRIZE{ speedAlakazm = 400; alakazamFirst = TRUE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES recognizes that Alakazam is faster and can KO, and will switch it in + PARAMETRIZE { speedAlakazm = 200; alakazamFirst = TRUE; } // AI will always send out Alakazan as it sees a KO with Focus Blast, even if Alakazam dies before it can get it off + PARAMETRIZE { speedAlakazm = 200; alakazamFirst = FALSE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES lets AI see that Alakazam would be KO'd before it can KO, and won't switch it in + PARAMETRIZE { speedAlakazm = 400; alakazamFirst = TRUE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES recognizes that Alakazam is faster and can KO, and will switch it in GIVEN { ASSUME(gMovesInfo[MOVE_PSYCHIC].category == DAMAGE_CATEGORY_SPECIAL); @@ -634,8 +634,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI considers hazard damage whe u32 aiIsSmart = 0; u32 aiSmartSwitchFlags = 0; - PARAMETRIZE{ aiIsSmart = 0; aiSmartSwitchFlags = 0; } // AI doesn't care about hazard damage resulting in Pokemon being KO'd - PARAMETRIZE{ aiIsSmart = 1; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES avoids being KO'd as a result of hazards damage + PARAMETRIZE { aiIsSmart = 0; aiSmartSwitchFlags = 0; } // AI doesn't care about hazard damage resulting in Pokemon being KO'd + PARAMETRIZE { aiIsSmart = 1; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES avoids being KO'd as a result of hazards damage GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags); @@ -656,10 +656,10 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize u32 move2; u32 expectedIndex; - PARAMETRIZE{ expectedIndex = 3; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; } // When not smart, AI will only switch in a defensive mon if it has a SE move, otherwise will just default to damage - PARAMETRIZE{ expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; } - PARAMETRIZE{ expectedIndex = 2; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // When smart, AI will prioritize SE move, but still switch in good type matchup without SE move - PARAMETRIZE{ expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } + PARAMETRIZE { expectedIndex = 3; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; } // When not smart, AI will only switch in a defensive mon if it has a SE move, otherwise will just default to damage + PARAMETRIZE { expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; } + PARAMETRIZE { expectedIndex = 2; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // When smart, AI will prioritize SE move, but still switch in good type matchup without SE move + PARAMETRIZE { expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags); @@ -716,8 +716,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemo { u32 move1; - PARAMETRIZE{ move1 = MOVE_TACKLE; } - PARAMETRIZE{ move1 = MOVE_RAPID_SPIN; } + PARAMETRIZE { move1 = MOVE_TACKLE; } + PARAMETRIZE { move1 = MOVE_RAPID_SPIN; } GIVEN { ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL); diff --git a/test/battle/hold_effect/cure_status.c b/test/battle/hold_effect/cure_status.c index 5f985b803e..37b88c4d07 100644 --- a/test/battle/hold_effect/cure_status.c +++ b/test/battle/hold_effect/cure_status.c @@ -163,12 +163,12 @@ SINGLE_BATTLE_TEST("Berry hold effect cures status if a pokemon enters a battle" u16 status; u16 item; - PARAMETRIZE{ status = STATUS1_BURN; item = ITEM_RAWST_BERRY; } - PARAMETRIZE{ status = STATUS1_FREEZE; item = ITEM_ASPEAR_BERRY; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; item = ITEM_CHERI_BERRY; } - PARAMETRIZE{ status = STATUS1_POISON; item = ITEM_PECHA_BERRY; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; item = ITEM_PECHA_BERRY; } - PARAMETRIZE{ status = STATUS1_SLEEP; item = ITEM_CHESTO_BERRY; } + PARAMETRIZE { status = STATUS1_BURN; item = ITEM_RAWST_BERRY; } + PARAMETRIZE { status = STATUS1_FREEZE; item = ITEM_ASPEAR_BERRY; } + PARAMETRIZE { status = STATUS1_PARALYSIS; item = ITEM_CHERI_BERRY; } + PARAMETRIZE { status = STATUS1_POISON; item = ITEM_PECHA_BERRY; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; item = ITEM_PECHA_BERRY; } + PARAMETRIZE { status = STATUS1_SLEEP; item = ITEM_CHESTO_BERRY; } GIVEN { ASSUME(gItemsInfo[ITEM_RAWST_BERRY].holdEffect == HOLD_EFFECT_CURE_BRN); diff --git a/test/battle/item_effect/cure_status.c b/test/battle/item_effect/cure_status.c index f73bbae327..6c7cc2e64d 100644 --- a/test/battle/item_effect/cure_status.c +++ b/test/battle/item_effect/cure_status.c @@ -112,12 +112,12 @@ SINGLE_BATTLE_TEST("Ice Heal heals a battler from being frozen") SINGLE_BATTLE_TEST("Full Heal heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_FULL_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -134,12 +134,12 @@ SINGLE_BATTLE_TEST("Full Heal heals a battler from any primary status") SINGLE_BATTLE_TEST("Heal Powder heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_HEAL_POWDER].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -156,12 +156,12 @@ SINGLE_BATTLE_TEST("Heal Powder heals a battler from any primary status") SINGLE_BATTLE_TEST("Pewter Crunchies heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_PEWTER_CRUNCHIES].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -178,12 +178,12 @@ SINGLE_BATTLE_TEST("Pewter Crunchies heals a battler from any primary status") SINGLE_BATTLE_TEST("Lava Cookies heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_LAVA_COOKIE].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -200,12 +200,12 @@ SINGLE_BATTLE_TEST("Lava Cookies heals a battler from any primary status") SINGLE_BATTLE_TEST("Rage Candy Bar heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_RAGE_CANDY_BAR].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -222,12 +222,12 @@ SINGLE_BATTLE_TEST("Rage Candy Bar heals a battler from any primary status") SINGLE_BATTLE_TEST("Old Gateu heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_OLD_GATEAU].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -244,12 +244,12 @@ SINGLE_BATTLE_TEST("Old Gateu heals a battler from any primary status") SINGLE_BATTLE_TEST("Casteliacone heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_CASTELIACONE].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -266,12 +266,12 @@ SINGLE_BATTLE_TEST("Casteliacone heals a battler from any primary status") SINGLE_BATTLE_TEST("Lumiose Galette heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_LUMIOSE_GALETTE].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -288,12 +288,12 @@ SINGLE_BATTLE_TEST("Lumiose Galette heals a battler from any primary status") SINGLE_BATTLE_TEST("Shalour Sable heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_SHALOUR_SABLE].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -310,12 +310,12 @@ SINGLE_BATTLE_TEST("Shalour Sable heals a battler from any primary status") SINGLE_BATTLE_TEST("Big Malasada heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_BIG_MALASADA].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } diff --git a/test/battle/item_effect/heal_and_cure_status.c b/test/battle/item_effect/heal_and_cure_status.c index 80719cf231..70dc8322db 100644 --- a/test/battle/item_effect/heal_and_cure_status.c +++ b/test/battle/item_effect/heal_and_cure_status.c @@ -9,13 +9,13 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } - PARAMETRIZE{ status = STATUS1_NONE; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_NONE; } GIVEN { PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); Status1(status); } OPPONENT(SPECIES_WOBBUFFET); @@ -35,13 +35,13 @@ SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary s SINGLE_BATTLE_TEST("Full Restore restores a party members HP and cures any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } - PARAMETRIZE{ status = STATUS1_NONE; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_NONE; } GIVEN { PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); Status1(status); } PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(300); Status1(status); } @@ -64,12 +64,12 @@ SINGLE_BATTLE_TEST("Full Restore restores a party members HP and cures any prima SINGLE_BATTLE_TEST("Full Restore heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { PLAYER(SPECIES_WOBBUFFET) { Status1(status); } OPPONENT(SPECIES_WYNAUT); @@ -86,12 +86,12 @@ SINGLE_BATTLE_TEST("Full Restore heals a battler from any primary status") SINGLE_BATTLE_TEST("Full Restore heals a party member from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WYNAUT) { Status1(status); } diff --git a/test/battle/move_effect/acrobatics.c b/test/battle/move_effect/acrobatics.c index bc34719a42..70953d0958 100644 --- a/test/battle/move_effect/acrobatics.c +++ b/test/battle/move_effect/acrobatics.c @@ -1,5 +1,48 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Acrobatics doubles in power if the user has no held item"); -TO_DO_BATTLE_TEST("Acrobatics still doubles in power when Flying Gem is consumed"); +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_ACROBATICS].effect == EFFECT_ACROBATICS); + ASSUME(gMovesInfo[MOVE_ACROBATICS].type == TYPE_FLYING); +} + +SINGLE_BATTLE_TEST("Acrobatics doubles in power if the user has no held item", s16 damage) +{ + u16 heldItem; + PARAMETRIZE { heldItem = ITEM_POTION; } + PARAMETRIZE { heldItem = ITEM_NONE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(heldItem); } + } WHEN { + TURN { MOVE(opponent, MOVE_ACROBATICS); } + } SCENE { + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Acrobatics still doubles in power when Flying Gem is consumed", s16 damage) +{ + u16 heldItem; + PARAMETRIZE { heldItem = ITEM_NONE; } + PARAMETRIZE { heldItem = ITEM_FLYING_GEM; } + GIVEN { + ASSUME(I_GEM_BOOST_POWER >= GEN_6); + ASSUME(gItemsInfo[ITEM_FLYING_GEM].holdEffect == HOLD_EFFECT_GEMS); + ASSUME(gItemsInfo[ITEM_FLYING_GEM].secondaryId == TYPE_FLYING); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(heldItem); } + } WHEN { + TURN { MOVE(opponent, MOVE_ACROBATICS); } + } SCENE { + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + if (I_GEM_BOOST_POWER >= GEN_6) + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.3), (results[1].damage)); + else + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), (results[1].damage)); + } +} diff --git a/test/battle/move_effect/attack_accuracy_up.c b/test/battle/move_effect/attack_accuracy_up.c index 025a0cb86f..102f4d4d21 100644 --- a/test/battle/move_effect/attack_accuracy_up.c +++ b/test/battle/move_effect/attack_accuracy_up.c @@ -1,4 +1,18 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Hone Claws increases Attack and Accuracy by one stage each"); +SINGLE_BATTLE_TEST("Hone Claws increases Attack and Accuracy by one stage each") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_HONE_CLAWS].effect == EFFECT_ATTACK_ACCURACY_UP); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_HONE_CLAWS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HONE_CLAWS, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Attack rose!"); + MESSAGE("Wobbuffet's accuracy rose!"); + } +} diff --git a/test/battle/move_effect/aura_wheel.c b/test/battle/move_effect/aura_wheel.c index fa4190dbdf..3d601f3583 100644 --- a/test/battle/move_effect/aura_wheel.c +++ b/test/battle/move_effect/aura_wheel.c @@ -10,8 +10,8 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Aura Wheel raises Speed; fails if the user is not Morpeko") { u16 species; - PARAMETRIZE{ species = SPECIES_WOBBUFFET; } - PARAMETRIZE{ species = SPECIES_MORPEKO; } + PARAMETRIZE { species = SPECIES_WOBBUFFET; } + PARAMETRIZE { species = SPECIES_MORPEKO; } GIVEN { PLAYER(species); OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect/embargo.c b/test/battle/move_effect/embargo.c index 628fc44119..a9b1b4523d 100644 --- a/test/battle/move_effect/embargo.c +++ b/test/battle/move_effect/embargo.c @@ -253,8 +253,8 @@ SINGLE_BATTLE_TEST("Embargo disables the effect of the Plate items on the move J { u32 heldItem; - PARAMETRIZE{ heldItem = ITEM_NONE; } - PARAMETRIZE{ heldItem = ITEM_PIXIE_PLATE; } + PARAMETRIZE { heldItem = ITEM_NONE; } + PARAMETRIZE { heldItem = ITEM_PIXIE_PLATE; } GIVEN { PLAYER(SPECIES_ARCEUS) { Item(heldItem); }; OPPONENT(SPECIES_DRAGONITE); @@ -274,8 +274,8 @@ SINGLE_BATTLE_TEST("Embargo disables the effect of the Drive items on the move T { u32 heldItem; - PARAMETRIZE{ heldItem = ITEM_NONE; } - PARAMETRIZE{ heldItem = ITEM_SHOCK_DRIVE; } + PARAMETRIZE { heldItem = ITEM_NONE; } + PARAMETRIZE { heldItem = ITEM_SHOCK_DRIVE; } GIVEN { PLAYER(SPECIES_GENESECT) { Item(heldItem); }; OPPONENT(SPECIES_GYARADOS); @@ -295,8 +295,8 @@ SINGLE_BATTLE_TEST("Embargo disables the effect of the Memory items on the move { u32 heldItem; - PARAMETRIZE{ heldItem = ITEM_NONE; } - PARAMETRIZE{ heldItem = ITEM_FIRE_MEMORY; } + PARAMETRIZE { heldItem = ITEM_NONE; } + PARAMETRIZE { heldItem = ITEM_FIRE_MEMORY; } GIVEN { PLAYER(SPECIES_SILVALLY) { Item(heldItem); }; OPPONENT(SPECIES_VENUSAUR); diff --git a/test/battle/move_effect/roost.c b/test/battle/move_effect/roost.c index 1ae84dd088..b4c0186bf0 100644 --- a/test/battle/move_effect/roost.c +++ b/test/battle/move_effect/roost.c @@ -111,24 +111,24 @@ SINGLE_BATTLE_TEST("Roost suppresses the user's Flying-typing this turn, then re SINGLE_BATTLE_TEST("Roost, if used by a Flying/Flying type, treats the user as a Normal-type (or Typeless in Gen. 4) until the end of the turn") { u32 damagingMove; - PARAMETRIZE{ damagingMove = MOVE_POUND; } - PARAMETRIZE{ damagingMove = MOVE_KARATE_CHOP; } - PARAMETRIZE{ damagingMove = MOVE_GUST; } - PARAMETRIZE{ damagingMove = MOVE_POISON_STING; } - PARAMETRIZE{ damagingMove = MOVE_EARTHQUAKE; } - PARAMETRIZE{ damagingMove = MOVE_ROCK_THROW; } - PARAMETRIZE{ damagingMove = MOVE_LEECH_LIFE; } - PARAMETRIZE{ damagingMove = MOVE_LICK; } - PARAMETRIZE{ damagingMove = MOVE_STEEL_WING; } - PARAMETRIZE{ damagingMove = MOVE_EMBER; } - PARAMETRIZE{ damagingMove = MOVE_WATER_GUN; } - PARAMETRIZE{ damagingMove = MOVE_VINE_WHIP; } - PARAMETRIZE{ damagingMove = MOVE_THUNDER_SHOCK; } - PARAMETRIZE{ damagingMove = MOVE_CONFUSION; } - PARAMETRIZE{ damagingMove = MOVE_ICE_BEAM; } - PARAMETRIZE{ damagingMove = MOVE_DRAGON_BREATH; } - PARAMETRIZE{ damagingMove = MOVE_BITE; } - PARAMETRIZE{ damagingMove = MOVE_DISARMING_VOICE; } + PARAMETRIZE { damagingMove = MOVE_POUND; } + PARAMETRIZE { damagingMove = MOVE_KARATE_CHOP; } + PARAMETRIZE { damagingMove = MOVE_GUST; } + PARAMETRIZE { damagingMove = MOVE_POISON_STING; } + PARAMETRIZE { damagingMove = MOVE_EARTHQUAKE; } + PARAMETRIZE { damagingMove = MOVE_ROCK_THROW; } + PARAMETRIZE { damagingMove = MOVE_LEECH_LIFE; } + PARAMETRIZE { damagingMove = MOVE_LICK; } + PARAMETRIZE { damagingMove = MOVE_STEEL_WING; } + PARAMETRIZE { damagingMove = MOVE_EMBER; } + PARAMETRIZE { damagingMove = MOVE_WATER_GUN; } + PARAMETRIZE { damagingMove = MOVE_VINE_WHIP; } + PARAMETRIZE { damagingMove = MOVE_THUNDER_SHOCK; } + PARAMETRIZE { damagingMove = MOVE_CONFUSION; } + PARAMETRIZE { damagingMove = MOVE_ICE_BEAM; } + PARAMETRIZE { damagingMove = MOVE_DRAGON_BREATH; } + PARAMETRIZE { damagingMove = MOVE_BITE; } + PARAMETRIZE { damagingMove = MOVE_DISARMING_VOICE; } GIVEN { ASSUME(gSpeciesInfo[SPECIES_TORNADUS].types[0] == TYPE_FLYING); @@ -179,24 +179,24 @@ SINGLE_BATTLE_TEST("Roost, if used by a Flying/Flying type, treats the user as a SINGLE_BATTLE_TEST("Roost, if used by a Mystery/Flying type, treats the user as a Mystery/Mystery type until the end of the turn") { u32 damagingMove; - PARAMETRIZE{ damagingMove = MOVE_POUND; } - PARAMETRIZE{ damagingMove = MOVE_KARATE_CHOP; } - PARAMETRIZE{ damagingMove = MOVE_GUST; } - PARAMETRIZE{ damagingMove = MOVE_POISON_STING; } - PARAMETRIZE{ damagingMove = MOVE_EARTHQUAKE; } - PARAMETRIZE{ damagingMove = MOVE_ROCK_THROW; } - PARAMETRIZE{ damagingMove = MOVE_LEECH_LIFE; } - PARAMETRIZE{ damagingMove = MOVE_LICK; } - PARAMETRIZE{ damagingMove = MOVE_STEEL_WING; } - PARAMETRIZE{ damagingMove = MOVE_EMBER; } - PARAMETRIZE{ damagingMove = MOVE_WATER_GUN; } - PARAMETRIZE{ damagingMove = MOVE_VINE_WHIP; } - PARAMETRIZE{ damagingMove = MOVE_THUNDER_SHOCK; } - PARAMETRIZE{ damagingMove = MOVE_CONFUSION; } - PARAMETRIZE{ damagingMove = MOVE_ICE_BEAM; } - PARAMETRIZE{ damagingMove = MOVE_DRAGON_BREATH; } - PARAMETRIZE{ damagingMove = MOVE_BITE; } - PARAMETRIZE{ damagingMove = MOVE_DISARMING_VOICE; } + PARAMETRIZE { damagingMove = MOVE_POUND; } + PARAMETRIZE { damagingMove = MOVE_KARATE_CHOP; } + PARAMETRIZE { damagingMove = MOVE_GUST; } + PARAMETRIZE { damagingMove = MOVE_POISON_STING; } + PARAMETRIZE { damagingMove = MOVE_EARTHQUAKE; } + PARAMETRIZE { damagingMove = MOVE_ROCK_THROW; } + PARAMETRIZE { damagingMove = MOVE_LEECH_LIFE; } + PARAMETRIZE { damagingMove = MOVE_LICK; } + PARAMETRIZE { damagingMove = MOVE_STEEL_WING; } + PARAMETRIZE { damagingMove = MOVE_EMBER; } + PARAMETRIZE { damagingMove = MOVE_WATER_GUN; } + PARAMETRIZE { damagingMove = MOVE_VINE_WHIP; } + PARAMETRIZE { damagingMove = MOVE_THUNDER_SHOCK; } + PARAMETRIZE { damagingMove = MOVE_CONFUSION; } + PARAMETRIZE { damagingMove = MOVE_ICE_BEAM; } + PARAMETRIZE { damagingMove = MOVE_DRAGON_BREATH; } + PARAMETRIZE { damagingMove = MOVE_BITE; } + PARAMETRIZE { damagingMove = MOVE_DISARMING_VOICE; } GIVEN { ASSUME(gSpeciesInfo[SPECIES_MOLTRES].types[0] == TYPE_FIRE); diff --git a/test/battle/move_effect/strength_sap.c b/test/battle/move_effect/strength_sap.c index 000f5ff41a..0b055edb12 100644 --- a/test/battle/move_effect/strength_sap.c +++ b/test/battle/move_effect/strength_sap.c @@ -10,8 +10,8 @@ SINGLE_BATTLE_TEST("Strength Sap lowers Attack by 1 and restores HP based on tar { u32 atkStat = 0; - PARAMETRIZE{ atkStat = 100; } - PARAMETRIZE{ atkStat = 50; } + PARAMETRIZE { atkStat = 100; } + PARAMETRIZE { atkStat = 50; } GIVEN { PLAYER(SPECIES_WOBBUFFET) { HP(200); } @@ -35,8 +35,8 @@ SINGLE_BATTLE_TEST("Strength Sap works exactly the same when attacker is behind { u32 atkStat = 0; - PARAMETRIZE{ atkStat = 100; } - PARAMETRIZE{ atkStat = 50; } + PARAMETRIZE { atkStat = 100; } + PARAMETRIZE { atkStat = 50; } GIVEN { PLAYER(SPECIES_WOBBUFFET) { HP(200); } @@ -65,7 +65,7 @@ SINGLE_BATTLE_TEST("Strength Sap lowers Attack by 1 and restores HP based on tar for (j = 0; j <= MAX_STAT_STAGE; j++) { if (j == DEFAULT_STAT_STAGE - 1) { continue; } // Ignore -6, because Strength Sap won't work otherwise - PARAMETRIZE{ statStage = j; } + PARAMETRIZE { statStage = j; } } GIVEN { diff --git a/test/battle/move_effect/two_turns_attack.c b/test/battle/move_effect/two_turns_attack.c index 9225afb48e..4b7adc6f81 100644 --- a/test/battle/move_effect/two_turns_attack.c +++ b/test/battle/move_effect/two_turns_attack.c @@ -243,8 +243,8 @@ SINGLE_BATTLE_TEST("Solar Beam and Solar Blade can be used instantly in Sunlight SINGLE_BATTLE_TEST("Solar Beam's power is halved in Rain", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_RAIN_DANCE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_RAIN_DANCE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -261,8 +261,8 @@ SINGLE_BATTLE_TEST("Solar Beam's power is halved in Rain", s16 damage) SINGLE_BATTLE_TEST("Solar Blade's power is halved in Rain", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_RAIN_DANCE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_RAIN_DANCE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WYNAUT); @@ -279,8 +279,8 @@ SINGLE_BATTLE_TEST("Solar Blade's power is halved in Rain", s16 damage) SINGLE_BATTLE_TEST("Solar Beam's power is halved in a Sandstorm", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SANDSTORM; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SANDSTORM; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; @@ -297,8 +297,8 @@ SINGLE_BATTLE_TEST("Solar Beam's power is halved in a Sandstorm", s16 damage) SINGLE_BATTLE_TEST("Solar Blade's power is halved in a Sandstorm", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SANDSTORM; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SANDSTORM; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; @@ -315,8 +315,8 @@ SINGLE_BATTLE_TEST("Solar Blade's power is halved in a Sandstorm", s16 damage) SINGLE_BATTLE_TEST("Solar Beam's power is halved in Hail", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_HAIL; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_HAIL; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; @@ -333,8 +333,8 @@ SINGLE_BATTLE_TEST("Solar Beam's power is halved in Hail", s16 damage) SINGLE_BATTLE_TEST("Solar Blade's power is halved in Hail", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_HAIL; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_HAIL; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; @@ -351,8 +351,8 @@ SINGLE_BATTLE_TEST("Solar Blade's power is halved in Hail", s16 damage) SINGLE_BATTLE_TEST("Solar Beam's power is halved in Snow", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SNOWSCAPE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -369,8 +369,8 @@ SINGLE_BATTLE_TEST("Solar Beam's power is halved in Snow", s16 damage) SINGLE_BATTLE_TEST("Solar Blade's power is halved in Snow", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SNOWSCAPE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WYNAUT); diff --git a/test/battle/move_effect/weather_ball.c b/test/battle/move_effect/weather_ball.c index 38c902f507..1656b60d87 100644 --- a/test/battle/move_effect/weather_ball.c +++ b/test/battle/move_effect/weather_ball.c @@ -9,8 +9,8 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Fire-type move in Sunlight", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SUNNY_DAY; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SUNNY_DAY; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_MEGANIUM); @@ -27,8 +27,8 @@ SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Fire-type move SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Water-type move in Rain", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_RAIN_DANCE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_RAIN_DANCE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_ARCANINE); @@ -45,8 +45,8 @@ SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Water-type mov SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Rock-type move in a Sandstorm", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SANDSTORM; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SANDSTORM; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_MAGMAR) { Item(ITEM_SAFETY_GOGGLES); }; @@ -63,9 +63,9 @@ SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Rock-type move SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to an Ice-type move in Hail and Snow", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_HAIL; } - PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_HAIL; } + PARAMETRIZE { move = MOVE_SNOWSCAPE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_DRAGONAIR) { Item(ITEM_SAFETY_GOGGLES); }; diff --git a/test/battle/move_effects_combined/hurricane.c b/test/battle/move_effects_combined/hurricane.c index eab843e720..61acac6649 100644 --- a/test/battle/move_effects_combined/hurricane.c +++ b/test/battle/move_effects_combined/hurricane.c @@ -32,4 +32,42 @@ SINGLE_BATTLE_TEST("Hurricane bypasses accuracy checks in Rain") NONE_OF { MESSAGE("Wobbuffet's attack missed!"); } } } -TO_DO_BATTLE_TEST("Hurricane Veil can hit airborne targets") // Fly, Bounce, Sky Drop + +SINGLE_BATTLE_TEST("Hurricane can hit airborne targets (Fly, Bounce)") +{ + u16 move; + PARAMETRIZE { move = MOVE_FLY; } + PARAMETRIZE { move = MOVE_BOUNCE; } + GIVEN { + ASSUME(gMovesInfo[MOVE_FLY].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_FLY].argument)) == STATUS3_ON_AIR); + ASSUME(gMovesInfo[MOVE_BOUNCE].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_BOUNCE].argument)) == STATUS3_ON_AIR); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(move); } + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_HURRICANE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HURRICANE, player); + NONE_OF { MESSAGE("Wobbuffet's attack missed!"); } + } +} + +DOUBLE_BATTLE_TEST("Hurricane can hit airborne targets (Sky Drop)") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_SKY_DROP].effect == EFFECT_SKY_DROP); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_SKY_DROP].argument)) == STATUS3_ON_AIR); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_SKY_DROP, target: opponentLeft); MOVE(playerRight, MOVE_HURRICANE, target: playerLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HURRICANE, playerRight); + NONE_OF { MESSAGE("Wobbuffet's attack missed!"); } + } +} diff --git a/test/battle/weather/sandstorm.c b/test/battle/weather/sandstorm.c index 5d7a6f1713..2f3f4e7ca2 100644 --- a/test/battle/weather/sandstorm.c +++ b/test/battle/weather/sandstorm.c @@ -20,8 +20,8 @@ SINGLE_BATTLE_TEST("Sandstorm deals 1/16 damage per turn") SINGLE_BATTLE_TEST("Sandstorm multiplies the special defense of Rock-types by 1.5x", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_SANDSTORM; } - PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SANDSTORM; } + PARAMETRIZE { move = MOVE_CELEBRATE; } GIVEN { ASSUME(gMovesInfo[MOVE_SWIFT].category == DAMAGE_CATEGORY_SPECIAL); PLAYER(SPECIES_WOBBUFFET) ; diff --git a/test/battle/weather/snow.c b/test/battle/weather/snow.c index c617f651c4..6ce3e1e0f2 100644 --- a/test/battle/weather/snow.c +++ b/test/battle/weather/snow.c @@ -13,8 +13,8 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Snow multiplies the defense of Ice-types by 1.5x", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_SNOWSCAPE; } - PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SNOWSCAPE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_GLALIE); From c31f982a7b229383cc3802eb6b2de7c3040c3277 Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Fri, 21 Jun 2024 09:15:00 +0300 Subject: [PATCH 06/26] Fix fury cutter scaling up to 640 power (#4846) * Fix fury cutter scaling up to 640 power * Replace three instances of int i with one int turn * dynamic test --- src/battle_script_commands.c | 11 +++++++- test/battle/move_effect/fury_cutter.c | 38 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 test/battle/move_effect/fury_cutter.c diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 7bf1dfe7fd..0d57f6e3be 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -13328,7 +13328,16 @@ static void Cmd_handlefurycutter(void) } else { - if (gDisableStructs[gBattlerAttacker].furyCutterCounter != 5 + u32 max; + + if (B_UPDATED_MOVE_DATA >= GEN_6) + max = 3; + else if (B_UPDATED_MOVE_DATA == GEN_5) + max = 4; + else + max = 5; + + if (gDisableStructs[gBattlerAttacker].furyCutterCounter < max && gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT) // Don't increment counter on first hit gDisableStructs[gBattlerAttacker].furyCutterCounter++; diff --git a/test/battle/move_effect/fury_cutter.c b/test/battle/move_effect/fury_cutter.c new file mode 100644 index 0000000000..cf3871964e --- /dev/null +++ b/test/battle/move_effect/fury_cutter.c @@ -0,0 +1,38 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_FURY_CUTTER].effect == EFFECT_FURY_CUTTER); +} + +SINGLE_BATTLE_TEST("Fury Cutter power doubles with each use, up to 160 power") +{ + s16 damage[6]; + int turn; + int maxTurns; + + if (B_UPDATED_MOVE_DATA >= GEN_6) + maxTurns = 4; + else if (B_UPDATED_MOVE_DATA == GEN_5) + maxTurns = 5; + else + maxTurns = 6; + + GIVEN { + PLAYER(SPECIES_CROBAT); + OPPONENT(SPECIES_LINOONE) { HP(900); } + } WHEN { + for (turn = 0; turn < maxTurns; turn++) + TURN { MOVE(player, MOVE_FURY_CUTTER); } + } SCENE { + for (turn = 0; turn < maxTurns; turn++) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_CUTTER, player); + HP_BAR(opponent, captureDamage: &damage[turn]); + } + } THEN { + for (turn = 1; turn < maxTurns - 1; turn++) + EXPECT_MUL_EQ(damage[turn - 1], UQ_4_12(2.0), damage[turn]); + EXPECT_EQ(damage[maxTurns - 2], damage[maxTurns - 1]); + } +} From d0bb03e5a9998a30feebedd2d49860631f8e6d1e Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 21 Jun 2024 12:17:12 +0200 Subject: [PATCH 07/26] Fix AI's right slot switching-in too early (#4851) * apply safe switching to trainer doubles battles - with a config * get rid of the config * make it compile --- data/battle_scripts_1.s | 18 +++++++++--------- include/constants/battle_script_commands.h | 4 ++-- src/battle_script_commands.c | 20 +++++++++++++------- src/battle_util.c | 4 ++-- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 33b3095953..32015580b8 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -5407,7 +5407,7 @@ BattleScript_GiveExp:: BattleScript_HandleFaintedMon:: setbyte sSHIFT_SWITCHED, 0 - checkteamslost BattleScript_LinkHandleFaintedMonMultiple + checkteamslost BattleScript_HandleFaintedMonMultiple jumpifbyte CMP_NOT_EQUAL, gBattleOutcome, 0, BattleScript_FaintedMonEnd jumpifbattletype BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE, BattleScript_FaintedMonTryChoose jumpifword CMP_NO_COMMON_BITS, gHitMarker, HITMARKER_PLAYER_FAINTED, BattleScript_FaintedMonTryChoose @@ -5488,13 +5488,13 @@ BattleScript_FaintedMonShiftSwitched: copybyte gBattlerTarget, sSAVED_BATTLER goto BattleScript_FaintedMonSendOutNewEnd -BattleScript_LinkHandleFaintedMonMultiple:: - openpartyscreen BS_FAINTED_LINK_MULTIPLE_1, BattleScript_LinkHandleFaintedMonMultipleStart -BattleScript_LinkHandleFaintedMonMultipleStart:: +BattleScript_HandleFaintedMonMultiple:: + openpartyscreen BS_FAINTED_MULTIPLE_1, BattleScript_HandleFaintedMonMultipleStart +BattleScript_HandleFaintedMonMultipleStart:: switchhandleorder BS_FAINTED, 0 - openpartyscreen BS_FAINTED_LINK_MULTIPLE_2, BattleScript_LinkHandleFaintedMonMultipleEnd + openpartyscreen BS_FAINTED_MULTIPLE_2, BattleScript_HandleFaintedMonMultipleEnd switchhandleorder BS_FAINTED, 0 -BattleScript_LinkHandleFaintedMonLoop:: +BattleScript_HandleFaintedMonLoop:: switchhandleorder BS_FAINTED, 3 drawpartystatussummary BS_FAINTED getswitchedmondata BS_FAINTED @@ -5506,9 +5506,9 @@ BattleScript_LinkHandleFaintedMonLoop:: hidepartystatussummary BS_FAINTED switchinanim BS_FAINTED, FALSE waitstate - switchineffects BS_FAINTED_LINK_MULTIPLE_1 - jumpifbytenotequal gBattlerFainted, gBattlersCount, BattleScript_LinkHandleFaintedMonLoop -BattleScript_LinkHandleFaintedMonMultipleEnd:: + switchineffects BS_FAINTED_MULTIPLE_1 + jumpifbytenotequal gBattlerFainted, gBattlersCount, BattleScript_HandleFaintedMonLoop +BattleScript_HandleFaintedMonMultipleEnd:: end2 BattleScript_LocalTrainerBattleWon:: diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 6fdbcc9e96..10e7dfea51 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -63,8 +63,8 @@ #define BS_EFFECT_BATTLER 2 #define BS_FAINTED 3 #define BS_ATTACKER_WITH_PARTNER 4 // for Cmd_updatestatusicon -#define BS_FAINTED_LINK_MULTIPLE_1 5 // for openpartyscreen -#define BS_FAINTED_LINK_MULTIPLE_2 6 // for openpartyscreen +#define BS_FAINTED_MULTIPLE_1 5 // for openpartyscreen +#define BS_FAINTED_MULTIPLE_2 6 // for openpartyscreen #define BS_BATTLER_0 7 #define BS_ATTACKER_SIDE 8 // for Cmd_jumpifability #define BS_TARGET_SIDE 9 // for Cmd_jumpifability diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0d57f6e3be..2aa68b896d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -4633,7 +4633,7 @@ bool32 NoAliveMonsForEitherParty(void) return (NoAliveMonsForPlayer() || NoAliveMonsForOpponent()); } -// For battles that aren't BATTLE_TYPE_LINK or BATTLE_TYPE_RECORDED_LINK, the only thing this +// For battles that aren't BATTLE_TYPE_LINK or BATTLE_TYPE_RECORDED_LINK or double trainer battles, the only thing this // command does is check whether the player has won/lost by totaling each team's HP. It then // sets gBattleOutcome accordingly, if necessary. static void Cmd_checkteamslost(void) @@ -4649,10 +4649,16 @@ static void Cmd_checkteamslost(void) if (NoAliveMonsForOpponent()) gBattleOutcome |= B_OUTCOME_WON; - // For link battles that haven't ended, count number of empty battler spots - // In link multi battles, jump to pointer if more than 1 spot empty + // Fair switching - everyone has to switch in most at the same time, without knowing which pokemon the other trainer selected. + // In vanilla Emerald this was only used for link battles, in expansion it's also used for regular trainers in double battles. + // For battles that haven't ended, count number of empty battler spots + // In multi battles, jump to pointer if more than 1 spot empty // In non-multi battles, jump to pointer if 1 spot is missing on both sides - if (gBattleOutcome == 0 && (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))) + if (gBattleOutcome == 0 + && (((gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))) + || ((gBattleTypeFlags & BATTLE_TYPE_TRAINER) && (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + ) + ) { s32 i, emptyPlayerSpots, emptyOpponentSpots; @@ -6769,7 +6775,7 @@ static void Cmd_openpartyscreen(void) u32 i, battler = 0; const u8 *failInstr = cmd->failInstr; - if (cmd->battler == BS_FAINTED_LINK_MULTIPLE_1) + if (cmd->battler == BS_FAINTED_MULTIPLE_1) { if ((gBattleTypeFlags & BATTLE_TYPE_MULTI) || !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) { @@ -6924,7 +6930,7 @@ static void Cmd_openpartyscreen(void) } gBattlescriptCurrInstr = cmd->nextInstr; } - else if (cmd->battler == BS_FAINTED_LINK_MULTIPLE_2) + else if (cmd->battler == BS_FAINTED_MULTIPLE_2) { if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { @@ -7307,7 +7313,7 @@ static void Cmd_switchineffects(void) gBattleStruct->hpOnSwitchout[GetBattlerSide(i)] = gBattleMons[i].hp; } - if (cmd->battler == BS_FAINTED_LINK_MULTIPLE_1) + if (cmd->battler == BS_FAINTED_MULTIPLE_1) { u32 hitmarkerFaintBits = gHitMarker >> 28; diff --git a/src/battle_util.c b/src/battle_util.c index 300cad8630..7c8690ff01 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -958,11 +958,11 @@ u8 GetBattlerForBattleScript(u8 caseId) case BS_FAINTED: ret = gBattlerFainted; break; - case BS_FAINTED_LINK_MULTIPLE_1: + case BS_FAINTED_MULTIPLE_1: ret = gBattlerFainted; break; case BS_ATTACKER_WITH_PARTNER: - case BS_FAINTED_LINK_MULTIPLE_2: + case BS_FAINTED_MULTIPLE_2: case BS_ATTACKER_SIDE: case BS_TARGET_SIDE: case BS_PLAYER1: From 438f478146b114d2da581320f211e906d6827987 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 21 Jun 2024 12:19:43 +0200 Subject: [PATCH 08/26] Fix comatose transform gastro acid interaction (#4852) --- src/battle_util.c | 7 +++++ test/battle/ability/comatose.c | 54 ++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 test/battle/ability/comatose.c diff --git a/src/battle_util.c b/src/battle_util.c index 7c8690ff01..7b1cde1474 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6185,7 +6185,14 @@ u32 GetBattlerAbility(u32 battler) bool32 noAbilityShield = GetBattlerHoldEffectIgnoreAbility(battler, TRUE) != HOLD_EFFECT_ABILITY_SHIELD; if (gAbilitiesInfo[gBattleMons[battler].ability].cantBeSuppressed) + { + // Edge case: pokemon under the effect of gastro acid transforms into a pokemon with Comatose (Todo: verify how other unsuppressable abilities behave) + if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED + && gStatuses3[battler] & STATUS3_GASTRO_ACID + && gBattleMons[battler].ability == ABILITY_COMATOSE) + return ABILITY_NONE; return gBattleMons[battler].ability; + } if (gStatuses3[battler] & STATUS3_GASTRO_ACID) return ABILITY_NONE; diff --git a/test/battle/ability/comatose.c b/test/battle/ability/comatose.c new file mode 100644 index 0000000000..bd991c258e --- /dev/null +++ b/test/battle/ability/comatose.c @@ -0,0 +1,54 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Comatose prevents status-inducing moves") +{ + u32 move; + + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISONPOWDER; } + PARAMETRIZE { move = MOVE_SLEEP_POWDER; } + PARAMETRIZE { move = MOVE_THUNDER_WAVE; } + + GIVEN { + PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); } + } SCENE { + MESSAGE("Komala is drowsing!"); + + NOT ANIMATION(ANIM_TYPE_MOVE, move, opponent); + ABILITY_POPUP(player, ABILITY_COMATOSE); + MESSAGE("It doesn't affect Komala…"); + } +} + +SINGLE_BATTLE_TEST("Comatose may be suppressed if pokemon transformed into a pokemon with Comatose ability and was under the effects of Gastro Acid") +{ + u32 move; + + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISONPOWDER; } + PARAMETRIZE { move = MOVE_SLEEP_POWDER; } + PARAMETRIZE { move = MOVE_THUNDER_WAVE; } + + GIVEN { + PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); Speed(30); } + OPPONENT(SPECIES_DITTO) { Speed(20); } + } WHEN { + TURN { MOVE(player, MOVE_GASTRO_ACID); MOVE(opponent, MOVE_TRANSFORM); } + TURN { MOVE(player, move); } + } SCENE { + MESSAGE("Komala is drowsing!"); + MESSAGE("Komala used Gastro Acid!"); + MESSAGE("Foe Ditto used Transform!"); + MESSAGE("Foe Ditto transformed into Komala!"); + + ANIMATION(ANIM_TYPE_MOVE, move, player); + if (move == MOVE_POISONPOWDER) { STATUS_ICON(opponent, poison: TRUE); } + else if (move == MOVE_TOXIC) { STATUS_ICON(opponent, badPoison: TRUE); } + else if (move == MOVE_THUNDER_WAVE) { STATUS_ICON(opponent, paralysis: TRUE); } + else if (move == MOVE_SLEEP_POWDER) { STATUS_ICON(opponent, sleep: TRUE); } + } +} From 9f7a83fd6a595772d68b8c3ebeb88703de8d23c2 Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Fri, 21 Jun 2024 12:21:29 +0200 Subject: [PATCH 09/26] Pret merge (20th of June) (#4845) * Don't assume bash is at /bin/bash * Add NixOS install instructions * Add support for LF line endings to gbagfx --------- Co-authored-by: leo60228 Co-authored-by: Martin Griffin Co-authored-by: Alex Nash --- INSTALL.md | 7 +++++++ Makefile | 2 +- asmdiff.sh | 2 +- tools/gbagfx/jasc_pal.c | 8 ++++++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index fe8ea2e391..ed7d5bad08 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -475,6 +475,13 @@ devkitARM is now installed. Then proceed to [Choosing where to store pokeemerald Expansion (Linux)](#choosing-where-to-store-pokeemerald-expansion-linux). +### NixOS +Run the following command to start an interactive shell with the necessary packages: +```bash +nix-shell -p pkgsCross.arm-embedded.stdenv.cc git pkg-config libpng +``` +Then proceed to [Choosing where to store pokeemerald Expansion (Linux)](#choosing-where-to-store-pokeemerald-expansion-linux). + ### Other distributions _(Specific instructions for other distributions would be greatly appreciated!)_ diff --git a/Makefile b/Makefile index 7862d7215f..0428c36970 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,7 @@ MODERN_ELF_NAME := $(MODERN_ROM_NAME:.gba=.elf) MODERN_MAP_NAME := $(MODERN_ROM_NAME:.gba=.map) MODERN_OBJ_DIR_NAME := build/modern -SHELL := /bin/bash -o pipefail +SHELL := bash -o pipefail ELF = $(ROM:.gba=.elf) MAP = $(ROM:.gba=.map) diff --git a/asmdiff.sh b/asmdiff.sh index f5a7010747..aca670e324 100755 --- a/asmdiff.sh +++ b/asmdiff.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash if [[ -d "$DEVKITARM/bin/" ]]; then OBJDUMP_BIN="$DEVKITARM/bin/arm-none-eabi-objdump" diff --git a/tools/gbagfx/jasc_pal.c b/tools/gbagfx/jasc_pal.c index e5ba9c3c2f..8d4bb137d5 100644 --- a/tools/gbagfx/jasc_pal.c +++ b/tools/gbagfx/jasc_pal.c @@ -46,10 +46,14 @@ void ReadJascPaletteLine(FILE *fp, char *line) } if (c == '\n') - FATAL_ERROR("LF line endings aren't supported.\n"); + { + line[length] = 0; + + return; + } if (c == EOF) - FATAL_ERROR("Unexpected EOF. No CRLF at end of file.\n"); + FATAL_ERROR("Unexpected EOF. No LF or CRLF at end of file.\n"); if (c == 0) FATAL_ERROR("NUL character in file.\n"); From 6893d8dbef6e8d038a2ff44c58c70549eb1cad0e Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Sat, 22 Jun 2024 10:52:25 +0300 Subject: [PATCH 10/26] ignoresTargetAbility doesnt ignore the attacker's ability (#4855) --- src/battle_util.c | 1 + .../move_flags/ignores_target_ability.c | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 test/battle/move_flags/ignores_target_ability.c diff --git a/src/battle_util.c b/src/battle_util.c index 7b1cde1474..3f6067c396 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6205,6 +6205,7 @@ u32 GetBattlerAbility(u32 battler) if (((IsMoldBreakerTypeAbility(gBattleMons[gBattlerAttacker].ability) && !(gStatuses3[gBattlerAttacker] & STATUS3_GASTRO_ACID)) || gMovesInfo[gCurrentMove].ignoresTargetAbility) + && battler != gBattlerAttacker && gAbilitiesInfo[gBattleMons[battler].ability].breakable && noAbilityShield && gBattlerByTurnOrder[gCurrentTurnActionNumber] == gBattlerAttacker diff --git a/test/battle/move_flags/ignores_target_ability.c b/test/battle/move_flags/ignores_target_ability.c new file mode 100644 index 0000000000..2836f4838e --- /dev/null +++ b/test/battle/move_flags/ignores_target_ability.c @@ -0,0 +1,74 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_SUNSTEEL_STRIKE].ignoresTargetAbility); + ASSUME(gMovesInfo[MOVE_MOONGEIST_BEAM].ignoresTargetAbility); + ASSUME(gMovesInfo[MOVE_PHOTON_GEYSER].ignoresTargetAbility); +} + +SINGLE_BATTLE_TEST("ignoresTargetAbility moves do not ignore the attacker's own ability", s16 damage) +{ + u32 ability, move; + + PARAMETRIZE { move = MOVE_SUNSTEEL_STRIKE; ability = ABILITY_MAGIC_GUARD; } + PARAMETRIZE { move = MOVE_SUNSTEEL_STRIKE; ability = ABILITY_UNAWARE; } + PARAMETRIZE { move = MOVE_MOONGEIST_BEAM; ability = ABILITY_MAGIC_GUARD; } + PARAMETRIZE { move = MOVE_MOONGEIST_BEAM; ability = ABILITY_UNAWARE; } + PARAMETRIZE { move = MOVE_PHOTON_GEYSER; ability = ABILITY_MAGIC_GUARD; } + PARAMETRIZE { move = MOVE_PHOTON_GEYSER; ability = ABILITY_UNAWARE; } + + ASSUME(gAbilitiesInfo[ABILITY_UNAWARE].breakable); + ASSUME(gMovesInfo[MOVE_IRON_DEFENSE].effect == EFFECT_DEFENSE_UP_2); + ASSUME(gMovesInfo[MOVE_AMNESIA].effect == EFFECT_SPECIAL_DEFENSE_UP_2); + + GIVEN { + PLAYER(SPECIES_CLEFABLE) { Speed(1); Ability(ability); } + OPPONENT(SPECIES_ARON) { Speed(2); } + } WHEN { + if (gMovesInfo[move].category == DAMAGE_CATEGORY_PHYSICAL) + TURN { MOVE(opponent, MOVE_IRON_DEFENSE); MOVE(player, move); } + else + TURN { MOVE(opponent, MOVE_AMNESIA); MOVE(player, move); } + } SCENE { + if (gMovesInfo[move].category == DAMAGE_CATEGORY_PHYSICAL) + ANIMATION(ANIM_TYPE_MOVE, MOVE_IRON_DEFENSE, opponent); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_AMNESIA, opponent); + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); + EXPECT_MUL_EQ(results[2].damage, UQ_4_12(2.0), results[3].damage); + EXPECT_MUL_EQ(results[4].damage, UQ_4_12(2.0), results[5].damage); + } +} + +SINGLE_BATTLE_TEST("ignoresTargetAbility moves do ignore target's abilities", s16 damage) +{ + u32 ability, move; + + PARAMETRIZE { move = MOVE_SUNSTEEL_STRIKE; ability = ABILITY_INNER_FOCUS; } + PARAMETRIZE { move = MOVE_SUNSTEEL_STRIKE; ability = ABILITY_MULTISCALE; } + PARAMETRIZE { move = MOVE_MOONGEIST_BEAM; ability = ABILITY_INNER_FOCUS; } + PARAMETRIZE { move = MOVE_MOONGEIST_BEAM; ability = ABILITY_MULTISCALE; } + PARAMETRIZE { move = MOVE_PHOTON_GEYSER; ability = ABILITY_INNER_FOCUS; } + PARAMETRIZE { move = MOVE_PHOTON_GEYSER; ability = ABILITY_MULTISCALE; } + + ASSUME(gAbilitiesInfo[ABILITY_MULTISCALE].breakable); + + GIVEN { + PLAYER(SPECIES_AZUMARILL); + OPPONENT(SPECIES_DRAGONITE) { Ability(ability); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_EQ(results[2].damage, results[3].damage); + EXPECT_EQ(results[4].damage, results[5].damage); + } +} From 8ac739ea1abe4c75e2deec059f95d6d5e64c1f91 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sat, 22 Jun 2024 09:52:39 +0200 Subject: [PATCH 11/26] [Update moves_info.h] wrong move names (#4854) --- src/data/moves_info.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 8351392613..4d04277348 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -15017,7 +15017,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = [MOVE_FLORAL_HEALING] = { - .name = HANDLE_EXPANDED_MOVE_NAME("FloralHealng", "Floral Healng"), + .name = HANDLE_EXPANDED_MOVE_NAME("FloralHealng", "Floral Healing"), .description = COMPOUND_STRING( "Restores an ally's HP.\n" "Heals more on grass."), @@ -17678,7 +17678,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = [MOVE_JUNGLE_HEALING] = { - .name = HANDLE_EXPANDED_MOVE_NAME("JungleHealng", "Jungle Healng"), + .name = HANDLE_EXPANDED_MOVE_NAME("JungleHealng", "Jungle Healing"), .description = COMPOUND_STRING( "Heals HP and status of\n" "itself and allies in battle."), @@ -20164,7 +20164,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = }, [MOVE_OCEANIC_OPERETTA] = { - .name = COMPOUND_STRING("Oceaning Operetta"), + .name = COMPOUND_STRING("Oceanic Operetta"), .description = sNullDescription, .effect = EFFECT_HIT, .power = 195, From f73ad41fa1c053cffaffa58c19b54299a5ab8692 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Sat, 22 Jun 2024 12:07:36 +0200 Subject: [PATCH 12/26] Tests for post-ko switch scenarios (#4856) --- test/battle/ability/download.c | 5 +- test/battle/ability/intimidate.c | 29 +++-- test/battle/ability/switch_in_abilities.c | 132 ++++++++++++++++++++++ 3 files changed, 153 insertions(+), 13 deletions(-) create mode 100644 test/battle/ability/switch_in_abilities.c diff --git a/test/battle/ability/download.c b/test/battle/ability/download.c index d0e896fd7a..9a8d535a81 100644 --- a/test/battle/ability/download.c +++ b/test/battle/ability/download.c @@ -56,6 +56,7 @@ SINGLE_BATTLE_TEST("Download raises Sp.Attack if enemy has lower Sp. Def than De SINGLE_BATTLE_TEST("Download doesn't activate if target hasn't been sent out yet", s16 damagePhysical, s16 damageSpecial) { u32 ability; + KNOWN_FAILING; PARAMETRIZE { ability = ABILITY_TRACE; } PARAMETRIZE { ability = ABILITY_DOWNLOAD; } GIVEN { @@ -73,13 +74,13 @@ SINGLE_BATTLE_TEST("Download doesn't activate if target hasn't been sent out yet // Everyone faints. MESSAGE("Go! Porygon!"); - MESSAGE("2 sent out Porygon2!"); - NONE_OF { ABILITY_POPUP(player, ABILITY_DOWNLOAD); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); MESSAGE("Porygon's Download raised its Attack!"); } + MESSAGE("2 sent out Porygon2!"); + if (ability == ABILITY_DOWNLOAD) { ABILITY_POPUP(opponent, ABILITY_DOWNLOAD); diff --git a/test/battle/ability/intimidate.c b/test/battle/ability/intimidate.c index 08560ccb00..ad37e4e081 100644 --- a/test/battle/ability/intimidate.c +++ b/test/battle/ability/intimidate.c @@ -59,6 +59,7 @@ SINGLE_BATTLE_TEST("Intimidate (opponent) lowers player's attack after KO", s16 DOUBLE_BATTLE_TEST("Intimidate doesn't activate on an empty field in a double battle") { + KNOWN_FAILING; GIVEN { ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); PLAYER(SPECIES_WOBBUFFET); @@ -78,23 +79,29 @@ DOUBLE_BATTLE_TEST("Intimidate doesn't activate on an empty field in a double ba // Everyone faints. MESSAGE("Go! Ekans!"); - MESSAGE("2 sent out Arbok!"); - MESSAGE("Go! Abra!"); - MESSAGE("2 sent out Wynaut!"); - NONE_OF { ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); - MESSAGE("Ekans's Intimidate cuts Foe Arbok's attack!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); - MESSAGE("Ekans's Intimidate cuts Foe Wynaut's attack!"); - + } + MESSAGE("2 sent out Arbok!"); + NONE_OF { ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); - MESSAGE("Foe Arbok's Intimidate cuts Ekans's attack!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); - MESSAGE("Foe Arbok's Intimidate cuts Abra's attack!"); } + MESSAGE("Go! Abra!"); + MESSAGE("2 sent out Wynaut!"); + // Intimidate activates after all battlers have been brought out + ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("Ekans's Intimidate cuts Foe Arbok's attack!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("Ekans's Intimidate cuts Foe Wynaut's attack!"); + + ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Foe Arbok's Intimidate cuts Ekans's attack!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + MESSAGE("Foe Arbok's Intimidate cuts Abra's attack!"); } } diff --git a/test/battle/ability/switch_in_abilities.c b/test/battle/ability/switch_in_abilities.c new file mode 100644 index 0000000000..b466d0c646 --- /dev/null +++ b/test/battle/ability/switch_in_abilities.c @@ -0,0 +1,132 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order at the battle's start - Single Battle") +{ + u32 spdPlayer, spdOpponent; + + PARAMETRIZE { spdPlayer = 5; spdOpponent = 1; } + PARAMETRIZE { spdOpponent = 5; spdPlayer = 1; } + + GIVEN { + PLAYER(SPECIES_EKANS) { Speed(spdPlayer); Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_NINETALES) { Speed(spdOpponent); Ability(ABILITY_DROUGHT); } + } WHEN { + TURN { ; } + } SCENE { + if (spdPlayer > spdOpponent) { + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponent, ABILITY_DROUGHT); + } else { + ABILITY_POPUP(opponent, ABILITY_DROUGHT); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + } + } +} + +DOUBLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order at the battle's start - Double Battle") +{ + u32 spdPlayer1, spdPlayer2, spdOpponent1, spdOpponent2; + + PARAMETRIZE { spdPlayer1 = 5; spdPlayer2 = 4; spdOpponent1 = 3; spdOpponent2 = 2; } + PARAMETRIZE { spdPlayer1 = 2; spdPlayer2 = 3; spdOpponent1 = 4; spdOpponent2 = 5; } + PARAMETRIZE { spdPlayer1 = 4; spdPlayer2 = 3; spdOpponent1 = 5; spdOpponent2 = 2; } + + GIVEN { + PLAYER(SPECIES_KYOGRE) { Speed(spdPlayer1); Ability(ABILITY_DRIZZLE); } + PLAYER(SPECIES_GYARADOS) { Speed(spdPlayer2); Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_PORYGON2) { Speed(spdOpponent1); Ability(ABILITY_DOWNLOAD); } + OPPONENT(SPECIES_PINSIR) { Speed(spdOpponent2); Ability(ABILITY_MOLD_BREAKER); } + } WHEN { + TURN { ; } + } SCENE { + if (spdPlayer1 == 5) { + ABILITY_POPUP(playerLeft, ABILITY_DRIZZLE); + ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponentLeft, ABILITY_DOWNLOAD); + ABILITY_POPUP(opponentRight, ABILITY_MOLD_BREAKER); + } else if (spdOpponent2 == 5) { + ABILITY_POPUP(opponentRight, ABILITY_MOLD_BREAKER); + ABILITY_POPUP(opponentLeft, ABILITY_DOWNLOAD); + ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE); + ABILITY_POPUP(playerLeft, ABILITY_DRIZZLE); + } else { + ABILITY_POPUP(opponentLeft, ABILITY_DOWNLOAD); + ABILITY_POPUP(playerLeft, ABILITY_DRIZZLE); + ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponentRight, ABILITY_MOLD_BREAKER); + } + } +} + +SINGLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order after post-KO switch - Single Battle") +{ + u32 spdPlayer, spdOpponent; + + KNOWN_FAILING; + + PARAMETRIZE { spdPlayer = 5; spdOpponent = 1; } + PARAMETRIZE { spdOpponent = 5; spdPlayer = 1; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(1); } + PLAYER(SPECIES_EKANS) { Speed(spdPlayer); Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(1); } + OPPONENT(SPECIES_PORYGON2) { Speed(spdOpponent); Ability(ABILITY_DOWNLOAD); } + } WHEN { + TURN { MOVE(player, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + TURN { ; } + } SCENE { + MESSAGE("Wobbuffet used Explosion!"); + if (spdPlayer > spdOpponent) { + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponent, ABILITY_DOWNLOAD); + } else { + ABILITY_POPUP(opponent, ABILITY_DOWNLOAD); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + } + } +} + +DOUBLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order after post-KO switch - Double Battle") +{ + u32 spdPlayer1, spdPlayer2, spdOpponent1, spdOpponent2; + + KNOWN_FAILING; + + PARAMETRIZE { spdPlayer1 = 5; spdPlayer2 = 4; spdOpponent1 = 3; spdOpponent2 = 2; } + PARAMETRIZE { spdPlayer1 = 2; spdPlayer2 = 3; spdOpponent1 = 4; spdOpponent2 = 5; } + PARAMETRIZE { spdPlayer1 = 4; spdPlayer2 = 3; spdOpponent1 = 5; spdOpponent2 = 2; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(1); } + PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(1); } + PLAYER(SPECIES_TYRANITAR) { Speed(spdPlayer1); Ability(ABILITY_SAND_STREAM); } + PLAYER(SPECIES_GYARADOS) { Speed(spdPlayer2); Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(1); } + OPPONENT(SPECIES_WEEZING_GALARIAN) { Speed(spdOpponent1); Ability(ABILITY_MISTY_SURGE); } + OPPONENT(SPECIES_VULPIX_ALOLAN) { Speed(spdOpponent2); Ability(ABILITY_SNOW_WARNING); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_EXPLOSION); SEND_OUT(playerLeft, 2); SEND_OUT(opponentLeft, 2); SEND_OUT(playerRight, 3); SEND_OUT(opponentRight, 3); } + TURN { ; } + } SCENE { + MESSAGE("Wobbuffet used Explosion!"); + if (spdPlayer1 == 5) { + ABILITY_POPUP(playerLeft, ABILITY_SAND_STREAM); + ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponentLeft, ABILITY_MISTY_SURGE); + ABILITY_POPUP(opponentRight, ABILITY_SNOW_WARNING); + } else if (spdOpponent2 == 5) { + ABILITY_POPUP(opponentRight, ABILITY_SNOW_WARNING); + ABILITY_POPUP(opponentLeft, ABILITY_MISTY_SURGE); + ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE); + ABILITY_POPUP(playerLeft, ABILITY_SAND_STREAM); + } else { + ABILITY_POPUP(opponentLeft, ABILITY_MISTY_SURGE); + ABILITY_POPUP(playerLeft, ABILITY_SAND_STREAM); + ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponentRight, ABILITY_SNOW_WARNING); + } + } +} From e74264f6defa4eaaaa859826cd44448b25b6d35c Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sat, 22 Jun 2024 19:39:01 +0200 Subject: [PATCH 13/26] Ported test changes from #4857 (#4859) --- test/battle/ability/intimidate.c | 29 ++++++++++++++++++++++++++ test/battle/ability/supreme_overlord.c | 3 ++- test/battle/ability/sword_of_ruin.c | 3 ++- test/battle/ability/tablets_of_ruin.c | 3 ++- test/battle/ability/vessel_of_ruin.c | 3 ++- test/battle/ability/zero_to_hero.c | 3 ++- test/battle/move_effect/hit_escape.c | 4 ++-- 7 files changed, 41 insertions(+), 7 deletions(-) diff --git a/test/battle/ability/intimidate.c b/test/battle/ability/intimidate.c index ad37e4e081..46742e6935 100644 --- a/test/battle/ability/intimidate.c +++ b/test/battle/ability/intimidate.c @@ -217,3 +217,32 @@ SINGLE_BATTLE_TEST("Intimidate can not further lower opponents Atk stat if it is EXPECT_EQ(player->statStages[STAT_ATK], MIN_STAT_STAGE); } } + +DOUBLE_BATTLE_TEST("Intimidate is not going to trigger if a mon switches out through u-turn and the opposing field is empty") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_WYNAUT) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_TREECKO); + OPPONENT(SPECIES_TORCHIC); + } WHEN { + TURN { + MOVE(opponentRight, MOVE_HEALING_WISH); + MOVE(playerLeft, MOVE_U_TURN, target: opponentLeft); + SEND_OUT(playerLeft, 2); + SEND_OUT(opponentLeft, 2); + SEND_OUT(opponentRight, 3); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, playerLeft); + HP_BAR(opponentLeft); + MESSAGE("2 sent out Treecko!"); + MESSAGE("2 sent out Torchic!"); + NOT ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); + } +} diff --git a/test/battle/ability/supreme_overlord.c b/test/battle/ability/supreme_overlord.c index 061324c739..11ab83d7f5 100644 --- a/test/battle/ability/supreme_overlord.c +++ b/test/battle/ability/supreme_overlord.c @@ -94,6 +94,7 @@ SINGLE_BATTLE_TEST("Supreme Overlord does not boost attack if party members are SINGLE_BATTLE_TEST("Supreme Overlord's message displays correctly after all battlers fainted - Player") { + KNOWN_FAILING; // Explosion causes the ability to wait GIVEN { ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); PLAYER(SPECIES_WOBBUFFET) { HP(1);} @@ -107,9 +108,9 @@ SINGLE_BATTLE_TEST("Supreme Overlord's message displays correctly after all batt ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); // Everyone faints. MESSAGE("Go! Kingambit!"); + MESSAGE("2 sent out Wobbuffet!"); ABILITY_POPUP(player, ABILITY_SUPREME_OVERLORD); MESSAGE("Kingambit gained strength from the fallen!"); - MESSAGE("2 sent out Wobbuffet!"); } } diff --git a/test/battle/ability/sword_of_ruin.c b/test/battle/ability/sword_of_ruin.c index 329031f68d..493c50f0d2 100644 --- a/test/battle/ability/sword_of_ruin.c +++ b/test/battle/ability/sword_of_ruin.c @@ -32,6 +32,7 @@ SINGLE_BATTLE_TEST("Sword of Ruin reduces Defense if opposing mon's ability does SINGLE_BATTLE_TEST("Sword of Ruin's message displays correctly after all battlers fainted - Player") { + KNOWN_FAILING; // Explosion causes the ability to wait GIVEN { ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); PLAYER(SPECIES_WOBBUFFET) { HP(1);} @@ -46,9 +47,9 @@ SINGLE_BATTLE_TEST("Sword of Ruin's message displays correctly after all battler ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); // Everyone faints. MESSAGE("Go! Chien-Pao!"); + MESSAGE("2 sent out Wobbuffet!"); ABILITY_POPUP(player, ABILITY_SWORD_OF_RUIN); MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!"); - MESSAGE("2 sent out Wobbuffet!"); } } diff --git a/test/battle/ability/tablets_of_ruin.c b/test/battle/ability/tablets_of_ruin.c index abdaab7563..0550cdc814 100644 --- a/test/battle/ability/tablets_of_ruin.c +++ b/test/battle/ability/tablets_of_ruin.c @@ -32,6 +32,7 @@ SINGLE_BATTLE_TEST("Tablets of Ruin reduces Attack if opposing mon's ability doe SINGLE_BATTLE_TEST("Tablets of Ruin's message displays correctly after all battlers fainted - Player") { + KNOWN_FAILING; // Explosion causes the ability to wait GIVEN { ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); PLAYER(SPECIES_WOBBUFFET) { HP(1);} @@ -46,9 +47,9 @@ SINGLE_BATTLE_TEST("Tablets of Ruin's message displays correctly after all battl ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); // Everyone faints. MESSAGE("Go! Wo-Chien!"); + MESSAGE("2 sent out Wobbuffet!"); ABILITY_POPUP(player, ABILITY_TABLETS_OF_RUIN); MESSAGE("Wo-Chien's Tablets of Ruin weakened the Attack of all surrounding Pokémon!"); - MESSAGE("2 sent out Wobbuffet!"); } } diff --git a/test/battle/ability/vessel_of_ruin.c b/test/battle/ability/vessel_of_ruin.c index ce8eae4dab..6a65616ed4 100644 --- a/test/battle/ability/vessel_of_ruin.c +++ b/test/battle/ability/vessel_of_ruin.c @@ -32,6 +32,7 @@ SINGLE_BATTLE_TEST("Vessel of Ruin reduces Sp. Atk if opposing mon's ability doe SINGLE_BATTLE_TEST("Vessel of Ruin's message displays correctly after all battlers fainted - Player") { + KNOWN_FAILING; // Explosion causes the ability to wait GIVEN { ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); PLAYER(SPECIES_WOBBUFFET) { HP(1);} @@ -46,9 +47,9 @@ SINGLE_BATTLE_TEST("Vessel of Ruin's message displays correctly after all battle ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); // Everyone faints. MESSAGE("Go! Ting-Lu!"); + MESSAGE("2 sent out Wobbuffet!"); ABILITY_POPUP(player, ABILITY_VESSEL_OF_RUIN); MESSAGE("Ting-Lu's Vessel of Ruin weakened the Sp. Atk of all surrounding Pokémon!"); - MESSAGE("2 sent out Wobbuffet!"); } } diff --git a/test/battle/ability/zero_to_hero.c b/test/battle/ability/zero_to_hero.c index b4e64d9357..91725f50db 100644 --- a/test/battle/ability/zero_to_hero.c +++ b/test/battle/ability/zero_to_hero.c @@ -138,6 +138,7 @@ SINGLE_BATTLE_TEST("Imposter doesn't apply the heroic transformation message whe SINGLE_BATTLE_TEST("Zero to Hero's message displays correctly after all battlers fainted - Player") { + KNOWN_FAILING; // Explosion causes the ability to wait GIVEN { ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); PLAYER(SPECIES_PALAFIN_ZERO); @@ -153,9 +154,9 @@ SINGLE_BATTLE_TEST("Zero to Hero's message displays correctly after all battlers ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); // Everyone faints. MESSAGE("Go! Palafin!"); + MESSAGE("2 sent out Wobbuffet!"); ABILITY_POPUP(player, ABILITY_ZERO_TO_HERO); MESSAGE("Palafin underwent a heroic transformation!"); - MESSAGE("2 sent out Wobbuffet!"); } } diff --git a/test/battle/move_effect/hit_escape.c b/test/battle/move_effect/hit_escape.c index 235e0773fe..86763c4127 100644 --- a/test/battle/move_effect/hit_escape.c +++ b/test/battle/move_effect/hit_escape.c @@ -126,6 +126,7 @@ SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE); ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player); HP_BAR(opponent); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); MESSAGE("2 sent out Wynaut!"); @@ -137,7 +138,6 @@ SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: opposing side") { - KNOWN_FAILING; GIVEN { PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); }; PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); } @@ -149,7 +149,7 @@ SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE); ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player); HP_BAR(opponent); - NOT ABILITY_POPUP(player, ABILITY_INTIMIDATE); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); MESSAGE("2 sent out Wynaut!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); From ec75a75498993497300dc1e7dd2b365e181245b2 Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Sun, 23 Jun 2024 22:17:58 +0300 Subject: [PATCH 14/26] Update plasma_fists.c (#4865) --- test/battle/move_effect/plasma_fists.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/battle/move_effect/plasma_fists.c b/test/battle/move_effect/plasma_fists.c index a7b1ac5f6a..1252de7b54 100644 --- a/test/battle/move_effect/plasma_fists.c +++ b/test/battle/move_effect/plasma_fists.c @@ -47,20 +47,19 @@ SINGLE_BATTLE_TEST("Plasma Fists turns normal moves into electric for the remain } } -SINGLE_BATTLE_TEST("Plasma Fists type-changing effect is applied after Pixilate") +SINGLE_BATTLE_TEST("Plasma Fists type-changing effect does not override Pixilate") { GIVEN { PLAYER(SPECIES_KRABBY) { Speed(300); }; - OPPONENT(SPECIES_ALTARIA) { Speed(1); Item(ITEM_ALTARIANITE); } + OPPONENT(SPECIES_SYLVEON) { Speed(1); Ability(ABILITY_PIXILATE); } } WHEN { - TURN { MOVE(player, MOVE_PLASMA_FISTS); MOVE(opponent, MOVE_EMBER, megaEvolve: TRUE); } + TURN { MOVE(player, MOVE_PLASMA_FISTS); MOVE(opponent, MOVE_TACKLE); } } SCENE { - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); MESSAGE("Krabby used Plasma Fists!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_PLASMA_FISTS, player); MESSAGE("A deluge of ions showers the battlefield!"); - MESSAGE("Foe Altaria used Ember!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent); + MESSAGE("Foe Sylveon used Tackle!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); NOT MESSAGE("It's super effective!"); } } From a8ae1a03422f4f4446f2cde77c6036e34dc55ff1 Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Mon, 24 Jun 2024 12:19:12 +0200 Subject: [PATCH 15/26] added missing minior description + test for missing descriptions (#4858) --- src/data/pokemon/species_info.h | 6 +----- src/data/pokemon/species_info/gen_7_families.h | 1 + src/data/pokemon/species_info/shared_dex_text.h | 7 +++++++ test/species.c | 16 ++++++++++++++++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/data/pokemon/species_info.h b/src/data/pokemon/species_info.h index 164edebbc9..c9bfa7a246 100644 --- a/src/data/pokemon/species_info.h +++ b/src/data/pokemon/species_info.h @@ -31,11 +31,7 @@ const struct SpeciesInfo gSpeciesInfo[] = .categoryName = _("Unknown"), .height = 0, .weight = 0, - .description = COMPOUND_STRING( - "This is a newly discovered Pokémon.\n" - "It is currently under investigation.\n" - "No detailed information is available\n" - "at this time."), + .description = gFallbackPokedexText, .pokemonScale = 256, .pokemonOffset = 0, .trainerScale = 256, diff --git a/src/data/pokemon/species_info/gen_7_families.h b/src/data/pokemon/species_info/gen_7_families.h index fdb038b4c6..629ad59aa9 100644 --- a/src/data/pokemon/species_info/gen_7_families.h +++ b/src/data/pokemon/species_info/gen_7_families.h @@ -3617,6 +3617,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .baseSpAttack = 60, \ .baseSpDefense = 100, \ .weight = 400, \ + .description = gMiniorMeteorPokedexText, \ .frontPic = gMonFrontPic_MiniorMeteor, \ .frontPicSize = MON_COORDS_SIZE(48, 40), \ .frontPicYOffset = 14, \ diff --git a/src/data/pokemon/species_info/shared_dex_text.h b/src/data/pokemon/species_info/shared_dex_text.h index dbfef82d74..0c591bd811 100644 --- a/src/data/pokemon/species_info/shared_dex_text.h +++ b/src/data/pokemon/species_info/shared_dex_text.h @@ -1,3 +1,10 @@ +// fallback +const u8 gFallbackPokedexText[] = _( + "This is a newly discovered Pokémon.\n" + "It is currently under investigation.\n" + "No detailed information is available\n" + "at this time."); + // Gen 1 families const u8 gRaticateAlolanPokedexText[] = _( "It forms a group of Rattata, which it \n" diff --git a/test/species.c b/test/species.c index 3ad7495d23..7099855443 100644 --- a/test/species.c +++ b/test/species.c @@ -1,4 +1,5 @@ #include "global.h" +#include "string_util.h" #include "test/test.h" #include "constants/form_change_types.h" @@ -129,3 +130,18 @@ TEST("No species has two evolutions that use the evolution tracker") EXPECT(evolutionTrackerEvolutions < 2); } + +extern const u8 gFallbackPokedexText[]; + +TEST("Every species has a description") +{ + u32 i; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + PARAMETRIZE { species = i; } + } + + EXPECT_NE(StringCompare(GetSpeciesPokedexDescription(species), gFallbackPokedexText), 0); +} From f2e848248859746d4344176893f74ee39ed1aa3e Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Mon, 24 Jun 2024 20:37:47 +0200 Subject: [PATCH 16/26] Fix how switch-in effects are played out after multiple faints in the same turn (#4864) * Multiple switch-ins after fainting * empty new lines * Fix failing tests --- data/battle_scripts_1.s | 1 + include/battle.h | 6 +- src/battle_script_commands.c | 101 +++++++++++++++------- test/battle/ability/beads_of_ruin.c | 2 +- test/battle/ability/download.c | 2 +- test/battle/ability/intimidate.c | 1 - test/battle/ability/supreme_overlord.c | 1 - test/battle/ability/switch_in_abilities.c | 4 - test/battle/ability/sword_of_ruin.c | 1 - test/battle/ability/tablets_of_ruin.c | 1 - test/battle/ability/vessel_of_ruin.c | 1 - test/battle/ability/zero_to_hero.c | 1 - test/battle/move_effect/sticky_web.c | 10 +-- 13 files changed, 84 insertions(+), 48 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 32015580b8..1bbb759460 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -5509,6 +5509,7 @@ BattleScript_HandleFaintedMonLoop:: switchineffects BS_FAINTED_MULTIPLE_1 jumpifbytenotequal gBattlerFainted, gBattlersCount, BattleScript_HandleFaintedMonLoop BattleScript_HandleFaintedMonMultipleEnd:: + switchineffects BS_FAINTED_MULTIPLE_2 end2 BattleScript_LocalTrainerBattleWon:: diff --git a/include/battle.h b/include/battle.h index 13359d0f4f..addf654d91 100644 --- a/include/battle.h +++ b/include/battle.h @@ -662,7 +662,11 @@ struct BattleStruct u16 abilityPreventingSwitchout; u8 hpScale; u16 synchronizeMoveEffect; - bool8 anyMonHasTransformed; + u8 anyMonHasTransformed:1; // Only used in battle_tv.c + u8 multipleSwitchInBattlers:4; // One bit per battler + u8 multipleSwitchInState:2; + u8 multipleSwitchInCursor:3; + u8 multipleSwitchInSortedBattlers[MAX_BATTLERS_COUNT]; void (*savedCallback)(void); u16 usedHeldItems[PARTY_SIZE][NUM_BATTLE_SIDES]; // For each party member and side. For harvest, recycle u16 chosenItem[MAX_BATTLERS_COUNT]; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 2aa68b896d..83c910f6b0 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -4633,7 +4633,7 @@ bool32 NoAliveMonsForEitherParty(void) return (NoAliveMonsForPlayer() || NoAliveMonsForOpponent()); } -// For battles that aren't BATTLE_TYPE_LINK or BATTLE_TYPE_RECORDED_LINK or double trainer battles, the only thing this +// For battles that aren't BATTLE_TYPE_LINK or BATTLE_TYPE_RECORDED_LINK or trainer battles, the only thing this // command does is check whether the player has won/lost by totaling each team's HP. It then // sets gBattleOutcome accordingly, if necessary. static void Cmd_checkteamslost(void) @@ -4650,15 +4650,11 @@ static void Cmd_checkteamslost(void) gBattleOutcome |= B_OUTCOME_WON; // Fair switching - everyone has to switch in most at the same time, without knowing which pokemon the other trainer selected. - // In vanilla Emerald this was only used for link battles, in expansion it's also used for regular trainers in double battles. + // In vanilla Emerald this was only used for link battles, in expansion it's also used for regular trainer battles. // For battles that haven't ended, count number of empty battler spots // In multi battles, jump to pointer if more than 1 spot empty // In non-multi battles, jump to pointer if 1 spot is missing on both sides - if (gBattleOutcome == 0 - && (((gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))) - || ((gBattleTypeFlags & BATTLE_TYPE_TRAINER) && (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) - ) - ) + if (gBattleOutcome == 0 && (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_TRAINER))) { s32 i, emptyPlayerSpots, emptyOpponentSpots; @@ -7148,13 +7144,8 @@ bool32 DoSwitchInAbilities(u32 battler) || AbilityBattleEffects(ABILITYEFFECT_TRACE2, 0, 0, 0, 0)); } -static void Cmd_switchineffects(void) +static void UpdateSentMonFlags(u32 battler) { - CMD_ARGS(u8 battler); - - s32 i; - u32 battler = GetBattlerForBattleScript(cmd->battler); - UpdateSentPokesToOpponentValue(battler); gHitMarker &= ~HITMARKER_FAINTED(battler); @@ -7162,7 +7153,11 @@ static void Cmd_switchineffects(void) if (!BattlerHasAi(battler)) gBattleStruct->appearedInBattle |= gBitTable[gBattlerPartyIndexes[battler]]; +} +static bool32 DoSwitchInEffectsForBattler(u32 battler) +{ + u32 i; // Neutralizing Gas announces itself before hazards if (gBattleMons[battler].ability == ABILITY_NEUTRALIZING_GAS && gSpecialStatuses[battler].announceNeutralizingGas == 0) { @@ -7280,7 +7275,7 @@ static void Cmd_switchineffects(void) BattleScriptPushCursor(); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_Z_HP_TRAP; gBattlescriptCurrInstr = BattleScript_HealReplacementZMove; - return; + return TRUE; } else { @@ -7295,9 +7290,9 @@ static void Cmd_switchineffects(void) gDisableStructs[battler].truantSwitchInHack = 0; if (DoSwitchInAbilities(battler) || ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battler, FALSE)) - return; + return TRUE; else if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0)) - return; + return TRUE; gDisableStructs[battler].stickyWebDone = FALSE; gDisableStructs[battler].spikesDone = FALSE; @@ -7313,22 +7308,68 @@ static void Cmd_switchineffects(void) gBattleStruct->hpOnSwitchout[GetBattlerSide(i)] = gBattleMons[i].hp; } - if (cmd->battler == BS_FAINTED_MULTIPLE_1) - { - u32 hitmarkerFaintBits = gHitMarker >> 28; - - gBattlerFainted++; - while (1) - { - if (hitmarkerFaintBits & gBitTable[gBattlerFainted] && !(gAbsentBattlerFlags & gBitTable[gBattlerFainted])) - break; - if (gBattlerFainted >= gBattlersCount) - break; - gBattlerFainted++; - } - } gBattleStruct->forcedSwitch &= ~(gBitTable[battler]); + return FALSE; + } + + return TRUE; // Effect's script plays. +} + +static void Cmd_switchineffects(void) +{ + CMD_ARGS(u8 battler); + u32 i, battler = GetBattlerForBattleScript(cmd->battler); + + switch (cmd->battler) + { + // Multiple mons fainted and are being switched-in. Their abilities/hazards will play according to speed ties. + case BS_FAINTED_MULTIPLE_1: // Saves the battlers. + gBattleStruct->multipleSwitchInBattlers |= gBitTable[battler]; + UpdateSentMonFlags(battler); + + // Increment fainted battler. + do + { + gBattlerFainted++; + if (gBattlerFainted >= gBattlersCount) + break; + if (gHitMarker & HITMARKER_FAINTED(gBattlerFainted) && !(gAbsentBattlerFlags & gBitTable[gBattlerFainted])) + break; + } while (1); + gBattlescriptCurrInstr = cmd->nextInstr; + return; + case BS_FAINTED_MULTIPLE_2: // Plays hazards/abilities. + switch (gBattleStruct->multipleSwitchInState) + { + case 0: // Sort battlers by speed + for (i = 0; i < gBattlersCount; i++) + gBattleStruct->multipleSwitchInSortedBattlers[i] = i; + SortBattlersBySpeed(gBattleStruct->multipleSwitchInSortedBattlers, FALSE); + gBattleStruct->multipleSwitchInState++; + gBattleStruct->multipleSwitchInCursor = 0; + // Loop through all available battlers + case 1: + for (; gBattleStruct->multipleSwitchInCursor < gBattlersCount; gBattleStruct->multipleSwitchInCursor++) + { + gBattlerFainted = gBattleStruct->multipleSwitchInSortedBattlers[gBattleStruct->multipleSwitchInCursor]; + if (gBattleStruct->multipleSwitchInBattlers & gBitTable[gBattlerFainted]) + { + if (DoSwitchInEffectsForBattler(gBattlerFainted)) + return; + } + } + // All battlers done, end + gBattleStruct->multipleSwitchInBattlers = 0; + gBattleStruct->multipleSwitchInState = 0; + gBattlescriptCurrInstr = cmd->nextInstr; + } + break; + default: + UpdateSentMonFlags(battler); + if (!DoSwitchInEffectsForBattler(battler)) + gBattlescriptCurrInstr = cmd->nextInstr; + break; } } diff --git a/test/battle/ability/beads_of_ruin.c b/test/battle/ability/beads_of_ruin.c index 2fc4f9cdc1..f7c9c267dc 100644 --- a/test/battle/ability/beads_of_ruin.c +++ b/test/battle/ability/beads_of_ruin.c @@ -46,9 +46,9 @@ SINGLE_BATTLE_TEST("Beads of Ruin's message displays correctly after all battler ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); // Everyone faints. MESSAGE("Go! Chi-Yu!"); + MESSAGE("2 sent out Wobbuffet!"); ABILITY_POPUP(player, ABILITY_BEADS_OF_RUIN); MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!"); - MESSAGE("2 sent out Wobbuffet!"); } } diff --git a/test/battle/ability/download.c b/test/battle/ability/download.c index 9a8d535a81..4a6daf8d18 100644 --- a/test/battle/ability/download.c +++ b/test/battle/ability/download.c @@ -56,7 +56,7 @@ SINGLE_BATTLE_TEST("Download raises Sp.Attack if enemy has lower Sp. Def than De SINGLE_BATTLE_TEST("Download doesn't activate if target hasn't been sent out yet", s16 damagePhysical, s16 damageSpecial) { u32 ability; - KNOWN_FAILING; + PARAMETRIZE { ability = ABILITY_TRACE; } PARAMETRIZE { ability = ABILITY_DOWNLOAD; } GIVEN { diff --git a/test/battle/ability/intimidate.c b/test/battle/ability/intimidate.c index 46742e6935..09677523c1 100644 --- a/test/battle/ability/intimidate.c +++ b/test/battle/ability/intimidate.c @@ -59,7 +59,6 @@ SINGLE_BATTLE_TEST("Intimidate (opponent) lowers player's attack after KO", s16 DOUBLE_BATTLE_TEST("Intimidate doesn't activate on an empty field in a double battle") { - KNOWN_FAILING; GIVEN { ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); PLAYER(SPECIES_WOBBUFFET); diff --git a/test/battle/ability/supreme_overlord.c b/test/battle/ability/supreme_overlord.c index 11ab83d7f5..baf2076429 100644 --- a/test/battle/ability/supreme_overlord.c +++ b/test/battle/ability/supreme_overlord.c @@ -94,7 +94,6 @@ SINGLE_BATTLE_TEST("Supreme Overlord does not boost attack if party members are SINGLE_BATTLE_TEST("Supreme Overlord's message displays correctly after all battlers fainted - Player") { - KNOWN_FAILING; // Explosion causes the ability to wait GIVEN { ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); PLAYER(SPECIES_WOBBUFFET) { HP(1);} diff --git a/test/battle/ability/switch_in_abilities.c b/test/battle/ability/switch_in_abilities.c index b466d0c646..79cf2b2dc9 100644 --- a/test/battle/ability/switch_in_abilities.c +++ b/test/battle/ability/switch_in_abilities.c @@ -63,8 +63,6 @@ SINGLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order after post-KO swi { u32 spdPlayer, spdOpponent; - KNOWN_FAILING; - PARAMETRIZE { spdPlayer = 5; spdOpponent = 1; } PARAMETRIZE { spdOpponent = 5; spdPlayer = 1; } @@ -92,8 +90,6 @@ DOUBLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order after post-KO swi { u32 spdPlayer1, spdPlayer2, spdOpponent1, spdOpponent2; - KNOWN_FAILING; - PARAMETRIZE { spdPlayer1 = 5; spdPlayer2 = 4; spdOpponent1 = 3; spdOpponent2 = 2; } PARAMETRIZE { spdPlayer1 = 2; spdPlayer2 = 3; spdOpponent1 = 4; spdOpponent2 = 5; } PARAMETRIZE { spdPlayer1 = 4; spdPlayer2 = 3; spdOpponent1 = 5; spdOpponent2 = 2; } diff --git a/test/battle/ability/sword_of_ruin.c b/test/battle/ability/sword_of_ruin.c index 493c50f0d2..7fc2c30d2a 100644 --- a/test/battle/ability/sword_of_ruin.c +++ b/test/battle/ability/sword_of_ruin.c @@ -32,7 +32,6 @@ SINGLE_BATTLE_TEST("Sword of Ruin reduces Defense if opposing mon's ability does SINGLE_BATTLE_TEST("Sword of Ruin's message displays correctly after all battlers fainted - Player") { - KNOWN_FAILING; // Explosion causes the ability to wait GIVEN { ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); PLAYER(SPECIES_WOBBUFFET) { HP(1);} diff --git a/test/battle/ability/tablets_of_ruin.c b/test/battle/ability/tablets_of_ruin.c index 0550cdc814..d8858aca2e 100644 --- a/test/battle/ability/tablets_of_ruin.c +++ b/test/battle/ability/tablets_of_ruin.c @@ -32,7 +32,6 @@ SINGLE_BATTLE_TEST("Tablets of Ruin reduces Attack if opposing mon's ability doe SINGLE_BATTLE_TEST("Tablets of Ruin's message displays correctly after all battlers fainted - Player") { - KNOWN_FAILING; // Explosion causes the ability to wait GIVEN { ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); PLAYER(SPECIES_WOBBUFFET) { HP(1);} diff --git a/test/battle/ability/vessel_of_ruin.c b/test/battle/ability/vessel_of_ruin.c index 6a65616ed4..5f83a6c07f 100644 --- a/test/battle/ability/vessel_of_ruin.c +++ b/test/battle/ability/vessel_of_ruin.c @@ -32,7 +32,6 @@ SINGLE_BATTLE_TEST("Vessel of Ruin reduces Sp. Atk if opposing mon's ability doe SINGLE_BATTLE_TEST("Vessel of Ruin's message displays correctly after all battlers fainted - Player") { - KNOWN_FAILING; // Explosion causes the ability to wait GIVEN { ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); PLAYER(SPECIES_WOBBUFFET) { HP(1);} diff --git a/test/battle/ability/zero_to_hero.c b/test/battle/ability/zero_to_hero.c index 91725f50db..3939c6cb78 100644 --- a/test/battle/ability/zero_to_hero.c +++ b/test/battle/ability/zero_to_hero.c @@ -138,7 +138,6 @@ SINGLE_BATTLE_TEST("Imposter doesn't apply the heroic transformation message whe SINGLE_BATTLE_TEST("Zero to Hero's message displays correctly after all battlers fainted - Player") { - KNOWN_FAILING; // Explosion causes the ability to wait GIVEN { ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); PLAYER(SPECIES_PALAFIN_ZERO); diff --git a/test/battle/move_effect/sticky_web.c b/test/battle/move_effect/sticky_web.c index 6c6f8f156c..37f5fce077 100644 --- a/test/battle/move_effect/sticky_web.c +++ b/test/battle/move_effect/sticky_web.c @@ -56,7 +56,7 @@ DOUBLE_BATTLE_TEST("Sticky Web lowers Speed by 1 in a double battle after Explos OPPONENT(SPECIES_WOBBUFFET) {HP(1); Speed(1);} OPPONENT(SPECIES_WOBBUFFET) {HP(1); Speed(1);} OPPONENT(SPECIES_WYNAUT) {Speed(10);} - OPPONENT(SPECIES_WYNAUT) {Speed(10);} + OPPONENT(SPECIES_ALAKAZAM) {Speed(100);} } WHEN { TURN { MOVE(playerRight, MOVE_STICKY_WEB); MOVE(playerLeft, MOVE_EXPLOSION); SEND_OUT(playerLeft, 2); SEND_OUT(opponentLeft, 2); SEND_OUT(opponentRight, 3); } TURN {} @@ -65,13 +65,13 @@ DOUBLE_BATTLE_TEST("Sticky Web lowers Speed by 1 in a double battle after Explos 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("2 sent out Alakazam!"); + MESSAGE("Foe Alakazam was caught in a Sticky Web!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("Foe Alakazam's Speed fell!"); 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!"); } } From 566ad6d8699306f48bf8789e8e8a6748757c0730 Mon Sep 17 00:00:00 2001 From: leo60228 Date: Tue, 25 Jun 2024 05:10:52 -0400 Subject: [PATCH 17/26] Don't try to deploy to GitHub Pages if it's not enabled (#4809) --- .github/workflows/docs.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 244976ea1e..874ebdd590 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -26,8 +26,18 @@ jobs: run: | cd docs mdbook build + - name: Check if Pages is enabled + uses: octokit/request-action@v2.x + id: check_pages + continue-on-error: true + with: + route: GET /repos/{repo}/pages + repo: ${{ github.repository }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Setup Pages uses: actions/configure-pages@v4 + if: steps.check_pages.outcome == 'success' - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: @@ -35,3 +45,4 @@ jobs: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 + if: steps.check_pages.outcome == 'success' From cd5a862b95af54b2b8b768ee176f9e6538346665 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Wed, 26 Jun 2024 23:44:01 +0200 Subject: [PATCH 18/26] Fix Megas gaining abilities after fainting (#4873) --- include/battle_util.h | 2 +- src/battle_util.c | 12 ++++++++---- test/battle/form_change/mega_evolution.c | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/include/battle_util.h b/include/battle_util.h index da47b3444b..2866d9072f 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -190,7 +190,7 @@ bool32 IsBattlerMegaEvolved(u32 battler); bool32 IsBattlerPrimalReverted(u32 battler); bool32 IsBattlerUltraBursted(u32 battler); u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method); -bool32 TryBattleFormChange(u32 battler, u16 method); +bool32 TryBattleFormChange(u32 battler, u32 method); bool32 DoBattlersShareType(u32 battler1, u32 battler2); bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId); u32 GetIllusionMonSpecies(u32 battler); diff --git a/src/battle_util.c b/src/battle_util.c index 3f6067c396..ffa81eb2a7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10568,12 +10568,12 @@ bool32 CanBattlerFormChange(u32 battler, u16 method) return DoesSpeciesHaveFormChangeMethod(gBattleMons[battler].species, method); } -bool32 TryBattleFormChange(u32 battler, u16 method) +bool32 TryBattleFormChange(u32 battler, u32 method) { - u8 monId = gBattlerPartyIndexes[battler]; - u8 side = GetBattlerSide(battler); + u32 monId = gBattlerPartyIndexes[battler]; + u32 side = GetBattlerSide(battler); struct Pokemon *party = GetBattlerParty(battler); - u16 targetSpecies; + u32 targetSpecies; if (!CanBattlerFormChange(battler, method)) return FALSE; @@ -10611,10 +10611,14 @@ bool32 TryBattleFormChange(u32 battler, u16 method) if (restoreSpecies) { + u32 abilityForm = gBattleMons[battler].ability; // Reverts the original species TryToSetBattleFormChangeMoves(&party[monId], method); SetMonData(&party[monId], MON_DATA_SPECIES, &gBattleStruct->changedSpecies[side][monId]); RecalcBattlerStats(battler, &party[monId]); + // Battler data is not updated with regular form's ability, not doing so could cause wrong ability activation. + if (method == FORM_CHANGE_FAINT) + gBattleMons[battler].ability = abilityForm; return TRUE; } } diff --git a/test/battle/form_change/mega_evolution.c b/test/battle/form_change/mega_evolution.c index b2426933f7..a42c003a3d 100644 --- a/test/battle/form_change/mega_evolution.c +++ b/test/battle/form_change/mega_evolution.c @@ -153,3 +153,26 @@ SINGLE_BATTLE_TEST("Regular Mega Evolution and Fervent Wish Mega Evolution can h EXPECT_EQ(opponent->species, SPECIES_GARDEVOIR_MEGA); } } + +SINGLE_BATTLE_TEST("Mega Evolved Pokemon do not change abilities after fainting") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_CRUNCH].makesContact == TRUE); + ASSUME(gSpeciesInfo[SPECIES_GARCHOMP_MEGA].abilities[0] != ABILITY_ROUGH_SKIN); + ASSUME(gSpeciesInfo[SPECIES_GARCHOMP_MEGA].abilities[1] != ABILITY_ROUGH_SKIN); + ASSUME(gSpeciesInfo[SPECIES_GARCHOMP_MEGA].abilities[2] != ABILITY_ROUGH_SKIN); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GARCHOMP) { Ability(ABILITY_ROUGH_SKIN); Item(ITEM_GARCHOMPITE); HP(1); } + } WHEN { + TURN { MOVE(player, MOVE_CRUNCH); MOVE(opponent, MOVE_CELEBRATE, megaEvolve: TRUE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CRUNCH, player); + MESSAGE("Foe Garchomp fainted!"); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_ROUGH_SKIN); + MESSAGE("Wobbuffet was hurt by Foe Garchomp's Rough Skin!"); + HP_BAR(player); + } + } +} From 26ef19d6c0048d26027746ce7cd8047ee1b4317e Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Thu, 27 Jun 2024 11:33:28 +0200 Subject: [PATCH 19/26] Fix wrong palette in palyer backsprite in recorded battle (#4875) --- src/battle_controller_recorded_player.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index 53860990c4..3a9ca1ec00 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -517,7 +517,7 @@ static void RecordedPlayerHandleIntroTrainerBallThrow(u32 battler) else trainerPicId = gSaveBlock2Ptr->playerGender + TRAINER_BACK_PIC_BRENDAN; - trainerPal = gTrainerSprites[trainerPicId].palette.data; + trainerPal = gTrainerBacksprites[trainerPicId].palette.data; BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F9, trainerPal, 24, Intro_TryShinyAnimShowHealthbox); } From 75be596933100a3e5c607f5189a4f5575549fe8d Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Sat, 29 Jun 2024 19:54:58 +0300 Subject: [PATCH 20/26] fix sticky hold softlock (#4885) --- data/battle_scripts_1.s | 2 +- src/battle_script_commands.c | 2 +- test/battle/ability/sticky_hold.c | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 test/battle/ability/sticky_hold.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 1bbb759460..8bceedfa47 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8573,7 +8573,7 @@ BattleScript_SynchronizeActivates:: return BattleScript_NoItemSteal:: - pause B_WAIT_TIME_SHORT + call BattleScript_AbilityPopUpTarget printstring STRINGID_PKMNSXMADEYINEFFECTIVE waitmessage B_WAIT_TIME_LONG return diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 83c910f6b0..eaa69de10d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3454,7 +3454,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) } else if (GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD) { - BattleScriptPushCursor(); + BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_NoItemSteal; gLastUsedAbility = gBattleMons[gBattlerTarget].ability; diff --git a/test/battle/ability/sticky_hold.c b/test/battle/ability/sticky_hold.c new file mode 100644 index 0000000000..2c7cc09535 --- /dev/null +++ b/test/battle/ability/sticky_hold.c @@ -0,0 +1,18 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Sticky Hold prevents item theft") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_THIEF, MOVE_EFFECT_STEAL_ITEM)); + PLAYER(SPECIES_URSALUNA) { Item(ITEM_NONE); } + OPPONENT(SPECIES_GASTRODON) { Ability(ABILITY_STICKY_HOLD); Item(ITEM_LIFE_ORB); } + } WHEN { + TURN { MOVE(player, MOVE_THIEF); } + } SCENE { + MESSAGE("Ursaluna used Thief!"); + ABILITY_POPUP(opponent, ABILITY_STICKY_HOLD); + MESSAGE("Foe Gastrodon's Sticky Hold made Thief ineffective!"); + } +} + From 3ec371de054deec0f4c0824316ca3509a5a71c18 Mon Sep 17 00:00:00 2001 From: Cafei <46283144+cafei-uh@users.noreply.github.com> Date: Sat, 29 Jun 2024 22:46:00 +0400 Subject: [PATCH 21/26] Fixes (#4887) --- graphics/pokemon/bounsweet/front.png | Bin 451 -> 388 bytes graphics/pokemon/bounsweet/normal.pal | 2 +- graphics/pokemon/bounsweet/shiny.pal | 4 ++-- graphics/pokemon/bruxish/back.png | Bin 708 -> 620 bytes graphics/pokemon/bruxish/front.png | Bin 666 -> 571 bytes graphics/pokemon/bruxish/shiny.pal | 4 ++-- graphics/pokemon/charjabug/anim_front.png | Bin 837 -> 816 bytes graphics/pokemon/charjabug/shiny.pal | 4 ++-- graphics/pokemon/lurantis/back.png | Bin 898 -> 820 bytes 9 files changed, 7 insertions(+), 7 deletions(-) diff --git a/graphics/pokemon/bounsweet/front.png b/graphics/pokemon/bounsweet/front.png index a31220dcf963253deca2ed8a627cb9aafbd96f90..9e09d6d728ff656d01aa26ca68d6539ebc8190e0 100644 GIT binary patch delta 327 zcmV-N0l5Cd1B3&R7#0Wv0001;w}I>c0004VQb$4nuFf3kkv=ki`1tq$005i=5^?|l z0Rl-xK~z|U?Ugx#!ypVr5oqyDfcqaeAYh!Ou^?53Us=U_5?~wWc6N5YKLD`C`w#$E z1R<;$R1lU%s0zPgaJVXP=qCjL?F>~g3JV@|^;iY12o^;nf;I;4L(gRccD!SD16BeC zRTg45;0bvoIxE9}iGc6zsf35|x^dc}B3jZ?wK<&k9Pt0AU=#|gd|Vr-pLeK35;r-9oaAu`7%xvQafMdr1;$<2(vv$OL* Za|hZT3)>qZz!d-h002ovPDHLkV1hAngqZ*U delta 390 zcmV;10eSv}1H%K57$yV*00013M{Ml?0004VQb$4nuFf3k00004XF*Lt006O%3;baP zkuEZS00000003T8tkM7g010qNS#tmY4#NNd4#NS*Z>VGd00A6HL_t(Y$L*6bP6IIz zMQ0^TX(({Q33jQ)mLasaeE@A<+f<CM8Ir+iA9LYwR_)gy!YWgYQWX*nYD{iZs3mnH~V`v0AT(ZWBiDy`CzUQ=l%_J k@xR7ro)#bs8l(x}2m0M%Qv#UqaR2}S07*qoM6N<$f?@Wi1ONa4 diff --git a/graphics/pokemon/bounsweet/normal.pal b/graphics/pokemon/bounsweet/normal.pal index 6ba3b9fb34..029ef53c85 100644 --- a/graphics/pokemon/bounsweet/normal.pal +++ b/graphics/pokemon/bounsweet/normal.pal @@ -15,5 +15,5 @@ JASC-PAL 248 136 136 88 104 96 184 192 192 -0 0 0 +248 248 248 0 0 0 diff --git a/graphics/pokemon/bounsweet/shiny.pal b/graphics/pokemon/bounsweet/shiny.pal index 14fc1f5ab3..704d72a19a 100644 --- a/graphics/pokemon/bounsweet/shiny.pal +++ b/graphics/pokemon/bounsweet/shiny.pal @@ -14,6 +14,6 @@ JASC-PAL 248 224 40 248 136 136 88 104 96 -184 192 192 -0 0 0 +200 192 128 +247 240 184 0 0 0 diff --git a/graphics/pokemon/bruxish/back.png b/graphics/pokemon/bruxish/back.png index 32aeed0b52920041c7d460e0d7a51e7cda87cbac..7969fb7197e2c4d42d8caa3804124d5a7a27d9de 100644 GIT binary patch delta 576 zcmV-G0>Ayl1?&Wn7#0Wv0001;w}I>c0004VQb$4nuFf3kkv=1T`1tt9z_7@mQ0UNz z`1tsckdVlzIK=1EHvj+u>PbXFR9J=WmfMcQAPhtcF<>mB{QuvsFF;7rm2!F6l`;=) zm3B@BW0QV9uU`EJIng8k#Cm`L@lSrbOK{+x5x_qNJS02@tc2SD*9dp_K?r`DV!Yi7 zAc&Y!yzE4gg>d121^_dQ1QBA4*9n*@D?-hIUoHcb5P9`Ymk6MD4}<*D;MmZ>u%O7GQ0UNz`1tsckdVlzI11{m)&Kwi32;bRa{vGi!vFvd!vV){sAK>D z0v$<2K~zY`?UTQ5(@+$~UCBa>br7&rLcSrD7~}f_PMV=~Vd2J7moi}c1xRj*Ds^DM z&4L7K;aV{Cj~sm8qDxy#S7Ku9TeMWFBAIi4ZsH)6+MQTXKY9Cp`p&uM92!n`3Q3;6P}8A#E0=ax^yxJtYUzGK|m9Sv^gKK_C4VU zh!J0nsk3x8Um+}jvrhZjRu;`XtA+q{Pyp&<&teC#)M3QQ`W1x$t5Pp+ku9oP7)ONfB4 yMLAPMo=v`++VtRSUp-06RkVf+UE>No74c0004VQb$4nuFf3kkv=|u0l7&;K~z|U?Ujvg zgD?z)mmfQc6Z-y-JD(FMYGEB9O`612v?A?&cI@DOecifs>o4S-yJq(x#0&nQ{@_m& zy8kl3pWcHD7XN$(9dKm_WcJg3PKhqqJ3#FjI_Xc5nTtw}B69cRN|Z^IafpyhhL=)p zt?5mo!vKq2n|8r}07nU#izop8kP84n9H;{dCq02+2_z9g63>48_>}j;uM-ekAiT$J z58m$*7{3?WfrxgnnctSQ9{|7}cIO1Qsu`mJ9@5Vz0Huh|9#ZM7cjdN-N}8q@0FJi? zx0Ovm#y1Oq0H|{fhIr4_%X(Jb4F< z42fpKh}e75Z*UR&9RftF6b4ZN&ZK*))%+fajXgZd1jJ0JsTjeH*%p;R#4#G-ebVt` z!i)7cyrfV{ay|@z#aEvp-u#hhDncurD1zDO;OAkC8>23g)^A8X3P3=uNz@{W zCBSYY@QM&pdlM)weh_f42p!M*{Ct?Pu?sz#0bRE2xANZMy!cm}|D`n6CI2e}#XIu{ pH%fv-^Qn5DIp^)SZr%Dn`Ua6A8ihqlowWb}002ovPDHLkV1lN1<*Wbz delta 595 zcmV-Z0<8VJ1eyhq7$yV*00013M{Ml?0004VQb$4nuFf3k00004XF*Lt006O%3;baP zkuE-e010qNS#tmY4#NNd4#NS*Z>VGd00Hw!L_t(Y$L*89Z__{&$Ins}WyFM$rI$<{ zeHl=v2;I_TByN?lf!Zn(NJw0snlP}?9}){vQF2zcf`Xlukl`{BRW0((MIG9mq5lKV zp>h)xJ2R-?$2(y)rTKbZ$3X--F4T|K-$2Qut+m zlBaBTH?m=?KmJZ>Zwi1oGA$My%>eH;>ZEgd1Ar(nU(NtIDI?(HWp9KT0QGcD%J|1P z1V3>^Y+h)9px@Sd)SM}h{pmnc%AVFgdKd-J*I{!t_!I+JQPi!89M9s9}TBTj0g7^X;gAN4N{{D$iI++Z&ux8Urt{uFh=-L%?q5KJH~n z4WAKC+C-vEuot=qc99akqiMt0ssHE zqn2zgNyZm;U$)krN`{)ak7YT-q}_E*O-)VzkT~d1ybdB!jTz%z5XZ}6gqSh&g;9fu zHQy&UL@z(HcK46VW^S<&c{GQu_@elcV|?z^wssa>@WcmXAWjeoX#< z0B|niF@XLifTb;*0>}sOHvroUIZ6QNHIDcj;HLmDfN|uBuaF3k;)?+y&#{yU5Nid% z%ag-`Zh&+X{cAP={0b1g0XPZRwt1e({NDh2mq_h`bKAE4Qh^lD1po{UJ)qY-p8$Ai z$pN%wwhIE#13>B`5lj4W?f?EZSu6e>o8=H!I)m2=m_SG{*3!VfnB-~ zSH$_+KN)_HegROOJu;+h{Rj8JeK3ZC#x*rHH8nLg{a=c)zdrq6V}H1)$^J)wd_H2h zUX&s*mcKAct2mwRepR@TTl{{5>1+1*;F|c(G6F$Tbf5m(_K`b}7W5Fld{+W&^5D&8 z=kMv6?c}2n@QpyO9N;JZatc!p@GnHYcSa3hLQ?eJT@mDJDp0_V@O?1ufOPKj)2x2U zE7KksS3o*aJH$u<2FY0hm^Aj?0UG|Lgd_ld04z3YLr(x3An9Kr@)}#^MH)KJ7XWc?6Oi># zfJSHR;)YEL>0JYGW)s0+TY)_67)Rqj$Fqw(KnNjvdqn3&+XkyvUzC7Jk)2V4`d125 zBLhp13RCDAodaOd@L!SOI~{)aca&a!vOF9w$+hrE=lSOyMK1pCzF(EIiF-XO?%vRO-)Tr|ByK7PrMEy zQH>e*yC9C2#RxH@@`X`@h&|tn8={x5S-X4FiRgQm1K?(B{u9wVfBWW;Du3zy29e_E zz6Nj`KrBufz{m2@h}e(f4*=&P9s}ra0$9evDS*5Ie*>_+kfQ{EUgL=00e%Yb0vJc0 z_zH;tX}%aB@*G=<0I^m8ygWH9=mtm+(Z6N`z^?$&8-SC5ZJXzr%>NCbpAxBEaBkbS zUpkQHISW|z)-VHlf6em=fR~mWK(spB1p(-SW(q4%044n$Ez3fogMDXR&_-VUC{F8; z+ynsD0RZoJ0M_tE3dLz%hau-*2c{kN-OIZML$S|i{lS{F1F+)bocxZ*E>)K}c`Kf_ zcO7mSE;)*!m`zOQi(Juh1vqy%sf31Ju9=H$2P%yZrrlzK* zrl$W(G4|J||7+|I7d6@ch|fn1*NajF?&U9x(k@P?yI&P9D-9ux^3uS zm*-^JnFD~;o;*I#rJppWh zq<@9TYiyMl8R$4)0K~aXK-NP88lAC=8#cvGpez7qYc>%KwiU?3j&UUZTYA@{hde+C zA$og6=S9Z`t9D*yQV3|>23O%E901O)bD-wLCx8MC;WeUcI*v35$ n&;y(Ki8nUUl6zBAQ-yv2tU4j6S=-x<00000NkvXXu0mjfX)A4Y diff --git a/graphics/pokemon/charjabug/shiny.pal b/graphics/pokemon/charjabug/shiny.pal index 58974fb24f..a5ffc233bc 100644 --- a/graphics/pokemon/charjabug/shiny.pal +++ b/graphics/pokemon/charjabug/shiny.pal @@ -15,5 +15,5 @@ JASC-PAL 248 160 72 112 72 24 176 112 48 -0 0 0 -0 0 0 +8 64 88 +176 168 176 diff --git a/graphics/pokemon/lurantis/back.png b/graphics/pokemon/lurantis/back.png index a080a77f099511c4d5bb9b31787a80276e082415..d133d63f7a81ff5a1d586b192ace7046f659b8af 100644 GIT binary patch delta 750 zcmVc0004VQb$4nuFf3kkv=|u0%SVYt(=eZzu3lbTKG1GuVOB|Iv){` zIWp($!mINU0l6DyCk~k{mTGJW0p7`;Cw)Lmx8bOs@Kiz=!a>2h-bYUVnj?6?_la^2f&7aOKhq^+#SI#{A>Y10AZBixJQ7i-vBK43?P~T zb6XC8SppG%+#W5@=Ls+pNXx&BmjWFDZjf63{yAhd;IhXrD@YvIsL7m&RfRsG(@-24P2I1&Fan6Of#R5MdizyyI!0 z1MnIN@lycwJ0O*F#*m-bfd;5f-$leyLqu7H@c9Bre9iU(q=(D^I?&`-KvESIFdicZ ze3bE;2qM6L0uCK;h(=HW6`)0wN{9S*ih^i!ih@u91t0}VckW8(LvYV5LGUXr7eGtH zD!l^qiDF)5vfkv~l|?+x?>(uZKe8lLV)<8~05S^rgutDz1XcmG{EVh;|KKyPJPiha zExa9|Fm?o@k)YE*00go9am|A%Adi5YcYPQo2mxPz3&8f(I!Va50n*|atAx!X2a^P9 zOy2Y<^Fhah1dg2v4FDN524F8i|1=S9026M&LPG7}NazW%+|FLoMCg&U0uZILv9|wx zxbw&%YJ%RhGr%SpM5&Ag)a7#j0UH5qvN-y`(FG(cV3Yut&tIa(5D7exb-kGzy9M-( zR~gVvD-ji8SB9&>R$w-!Y#|=Otk+W38&XVGd00P@dL_t(Y$BmUSYui8+$K~nhmJ7u$)JwEv z@ftb@4hQ-LI-8m^C5sh;E~>P2O2=dhjOVz1MgmPKrp`!RcRNuQhNq3ehJrI6= z?|1M0@16V{{O20bgU0MEcO8aD)<0-1caboEZ2T8s?EyBS+8BQpPB#4%MB!R$0F1g1 zjKR$pXhvYn`YB)^H+}*JU-p1V{kDEG1Osxt*%iT!KL}!DocLMvz7Bv~C-3|e+HGoB z;MxsS+5!V=fzM72!q{m8Tr#SQU3tKqO}Q0u$=I+5SOpr08S7^VXbaGhHGF_rfKzsV zrVl(j@PVerj4`Io*=z?ejp8o>u;pI)Krqt86pnLdlU@6r3NP1IJ1 z(7Q|M@n>N95`dj11C+-XP1AAui~913)@&oKgd*h3yAEu?PHX1;Jl^;ER*dz3Da#00000NkvXX Hu0mjfxVmc* From 6957fc70c901c84e9a246b3d1d5f5a251ed19ae4 Mon Sep 17 00:00:00 2001 From: Frank DeBlasio <35279583+fdeblasio@users.noreply.github.com> Date: Sat, 29 Jun 2024 16:12:26 -0400 Subject: [PATCH 22/26] Allowed rematches to occur if I_VS_SEEKER_CHARGING isn't enabled (#4866) --- include/vs_seeker.h | 1 + src/battle_setup.c | 19 ++++++++++--------- src/vs_seeker.c | 8 ++++++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/vs_seeker.h b/include/vs_seeker.h index 723e73bf37..d6795432b0 100644 --- a/include/vs_seeker.h +++ b/include/vs_seeker.h @@ -8,6 +8,7 @@ bool8 UpdateVsSeekerStepCounter(void); void MapResetTrainerRematches(u16 mapGroup, u16 mapNum); void ClearRematchMovementByTrainerId(void); u16 GetRematchTrainerIdVSSeeker(u16 trainerId); +bool32 IsVsSeekerEnabled(void); #define VSSEEKER_RECHARGE_STEPS 100 diff --git a/src/battle_setup.c b/src/battle_setup.c index 92fc2bea25..2fe016ec87 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -1916,15 +1916,16 @@ static bool32 HasAtLeastFiveBadges(void) void IncrementRematchStepCounter(void) { #if FREE_MATCH_CALL == FALSE - if (HasAtLeastFiveBadges() - && (I_VS_SEEKER_CHARGING != 0) - && (!CheckBagHasItem(ITEM_VS_SEEKER, 1))) - { - if (gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX) - gSaveBlock1Ptr->trainerRematchStepCounter = STEP_COUNTER_MAX; - else - gSaveBlock1Ptr->trainerRematchStepCounter++; - } + if (!HasAtLeastFiveBadges()) + return; + + if (IsVsSeekerEnabled()) + return; + + if (gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX) + gSaveBlock1Ptr->trainerRematchStepCounter = STEP_COUNTER_MAX; + else + gSaveBlock1Ptr->trainerRematchStepCounter++; #endif //FREE_MATCH_CALL } diff --git a/src/vs_seeker.c b/src/vs_seeker.c index 63b6c6f9d5..30dcb74ce0 100644 --- a/src/vs_seeker.c +++ b/src/vs_seeker.c @@ -577,6 +577,14 @@ u16 GetRematchTrainerIdVSSeeker(u16 trainerId) return gRematchTable[tableId].trainerIds[rematchTrainerIdx]; } +bool32 IsVsSeekerEnabled(void) +{ + if (I_VS_SEEKER_CHARGING == 0) + return FALSE; + + return (CheckBagHasItem(ITEM_VS_SEEKER, 1)); +} + static bool8 ObjectEventIdIsSane(u8 objectEventId) { struct ObjectEvent *objectEvent = &gObjectEvents[objectEventId]; From d81662daa2666736c9b2df140f3ec5e6dfde9c94 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Tue, 2 Jul 2024 14:39:41 +0200 Subject: [PATCH 23/26] Add parentheses to species egg macro (#4899) --- include/constants/species.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/constants/species.h b/include/constants/species.h index d4dcc82115..7345513124 100644 --- a/include/constants/species.h +++ b/include/constants/species.h @@ -1629,7 +1629,7 @@ #define SPECIES_URSHIFU_RAPID_STRIKE_STYLE_GIGANTAMAX 1522 #define SPECIES_MIMIKYU_TOTEM_BUSTED 1523 -#define SPECIES_EGG SPECIES_MIMIKYU_TOTEM_BUSTED + 1 +#define SPECIES_EGG (SPECIES_MIMIKYU_TOTEM_BUSTED + 1) #define NUM_SPECIES SPECIES_EGG From 594633aa1502409d8883d9c06d936498a14d97ae Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 3 Jul 2024 10:48:31 +0200 Subject: [PATCH 24/26] Remove Duplicate ai code from battle_ai_util.c (#4883) * Remove Duplicate ai code from battle_ai_util.c * Add GetBattlerAbility in toxic self check --- include/battle_ai_util.h | 2 - include/battle_util.h | 8 +- src/battle_ai_main.c | 6 +- src/battle_ai_switch_items.c | 4 +- src/battle_ai_util.c | 66 ++-------------- src/battle_dynamax.c | 8 +- src/battle_script_commands.c | 23 +++--- src/battle_util.c | 128 ++++++++++++++++---------------- test/battle/ability/own_tempo.c | 17 +++++ test/battle/ai.c | 1 + test/battle/move_effect/toxic.c | 18 +++++ test/battle/status1/burn.c | 20 +++++ test/battle/status1/paralysis.c | 18 +++++ test/battle/status1/sleep.c | 18 +++++ 14 files changed, 185 insertions(+), 152 deletions(-) diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index bd2b8fb920..8ea2141aee 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -134,10 +134,8 @@ bool32 HasMagicCoatAffectedMove(u32 battler); bool32 HasSnatchAffectedMove(u32 battler); // status checks -bool32 AI_CanBeBurned(u32 battler, u32 ability); bool32 AI_CanGetFrostbite(u32 battler, u32 ability); bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability); -bool32 AI_CanSleep(u32 battler, u32 ability); bool32 IsBattlerIncapacitated(u32 battler, u32 ability); bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove); bool32 ShouldPoisonSelf(u32 battler, u32 ability); diff --git a/include/battle_util.h b/include/battle_util.h index 2866d9072f..fc1fdce996 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -235,10 +235,10 @@ bool32 MoveHasAdditionalEffectSelf(u32 move, u32 moveEffect); bool32 MoveHasAdditionalEffectSelfArg(u32 move, u32 moveEffect, u32 argument); bool32 MoveHasChargeTurnAdditionalEffect(u32 move); -bool32 CanSleep(u32 battler); -bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget); -bool32 CanBeBurned(u32 battler); -bool32 CanBeParalyzed(u32 battler); +bool32 CanBeSlept(u32 battler, u32 ability); +bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility); +bool32 CanBeBurned(u32 battler, u32 ability); +bool32 CanBeParalyzed(u32 battler, u32 ability); bool32 CanBeFrozen(u32 battler); bool32 CanGetFrostbite(u32 battler); bool32 CanBeConfused(u32 battler); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 28823822ce..45761ee329 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1866,7 +1866,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_REST: - if (!AI_CanSleep(battlerAtk, aiData->abilities[battlerAtk])) + if (!CanBeSlept(battlerAtk, aiData->abilities[battlerAtk])) ADJUST_SCORE(-10); //fallthrough case EFFECT_RESTORE_HP: @@ -3456,7 +3456,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) } break; case EFFECT_REST: - if (!(AI_CanSleep(battlerAtk, aiData->abilities[battlerAtk]))) + if (!(CanBeSlept(battlerAtk, aiData->abilities[battlerAtk]))) { break; } @@ -3936,7 +3936,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(DECENT_EFFECT); break; case HOLD_EFFECT_FLAME_ORB: - if (!ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]) && AI_CanBeBurned(battlerAtk, aiData->abilities[battlerDef])) + if (!ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]) && CanBeBurned(battlerAtk, aiData->abilities[battlerDef])) ADJUST_SCORE(DECENT_EFFECT); break; case HOLD_EFFECT_BLACK_SLUDGE: diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 093db185d9..9b87c13d5a 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -442,7 +442,7 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult) { //Yawn if (gStatuses3[battler] & STATUS3_YAWN - && AI_CanSleep(battler, monAbility) + && CanBeSlept(battler, monAbility) && gBattleMons[battler].hp > gBattleMons[battler].maxHP / 3) { switchMon = TRUE; @@ -1291,8 +1291,6 @@ static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon hazardDamage += spikesDamage; } - // Toxic Spikes - // TODO: CanBePoisoned compatibility to avoid duplicate code if ((hazardFlags & SIDE_STATUS_TOXIC_SPIKES) && (defType1 != TYPE_POISON && defType2 != TYPE_POISON && defType1 != TYPE_STEEL && defType2 != TYPE_STEEL && ability != ABILITY_IMMUNITY && ability != ABILITY_POISON_HEAL && ability != ABILITY_COMATOSE diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index ea50b636a6..28b1b45ff0 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2624,48 +2624,18 @@ bool32 IsBattlerIncapacitated(u32 battler, u32 ability) return FALSE; } -bool32 AI_CanSleep(u32 battler, u32 ability) -{ - if (ability == ABILITY_INSOMNIA - || ability == ABILITY_VITAL_SPIRIT - || ability == ABILITY_COMATOSE - || gBattleMons[battler].status1 & STATUS1_ANY - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || (gFieldStatuses & (STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_ELECTRIC_TERRAIN)) - || IsAbilityStatusProtected(battler)) - return FALSE; - return TRUE; -} - bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove) { - if (!AI_CanSleep(battlerDef, defAbility) + if (!CanBeSlept(battlerDef, defAbility) || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) // shouldn't try to sleep mon that partner is trying to make sleep return FALSE; return TRUE; } -static bool32 AI_CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 move) -{ - u32 ability = AI_DATA->abilities[battlerDef]; - - if (!(CanPoisonType(battlerAtk, battlerDef)) - || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD - || gBattleMons[battlerDef].status1 & STATUS1_ANY - || ability == ABILITY_IMMUNITY - || ability == ABILITY_COMATOSE - || AI_IsAbilityOnSide(battlerDef, ABILITY_PASTEL_VEIL) - || gBattleMons[battlerDef].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battlerDef) - || AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) - return FALSE; - return TRUE; -} - bool32 ShouldPoisonSelf(u32 battler, u32 ability) { - if (AI_CanBePoisoned(battler, battler, 0) && ( + if (CanBePoisoned(battler, battler, GetBattlerAbility(battler)) && ( ability == ABILITY_MARVEL_SCALE || ability == ABILITY_POISON_HEAL || ability == ABILITY_QUICK_FEET @@ -2680,7 +2650,7 @@ bool32 ShouldPoisonSelf(u32 battler, u32 ability) bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove) { - if (!AI_CanBePoisoned(battlerAtk, battlerDef, move) + if (!CanBePoisoned(battlerAtk, battlerDef, GetBattlerAbility(battlerDef)) || AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0 || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) @@ -2693,20 +2663,9 @@ bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u3 return TRUE; } -static bool32 AI_CanBeParalyzed(u32 battler, u32 ability) -{ - if (ability == ABILITY_LIMBER - || ability == ABILITY_COMATOSE - || IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC) - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battler)) - return FALSE; - return TRUE; -} - bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove) { - if (!AI_CanBeParalyzed(battlerDef, defAbility) + if (!CanBeParalyzed(battlerDef, defAbility) || AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0 || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) @@ -2740,19 +2699,6 @@ bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battler return TRUE; } -bool32 AI_CanBeBurned(u32 battler, u32 ability) -{ - if (ability == ABILITY_WATER_VEIL - || ability == ABILITY_WATER_BUBBLE - || ability == ABILITY_COMATOSE - || IS_BATTLER_OF_TYPE(battler, TYPE_FIRE) - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battler) - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD) - return FALSE; - return TRUE; -} - bool32 AI_CanGetFrostbite(u32 battler, u32 ability) { if (ability == ABILITY_MAGMA_ARMOR @@ -2767,7 +2713,7 @@ bool32 AI_CanGetFrostbite(u32 battler, u32 ability) bool32 ShouldBurnSelf(u32 battler, u32 ability) { - if (AI_CanBeBurned(battler, ability) && ( + if (CanBeBurned(battler, ability) && ( ability == ABILITY_QUICK_FEET || ability == ABILITY_HEATPROOF || ability == ABILITY_MAGIC_GUARD @@ -2781,7 +2727,7 @@ bool32 ShouldBurnSelf(u32 battler, u32 ability) bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove) { - if (!AI_CanBeBurned(battlerDef, defAbility) + if (!CanBeBurned(battlerDef, defAbility) || AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0 || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(battlerAtkPartner, battlerDef, partnerMove)) diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index 557ed604fe..8dbde6d821 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -791,7 +791,7 @@ void BS_SetMaxMoveEffect(void) { static const u8 sSnoozeEffects[] = {TRUE, FALSE}; if (!(gStatuses3[gBattlerTarget] & STATUS3_YAWN) - && CanSleep(gBattlerTarget) + && CanBeSlept(gBattlerTarget, GetBattlerAbility(gBattlerTarget)) && RandomElement(RNG_G_MAX_SNOOZE, sSnoozeEffects)) // 50% chance of success { gStatuses3[gBattlerTarget] |= STATUS3_YAWN_TURN(2); @@ -897,7 +897,7 @@ void BS_TrySetStatus1(void) switch (status1) { case STATUS1_POISON: - if (CanBePoisoned(gBattlerAttacker, gBattlerTarget)) + if (CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget))) { gBattleMons[gBattlerTarget].status1 |= STATUS1_POISON; gBattleCommunication[MULTISTRING_CHOOSER] = 0; @@ -905,7 +905,7 @@ void BS_TrySetStatus1(void) } break; case STATUS1_PARALYSIS: - if (CanBeParalyzed(gBattlerTarget)) + if (CanBeParalyzed(gBattlerTarget, GetBattlerAbility(gBattlerTarget))) { gBattleMons[gBattlerTarget].status1 |= STATUS1_PARALYSIS; gBattleCommunication[MULTISTRING_CHOOSER] = 3; @@ -913,7 +913,7 @@ void BS_TrySetStatus1(void) } break; case STATUS1_SLEEP: - if (CanSleep(gBattlerTarget)) + if (CanBeSlept(gBattlerTarget, GetBattlerAbility(gBattlerTarget))) { if (B_SLEEP_TURNS >= GEN_5) gBattleMons[gBattlerTarget].status1 |= STATUS1_SLEEP_TURN((Random() % 3) + 2); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index eaa69de10d..29737bde1f 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2907,7 +2907,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) if (i != gBattlersCount) break; - if (!CanSleep(gEffectBattler)) + if (!CanBeSlept(gEffectBattler, GetBattlerAbility(gEffectBattler))) break; cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler); @@ -2946,7 +2946,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT; RESET_RETURN } - if (!CanBePoisoned(gBattleScripting.battler, gEffectBattler)) + if (!CanBePoisoned(gBattleScripting.battler, gEffectBattler, GetBattlerAbility(gEffectBattler))) break; statusChanged = TRUE; @@ -2990,7 +2990,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) break; } - if (!CanBeBurned(gEffectBattler)) + if (!CanBeBurned(gEffectBattler, GetBattlerAbility(gEffectBattler))) break; statusChanged = TRUE; @@ -3055,7 +3055,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) } if (!CanParalyzeType(gBattleScripting.battler, gEffectBattler)) break; - if (!CanBeParalyzed(gEffectBattler)) + if (!CanBeParalyzed(gEffectBattler, GetBattlerAbility(gEffectBattler))) break; statusChanged = TRUE; @@ -3093,7 +3093,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) } if (gBattleMons[gEffectBattler].status1) break; - if (CanBePoisoned(gBattleScripting.battler, gEffectBattler)) + if (CanBePoisoned(gBattleScripting.battler, gEffectBattler, GetBattlerAbility(gEffectBattler))) { // It's redundant, because at this point we know the status1 value is 0. gBattleMons[gEffectBattler].status1 &= ~STATUS1_TOXIC_POISON; @@ -5514,7 +5514,7 @@ static void Cmd_moveend(void) } // Not strictly a protect effect, but works the same way else if (gProtectStructs[gBattlerTarget].beakBlastCharge - && CanBeBurned(gBattlerAttacker) + && CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; @@ -9940,16 +9940,17 @@ static void Cmd_various(void) case VARIOUS_PSYCHO_SHIFT: { VARIOUS_ARGS(const u8 *failInstr); + u32 targetAbility = GetBattlerAbility(gBattlerTarget); // Psycho shift works - if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget)) + if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 0; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 1; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 2; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 3; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanSleep(gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanBeSlept(gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 4; else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_FROSTBITE) && CanBeFrozen(gBattlerTarget)) gBattleCommunication[MULTISTRING_CHOOSER] = 5; diff --git a/src/battle_util.c b/src/battle_util.c index ffa81eb2a7..debcd6b6d2 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5455,8 +5455,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_EFFECT_SPORE: + { + u32 ability = GetBattlerAbility(gBattlerAttacker); if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GRASS) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_OVERCOAT + && ability != ABILITY_OVERCOAT && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES) { i = Random() % 3; @@ -5469,7 +5471,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED - && CanSleep(gBattlerAttacker) + && CanBeSlept(gBattlerAttacker, ability) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker) && (Random() % 3) == 0) @@ -5482,6 +5484,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } } + } break; POISON_POINT: case ABILITY_POISON_POINT: @@ -5489,7 +5492,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED - && CanBePoisoned(gBattlerTarget, gBattlerAttacker) + && CanBePoisoned(gBattlerTarget, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker) && RandomWeighted(RNG_POISON_POINT, 2, 1)) @@ -5508,7 +5511,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED - && CanBeParalyzed(gBattlerAttacker) + && CanBeParalyzed(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker) && RandomWeighted(RNG_STATIC, 2, 1)) @@ -5528,7 +5531,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && (IsMoveMakingContact(move, gBattlerAttacker)) && TARGET_TURN_DAMAGED - && CanBeBurned(gBattlerAttacker) + && CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) && RandomWeighted(RNG_FLAME_BODY, 2, 1)) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN; @@ -5745,7 +5748,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerTarget].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg - && CanBePoisoned(gBattlerAttacker, gBattlerTarget) + && CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget)) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker) && TARGET_TURN_DAMAGED // Need to actually hit the target @@ -6327,83 +6330,78 @@ bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag) return IsBattlerGrounded(battler); } -bool32 CanSleep(u32 battler) +bool32 CanBeSlept(u32 battler, u32 ability) { - u16 ability = GetBattlerAbility(battler); if (ability == ABILITY_INSOMNIA - || ability == ABILITY_VITAL_SPIRIT - || ability == ABILITY_COMATOSE - || ability == ABILITY_PURIFYING_SALT - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityOnSide(battler, ABILITY_SWEET_VEIL) - || IsAbilityStatusProtected(battler) - || IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_MISTY_TERRAIN)) - return FALSE; - return TRUE; -} - -bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget) -{ - u16 ability = GetBattlerAbility(battlerTarget); - - if (!(CanPoisonType(battlerAttacker, battlerTarget)) - || gSideStatuses[GetBattlerSide(battlerTarget)] & SIDE_STATUS_SAFEGUARD - || gBattleMons[battlerTarget].status1 & STATUS1_ANY - || ability == ABILITY_IMMUNITY + || ability == ABILITY_VITAL_SPIRIT || ability == ABILITY_COMATOSE || ability == ABILITY_PURIFYING_SALT - || IsAbilityOnSide(battlerTarget, ABILITY_PASTEL_VEIL) - || IsAbilityStatusProtected(battlerTarget) - || IsBattlerTerrainAffected(battlerTarget, STATUS_FIELD_MISTY_TERRAIN)) + || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD + || gBattleMons[battler].status1 & STATUS1_ANY + || IsAbilityOnSide(battler, ABILITY_SWEET_VEIL) + || IsAbilityStatusProtected(battler) + || IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } -bool32 CanBeBurned(u32 battler) +bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility) +{ + if (!(CanPoisonType(battlerAtk, battlerDef)) + || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD + || gBattleMons[battlerDef].status1 & STATUS1_ANY + || defAbility == ABILITY_IMMUNITY + || defAbility == ABILITY_COMATOSE + || defAbility == ABILITY_PURIFYING_SALT + || IsAbilityOnSide(battlerDef, ABILITY_PASTEL_VEIL) + || IsAbilityStatusProtected(battlerDef) + || IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) + return FALSE; + return TRUE; +} + +bool32 CanBeBurned(u32 battler, u32 ability) { - u16 ability = GetBattlerAbility(battler); if (IS_BATTLER_OF_TYPE(battler, TYPE_FIRE) + || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD + || gBattleMons[battler].status1 & STATUS1_ANY + || ability == ABILITY_WATER_VEIL + || ability == ABILITY_WATER_BUBBLE + || ability == ABILITY_COMATOSE + || ability == ABILITY_THERMAL_EXCHANGE + || ability == ABILITY_PURIFYING_SALT + || IsAbilityStatusProtected(battler) + || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) + return FALSE; + return TRUE; +} + +bool32 CanBeParalyzed(u32 battler, u32 ability) +{ + if ((B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC)) || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || gBattleMons[battler].status1 & STATUS1_ANY - || ability == ABILITY_WATER_VEIL - || ability == ABILITY_WATER_BUBBLE + || ability == ABILITY_LIMBER || ability == ABILITY_COMATOSE - || ability == ABILITY_THERMAL_EXCHANGE || ability == ABILITY_PURIFYING_SALT + || gBattleMons[battler].status1 & STATUS1_ANY || IsAbilityStatusProtected(battler) || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } -bool32 CanBeParalyzed(u32 battler) -{ - u16 ability = GetBattlerAbility(battler); - if ((B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC)) - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || ability == ABILITY_LIMBER - || ability == ABILITY_COMATOSE - || ability == ABILITY_PURIFYING_SALT - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battler) - || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) - return FALSE; - return TRUE; -} - bool32 CanBeFrozen(u32 battler) { u16 ability = GetBattlerAbility(battler); if (IS_BATTLER_OF_TYPE(battler, TYPE_ICE) - || IsBattlerWeatherAffected(battler, B_WEATHER_SUN) - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || ability == ABILITY_MAGMA_ARMOR - || ability == ABILITY_COMATOSE - || ability == ABILITY_PURIFYING_SALT - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battler) - || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) + || IsBattlerWeatherAffected(battler, B_WEATHER_SUN) + || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD + || ability == ABILITY_MAGMA_ARMOR + || ability == ABILITY_COMATOSE + || ability == ABILITY_PURIFYING_SALT + || gBattleMons[battler].status1 & STATUS1_ANY + || IsAbilityStatusProtected(battler) + || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } @@ -6426,8 +6424,8 @@ bool32 CanGetFrostbite(u32 battler) bool32 CanBeConfused(u32 battler) { if (GetBattlerAbility(battler) == ABILITY_OWN_TEMPO - || gBattleMons[battler].status2 & STATUS2_CONFUSION - || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) + || gBattleMons[battler].status2 & STATUS2_CONFUSION + || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } @@ -7907,7 +7905,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) switch (battlerHoldEffect) { case HOLD_EFFECT_TOXIC_ORB: - if (CanBePoisoned(battler, battler)) + if (CanBePoisoned(battler, battler, GetBattlerAbility(battler))) { effect = ITEM_STATUS_CHANGE; gBattleMons[battler].status1 = STATUS1_TOXIC_POISON; @@ -7916,7 +7914,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) } break; case HOLD_EFFECT_FLAME_ORB: - if (CanBeBurned(battler)) + if (CanBeBurned(battler, battlerAbility)) { effect = ITEM_STATUS_CHANGE; gBattleMons[battler].status1 = STATUS1_BURN; @@ -8185,7 +8183,7 @@ u8 IsMonDisobedient(void) obedienceLevel = levelReferenced - obedienceLevel; calc = (Random() & 255); - if (calc < obedienceLevel && CanSleep(gBattlerAttacker)) + if (calc < obedienceLevel && CanBeSlept(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))) { // try putting asleep int i; diff --git a/test/battle/ability/own_tempo.c b/test/battle/ability/own_tempo.c index d5a6f58a72..a37bc0024a 100644 --- a/test/battle/ability/own_tempo.c +++ b/test/battle/ability/own_tempo.c @@ -56,6 +56,23 @@ SINGLE_BATTLE_TEST("Own Tempo prevents confusion from moves by the user") } } +SINGLE_BATTLE_TEST("Own Tempo is ignored by Mold Breaker") +{ + KNOWN_FAILING; // Ideally the func CanBeConfused should be split into AttackerCanBeConfused and TargetCanBeConfused or we do it in the same func but have a check for when battlerAtk == battlerDef + GIVEN { + ASSUME(gMovesInfo[MOVE_CONFUSE_RAY].effect == EFFECT_CONFUSE); + PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); } + OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); }; + } WHEN { + TURN { MOVE(player, MOVE_CONFUSE_RAY); } + } SCENE { + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO); + MESSAGE("Foe Slowpoke's Own Tempo prevents confusion!"); + } + } +} + SINGLE_BATTLE_TEST("Own Tempo cures confusion obtained from an opponent with Mold Breaker") { KNOWN_FAILING; diff --git a/test/battle/ai.c b/test/battle/ai.c index 31a354b52c..e98cb7b08e 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -1039,3 +1039,4 @@ AI_SINGLE_BATTLE_TEST("AI avoids contact moves against rocky helmet") TURN { EXPECT_MOVES(opponent, MOVE_LEAFAGE, MOVE_BRANCH_POKE); } } } + diff --git a/test/battle/move_effect/toxic.c b/test/battle/move_effect/toxic.c index 35bfaa0644..804ed56b8f 100644 --- a/test/battle/move_effect/toxic.c +++ b/test/battle/move_effect/toxic.c @@ -48,3 +48,21 @@ SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type") } } } + +AI_SINGLE_BATTLE_TEST("AI avoids toxic when it can not poison target") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_SNORLAX; ability = ABILITY_IMMUNITY; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; } + PARAMETRIZE { species = SPECIES_BULBASAUR; ability = ABILITY_OVERGROW; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_TOXIC); } + } WHEN { + TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_TOXIC); } // Both get -10 + } +} diff --git a/test/battle/status1/burn.c b/test/battle/status1/burn.c index c49bd7a3f2..4da40589fb 100644 --- a/test/battle/status1/burn.c +++ b/test/battle/status1/burn.c @@ -35,3 +35,23 @@ SINGLE_BATTLE_TEST("Burn reduces Attack by 50%", s16 damage) EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); } } + +AI_SINGLE_BATTLE_TEST("AI avoids Will-o-Wisp when it can not burn target") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_BUIZEL; ability = ABILITY_WATER_VEIL; } + PARAMETRIZE { species = SPECIES_DEWPIDER; ability = ABILITY_WATER_BUBBLE; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_ARCTIBAX; ability = ABILITY_THERMAL_EXCHANGE; } + PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; } + PARAMETRIZE { species = SPECIES_CHARMANDER; ability = ABILITY_BLAZE; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_WILL_O_WISP); } + } WHEN { + TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_WILL_O_WISP); } // Both get -10 + } +} diff --git a/test/battle/status1/paralysis.c b/test/battle/status1/paralysis.c index b6edaf2635..558ce4fc37 100644 --- a/test/battle/status1/paralysis.c +++ b/test/battle/status1/paralysis.c @@ -42,3 +42,21 @@ SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn") MESSAGE("Wobbuffet is paralyzed! It can't move!"); } } + +AI_SINGLE_BATTLE_TEST("AI avoids Thunder Wave when it can not paralyse target") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_HITMONLEE; ability = ABILITY_LIMBER; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; } + PARAMETRIZE { species = SPECIES_PIKACHU; ability = ABILITY_STATIC; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_THUNDER_WAVE); } + } WHEN { + TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_THUNDER_WAVE); } // Both get -10 + } +} diff --git a/test/battle/status1/sleep.c b/test/battle/status1/sleep.c index b3dd403eb1..f42f4bd714 100644 --- a/test/battle/status1/sleep.c +++ b/test/battle/status1/sleep.c @@ -21,3 +21,21 @@ SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move") MESSAGE("Wobbuffet used Celebrate!"); } } + +AI_SINGLE_BATTLE_TEST("AI avoids hypnosis when it can not put target to sleep") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_HOOTHOOT; ability = ABILITY_INSOMNIA; } + PARAMETRIZE { species = SPECIES_MANKEY; ability = ABILITY_VITAL_SPIRIT; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_HYPNOSIS); } + } WHEN { + TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_HYPNOSIS); } // Both get -10 + } +} From dd7b73521141a2c252ffc31ce80e941f4e27994b Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 3 Jul 2024 22:53:15 +0200 Subject: [PATCH 25/26] Fixes Grassy Terrain heal turn duration (#4903) --- data/battle_scripts_1.s | 10 +++++++++- include/battle_scripts.h | 1 + src/battle_util.c | 5 ++++- test/battle/terrain/grassy.c | 24 ++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 8bceedfa47..028c85b05c 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -5931,6 +5931,10 @@ BattleScript_MagicRoomEnds:: waitmessage B_WAIT_TIME_LONG end2 +BattleScript_GrassyTerrainEnds:: + call BattleScript_GrassyTerrainHeals_Ret + goto BattleScript_TerrainEnds + BattleScript_TerrainEnds_Ret:: printfromtable gTerrainStringIds waitmessage B_WAIT_TIME_LONG @@ -8225,6 +8229,10 @@ BattleScript_MoveUsedPsychicTerrainPrevents:: goto BattleScript_MoveEnd BattleScript_GrassyTerrainHeals:: + call BattleScript_GrassyTerrainHeals_Ret + end2 + +BattleScript_GrassyTerrainHeals_Ret:: setbyte gBattleCommunication, 0 BattleScript_GrassyTerrainLoop: copyarraywithindex gBattlerAttacker, gBattlerByTurnOrder, gBattleCommunication, 1 @@ -8242,7 +8250,7 @@ BattleScript_GrassyTerrainLoopEnd:: bicword gHitMarker, HITMARKER_IGNORE_BIDE | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE jumpifword CMP_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_PERMANENT, BattleScript_GrassyTerrainHealEnd BattleScript_GrassyTerrainHealEnd: - end2 + return BattleScript_AbilityNoSpecificStatLoss:: pause B_WAIT_TIME_SHORT diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 1fd77449f6..148a91783b 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -265,6 +265,7 @@ extern const u8 BattleScript_WonderRoomEnds[]; extern const u8 BattleScript_MagicRoomEnds[]; extern const u8 BattleScript_TerrainEnds[]; extern const u8 BattleScript_TerrainEnds_Ret[]; +extern const u8 BattleScript_GrassyTerrainEnds[]; extern const u8 BattleScript_MudSportEnds[]; extern const u8 BattleScript_WaterSportEnds[]; extern const u8 BattleScript_SturdiedMsg[]; diff --git a/src/battle_util.c b/src/battle_util.c index debcd6b6d2..e785679de2 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1716,7 +1716,10 @@ static bool32 EndTurnTerrain(u32 terrainFlag, u32 stringTableId) gFieldStatuses &= ~terrainFlag; TryToRevertMimicryAndFlags(); gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId; - BattleScriptExecute(BattleScript_TerrainEnds); + if (terrainFlag & STATUS_FIELD_GRASSY_TERRAIN) + BattleScriptExecute(BattleScript_GrassyTerrainEnds); + else + BattleScriptExecute(BattleScript_TerrainEnds); return TRUE; } } diff --git a/test/battle/terrain/grassy.c b/test/battle/terrain/grassy.c index 678352dc5e..862c9052fd 100644 --- a/test/battle/terrain/grassy.c +++ b/test/battle/terrain/grassy.c @@ -85,3 +85,27 @@ SINGLE_BATTLE_TEST("Grassy Terrain lasts for 5 turns") MESSAGE("The grass disappeared from the battlefield."); } } + +SINGLE_BATTLE_TEST("Grassy Terrain heals the pokemon on the field for the duration of the terrain, including last turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); }; + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_GRASSY_TERRAIN); } + TURN {} + TURN {} + TURN {} + TURN {} + } SCENE { + MESSAGE("Foe Wobbuffet used Celebrate!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, player); + MESSAGE("Grass grew to cover the battlefield!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("The grass disappeared from the battlefield."); + } +} From 13b8daf36b130120a53bfd97a103283644da3073 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 3 Jul 2024 22:53:41 +0200 Subject: [PATCH 26/26] Supersweet Syrup wrong ability desc (#4902) --- src/data/abilities.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/abilities.h b/src/data/abilities.h index 8b81057b4f..8fc7d699e1 100644 --- a/src/data/abilities.h +++ b/src/data/abilities.h @@ -2548,7 +2548,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = #else .name = _("SuprswtSyrup"), #endif - .description = COMPOUND_STRING("Lowers the foe's Speed."), + .description = COMPOUND_STRING("Lowers the foe's Evasion."), .aiRating = 5, },